项目场景:
最近遇到一个问题,64位程序向32位程序发送一个WM_COPYDATA消息传递指针字符串, 发现传递的数据和收到的数据不一致。同样代码,64位程序编译成32位再发送消息的话,能正常收到数据。
问题描述
接收方和发送方的主体代码如下
发送方(64位程序)
// 消息构造体
typedef struct tagCOPYDATASTRUCT {
ULONG_PTR dwData; //用户定义数据
DWORD cbData; //用户定义数据的长度
__field_bcount(cbData) PVOID lpData; //指向用户定义数据的指针
} COPYDATASTRUCT, *PCOPYDATASTRUCT;
// 消息发送函数实现
void CSendDlg::OnDataSend()
{
CWnd *pWnd = CWnd::FindWindow(NULL,"接收窗口的标题");
TCHAR msg[255] = "HELLO";
COPYDATASTRUCT cpd;
cpd.dwData = 0;
cpd.cbData = 255 + 1;//多加一个长度,防止乱码
cpd.lpData = @msg;
pWnd->SendMessage(WM_COPYDATA,NULL,(LPARAM)&cpd);
}
接收方(32位程序)
// 声明
afx_msg BOOLOnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct);
// 添加消息映射
ON_WM_COPYDATA()
// 函数实现
BOOL CReceiveDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//return CDialogEx::OnCopyData(pWnd, pCopyDataStruct);
TCHAR msgReceive[256];
memset(msgReceive, 0, sizeof(msgReceive));
wcscpy_s(msgReceive, (TCHAR*)pCopyDataStruct->lpData);
return CDialogEx::OnCopyData(pWnd, pCopyDataStruct);
}
原因分析:
这个问题是因为64位程序和32位程序在内存地址表示上的差异导致的。在64位程序中指针的大小是8字节,而在32位程序中指针的大小是4字节。当64位程序试图将一个指针发送给32位程序时,它实际上只发送了指针的低4字节,而高4字节则被截断,即指针截断。
假设我们有一个64位指针 0x12345678abcdefgh,这个指针在传递给32位程序时,会被截断为32位指针 0x12345678。这意味着,原本在64位程序中通过该指针可以访问到的内存地址,在32位程序中只能访问到 0x12345678 对应的内存地址。因此,如果32位程序试图通过这个截断后的指针来访问原本64位程序可以访问到的内存地址,就可能会发生访问越界的错误。
在16进制表示法中,一个4字节的指针地址通常表示为8个十六进制数字的序列(每个字节是2个十六进制数字)
解决方案:
可以考虑改变设计,避免直接在消息中传递指针,而是选择传递其他类型的数据。