大多数程序都有接收拖放文件的功能,即是用鼠标把文件拖放到程序窗口上方,符合格式的文件就会自动被程序打开。最近自己对编写的程序增加了一个拖放文件的功能,在 Windows XP、Windows Server 2003 系统上拖放文件功能正常,而在 Windows 7 系统上拖放文件功能不管用,毫无反应。经过一番探讨,顺利解决,故对相关知识的吸收与实践整合于此。

举例实证:(点击下载)

  OK,使用 Visual Studio 新建一个简单的对话框程序,将【对话框】-【属性】-【行为】-【Accept Files】置为【True】后,再使用菜单中【项目】-【类向导】添加对于拖放文件消息【WM_DROPFILES】的消息映射处理方法,代码如下所示:

?
[WM_DROPFILES]消息处理方法代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void CExampleDlg::OnDropFiles(HDROP hDropInfo)
{
    UINT nCount;
    TCHAR szPath[MAX_PATH];
    nCount = DragQueryFile(hDropInfo, 0xFFFFFFFF, NULL, 0);
    if (nCount)
    {
        for (UINT nIndex = 0; nIndex < nCount; ++nIndex)
        {
            DragQueryFile(hDropInfo, nIndex, szPath, _countof(szPath));
            MessageBox(szPath, _T("WM_DROPFILES"));
        }
    }
    DragFinish(hDropInfo);
    CDialogEx::OnDropFiles(hDropInfo);
}

  生成可执行程序,并直接运行程序后,并拖放文件于对话框上方,完全没问题,程序接收到了【WM_DROPFILES】消息,如下图所示:

  然而,找到生成的可执行程序,【右键】-【以管理员身份运行】后,你再拖放文件于上图所示对话框上,则不会弹出上图所示【WM_DROPFILES】消息框,即测试对话框窗口没有接收到【WM_DROPFILES】消息,上方的消息处理方法代码就不会调用了,如下图所示:

  因此,问题的关键在于你有没有【以管理员身份运行】程序;如果【以管理员身份运行】程序,则程序窗口接收不到【WM_DROPFILES】消息;反之,直接运行程序,则可以接收之。这是为何呢?这与 Windows 7 的安全权限机制体系有关,本文下半部分再讲,先介绍:【解决Win7系统下以管理员身份运行的程序接收不到拖放文件消息[WM_DROPFILES]问题的方法】。

  如本例简单的对话框程序,在 CExampleDlg::OnInitDialog() 方法中添加如下代码:

?
添加如下高亮处代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
BOOL CExampleDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    ChangeWindowMessageFilter(WM_DROPFILES, MSGFLT_ADD);
    ChangeWindowMessageFilter(0x0049, MSGFLT_ADD);       // 0x0049 == WM_COPYGLOBALDATA
    //  ChangeWindowMessageFilterEx(m_hWnd, WM_DROPFILES, MSGFLT_ALLOW, NULL);
    //  ChangeWindowMessageFilterEx(m_hWnd, 0x0049, MSGFLT_ALLOW, NULL);       // 0x0049 == WM_COPYGLOBALDATA
    // ::DragAcceptFiles(m_hWnd, TRUE); // 对话框程序可在其【属性】-【行为】-【Accept Files】置为【True】,而不用调用此行。反之则可,两者可选其一嘛~~~
    // ..........
    return TRUE;
}

  另外提下,如果你自己创建窗口而不是创建对话框,则无法像对话框一样可以在【属性】-【行为】-【Accept Files】置为【True】以表示你的程序是否对消息【WM_DROPFILES】有兴趣处理,当然能不能接收得到,是另一外码事,但这是前提。因此可在你的窗口初始化函数(如CFuckWnd::OnCreate)或其它适合的位置调用API接口:

?
Registers whether a window accepts dropped files.
1
VOID DragAcceptFiles(HWND hWnd, BOOL fAccept);

  ChangeWindowMessageFilter 与 ChangeWindowMessageFilterEx 这两个 API 为 Win7 系统新增的API接口,而 WinXP 系统没有这两个接口。查看MSDN可知,ChangeWindowMessageFilter 作用:从 UIPI【用户界面特权隔离】消息过滤器中增加或移除一个消息。ChangeWindowMessageFilterEx 作用:为特定窗口修改 UIPI【用户界面特权隔离】消息过滤器。关于【用户界面特权隔离】,本文下部分再讲。

  简而言之,上述代码即是让系统对该测试程序不再过滤掉【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 系统下就肯定获取不到其入口地址,但没关系,不会影响程序运行,亦不会报错。代码如下:

?
在示例 CExampleDlg 类中添加如下方法代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
BOOL CExampleDlg::ChangeWndMessageFilterOk(UINT nMessage, BOOL bAllow)
{
    typedef BOOL (WINAPI * ChangeWindowMessageFilterOkFn)(UINT, DWORD);
    HMODULE hModUser32 = NULL;
    hModUser32 = LoadLibrary(_T("user32.dll"));
    if (hModUser32 == NULL) {
        return FALSE;
    }
    ChangeWindowMessageFilterOkFn pfnChangeWindowMessageFilter = (ChangeWindowMessageFilterOkFn) GetProcAddress(hModUser32, "ChangeWindowMessageFilter");
    if (pfnChangeWindowMessageFilter == NULL)
    {
        FreeLibrary(hModUser32);
        return FALSE;
    }
    FreeLibrary(hModUser32);
    return pfnChangeWindowMessageFilter(nMessage, bAllow ? MSGFLT_ADD : MSGFLT_REMOVE);
}

?
更改为如下高亮处代码行:
1
2
3
4
5
6
7
8
9
10
11
BOOL CExampleDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    ChangeWndMessageFilterOk(WM_DROPFILES, MSGFLT_ADD);
    ChangeWndMessageFilterOk(0x0049, MSGFLT_ADD);       // 0x0049 == WM_COPYGLOBALDATA
    // ..........
    return TRUE;
}

  接下来生成可执行程序,在任何系统环境下,均能运行正常,且能接收到【WM_DROPFILES】消息了。

Windows 7: Message Integrity Check(消息完整性检查)

  消息完整性检查(Message Integrity Check),是 Windows 7 增加的 Windows 安全对象访问控制安全机制,系统利用完整性级别对一个安全对象进行标记,通过降低进程的完整性级别可以限制其对安全对象的写入权限,这一点类似于用户帐户组的成员被限制访问系统组件这种方式。完整性检查机制使得用更少的权限或以更低的完整性级别运行一些程序,会降低进程修改系统或损害用户数据文件的可能性。在 Windows 7 中消息完整性检查分为 6 个等级,如表 1 所示:

表1 MIC等级
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 下两个进程通过窗口消息通信的测试结果:

表2 通过窗口消息完成进程通信的测试结果
发送方进程权限 接收方进程权限 进程通信结果
成功
成功
成功

失败
失败
成功

成功
中/高+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等级,以免影响用户体验。

解决Win7系统下以管理员身份运行的程序接收不到拖放文件消息[WM_DROPFILES]问题的方法相关推荐

  1. 解决系统下以管理员身份运行的程序接收不到拖放文件消息[WM_DROPFILES]问题的方法

    大多数程序都有接收拖放文件的功能,即是用鼠标把文件拖放到程序窗口上方,符合格式的文件就会自动被程序打开.最近自己对编写的程序增加了一个拖放文件的功能,在 Windows XP.Windows Serv ...

  2. win10pin不可用进不去系统_解决win7系统下连接网络打印机不可用的处理方法

    解决win7系统下连接网络打印机不可用的处理方法 作者:电脑帮帮手 win7系统连接网络打印机,相对Xp系统更为智能化,它会自动帮你搜索可用打印机驱动默认给你安装,但是有些网络打印机驱动,自身系统未必 ...

  3. win7如何设置以管理员身份运行

    win7如何设置以管理员身份运行 对于win7新用户,可能很多朋友在运行软件或者安装的时候,会经常遇到以管理员身份运行的窗口,对于用户来说,真的会有些烦恼,这里为大家提供一些办法,希望对大家有所帮助. ...

  4. 不解禁administrator账号的情况下以管理员身份运行bat文件

    在Bat文件内容前插入以下一段代码,可在不解禁administrator账号的情况下以管理员身份运行bat文件中的命令: @echo off :: BatchGotAdmin  :---------- ...

  5. Runas命令:能让域用户/普通User用户以管理员身份运行指定程序

    在某些情况下,为了安全起见,大部分公司都会使用域控制器或只会给员工电脑user的用户权限,这样做能大大提高安全性和可控性,但由此也带来了一些困扰. 比如:某些特定的部门(如财务,物流)没有管理员权限, ...

  6. Runas命令:能让域用户/普通User用户以管理员身份运行指定程序。

    注:本文由Colin撰写,版权所有!转载请注明原文地址,谢谢合作! 在某些情况下,为了安全起见,大部分公司都会使用域控制器或只会给员工电脑user的用户权限,这样做能大大提高安全性和可控性,但由此也带 ...

  7. win7系统下以管理员运行cmd窗口

    百度折腾了很久,最简单最粗暴的就是去    ----c盘   -----windows ---system32----右键cmd.exe  以管理员身份运行

  8. 如何自动以管理员身份运行.NET程序?

    windows 7和vista提高的系统的安全性,同时需要明确指定"以管理员身份运行"才可赋予被运行软件比较高级的权限,比如访问注册表等.否则,当以普通身份运行的程序需要访问较高级 ...

  9. Win10系统 默认以管理员权限运行所有程序

    WIN10中发现如ahk和目标程序不都是管理员权限,或不全是非管理员权限运行会有各种问题,遂找解决办法.以下文章来自网络,亲侧好用. 非原创,用的是网址 win10如何默认所有软件以管理员身份运行-系 ...

最新文章

  1. JS创建多个下载任务
  2. android wifi视频监控软件,WiFi环境下Android智能视频监控系统研究与实现
  3. windows编程之GDI基础--设备内容(二)
  4. tcp发送方的发送速度由接收方给出的接收窗口决定_TCP协议的详解
  5. 拓端tecdat|R语言高级图像处理
  6. C语言 数组和指针
  7. mybatis-plus批量insert效率低下怎么办(mysql)
  8. 【数据分析认知课(一):数据分析思维观】——读后感
  9. 信息流短视频时长多目标优化
  10. 支付宝报错 未设置签名参数
  11. latex插图编号_LaTex技巧[26]:Latex重新为图片编号
  12. Android使用后端云Bmob实现登录、注册及失物招领
  13. mmkv原理,Android多进程从头讲到尾,成功定级腾讯T3-2
  14. 经验分享|裸金属服务器部署
  15. css3 图片阴影、翘边效果
  16. API--天气查询(高德api)
  17. Android组件间数据传递
  18. 产能提升咨询项目阶段评审结果
  19. tar分卷压缩/解压大文件
  20. 从苏宁电器到卡巴斯基第17篇:我在苏宁电器当营业员 VIII

热门文章

  1. uniapp小程序生成名片海报
  2. LSTM模型结构讲解
  3. 何谓商道?---浅读《商道》有感
  4. 笔记本电脑如何通过HDMI连接屏幕进行投影
  5. lr监控虚拟服务器,lr如何监控服务器
  6. C语言结构体变量和结构体数组-学习笔记(十六)
  7. Python和C++通用语音识别模型
  8. Gosu.ai获190万美元,用于自动化游戏培训建议
  9. Docker下安装zookeeper(单机 集群)
  10. vue:前端导出word 加图片:前端