4

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

这在原则上运作良好,但在目标进程有很大不同的情况下会失败:

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

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

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

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

其他人是否必须解决类似的问题?有更简单的解决方案吗?

4

3 回答 3

1

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

例如,TVM_GETITEM即使消息的调用者和接收者之间存在 32 位 <-> 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 位远程进程也可以访问它)。

于 2011-01-05T10:32:13.960 回答
0

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

于 2010-12-20T08:55:51.063 回答
0

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

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

于 2010-12-20T09:20:49.230 回答