zoukankan      html  css  js  c++  java
  • 解决C#拖放文件无效的问题

    我们可以向ArcMap窗口中直接拖放mxd工程文件、shp矢量文件 、tif栅格文件等,十分方便;使用控件的DragEnter和DragDrop即可实现拖放操作,但在Win7和Win10系统中,如果程序以管理员运行,则实现的拖动操作无效。

    原因分析

    Windows消息是一种进程间通信机制,为了防止较低等级的进程窗口向较等级进程窗口发送消息,Windows引用了用户界面特权隔离(简称UIPI)机制。正是由于这种机制,导致了如果以管理员运行应用程序,拖放操作就会被系统过滤而无效。

    解决方案

    采用 ChangeWindowMessageFilterEx 函数,为指定窗口修改用户界面特权隔离 (UIPI) 消息过滤器。

    先使用Windows API实现如下类:

    using System;
    using System.ComponentModel;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Windows.Forms;
    
    namespace Frame.Utility.Handler
    {
        public sealed class FileDropHandler : IMessageFilter, IDisposable
        {
    
            #region API函数
    
            /// <summary>
            /// 指定窗口修改用户界面特权隔离 (UIPI) 消息过滤器
            /// </summary>
            /// <param name="hWnd">窗口句柄</param>
            /// <param name="message">允许或阻止的消息</param>
            /// <param name="action">要执行的操作</param>
            /// <param name="pChangeFilterStruct"></param>
            /// <returns></returns>
            [DllImport("user32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool ChangeWindowMessageFilterEx(IntPtr hWnd, uint message, ChangeFilterAction action, in ChangeFilterStruct pChangeFilterStruct);
    
            /// <summary>
            /// 窗口是否接受从shell拖放过来的文件
            /// </summary>
            /// <param name="hWnd">窗口句柄</param>
            /// <param name="fAccept">true接收拖拽,false拒绝拖拽</param>
            [DllImport("shell32.dll", SetLastError = false, CallingConvention = CallingConvention.Winapi)]
            private static extern void DragAcceptFiles(IntPtr hWnd, bool fAccept);
    
            /// <summary>
            /// 成功拖放操作后获取被拖放文件的名称等信息
            /// </summary>
            /// <param name="hWnd">句柄</param>
            /// <param name="iFile">文件索引编号</param>
            /// <param name="lpszFile">存放函数返回的文件路径</param>
            /// <param name="cch">缓冲区的字符数</param>
            /// <returns></returns>
            [DllImport("shell32.dll", SetLastError = false, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
            private static extern uint DragQueryFile(IntPtr hWnd, uint iFile, StringBuilder lpszFile, int cch);
    
            /// <summary>
            /// 释放shell为传递文件名而开辟的内存空间
            /// </summary>
            /// <param name="hDrop">窗口句柄</param>
            [DllImport("shell32.dll", SetLastError = false, CallingConvention = CallingConvention.Winapi)]
            private static extern void DragFinish(IntPtr hDrop);
    
            [StructLayout(LayoutKind.Sequential)]
            private struct ChangeFilterStruct
            {
                public uint CbSize;
                private readonly ChangeFilterStatu ExtStatus;
            }
    
            private enum ChangeFilterAction : uint
            {
                /// <summary>
                /// 允许消息通过过滤器(包括来自低特权的进程)
                /// </summary>
                MSGFLT_ALLOW,
    
                /// <summary>
                /// 如果消息来自低特权的进程,阻止它转递给窗口
                /// </summary>
                MSGFLT_DISALLOW,
    
                /// <summary>
                /// 为窗口重置消息过滤器为默认
                /// </summary>
                MSGFLT_RESET
    
            }
    
            private enum ChangeFilterStatu : uint
            {
                MSGFLTINFO_NONE,
                MSGFLTINFO_ALREADYALLOWED_FORWND,
                MSGFLTINFO_ALREADYDISALLOWED_FORWND,
                MSGFLTINFO_ALLOWED_HIGHER
            }
    
            private const uint WM_COPYGLOBALDATA = 0x0049;
            private const uint WM_COPYDATA = 0x004A;
            private const uint WM_DROPFILES = 0x0233;
    
            #endregion
    
    
            private const uint GetIndexCount = 0xFFFFFFFFU;
    
            private Control _containerControl;
    
            private readonly bool _disposeControl;
    
            public FileDropHandler(Control containerControl) : this(containerControl, false) { }
    
            public FileDropHandler(Control containerControl, bool releaseControl)
            {
                _containerControl = containerControl ?? throw new ArgumentNullException("control", "control is null.");
    
                if (containerControl.IsDisposed) throw new ObjectDisposedException("control");
    
                _disposeControl = releaseControl;
    
                var status = new ChangeFilterStruct() { CbSize = 8 };
    
                if (!ChangeWindowMessageFilterEx(containerControl.Handle, WM_DROPFILES, ChangeFilterAction.MSGFLT_ALLOW, in status)) throw new Win32Exception(Marshal.GetLastWin32Error());
                if (!ChangeWindowMessageFilterEx(containerControl.Handle, WM_COPYGLOBALDATA, ChangeFilterAction.MSGFLT_ALLOW, in status)) throw new Win32Exception(Marshal.GetLastWin32Error());
                if (!ChangeWindowMessageFilterEx(containerControl.Handle, WM_COPYDATA, ChangeFilterAction.MSGFLT_ALLOW, in status)) throw new Win32Exception(Marshal.GetLastWin32Error());
                DragAcceptFiles(containerControl.Handle, true);
    
                Application.AddMessageFilter(this);
            }
    
            public bool PreFilterMessage(ref Message m)
            {
                if (_containerControl == null || _containerControl.IsDisposed) return false;
                if (_containerControl.AllowDrop) return _containerControl.AllowDrop = false;
                if (m.Msg == WM_DROPFILES)
                {
                    var handle = m.WParam;
    
                    var fileCount = DragQueryFile(handle, GetIndexCount, null, 0);
    
                    var fileNames = new string[fileCount];
    
                    var sb = new StringBuilder(262);
                    var charLength = sb.Capacity;
                    for (uint i = 0; i < fileCount; i++)
                    {
                        if (DragQueryFile(handle, i, sb, charLength) > 0) fileNames[i] = sb.ToString();
                    }
                    DragFinish(handle);
                    _containerControl.AllowDrop = true;
                    _containerControl.DoDragDrop(fileNames, DragDropEffects.All);
                    _containerControl.AllowDrop = false;
                    return true;
                }
                return false;
            }
    
            public void Dispose()
            {
                if (_containerControl == null)
                {
                    if (_containerControl != null && _disposeControl && !_containerControl.IsDisposed) _containerControl.Dispose();
                    Application.RemoveMessageFilter(this);
                    _containerControl = null;
                }
            }
        }
    }

    在窗体中新建全局变量:

    public FileDropHandler FileDropHandler;

    在窗体的Load函数中初始化需要拖放的控件:

    FileDropHandler = new FileDropHandler(gridData);

    作者:我也是个傻瓜
    出处:http://www.cnblogs.com/liweis/
    签名:成熟是一种明亮而不刺眼的光辉。

  • 相关阅读:
    solr 重要的知识点
    Sole跟Tomcat的整合 Solr4.4 + Tomcat
    常用SQL总结
    T4生成实体和简单的CRUD操作
    磁盘分区
    salt一键部署zookeeper
    salt一键部署gcc-c++
    salt一键部署python
    linux目录文件权限的区别。
    salt一键部署jdk
  • 原文地址:https://www.cnblogs.com/liweis/p/15580336.html
Copyright © 2011-2022 走看看