1

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

ilkerpyci
  • 99
  • 1
  • 9
  • 5
    [WM_COPYDATA](https://msdn.microsoft.com/en-us/library/windows/desktop/ms649011.aspx) can marshal a single chunk of memory across process boundaries. If you need to communicate structured data, you'll need to serialize it, and deserialize it in the receiving process. I'm guessing that `OTHERSTRUCT` contains pointers, either directly or indirectly. Pointers are only valid in the process where they originate from. – IInspectable Jan 30 '16 at 23:48
  • OTHERSTRUCT has 5 wchar_t arrays. Nothing more. But when i try to copy them, it gives error unlike the first example. – ilkerpyci Jan 30 '16 at 23:52
  • You have to show the declaration of `OTHERSTRUCT` – Barmak Shemirani Jan 31 '16 at 00:09
  • Show the declaration of `OTHERSTRUCT` and the code that fills in `MYSTRUCT2` on the sending side. Are those wchar_t arrays nul-terminated? – dxiv Jan 31 '16 at 00:10
  • Unless I'm blind, there's nothing wrong with the part of the code just posted (including the structure definition). Perhaps there's a problem with the code that is sending the message? (Best guess: you're calculating the size incorrectly.) – Harry Johnston Jan 31 '16 at 00:17
  • I have edited and added OTHERSRUCT. Also I tried to explain how it's filled. – ilkerpyci Jan 31 '16 at 00:24
  • I think we need to see the code that calls PostMessage / SendMessage. – Harry Johnston Jan 31 '16 at 00:25
  • And I'm calculating the size with, sizeof(OTHERSTRUCT) * length_of_array – ilkerpyci Jan 31 '16 at 00:25
  • You can't send a vector, or any other non-POD class object, via WM_COPYDATA. For one thing, the layout in memory isn't defined by the standard, and may contain process-specific content such as a vtable pointer. For another, a vector is implemented using pointers, which are only valid in the original process. An array is fine, that's POD. A vector isn't. – Harry Johnston Jan 31 '16 at 00:51
  • Possible duplicate of [What are POD types in C++?](http://stackoverflow.com/questions/146452/what-are-pod-types-in-c) – Harry Johnston Jan 31 '16 at 00:54
  • I tried to convert it to array but it crashed again. That's why I tought it won't make any difference if it's an array or a vector. Maybe I made another unrelated mistake when coding that, not sure now. I will try that again. – ilkerpyci Jan 31 '16 at 00:57

2 Answers2

2

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.

rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • Thanks for sample code. Now it works. One more question: What is the best approach to copy incoming data into an already existing vector? I created a temporary vector with a constructor as in your code, then used assignment operator: `gList = recvData;` How can we do it without creating temporary vector? – ilkerpyci Jan 31 '16 at 22:12
  • @ilkerpyci: If you use C++11 or later, then `std::vector` is moveable, so `gList = std::vector(begin, end)` just works (or `gList = std::move(recvData)` if you prefer a named variable to a temporary). If you are stuck with pre C++11 or want to avoid moves, you can write `gList.clear(); gList.append(gList.end(), begin, end);`. – rodrigo Jan 31 '16 at 23:28
2

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;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770