WPF 實現文件、圖標拖放功能(支持UAC的那種)
WPF實現文件拖放功能,正常情況并沒有什么問題,但是如果你的程序使用管理員身份啟動,你就會發現文件拖放功能就會失效。
這是因為WPF 在不同UAC等級下,是不允許拖放的。
原理很簡單,與桌面相關聯的進程為 explorer.exe,即 explorer.exe 這個進程啟動的方式是非管理員身份,當你的程序使用管理員身份啟動時,就會導致拖放失敗。
因為二者的權限不一樣,系統不允許不同權限的進程進行通訊,包括進程通訊等操作。
解決方案:
方案一(不推薦):讓 explorer.exe 也使用管理員身份啟動。舉例Win7系統只需這樣設置下并重啟系統即可,如下圖:
這種方案能解決問題,但是你不可能讓用戶去做這種操作,所以只能算一個解決方案,并不能解決實際的問題。
方案二:讓你的程序使用非管理員啟動,程序中需要管理員身份的操作,一般為涉及到注冊表操作或驅動操作,可以考慮將這部分操作放到一個服務里單獨操作,可以理解為程序分成服務與應用程序兩塊,需要管理員身份操作的
功能部分放到服務里實現,界面相關的操作在應用程序里實現。
這種方案也能解決,并且問題解決的比較徹底,但是項目工程量比較大的情況下,工作量就比較大了,為一個文件拖放的功能,增加了較大的工作量,得不償失。
方案三:提供一個折中的辦法,WPF經過我較長的上網搜索及研究,沒有找到合適的辦法解決這個問題,但是 WinForm 通過消息Hook卻能實現,所以這個折中的辦法就是WPF+WinForm來解決這個問題。
下面我們將主要講解如何使用 WPF+WinForm 解決WPF程序使用管理員身份啟動后不能拖放文件的問題。
第一部分:使用 WinForm 解決使用不能拖動的問題,關鍵代碼如下
ElevatedDragDropManager.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; using System.Windows.Forms; using System.Windows; public class ElevatedDragDropManager : IMessageFilter { #region "P/Invoke" [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool ChangeWindowMessageFilterEx(IntPtr hWnd, uint msg, ChangeWindowMessageFilterExAction action, ref CHANGEFILTERSTRUCT changeInfo); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool ChangeWindowMessageFilter(uint msg, ChangeWindowMessageFilterFlags flags); [DllImport("shell32.dll")] private static extern void DragAcceptFiles(IntPtr hwnd, bool fAccept); [DllImport("shell32.dll")] private static extern uint DragQueryFile(IntPtr hDrop, uint iFile, [Out()] StringBuilder lpszFile, uint cch); [DllImport("shell32.dll")] private static extern bool DragQueryPoint(IntPtr hDrop, ref POINT lppt); [DllImport("shell32.dll")] private static extern void DragFinish(IntPtr hDrop); [StructLayout(LayoutKind.Sequential)] private struct POINT { public int X; public int Y; public POINT(int newX, int newY) { X = newX; Y = newY; } public static implicit operator System.Drawing.Point(POINT p) { return new System.Drawing.Point(p.X, p.Y); } public static implicit operator POINT(System.Drawing.Point p) { return new POINT(p.X, p.Y); } } private enum MessageFilterInfo : uint { None, AlreadyAllowed, AlreadyDisAllowed, AllowedHigher } private enum ChangeWindowMessageFilterExAction : uint { Reset, Allow, Disallow } private enum ChangeWindowMessageFilterFlags : uint { Add = 1, Remove = 2 } [StructLayout(LayoutKind.Sequential)] private struct CHANGEFILTERSTRUCT { public uint cbSize; public MessageFilterInfo ExtStatus; } #endregion public static ElevatedDragDropManager Instance = new ElevatedDragDropManager(); public event EventHandler<ElevatedDragDropArgs> ElevatedDragDrop; private const uint WM_DROPFILES = 0x233; private const uint WM_COPYDATA = 0x4a; private const uint WM_COPYGLOBALDATA = 0x49; private readonly bool IsVistaOrHigher = Environment.OSVersion.Version.Major >= 6; private readonly bool Is7OrHigher = (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor >= 1) || Environment.OSVersion.Version.Major > 6; public void EnableDragDrop(IntPtr hWnd) { if (Is7OrHigher) { CHANGEFILTERSTRUCT changeStruct = new CHANGEFILTERSTRUCT(); changeStruct.cbSize = Convert.ToUInt32(Marshal.SizeOf(typeof(CHANGEFILTERSTRUCT))); ChangeWindowMessageFilterEx(hWnd, WM_DROPFILES, ChangeWindowMessageFilterExAction.Allow, ref changeStruct); ChangeWindowMessageFilterEx(hWnd, WM_COPYDATA, ChangeWindowMessageFilterExAction.Allow, ref changeStruct); ChangeWindowMessageFilterEx(hWnd, WM_COPYGLOBALDATA, ChangeWindowMessageFilterExAction.Allow, ref changeStruct); } else if (IsVistaOrHigher) { ChangeWindowMessageFilter(WM_DROPFILES, ChangeWindowMessageFilterFlags.Add); ChangeWindowMessageFilter(WM_COPYDATA, ChangeWindowMessageFilterFlags.Add); ChangeWindowMessageFilter(WM_COPYGLOBALDATA, ChangeWindowMessageFilterFlags.Add); } DragAcceptFiles(hWnd, true); } public bool PreFilterMessage(ref Message m) { if (m.Msg == WM_DROPFILES) { HandleDragDropMessage(m); return true; } return false; } private void HandleDragDropMessage(Message m) { dynamic sb = new StringBuilder(260); uint numFiles = DragQueryFile(m.WParam, 0xffffffffu, sb, 0); dynamic list = new List<string>(); for (uint i = 0; i <= numFiles - 1; i++) { if (DragQueryFile(m.WParam, i, sb, Convert.ToUInt32(sb.Capacity) * 2) > 0) { list.Add(sb.ToString()); } } POINT p = default(POINT); DragQueryPoint(m.WParam, ref p); DragFinish(m.WParam); dynamic args = new ElevatedDragDropArgs(); args.HWnd = m.HWnd; args.Files = list; args.X = p.X; args.Y = p.Y; if (ElevatedDragDrop != null) { ElevatedDragDrop(this, args); } } } public class ElevatedDragDropArgs : EventArgs { public IntPtr HWnd { get { return m_HWnd; } set { m_HWnd = value; } } private IntPtr m_HWnd; public List<string> Files { get { return m_Files; } set { m_Files = value; } } private List<string> m_Files; public int X { get { return m_X; } set { m_X = value; } } private int m_X; public int Y { get { return m_Y; } set { m_Y = value; } } private int m_Y; public ElevatedDragDropArgs() { Files = new List<string>(); } }
Form1.cs
注:需要將Form1窗口的AllowDrop屬性設置為false,否則無法拖動文件。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace FileDragDrop { public partial class FileDragDrop : Form { public FileDragDrop() { InitializeComponent(); //this.AllowDrop設置為false this.AllowDrop = false; ElevatedDragDropManager filter = new ElevatedDragDropManager(); //開啟拖放功能 filter.EnableDragDrop(this.Handle); //添加消息過濾器 Application.AddMessageFilter(filter); //設置拖放結束回調 filter.ElevatedDragDrop += this.ElevatedDragDrop; } //拖放結束事件 private void ElevatedDragDrop(System.Object sender, ElevatedDragDropArgs e) { try { if (e.HWnd == this.Handle) { foreach (string file in e.Files) { //拖動文件 MessageBox.Show("ElevatedDragDrop File=" + (file) + "!"); } } } catch (Exception ex) { //異常信息 MessageBox.Show("ElevatedDragDrop error=" + (ex.TargetSite?.Name) + "!"); } } } }
最終的效果:
?
?




浙公網安備 33010602011771號