从非托管C++代码访问 WPF
Accessing WPF From Unmanaged C++ Code
Background
我目前正在尝试开发一些测试软件,这些软件需要通知桌面上的窗口创建和激活事件,响应这些事件并遍历小部件树,以便读取这些窗口上的所有可用文本。这需要在 32 位和 64 位上工作,理想情况下从 XP 向上(叹息),但肯定是 7 以上。
到目前为止我做了什么
我已经为WH_CBT
消息注册了一个SetWindowsHookEx()
风格的系统钩子,我专门处理HCBT_ACTIVATE
和HCBT_CREATEWND
事件。钩子在 DLL 中完成,并且有一个钩子程序控制它。该程序可用于 32 位和 64 位(因为HCBT
消息似乎不会路由回不同架构(32/64 位)的挂钩过程,不像键盘消息)。该软件全部用非托管C++代码编写,即不依赖于 CLI,并且该程序是一个控制台应用程序。
当我收到上述两个事件中的任何一个时,我会使用给定的HWND
句柄调用EnumChildWindows()
,并在每个窗口上调用GetWindowText()
。结果当前根据 PID 写出到各个日志文件中。
这一切都按预期工作,但仅限于此......
问题所在
如果我跑起来说MSVS,我可以看到顶级的东西,甚至是"你确定要这样做吗"简单警告对话框的文本内容,但对于选项对话框,我得到的只是确定和取消窗口小部件和一些无标题窗口。
阅读此内容,我相信原因是我看到了从老式Win32 API小部件到WPF控件小部件的过渡。
假设/要求
- 这是由于 Win32 到 WPF 的转换。
- 作为从 DLL 注入到每个进程的
SetWindowsEx()
样式钩子,它必须是非托管代码(即,您不能愉快地在某个随机进程中启动 CLI 并使用托管代码)。 - WPF 库示例都使用托管代码。
- 据观察,某些应用程序不使用可访问性 API,因此我忽略 了钩子。我需要尽可能减少
- 依赖关系,因为所测试的系统必须尽可能具有代表性(我知道,我到处注入代码,但这无济于事)。如果我能提供帮助,我不想安装额外的大型运行时等。
- 在测试期间,UAC 将处于默认级别。
问题
- 有没有办法从 DLL 中的非托管C++代码遍历 WPF 小部件层次结构?大概一定有某种我可以调用的本机/非托管 API?
- 如果做不到这一点,是否可以将
HWND
句柄转发到另一个进程,例如通过WM_COPYDATA
和隐藏窗口,该进程实际上是一个托管进程,然后可以代表注入的 DLL 遍历小部件树,访问 Win32 小部件,在需要时切换到 WPF?我的研究似乎说这是不可能的...... - 进程是否有权在打开 UAC 的情况下执行 (2)操作?
应该说我是一名Unix/Linux GUI/systems/内核软件工程师,因此,如果上述内容看起来相当基本,请提前道歉,但我只是找不到这个特定用例的答案。这也是我的第一个MS-Windows程序!
提前非常感谢,
John。
如果您只需要收到窗口创建和激活事件的通知 - 最佳使用
SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_DESTROY, 0, WinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT);
并在WinEventProc中查找idObject == OBJID_WINDOW && idChild == CHILDID_SELF这绝对不依赖于 WPF,甚至从低完整性进程中工作。另外 - 你不需要在这里调用EnumChildWindows,因为通知适用于所有窗口。不需要单独的 DLL 32/64 位,不需要注入,例如
VOID CALLBACK WinEventProc( HWINEVENTHOOK /*hWinEventHook*/, DWORD event,
HWND hwnd, LONG idObject, LONG idChild, DWORD /*dwEventThread*/,
DWORD /*dwmsEventTime*/)
{
if (event == EVENT_OBJECT_CREATE)
{
if (idObject == OBJID_WINDOW && idChild == CHILDID_SELF)//
{
WCHAR sz[256], cn[256];
if (!GetWindowText(hwnd, sz, RTL_NUMBER_OF(sz)))
{
*sz = 0;
}
if (!GetClassNameW(hwnd, cn, RTL_NUMBER_OF(cn)))
{
cn[0]='?', cn[1]=0;
}
DbgPrint("%p <%S>::<%S>n", hwnd, cn, sz);
}
}
}
对于 MSVS 下一个输出中的选项对话框:
0000000000040900 <#32770>::<>
00000000000408FC <tooltips_class32>::<>
00000000000408FE <SysTreeView32>::<>
00000000000408FA <Button>::<Show &all settings>
00000000000408F8 <Button>::<OK>
00000000000408F6 <Button>::<Cancel>
00000000000408F4 <Button>::<&Reset>
00000000000508DC <Button>::<Apply>
00000000000508DA <SysTabControl32>::<>
000000000003090A <#32770>::<>
000000000003090C <Button>::<&Reuse current document window, if saved>
000000000003090E <Button>::<&Detect when file is changed outside the environment>
0000000000030910 <Button>::<Auto-&load changes, if saved>
0000000000030912 <Button>::<Allo&w editing of read-only files; warn when attempt to save>
0000000000030914 <Button>::<&Open file using directory of currently active document>
0000000000030916 <Button>::<Chec&k for consistent line endings on load>
00000000001F08AA <Button>::<Displa&y warning when global undo will modify edited files>
0000000000260336 <Button>::<&Show Miscellaneous files in Solution Explorer>
00000000001D08A6 <Static>::<items saved in the &Miscellaneous files project>
00000000001208A4 <Edit>::<>
00000000001408AE <Button>::<Save documents as &Unicode when data cannot be saved in codepage>
- 为什么示例代码访问IUnknown中已删除的内存
- 如何使用 gcc 内联汇编器代码访问成员变量
- 在链接的程序集文件中,我想从 c++ 调用代码访问变量.是否可以在不触发访问冲突的情况下执行此操作?
- 使用MySQL C 连接器从C 代码访问MEMSQL
- 无法从C 代码访问C共享库方法
- 是否可以将匿名 lambda 函数绑定到对象以允许 lambda 中的代码访问对象的成员?
- 从本地代码访问apk中的压缩文件(从zip中读取zip)
- Unity 3D:从 C/C++ 代码访问 Unity 3D API
- Xcode c++ 无法通过代码访问文件
- 使用终止进程终止"mstsc.exe"进程时出现错误代码(5)访问被拒绝
- QT 5.4,无法从代码访问资源
- 为什么此代码访问向量超出其范围
- 使本机代码访问java方法和数据成员
- Dll 通过 excel 从 c++ 代码访问
- 通过静态变量从本机代码访问有状态托管对象
- 从标准C++代码访问iPhone的沙盒文档文件夹
- 从代码访问iOS设置
- <canvas> 从 NaCl 代码访问 HTML5
- 从 QT 中的C++代码访问 QML 元素
- 从非托管C++代码访问 WPF