带有数组与向量的WM_COPYDATA

时间:2016-01-30 23:37:03

标签: c++ arrays winapi vector ipc

我试图通过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;

我有一个被驱逐的全局向量&#39; gOtherStructList&#39;。在程序的各个部分中,填充了此全局变量。例如,

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

    ...
}

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

这是我发送给接收方应用的方式:

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数组但不能使用向量?

修改 根据内容丰富的评论编辑我的问题。

由于

2 个答案:

答案 0 :(得分:2)

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

您获得了std::vector::data()所需的指针:

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

注意:VC ++在某种程度上缺乏C ++支持,因此VS2010或之前不提供此data()。如果需要,您可以将其替换为:

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没用,而且有点误导。

答案 1 :(得分: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;
}