
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
/// <summary>
/// 给控件增加拖放功能的类
/// </summary>
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
    /// <summary>
    /// 拖放实例
    /// </summary>
    public static ElevatedDragDropManager Instance = new ElevatedDragDropManager();
    /// <summary>
    /// 高级拖放事件
    /// </summary>
    public event EventHandler<ElevatedDragDropArgs> ElevatedDragDrop;

    private const uint WM_DROPFILES = 0x233;
    private const uint WM_COPYDATA = 0x4a;

    private const uint WM_COPYGLOBALDATA = 0x49;
    /// <summary>
    /// 
    /// </summary>
    public readonly static bool IsVistaOrHigher = Environment.OSVersion.Version.Major >= 6;
    /// <summary>
    /// 
    /// </summary>

    public readonly static bool Is7OrHigher = (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor >= 1) || Environment.OSVersion.Version.Major > 6;
    /// <summary>
    /// 
    /// </summary>
    protected ElevatedDragDropManager()
    {
        Application.AddMessageFilter(this);
    }
    /// <summary>
    /// 使管理员方式运行时支持拖放
    /// </summary>
    /// <param name="hWnd"></param>
    public static void AdminDragEnable(IntPtr hWnd)
    {
        if (Is7OrHigher)
        {
            CHANGEFILTERSTRUCT changeStruct = new CHANGEFILTERSTRUCT
            {
                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);
        }
    }
    /// <summary>
    /// 使支持拖放属性
    /// </summary>
    /// <param name="hWnd"></param>
    public void EnableDragDrop(IntPtr hWnd)
    {
        if (Is7OrHigher)
        {
            CHANGEFILTERSTRUCT changeStruct = new CHANGEFILTERSTRUCT
            {
                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);
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="m"></param>
    /// <returns></returns>
    public bool PreFilterMessage(ref Message m)
    {
        if (m.Msg == WM_DROPFILES)
        {
            HandleDragDropMessage(m);
            return true;
        }

        return false;
    }

    private void HandleDragDropMessage(Message m)
    {
        var sb = new StringBuilder(260);
        uint numFiles = DragQueryFile(m.WParam, 0xffffffffu, sb, 0);
        var 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;
        DragQueryPoint(m.WParam, ref p);
        DragFinish(m.WParam);

        var args = new ElevatedDragDropArgs
        {
            HWnd = m.HWnd,
            Files = list,
            X = p.X,
            Y = p.Y
        };

        ElevatedDragDrop?.Invoke(this, args);
    }
}

/// <summary>
/// 
/// </summary>
public class ElevatedDragDropArgs : EventArgs
{
    /// <summary>
    /// 
    /// </summary>
    public IntPtr HWnd { get; set; }

    /// <summary>
    /// 
    /// </summary>
    public List<string> Files { get; set; }
    /// <summary>
    /// 
    /// </summary>
    public int X { get; set; }
    /// <summary>
    /// 
    /// </summary>
    public int Y { get; set; }
    /// <summary>
    /// 
    /// </summary>
    public ElevatedDragDropArgs()
    {
        Files = new List<string>();
    }
}