简体   繁体   中英

WM_COPYDATA with array vs vector

I'm trying to achieve inter-process communication via WM_COPYDATA messages. lpData member of COPYDATASTRUCT can not contain pointers. My problem is, what is the difference between char arrays and other arrays or vectors.

When I use char array like here, it sends messages succesfully.

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

But when I use a vector, receiving application can not get it.

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;

I have an externed global vector 'gOtherStructList'. In the various parts of the program, this global variable is filled. For example,

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

    ...
}

Then, when I'm sending this global list, I'm copying it into MYSTRUCT2 variable (for unrelated reasons) with wcscpy() for every wchar_t of every element.

This is how I send to receiver app:

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);

If it makes difference, receiving application uses these messages like:

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);
}

I understand why we can't use pointers for WM_COPYDATA. What I don't understand is, what is the difference of these examples. Why we can use char arrays but not vectors?

Edit: Edited my question, based on informative comments.

Thanks

std::vector is internally implemented using pointers, so you cannot send that, but you can send its data, as it is guaranteed to be continuous in memory, and your internal struct is a POD.

You get the necessary pointer with std::vector::data() :

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

NOTE: VC++ somewhat lacks in C++ support, so this data() is not available in VS2010 or before. You can replace it, if needed with:

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

Just be sure that the vector is not empty.

And in the receiving side:

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);

Personally I find the MYSTRUCT2 useless and a bit misleading.

MYSTRUCT and OTHERSTRUCT contain all of their data inside of themselves, there are no pointers to outside memory, so they can be transmitted as-is as single memory blocks via WM_COPYDATA .

MYSTRUCT2 , on the other hand, contains a std::vector of OTHERSTRUCT elements. Only the vector itself resides inside of MYSTRUCT2 itself. The vector internally contains a pointer (amongst other things) to a OTHERSTRUCT array located elsewhere in memory. Because of this, MYSTRUCT2 cannot be transmitted as-is as a single memory block via WM_COPYDATA , it needs to be serialized into a flat data block for sending, and then deserialized when received, eg:

#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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM