WM_COPYDATA 与数组与向量

Posted

技术标签:

【中文标题】WM_COPYDATA 与数组与向量【英文标题】:WM_COPYDATA with array vs vector 【发布时间】:2016-01-30 23:37:03 【问题描述】:

我正在尝试通过 WM_COPYDATA 消息实现进程间通信。 COPYDATASTRUCT 的 lpData 成员不能包含指针。我的问题是,char数组和其他数组或向量有什么区别。

当我在这里使用 char 数组时,它会成功发送消息。

typedef struct tagMYSTRUCT 
wchar_t x[40] = 0;
 MYSTRUCT, *PMYSTRUCT;

但是当我使用向量时,接收应用程序无法获取它。

typedef struct tagOTHERSTRUCT 
    wchar_t one[40] =  0 ;
    wchar_t two[20] =  0 ;
    wchar_t three[20] =  0 ;
    wchar_t four[4] =  0 ;
    wchar_t five[3] =  0 ;
 OTHERSTRUCT, *POTHERSTRUCT;

typedef struct tagMYSTRUCT2 
    std::vector<OTHERSTRUCT> y;
 MYSTRUCT2, *PMYSTRUCT2;

我有一个外部全局向量“gOtherStructList”。在程序的各个部分,这个全局变量被填充。例如,

DWORD WINAPI foo(LPCTSTR lpchText) 
    ...
    if (sizeof(lpchText[0]) == 2) 
        wcscpy(gOtherStructList[0].one, lpchText);
    

    ...

然后,当我发送这个全局列表时,我将它复制到 MYSTRUCT2 变量中(出于不相关的原因),并为每个元素的每个 wchar_t 使用 wcscpy()。

这是我发送到接收者应用程序的方式:

MYSTRUCT2 my_struct;    //my_struct.y is a vector

// Filled my_struct.y here with wcscpy()

COPYDATASTRUCT cds;
cds.dwData = MY_CASE;
cds.cbData = sizeof(OTHERSTRUCT) *  my_struct.y.size();
cds.lpData = &my_struct;

SendMessage(gDataReceiver, WM_COPYDATA, NULL, (LPARAM)&cds);

如果有区别,接收应用程序会使用以下消息:

case WM_COPYDATA:

    PCOPYDATASTRUCT pcopydata = (PCOPYDATASTRUCT)lParam;

    switch (pcopydata->dwData) 

    case MY_CASE:

        // When I code this, it works
        PMYSTRUCT p = (PMYSTRUCT)(pcopydata->lpData);
        wcscpy(mylocalvar, p->x); // for char array

        ...
        // But, this doesn't work.

        std::cout << "New message received" << std::endl;    // this gets printed, then program crashes.

        PMYSTRUCT2 p = (PMYSTRUCT2)(pcopydata->lpData);
        OTHERSTRUCT mylocallist[100],
        wcscpy(mylocallist[0].one, p->y[0].one);

我明白为什么我们不能对 WM_COPYDATA 使用指针。我不明白的是,这些例子有什么区别。为什么我们可以使用 char 数组而不能使用向量?

编辑: 根据信息丰富的 cmets 编辑了我的问题。

谢谢

【问题讨论】:

WM_COPYDATA 可以跨进程边界编组单个内存块。如果您需要传递结构化数据,则需要对其进行序列化,并在接收过程中对其进行反序列化。我猜OTHERSTRUCT 直接或间接包含指针。指针仅在它们源自的进程中有效。 OTHERSTRUCT 有 5 个 wchar_t 数组。而已。但是当我尝试复制它们时,它会给出与第一个示例不同的错误。 你必须出示OTHERSTRUCT的声明 在发送端显示OTHERSTRUCT的声明和填写MYSTRUCT2的代码。那些 wchar_t 数组是 nul 终止的吗? 除非我是盲人,否则刚才贴出的部分代码(包括结构定义)没有任何问题。也许发送消息的代码有问题? (最佳猜测:您计算的尺寸不正确。) 【参考方案1】:

std::vector 在内部使用指针实现,因此您不能发送它,但您可以发送它的数据,因为它保证在内存中是连续的,并且您的内部结构是一个 POD。

您可以使用std::vector::data() 获得必要的指针:

cds.cbData = sizeof(OTHERSTRUCT) *  my_struct.y.size();
cds.lpData = my_struct.y.data();

注意:VC++ 有点缺乏对 C++ 的支持,所以这个 data() 在 VS2010 或更早版本中不可用。如果需要,您可以将其替换为:

cds.lpData = &my_struct.y[0];

只要确保向量不为空即可。

在接收方:

OTHERSTRUCT *begin = static_cast<OTHERSTRUCT*>(pcopydata->lpData);
OTHERSTRUCT *end = begin + pcopydata->cbData / sizeof(OTHERSTRUCT);
//copy the data into a vector, or treat them directly
std::vector<OTHERSTRUCT> recvData(begin, end);

我个人觉得MYSTRUCT2 没用而且有点误导。

【讨论】:

感谢您的示例代码。现在它起作用了。还有一个问题:将传入数据复制到现有向量中的最佳方法是什么?我在您的代码中使用构造函数创建了一个临时向量,然后使用了赋值运算符:gList = recvData; 我们如何在不创建临时向量的情况下做到这一点? @ilkerpyci:如果您使用 C++11 或更高版本,则 std::vector 是可移动的,因此 gList = std::vector(begin, end) 可以正常工作(或者 gList = std::move(recvData),如果您更喜欢命名变量而不是临时变量)。如果你卡在 C++11 之前或者想避免移动,你可以写gList.clear(); gList.append(gList.end(), begin, end);【参考方案2】:

MYSTRUCTOTHERSTRUCT 将所有数据都包含在自己内部,没有指向外部内存的指针,因此可以通过WM_COPYDATA 将它们作为单个内存块按原样传输。

另一方面,MYSTRUCT2 包含 std::vectorOTHERSTRUCT 元素。只有vector 本身位于MYSTRUCT2 自身内部。 vector 内部包含一个指针(除其他外),指向位于内存中其他位置的 OTHERSTRUCT 数组。正因为如此,MYSTRUCT2不能通过WM_COPYDATA作为单个内存块原样传输,需要序列化成平面数据块发送,然后在接收时反序列化,例如:

#pragma pack(push, 1)
typedef struct tagMYCDSDATA 
    int numItems;
    OTHERSTRUCT items[1];
 MYCDSDATA, *PMYCDSDATA;
#pragma pack(pop)

MYSTRUCT2 my_struct;
// Fill my_struct.y as needed...

int count = my_struct.y.size();    
std::vector<BYTE> buffer(sizeof(int) + (sizeof(OTHERSTRUCT) * count));
PMYCDSDATA cdsdata = reinterpret_cast<PMYCDSDATA>(&buffer[0]);
//or, if using C++11: PMYCDSDATA cdsdata = reinterpret_cast<PMYCDSDATA>(buffer.data());

data->numItems = count;
std::copy(my_struct.y.begin(), my_struct.y.end(), data->items);

COPYDATASTRUCT cds;
cds.dwData = MY_CASE;
cds.cbData = buffer.size();
cds.lpData = cdsdata;

SendMessage(gDataReceiver, WM_COPYDATA, NULL, reinterpret_cast<LPARAM>(&cds));

case WM_COPYDATA:

    PCOPYDATASTRUCT pcopydata = reinterpret_cast<PCOPYDATASTRUCT>(lParam);
    if (pcopydata->dwData == MY_CASE)
    
        std::cout << "New message received" << std::endl;

        PMYCDSDATA p = static_cast<PMYCDSDATA>(pcopydata->lpData);
        for(int i = 0; i < p->numItems; ++i)
        
            // use p->item[i].one, etc as needed...
        

        return 0;
    

    break;

【讨论】:

以上是关于WM_COPYDATA 与数组与向量的主要内容,如果未能解决你的问题,请参考以下文章

从 WM_COPYDATA 消息编组结构

python中的一维数组行向量与列向量

动态数组与 STL 向量的确切区别?

九. 常用类库向量与哈希5.向量及其应用

Rust语言教程 - 数组与向量

使用向量类实现堆栈的链表与动态数组