首页 > 解决方案 > 在 64 位和 32 位 (WOW64) 应用程序之间使用 WM_COPYDATA 是否安全?

问题描述

这里有一个关于消息的一般使用的很好的SO Q/A 会话,以及关于这是否可以在不同 32/64 位的应用程序之间工作的“讨论。然而,后者似乎专注于可能滥用正在传递的“数据指针”。所以,我在这里提出一个新问题。WM_COPYDATA

我正在努力让两个 Windows 应用程序相互通信/同步,作为第一轮方法,我正在使用 Windows Messaging 来实现这一点。现在一切似乎都很好……但我正在使用WM_COPYDATA消息在应用程序之间传递信息。

我的问题:当两个应用程序具有不同的 (32/64) 位数时,这种方法是否保证安全?我已经使用下面的代码对“客户端”和“服务器”之间的 32 与 64 构建的所有四种可能组合进行了一些测试,并且都按预期工作;但这仅仅是因为我得到了“幸运”的结果(来自可能的未定义行为),还是 WOW64 系统(特别是当服务器是 64 位而客户端是 32 时)是否负责所有必要的编组?

如果有人可以确认它可以保证工作,我将非常感谢确认这一点的“官方”链接/参考。

共享头文件:

static const WPARAM nmIdFilePath = 0x00001100;

struct nmLockInfoType {
    char filePathID[1024];
    // More elements will be added later!
};
static const nmLockInfoType nmLockInfoDefault = {
    "<<<Uninitialised Image Data Path>>>",
    //...
};
extern nmLockInfoType nmLockInfo; // MUST be provided by each app!
///nmLockInfoType nmLockInfo = nmLockInfoDefault; // Use this code to instatiate it (once, somewhere)!

服务器程序代码(在RegisterWindowMessage(L"HANDSHAKE");消息处理程序内):

//...
COPYDATASTRUCT cds;
cds.dwData = nmIdFilePath;           // Pre-defined ID
cds.cbData = sizeof(nmLockInfoType);
cds.lpData = &nmLockInfo;            // Pre-defined structure (see above)
//...
// Send a copy of the "Welcome Pack" data structure to the client app ...
::SendMessage(clientLock, WM_COPYDATA, WPARAM(m_hWnd), LPARAM(&cds)); // "clientLock is the HWND of the client app's MainWnd

客户端程序代码:

BOOL MyFrame::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
    switch (pCopyDataStruct->dwData)
    {
    case nmIdFilePath:
        memcpy(&nmLockInfo, pCopyDataStruct->lpData, pCopyDataStruct->cbData);
        return nmsSucceeded; // This is NON_ZERO so evaluates to TRUE
    // Other cases here ...
    } 
    return CMDIFrameWnd::OnCopyData(pWnd, pCopyDataStruct);
}

我特别关心客户端是 32 位但服务器是 64 位的情况;在这种情况下,它将向 32 位应用程序(尽管是 WOW64 应用程序)发送 64 位数据地址。内置的“编组”是否在 WOW64 情况下处理此问题?

标签: winapiwow64wm-copydata

解决方案


只有当我们遵循如何使用它的规则时它才是安全的。请参考以下 WM_COPYDATA 消息的备注:

传递的数据不得包含指针其他对接收数据的应用程序无法访问的对象的引用。

在发送此消息时,引用的数据不能被 发送进程的另一个线程更改

接收应用程序应将数据视为只读。lParam 参数仅在消息处理期间有效。接收应用程序不应释放 lParam 引用的内存。如果接收应用程序必须在 SendMessage 返回后访问数据,它必须将数据复制到本地缓冲区中。

例如,如果我们尝试传递数据类型:ULONG_PTR,那么从 64 位应用程序传递到 32 位应用程序时,数据副本可能无法正常工作。因为它在 32 位应用程序上是 32 位,在 64 位应用程序上是 64 位。

您可以通过修改下面的代码来测试它:

struct nmLockInfoType {
char filePathID[1024];
ULONG_PTR point64_32;
// More elements will be added later!
};

上面提到的场景,作为你测试的结果应该是安全的。如果您仍然担心,请随时告诉我。

此外,以下是有关开发 64 位应用程序的有用文档供您参考:

常见的 Visual C++ 64 位迁移问题


推荐阅读