通过 SendMessage 执行 IPC 时如何处理 32 位/64 位不匹配问题?

发布于 2024-10-08 14:09:42 字数 1728 浏览 1 评论 0 原文

我有一段 C++ 代码,它读出树项目的文本(包含在普通的 常用控件树视图)使用 TVM_GETITEM 窗口消息。接收消息的树视图位于不同的进程中,因此我使用了一点共享内存来存储窗口消息的参数之一所指向的结构。我必须做这项工作,因为远程进程不在我的控制之下(我正在编写一个类似于 Spy++ 的应用程序)。

原则上这工作得很好,但如果目标进程有很大不同,就会失败:

  1. 如果目标进程的代码是用定义的 UNICODE 构建的,但我自己的代码不是,则两个进程对结构会有不同的想法TVITEM 结构 中的字符串成员。我已经使用 IsWindowUnicode 调用解决了这个问题,然后显式发送 TVM_GETITEMATVM_GETITEMW(如有必要,重新编码结果)。

  2. 如果调用进程是在 32 位模式下构建的,而目标进程是 64 位(或反之亦然),则 TVITEM 结构 结构不同,因为指针具有不同的大小。

我目前正在寻找解决第二个问题的好方法。这个特定的用例(获取树项文本)只是一个示例,我的代码发送的其他窗口消息也存在同样的问题。现在,我正在考虑两种方法:

  1. 构建代码两次,然后根据目标进程的功能执行 32 位或 64 位代码。这需要对我们的构建和打包系统进行一些更改,并且需要将特定于体系结构的代码分解到专用进程中(现在它位于 DLL 中)。一旦完成,它应该可以很好地工作。
  2. 在运行时检测目标进程的图像格式,然后使用自定义结构而不是 TVITEM 结构 明确使用 32 位或 64 位宽指针的结构。这需要编写代码来检测远程进程的体系结构(我希望我可以通过调用 GetModuleFileName 在远程进程上,然后使用 图像帮助库)并硬编码两个结构(一个具有 32 位指针,一个具有 64 位指针)。此外,我必须确保共享内存地址位于 32 位地址空间中(以便我自己的代码始终可以访问它,即使它是在 32 位模式下编译的)。

还有其他人需要解决类似的问题吗?有更简单的解决方案吗?

I have a piece of C++ code which reads out the text of a tree item (as contained in a plain Common Controls Tree View) using the TVM_GETITEM window message. The tree view which receives the mesage is in a different process, so I'm using a little bit of shared memory for the structure which is pointed to by one of the arguments to the window message. I have to do this work since the remote process is not under my control (I'm writing an application similiar to Spy++).

This works well in principle, but fails in case the target process is substantially different:

  1. If the code of the target process was built with UNICODE defined but my own code wasn't, the two processes will have different ideas about the structure of the string members in the TVITEM structure. I solved this already using an IsWindowUnicode call and then explicitely sending either TVM_GETITEMA or TVM_GETITEMW (recoding the result if necessary).

  2. If the calling process was built in 32bit mode and the target process is 64bit (or the other way round), the layout (and size) of the TVITEM structure structure is different since pointers have a different size.

I'm currently trying to find a good way to solve the second issue. This particular use case (getting the tree item text) is just an example, the same issue exists for other window messages which my code is sending. Right now, I'm considering two approaches:

  1. Build my code twice and then execute either the 32bit or the 64bit code depending on what the target process does. This requires some changes to our build- and packaging system, and it requires factoring the code which is architecture specific out into a dedicated process (right now it's in a DLL). Once that is done, it should work nicely.
  2. Detect the image format of the target process at runtime and then use custom structs instead of the TVITEM structure structure which explicitely use 32bit or 64bit wide pointers. This requires writing code to detect the architecture of a remote process (I hope I can do this by calling GetModuleFileName on the remote process and then analyzing the PE header using the Image Help Library) and hardcoding two structs (one with 32bit pointers, one with 64bit). Furthermore, I have to make sure that the shared memory address is in the 32bit address space (so that my own code can always access it, even if it's compiled in 32bit mode).

Did anybody else have to solve a similiar problem? Are there easier solutions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

薔薇婲 2024-10-15 14:09:42

我最终在运行时检查远程进程是 32 位还是 64 位,然后在发送消息之前将正确的结构写入共享内存。

例如,即使存在 32 位 <-> 消息,您也可以使用 TVM_GETITEM 消息,如下所示。消息的调用者和接收者之间的 64 位混合:

/* This template is basically a copy of the TVITEM struct except that
 * all fields which return a pointer have a variable type. This allows
 * creating different types for different pointer sizes.
 */
template <typename AddrType>
struct TVITEM_3264 {
  UINT      mask;
  AddrType  hItem;
  UINT      state;
  UINT      stateMask;
  AddrType  pszText;
  int       cchTextMax;
  int       iImage;
  int       iSelectedImage;
  int       cChildren;
  AddrType  lParam;
};
typedef TVITEM_3264<UINT32> TVITEM32;
typedef TVITEM_3264<UINT64> TVITEM64;

// .... later, I can then use the above template like this:
LPARAM _itemInfo;
DWORD pid;
::GetWindowThreadProcessId( treeViewWindow, &pid );
if ( is64BitProcess( pid ) ) {
    TVITEM64 itemInfo;
    ZeroMemory( &itemInfo, sizeof( itemInfo ) );

    itemInfo.mask = TVIF_HANDLE | TVIF_TEXT;
    itemInfo.hItem = (UINT64)m_item;
    itemInfo.pszText = (UINT64)(LPTSTR)sharedMem->getSharedMemory( sizeof(itemInfo) );
    itemInfo.cchTextMax = MaxTextLength;
    _itemInfo = (LPARAM)sharedMem->write( &itemInfo, sizeof(itemInfo) );
} else {
    TVITEM32 itemInfo;
    ZeroMemory( &itemInfo, sizeof( itemInfo ) );

    itemInfo.mask = TVIF_HANDLE | TVIF_TEXT;
    itemInfo.hItem = (UINT32)m_item;
    itemInfo.pszText = (UINT32)(LPTSTR)sharedMem->getSharedMemory( sizeof(itemInfo) );
    itemInfo.cchTextMax = MaxTextLength;
    _itemInfo = (LPARAM)sharedMem->write( &itemInfo, sizeof(itemInfo) );
}

sharedMem->getSharedMemory 函数是一个小辅助函数,用于获取指向共享内存区域的指针;可选函数参数指定偏移值。重要的是共享内存区域应该始终位于 32 位地址空间中(这样即使是 32 位远程进程也可以访问它)。

I ended up checking whether the remote process is 32bit or 64bit at runtime, and then writing the right structure to the shared memory before sending a message.

For instance, here's how you can use the TVM_GETITEM message even if there's a 32bit <-> 64bit mixup between the caller and the receiver of the message:

/* This template is basically a copy of the TVITEM struct except that
 * all fields which return a pointer have a variable type. This allows
 * creating different types for different pointer sizes.
 */
template <typename AddrType>
struct TVITEM_3264 {
  UINT      mask;
  AddrType  hItem;
  UINT      state;
  UINT      stateMask;
  AddrType  pszText;
  int       cchTextMax;
  int       iImage;
  int       iSelectedImage;
  int       cChildren;
  AddrType  lParam;
};
typedef TVITEM_3264<UINT32> TVITEM32;
typedef TVITEM_3264<UINT64> TVITEM64;

// .... later, I can then use the above template like this:
LPARAM _itemInfo;
DWORD pid;
::GetWindowThreadProcessId( treeViewWindow, &pid );
if ( is64BitProcess( pid ) ) {
    TVITEM64 itemInfo;
    ZeroMemory( &itemInfo, sizeof( itemInfo ) );

    itemInfo.mask = TVIF_HANDLE | TVIF_TEXT;
    itemInfo.hItem = (UINT64)m_item;
    itemInfo.pszText = (UINT64)(LPTSTR)sharedMem->getSharedMemory( sizeof(itemInfo) );
    itemInfo.cchTextMax = MaxTextLength;
    _itemInfo = (LPARAM)sharedMem->write( &itemInfo, sizeof(itemInfo) );
} else {
    TVITEM32 itemInfo;
    ZeroMemory( &itemInfo, sizeof( itemInfo ) );

    itemInfo.mask = TVIF_HANDLE | TVIF_TEXT;
    itemInfo.hItem = (UINT32)m_item;
    itemInfo.pszText = (UINT32)(LPTSTR)sharedMem->getSharedMemory( sizeof(itemInfo) );
    itemInfo.cchTextMax = MaxTextLength;
    _itemInfo = (LPARAM)sharedMem->write( &itemInfo, sizeof(itemInfo) );
}

The sharedMem->getSharedMemory function is a little helper function to get a pointer to the shared memory region; the optional function argument specifies an offset value. What's important is that the shared memory region should always bee in the 32bit address space (so that even a 32bit remote process can access it).

不寐倦长更 2024-10-15 14:09:42

恕我直言,存在设计问题。我不知道你为什么这样做,也许你没有完全控制所有部分。但从基本的 MVC 角度来看,您是从视图中查看值,而不是向模型询问值。

IMHO there is a design problem. I don't know why your are doing this way, maybe you don't have total control of all parts. But in a basic MVC perspective, you are peeking values from a view instead of asking it to the model.

っ左 2024-10-15 14:09:42

我不熟悉这个特定的消息,但是如果 Windows TVM_GETITEM 消息应该跨进程正确运行,那么 Windows 应该在调用者的地址空间中填充 TVITEM 结构并为您处理任何需要的转换,而不需要您提供共享内存。如果不是,那么我怀疑您在这里看到的问题是否可以轻松解决,而无需进行一些不舒服的扭曲。

共享内存位让我感到困惑;通常你必须让两个进程明确地知道共享内存段,并且你没有提到 DLL 注入或类似的东西。被调用者到底如何知道其地址空间中的共享内存部分,以及如何使用它?您确定该 API 需要它吗?

I'm not familiar with this particular message, but if the Windows TVM_GETITEM message is supposed to function correctly across processes, then Windows should fill in the TVITEM struct in the caller's address space and handle any needed conversions for you, without needing you to supply shared memory. If it isn't, then I doubt the problem you're seeing here is easily solvable without some uncomfortable contortions.

The shared memory bit confuses me; generally you have to make both processes explicitly aware of the shared memory segment, and you didn't mention DLL injection or anything like that. How exactly is the callee made aware of the shared memory section in its address space, and how are you using it? Are you sure it's needed for this API?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文