解决 OnDropFiles 可能无响应的问题【转】
大多数程序都有接收拖放文件的功能,即是用鼠标把文件拖放到程序窗口上方,符合格式的文件就会自动被程序打开。最近自己对编写的程序增加了一个拖放文件的功能,在 Windows XP、Windows Server 2003 系统上拖放文件功能正常,而在 Windows 7 系统上拖放文件功能不管用,毫无反应。经过一番探讨,顺利解决,故对相关知识的吸收与实践整合于此。
举例实证:
OK,使用 Visual Studio 新建一个简单的对话框程序,将【对话框】-【属性】-【行为】-【Accept
Files】置为【True】后,再使用菜单中【项目】-【类向导】添加对于拖放文件消息【WM_DROPFILES】的消息映射处理方法,代码如下所示:
1 void CExampleDlg::OnDropFiles(HDROP hDropInfo)
2 {
3 UINT nCount;
4 TCHAR szPath[MAX_PATH];
5
6 nCount = DragQueryFile(hDropInfo, 0xFFFFFFFF, NULL, 0);
7 if (nCount)
8 {
9 for (UINT nIndex = 0; nIndex < nCount; ++nIndex)
10 {
11 DragQueryFile(hDropInfo, nIndex, szPath, _countof(szPath));
12 MessageBox(szPath, _T("WM_DROPFILES"));
13 }
14 }
15
16 DragFinish(hDropInfo);
17
18 CDialogEx::OnDropFiles(hDropInfo);
19 }
生成可执行程序,并直接运行程序后,并拖放文件于对话框上方,完全没问题,程序接收到了【WM_DROPFILES】消息,如下图所示:
然而,找到生成的可执行程序,【右键】-【以管理员身份运行】后,你再拖放文件于上图所示对话框上,则不会弹出上图所示【WM_DROPFILES】消息框,即测试对话框窗口没有接收到【WM_DROPFILES】消息,上方的消息处理方法代码就不会调用了,如下图所示:
因此,问题的关键在于你有没有【以管理员身份运行】程序;如果【以管理员身份运行】程序,则程序窗口接收不到【WM_DROPFILES】消息;反之,直接运行程序,则可以接收之。这是为何呢?这与 Windows 7 的安全权限机制体系有关,本文下半部分再讲,先介绍:【解决Win7系统下以管理员身份运行的程序接收不到拖放文件消息[WM_DROPFILES]问题的方法】。
如本例简单的对话框程序,在 CExampleDlg::OnInitDialog() 方法中添加如下代码:
1 BOOL CExampleDlg::OnInitDialog()
2 {
3 CDialogEx::OnInitDialog();
4
5 ChangeWindowMessageFilter(WM_DROPFILES, MSGFLT_ADD);
6 ChangeWindowMessageFilter(0x0049, MSGFLT_ADD); // 0x0049 == WM_COPYGLOBALDATA
7
8 // ChangeWindowMessageFilterEx(m_hWnd, WM_DROPFILES, MSGFLT_ALLOW, NULL);
9 // ChangeWindowMessageFilterEx(m_hWnd, 0x0049, MSGFLT_ALLOW, NULL); // 0x0049 == WM_COPYGLOBALDATA
10
11 // ::DragAcceptFiles(m_hWnd, TRUE); // 对话框程序可在其【属性】-【行为】-【Accept Files】置为【True】,而不用调用此行。反之则可,两者可选其一嘛~~~
12
13 // ..........
14
15 return TRUE;
16 }
另外提下,如果你自己创建窗口而不是创建对话框,则无法像对话框一样可以在【属性】-【行为】-【Accept Files】置为【True】以表示你的程序是否对消息【WM_DROPFILES】有兴趣处理,当然能不能接收得到,是另一外码事,但这是前提。因此可在你的窗口初始化函数(如CFuckWnd::OnCreate)或其它适合的位置调用API接口:
VOID
DragAcceptFiles(
HWND
hWnd,
BOOL
fAccept);
简而言之,上述代码即是让系统对该测试程序不再过滤掉【WM_DROPFILES】消息和【WM_COPYGLOBALDATA】消息(此消息 Windows 未公开),那么该测试程序进程或对话框窗口能接收到【WM_DROPFILES】消息。
再次生成可执行程序,然后【以管理员身份运行】,程序窗口就可以接收到拖放文件消息【WM_DROPFILES】了。
另外,如果以 Release 方式生成可执行程序后,在 WinXP、Win2003 系统下运行,则会出现如下图报错提示:
原因是 WinXP 及 Win2003 系统用户库 USER32.dll 没有 ChangeWindowMessageFilter、ChangeWindowMessageFilterEx 这两个接口,而 Windows 7 系统下,则新增之。由于 WinXP、Win2003 系统安全机制不会拦截消息,因此也就没有必要运行上述新增的代码了。但若想该测试程序在 WinXP、Win2003 系统下运行正常,且要在 Win7 系统下能正常接收上述消息,可直接从 Win7 之 USER32.dll 库中获取上述 API 函数入口地址,在 Win7 系统下肯定是可以获取到入口地址的;而在 XP 系统下就肯定获取不到其入口地址,但没关系,不会影响程序运行,亦不会报错。代码如下:
1 BOOL CExampleDlg::ChangeWndMessageFilterOk(UINT nMessage, BOOL bAllow)
2 {
3 typedef BOOL (WINAPI * ChangeWindowMessageFilterOkFn)(UINT, DWORD);
4
5 HMODULE hModUser32 = NULL;
6 hModUser32 = LoadLibrary(_T("user32.dll"));
7 if (hModUser32 == NULL) {
8 return FALSE;
9 }
10
11 ChangeWindowMessageFilterOkFn pfnChangeWindowMessageFilter = (ChangeWindowMessageFilterOkFn) GetProcAddress(hModUser32, "ChangeWindowMessageFilter");
12 if (pfnChangeWindowMessageFilter == NULL)
13 {
14 FreeLibrary(hModUser32);
15 return FALSE;
16 }
17
18 FreeLibrary(hModUser32);
19
20 return pfnChangeWindowMessageFilter(nMessage, bAllow ? MSGFLT_ADD : MSGFLT_REMOVE);
21 }
更改为如下高亮处代码行:
1 BOOL CExampleDlg::OnInitDialog()
2 {
3 CDialogEx::OnInitDialog();
4
5 ChangeWndMessageFilterOk(WM_DROPFILES, MSGFLT_ADD);
6 ChangeWndMessageFilterOk(0x0049, MSGFLT_ADD); // 0x0049 == WM_COPYGLOBALDATA
7
8 // ..........
9
10 return TRUE;
11 }
接下来生成可执行程序,在任何系统环境下,均能运行正常,且能接收到【WM_DROPFILES】消息了。
Windows 7: Message Integrity Check(消息完整性检查)
消息完整性检查(Message Integrity Check),是 Windows 7 增加的 Windows 安全对象访问控制安全机制,系统利用完整性级别对一个安全对象进行标记,通过降低进程的完整性级别可以限制其对安全对象的写入权限,这一点类似于用户帐户组的成员被限制访问系统组件这种方式。完整性检查机制使得用更少的权限或以更低的完整性级别运行一些程序,会降低进程修改系统或损害用户数据文件的可能性。在 Windows 7 中消息完整性检查分为 6 个等级,如表 1 所示:
MIC等级 | 说明 |
---|---|
SECURITY_MANDATORY_UNTRUSTED_RID | 不信任的MIC等级 |
SECURITY_MANDATORY_LOW_RID | 低MIC等级,如IE |
SECURITY_MANDATORY_MEDIUM_RID | 中MIC等级,默认为这个等级,如Explorer |
SECURITY_MANDATORY_HIGH_RID | 高MIC等级,以管理员身份运行的程序 |
SECURITY_MANDATORY_SYSTEM_RID | 系统MIC等级,一般是服务应用程序 |
SECURITY_MANDATORY_PROTECTED_PROCESS_RID | 被保护进程的MIC等级 |
MIC 等级的获取是和创建该对象的进程完整性级别相关的。比如在 Windows 7 系统中 Explorer.exe 的完整性级别是中 MIC 等级,因为权限继承的关系,其启动的进程具有的 MIC 等级也都默认是中 MIC 等级。一个安全对象的 MIC 等级由其访问控制列表项中的某一项标识,可以使用工具 Process Explorer 查看一个进程的 MIC 等级。
Windows 7 系统正常启动后,Explorer.exe 进程的 MIC 等级默认为:中 MIC 等级,如下图所示:
Windows 7: User Interface Privilege Isolation (用户界面特权隔离)
用户界面特权隔离(User Interface Privilege Isolation),则是 Windows 7 通过 MIC
机制新引入的一种安全特性,用于拦截接收比自身进程 MIC 等级低的进程发来的消息。UIPI
的目的是为了规范不同进程窗口之间的窗口消息处理过程,默认情况下,高权限进程不会接收到低权限进程发送的窗口消息的,但是低权限进程能够接收到高权限进程的窗口消息。UIPI
的本质是系统检查目标窗口和发送方是否具有相同的 MIC 等级或者发送方具有更高的 MIC
等级,如果符合上述条件,则允许消息的传递,否则将消息丢弃。
因此,在 Windows 7 操作系统中运行的用户进程,如果运行时具有不同的完整性等级,即具有不同的 MIC 等级,那么相互间的通信将会无法像 Windows XP 那样正常进行。
Windows 7: 高低权限进程通信的实现
通过以上分析可以看出,Windows 7 为了保证系统的安全性,严格地限制了高低权限进程间的通信。以下就进程是否属于同一个会话给出 Windows 7 高低权限进程通信的方案。
基于 Windows XP 应用程序经常需要互相之间能传递消息,Windows Vista 引入了 Change
WindowMessageFilter(UINT message, UINT
dwFlag)编程接口,用来添加或删除隔离级别的消息。Windows 7 引入一个新的编程接口
ChangeWindowMessageFilterEx(HWND hWnd, UINT message, DWORD
action,PCHANGEFILTERSTRUCT pChangeFilterStruct),事件的参数可以是
MSGFLT_ALLOW、MSGFLT_DISALLOW 和
MSGFLT_RESET,将窗口重设成默认筛选器,可选的架构允许操作结果以接收更多的信息。通过在高权限进程中对指定的某一个低权限进程所创建的窗口进行这样的设置,则低权限进程就可以通过发消息的形式与高权限进程通信了。以下是在
Windows 7 下两个进程通过窗口消息通信的测试结果:
发送方进程权限 | 接收方进程权限 | 进程通信结果 |
---|---|---|
低 | 低 | 成功 |
中 | 中 | 成功 |
高 | 高 | 成功 |
低 | 中 高 |
失败 |
中 | 高 | 失败 |
中 | 低 | 成功 |
高 | 中 低 |
成功 |
低 | 中/高+ChangeWindowMessageFilterEx | 自定义消息成功,系统消息失败 |
中 | 高+ChangeWindowMessageFilterEx | 自定义消息成功,系统消息失败 |
问世间,explorer.exe 进程为何物?
explorer.exe进程是微软为其Windows操作系统定义的的系统核心进程,它是较老的Windows
3.x文件管理器的替代品,在后来的系统中微软将其称为“Windows 资源管理器”。在功能上,explorer.exe进程为用户提供了图形用户界面(也称为图形壳),简单的说就是用来显示系统的桌面环境的,包括开始菜单、桌面下方的任务栏、桌面图标和文件管理。
【文件管理】为explorer.exe进程众多功能之一,平常我们双击桌面上的程序快捷方式或从资源管理器里双击某个文件以打开之,一般explorer.exe进程便会创建相应的程序进程,也就是说explorer.exe进程是你手动打开的程序进程的父进程。
OK,回归上述实例,再加阐释:
到此时,我们已了解到 MIC【消息完整性检查】、UIPI【用户界面特权隔离】、【Windows 7 高低权限进程通信的实现】等观念。且我们已知晓 explorer.exe 进程为何物,在本文实例中,那么我们从【桌面】或从【资源管理器】里用鼠标拖放文件到程序窗口上,其实就是【explorer.exe】和【测试[WM_DROPFILES]消息.exe】两个进程之间的通信,亦即【explorer.exe】向本例【测试[WM_DROPFILES]消息.exe】发送【WM_DROPFILES】消息。在
XP 系统下,两者无任何代沟,能顺利收发消息;而在 Win7
系统下,彼此能够收发消息,则没那么容易了,导致收发消息不畅的“第三者”便是MIC和UIPI。
情况一、explorer.exe进程MIC等级为:中MIC等级,实例程序进程MIC等级为:中MIC等级,两者相同
在Win7系统下,找到本实例生成的可执行程序,直接双击运行:测试[WM_DROPFILES]消息.exe,用【Process
Explorer】查看此进程的MIC等级:中MIC等级,和Win7系统正常启动后的Explorer.exe进程的MIC等级相同,则能成功从Explorer.exe进程管理的桌面或资源管理器中拖放文件到本实例程序窗口上,亦即本实例程序能正常接收到【WM_DROPFILES】消息并能处理之了。
情况二、explorer.exe进程MIC等级为:中MIC等级,实例程序进程MIC等级为:高MIC等级
在Win7系统下,找到本实例生成的可执行程序,右键【以管理员身份运行】,查看MIC等级为:高MIC等级,然后从资源管理器中拖放一个文件到本实例程序窗口,由于本实例程序进程MIC等级高于explorer.exe进程MIC等级,因此explorer.exe程序发送到本实例程序的消息,会被UIPI拦截掉,导致本实例程序无法接收之。
此种情况,要想接收到低MIC等级进程发来的拖放文件消息,则可以通过 ChangeWindowMessageFilter 在 UIPI【用户界面特权隔离】消息过滤器中添加【WM_DROPFILES】【WM_COPYGLOBALDATA】这两个消息,注意其消息过滤器相当于白名单,添加到过滤器,那么发送给对方进程的消息不会被UIPI机制拦截掉,会被放行,从而名正言顺地到达对方进程,而被接收处理之。具体代码在本文上半部分已讲述。
情况三、explorer.exe进程MIC等级为:高MIC等级,实例程序进程MIC等级为:高MIC等级
在Win7系统下,找到本实例生成的可执行程序,右键【以管理员身份运行】,查看MIC等级为:高MIC等级在【Windows
7: Message Integrity
Check(消息完整性检查)】一节中,正常启动的explorer.exe进程MIC等级为中MIC等级,如果把其MIC等级调为高MIC等级,那么从explorer.exe所管理的资源管理器中拖放文件到本实例程序窗口,由于等级同为高MIC等级,则消息不会被UIPI拦截了。那么打开任务管理器,先结束掉explorer.exe进程后,再在【任务管理器】-【文件】-【新建任务(运行…)】(如下图所示),则再从资源管理器中拖放文件到本实例程序窗口,也能正常被接收处理之。
结语:
对于上述情况一,如果你的程序不需要【以管理员身份运行】,则不需要额外操作即可正常接收拖放文件消息。
对于上述情况二、若程序需要【以管理员身份运行】,则另外需要添加额外代码(本文上半部分所述)修改UIPI消息过滤器以实现拖放文件消息的正常接收处理。
对于上述情况三、只为实验调试MIC等级高低来验证MIC等级机制,现实编程中,不好结束系统explorer.exe进程,再以管理员身份运行explorer.exe来调高其MIC等级,以免影响用户体验。
解决 OnDropFiles 可能无响应的问题【转】的更多相关文章
- Delphi XE6 使用定时器或者线程解决程序界面无响应问题
---恢复内容开始--- 介绍 在手机应用上,我们不应该使用速度慢的代码,当然我们在桌面程序上也应该避免这个,当手机应用长时间没有相应的时候,程序会提示“程序没响应,是否关闭”的提示,这个非常不好,所 ...
- 调用startActivityForResult,onActivityResult无响应的解决办法
三种情况: 1.执行startActivityForResult,没等到被调用的 Activity 返回,onActivityResult() 就被执行了.找了很久,终于通过小道消息得知,这与 Act ...
- C#中多线程写DataGridView出现滚动条导致程序卡死(无响应)的解决办法
因为写的程序涉及到多线程维护一个DataGridView,然后蛋疼的发现经常卡死...一开始以为是读写冲突的原因,然后就加了锁,问题依旧...然后发现每次出现滚动条的时候程序才会无响应,所以感觉应该是 ...
- oracle数据库连接无响应的解决
昨天中午时,查询到服务器的数据流水最晚记录是早上8点的,现场查看服务日志很奇怪,日志输出显示挂死在数据库连接这一步.多次调试无果,随后百度发现有资料显示oracle 10.2.1的版本有登录无响应的B ...
- windows资源管理器多标签打开 windows文件夹多标签浏览 浏览器tab页面一样浏览文件夹 clover win8 win10 报错 无响应问题怎么解决 clover卡死 clover怎么换皮肤
大家都知道,我们打开一堆文件夹的时候,是什么样子 “厚厚的一叠”图标堆叠在一起的,非常的不方便 那么,是不是可以像浏览器一样的tab页面展示呢? 答案是可以的 安装好就是这样子的 是不是方便漂亮了很多 ...
- Android ANR(应用无响应)解决分析【转】
本文转载自:https://blog.csdn.net/u014630142/article/details/81709459 来自: http://blog.csdn.net/tjy1985/art ...
- 解决安装fiddler后IE打开网页提示“代理服务器无响应”
环境:win8.1+IE11 安装fiddler4后,启动fiddler,IE11打开百度网站,打开失败:代理服务器无响应,如图: 在网上找了各种方法,修改fiddler的设置,均无法解决这个问题,无 ...
- MySQL删除数据库时无响应解决办法
删除远程主机上MySQL中的一个数据库时,远程主机一直在响应,无法正常删除. 这个问题的解决办法如下:在远程主机上登录MySQL,执行show full processlist;观察state和inf ...
- Win8/Win10下程序经常无响应的解决办法
如果你使用Win8/Win10系统时经常出现程序无响应的问题不仿试下如下解决办法. 表现症状: 任何程序都有可能出现无响应(记事本.Visual Studio.QQ.视频播放器等) 一旦一个程序出现未 ...
随机推荐
- 什么是DDoS引导程序IP Stresser?
1.什么是IP Stresser? IP Stresser是一款用于测试网络或服务器稳健性的工具.管理员可以运行压力测试,从而确定现有资源(带宽.CPU 等)是否足以处理附加负载. 测试个人网络或服务 ...
- xf浅谈_最短路
最短路问题(short-path problem):最短路问题是图论研究中的一个经典算法问题,指在寻找图(由结点和路径组成的)中两结点之间的最短路径.算法具体的形式包括: 1.确定起点的最短路径问题 ...
- C++实现二分法详解
二分法是在一个排好序的序列(数组,链表等)中,不断收缩区间来进行目标值查找的一种算法,下面我们就来探究二分法使用的一些细节,以及常用的场景: 寻找一个数: 寻找左侧边界: 寻找右侧边界. 一.二分法的 ...
- frp+nginx内网穿透
frp+nginx内网穿透 背景:自己有台内网Linux主机,希望被外网访问(ssh.http.https): 准备工作 内网Linux主机-c,可以访问c主机和外网的主机-s(windows/lin ...
- 网络编程+Python
一.网络编程(模块:socket,from socket import *): 1. 网络层的IP地址可以唯一标识网络中的主机,传输层的"协议+端口"则可以唯一标识主机中应用程序( ...
- OSI与TCP/IP各层的结构与功能,都有哪些协议?
学习计算机⽹络时我们⼀般采⽤折中的办法,也就是中和 OSI 和 TCP/IP 的优点,采⽤⼀种只有 五层协议的体系结构,这样既简洁⼜能将概念阐述清楚. 结合互联⽹的情况,⾃上⽽下地,⾮常简要的介绍⼀下 ...
- ECMAScript6.0
ECMAScript6.0相当于JavaScript的标准,它规定了浏览器脚本语言的标准,发布于2015年,目标是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言 N ...
- SpringBoot | 2.1 SpringBoot自动装配原理
@ 目录 前言 1. 引入配置文件与配置绑定 @ImportResource @ConfigurationProperties 1.1 @ConfigurationProperties + @Enab ...
- java+selenium UI自动化001
selenium是一个用于Web应用程序测试的工具,可以用来模拟用户在浏览器上的操作. 支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Googl ...
- MySQL字符串操作函数
使用方法:concat(str1,str2,-) 返回结果为连接参数产生的字符串.如有任何一个参数为NULL ,则返回值为 NULL. mysql> select concat('11',' ...