【问题标题】:How can I cope with 32bit/64bit mismatches when doing IPC via SendMessage?通过 SendMessage 进行 IPC 时如何应对 32 位/64 位不匹配?
【发布时间】:2010-12-20 08:42:52
【问题描述】:

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

这在原则上效果很好,但如果目标进程有很大不同,则会失败:

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

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

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

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

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

【问题讨论】:

    标签: c++ windows winapi sendmessage


    【解决方案1】:

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

    例如,即使消息的调用者和接收者之间存在 32 位 64 位混合,您也可以使用以下方法使用 TVM_GETITEM 消息:

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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 我必须做所有这些工作,因为接收过程确实不在我的控制之下。我正在开发一个类似 Spy++ 的应用程序。抱歉,我应该提前提到这一点 - 我相应地增加了这个问题。
      【解决方案3】:

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

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

      【讨论】:

      • 不幸的是,只有 16 位 Windows 可用的 Window 消息(如 LB_GETTEXT)才能跨进程正常工作。系统在后台进程之间复制数据。对于使用 Win32 引入的消息,这不会再隐含地发生;有关更多信息,请参阅microsoft.com/msj/0997/win320997.aspx 上的文章。
      • 您不必将代码注入远程进程以使其了解共享内存。 VirtualAllocEx (msdn.microsoft.com/en-us/library/aa366890(VS.85).aspx) 允许您在远程进程中分配内存,因此指向该内存的指针可以传递给SendMessage。您可以使用ReadProcessMemoryWriteProcessMemory 访问分配的内存。
      • 不错的链接,谢谢。我应该对 TVM_GETITEM 的 MSFT 文档没有提及任何跨进程问题感到惊讶,但遗憾的是我没有。我认为这是在这个级别工作的人“只知道”的事情。
      猜你喜欢
      • 2012-01-19
      • 2012-06-05
      • 1970-01-01
      • 2012-01-29
      • 1970-01-01
      • 2019-11-26
      • 2012-10-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多