简体   繁体   中英

C++ WINAPI Shared memory dynamic arrays

I'm trying to share an array of structs containing 2 dynamic arrays using shared memory to use the structs in another process. So far I can share an array of structs but these do not contain dynamic arrays yet. Any help on completing this problem would be great.

Expected:

typedef struct {
    int id;
    int type;
    int count;
    int[] values;
    int[] settings;
} Entry;

Current code:

typedef struct {
    int id;
    int type;
    int count;
} Entry;

BOOL DumpEntries(TCHAR* memName) {
     int size = entries.size() * sizeof(Entry) + sizeof(DWORD);

     ::hMapObject = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, memName);
     if (::hMapObject == NULL) {
          return FALSE;
     }

     ::vMapData = MapViewOfFile(::hMapObject, FILE_MAP_ALL_ACCESS, 0, 0, size);
     if (::vMapData == NULL) {
          CloseHandle(::hMapObject);
          return FALSE;
     }

     (*(DWORD*)::vMapData) = entries.size();
     Entry* eArray = (Entry*)(((DWORD*)::vMapData) + 1);
     for(int i = entries.size() - 1; i >= 0; i--) eArray[i] = entries.at(i);

     UnmapViewOfFile(::vMapData);
     return TRUE;
}

BOOL ReadEntries(TCHAR* memName, Entry** entries, DWORD &number_of_entries) {
     HANDLE hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, memName);
     if (hMapFile == NULL) {
          return FALSE;
     }

     DWORD *num_entries = (DWORD*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
     if (num_entries == NULL) {
          CloseHandle(hMapFile);
          return FALSE;
     }
     number_of_entries = *num_entries;

     if(number_of_entries == 0)
     {
         // special case: when no entries was found in buffer
         *entries = NULL;
         return true;
     }

     Entry* tmpEntries = (Entry*)(num_entries + 1);

     *entries = new Entry[*num_entries];

     for (UINT i = 0; i < *num_entries; i++) {
          (*entries)[i] = tmpEntries[i];
     }

     UnmapViewOfFile(num_entries);
     CloseHandle(hMapFile);

     return TRUE;
}

You can't pass pointers across process boundaries, so you can't store pointers inside the struct items that you put in the shared memory. What you can do is allocate the shared memory block itself to be large enough to hold all of the array data, and then use offsets to access the various arrays as needed, eg:

typedef struct
{
    int id;
    int type;
    int count;
    int *values;
    int *settings;
} Entry;

#pragma pack(push, 1)
typedef struct
{
    int id;
    int type;
    int count;
    DWORD values_offset;
    DWORD values_count; // if different than 'int count'
    DWORD settings_offset;
    DWORD settings_count; // if different than 'int count' 
} SharedMemEntry;
#pragma pack(pop)

BOOL DumpEntries(TCHAR* memName)
{
    DWORD NumValues = 0;
    DWORD NumSettings = 0;

    // or whatever your container actually is...
    for(std::vector<Entry>::reverse_iterator iter = entries.rbegin();
        iter != entries.rend(); ++iter)
    {
        Entry &e = *iter;

        // or whatever you have to do to calculate how many
        // integers are in the values[] and settings[] arrays...
        //
        NumValues += e.count;
        NumSettings += e.count;
    }

    DWORD memsize = sizeof(DWORD) +
                    (entries.size() * sizeof(SharedMemEntry)) +
                    (sizeof(int) * NumValues) +
                    (sizeof(int) * NumSettings);

    if (hMapObject != NULL)
        CloseHandle(hMapObject);

    hMapObject = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, memsize, memName);
    if (hMapObject == NULL) {
        return FALSE;
    }

    BYTE *vMapData = (BYTE*) MapViewOfFile(hMapObject, FILE_MAP_WRITE, 0, 0, memsize);
    if (vMapData == NULL) {
        CloseHandle(hMapObject);
        hMapObject = NULL;
        return FALSE;
    }

    DWORD *pEntryCount = (DWORD*) vMapData;
    *pEntryCount = entries.size();

    SharedMemEntry* pEntries = (SharedMemEntry*) (pEntryCount + 1);
    int *pValues = (int*) (pEntries + entries.size());
    int *pSettings = (int*) (pValues + NumValues);

    // or whatever your container actually is...
    for(std::vector<Entry>::reverse_iterator iter = entries.rbegin();
        iter != entries.rend(); ++iter)
    {
        Entry &e = *iter;
        SharedMemEntry &eEntry = *pEntries++;

        eEntry.id = e.id;
        eEntry.type = e.type;
        eEntry.count = e.count;

        eEntry.values_offset = ((BYTE*)pValues - vMapData);
        eEntry.values_count = e.count; // or whatever you need...
        for(DWORD k = 0; k < eEntry.values_count; ++k) {
            *pValues++ = e.values[k];
        }

        eEntry.settings_offset = ((BYTE*)pSettings - vMapData);
        eEntry.settings_count = e.count; // or whatever you need...
        for(DWORD k = 0; k < eEntry.settings_count; ++k) {
            *pSettings++ = e.settings[k];
        }
    }

    UnmapViewOfFile(vMapData);
    return TRUE;
}

BOOL ReadEntries(TCHAR* memName, Entry** entries, DWORD &num_entries)
{
    HANDLE hMapFile = OpenFileMapping(FILE_MAP_READ, FALSE, memName);
    if (hMapFile == NULL) {
        return FALSE;
    }

    BYTE *vMapData = (BYTE*) MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0);
    if (vMapData == NULL) {
        CloseHandle(hMapFile);
        return FALSE;
    }

    DWORD *pEntryCount = (DWORD*) vMapData;
    num_entries = *pEntryCount;

    if (num_entries == 0)
    {
        // special case: when no entries was found in buffer
        *entries = NULL;
    }
    else
    {
        SharedMemEntry *pEntries = (SharedMemEntry*) (pEntryCount + 1);

        *entries = new Entry[num_entries];

        for (DWORD i = 0; i < num_entries; ++i)
        {
            Entry &e = (*entries)[i];
            SharedMemEntry &eEntry = pEntries[i];

            e.id = eEntry.id;
            e.type = eEntry.type;
            e.count = eEntry.count;

            e.values = new int[eEntry.values_count];
            e.settings = new int[eEntry.settings_count];

            int *pValues = (int*) (vMapData + eEntry.values_offset);
            for(DWORD j = 0; j < eEntry.values_count; ++j) {
                e.values[j] = pValues[j];
            }

            int *pSettings = (int*) (vMapData + eEntry.settings_offset);
            for(DWORD j = 0; j < eEntry.settings_count; ++j) {
                e.settings[j] = pSettings[j];
            }
        }
    }

    UnmapViewOfFile(vMapData);
    CloseHandle(hMapFile);

    return TRUE;
}

Alternatively, you should consider automating the memory management and loops instead of doing everything manually:

typedef struct
{
    int id;
    int type;
    int count;
    std::vector<int> values;
    std::vector<int> settings;
} Entry;

#pragma pack(push, 1)
typedef struct
{
    int id;
    int type;
    int count;
    DWORD values_offset;
    DWORD values_count; // if different than 'int count'
    DWORD settings_offset;
    DWORD settings_count; // if different than 'int count' 
} SharedMemEntry;
#pragma pack(pop)

BOOL DumpEntries(TCHAR* memName)
{
    DWORD NumValues = 0;
    DWORD NumSettings = 0;

    // or whatever your container actually is...
    for(std::vector<Entry>::reverse_iterator iter = entries.rbegin();
        iter != entries.rend(); ++iter)
    {
        Entry &e = *iter;
        NumValues += e.values.size();
        NumSettings += e.settings.size();
    }

    DWORD memsize = sizeof(DWORD) +
                    (entries.size() * sizeof(SharedMemEntry)) +
                    (sizeof(int) * NumValues) +
                    (sizeof(int) * NumSettings);

    if (hMapObject != NULL)
        CloseHandle(hMapObject);

    hMapObject = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, memsize, memName);
    if (!hMapObject) {
        return FALSE;
    }

    std::unique_ptr<void, decltype(&UnmapViewOfFile)> uMapData( MapViewOfFile(hMapObject, FILE_MAP_WRITE, 0, 0, memsize), &UnmapViewOfFile );
    BYTE *vMapData = (BYTE*) uMapData.get();
    if (!vMapData) {
        CloseHandle(hMapObject);
        hMapObject = NULL;
        return FALSE;
    }

    DWORD *pEntryCount = (DWORD*) vMapData;
    *pEntryCount = entries.size();

    SharedMemEntry* pEntries = (SharedMemEntry*) (pEntryCount + 1);
    int *pValues = (int*) (pEntries + entries.size());
    int *pSettings = (int*) (pValues + NumValues);

    // or whatever your container actually is...
    for(std::vector<Entry>::reverse_iterator iter = entries.rbegin();
        iter != entries.rend(); ++iter)
    {
        Entry &e = *iter;
        SharedMemEntry &eEntry = *pEntries++;

        eEntry.id = e.id;
        eEntry.type = e.type;
        eEntry.count = e.count;

        eEntry.values_offset = ((BYTE*)pValues - vMapData);
        eEntry.values_count = e.values.size();
        pValues = std::copy(e.values.begin(), e.values.end(), pValues);

        eEntry.settings_offset = ((BYTE*)pSettings - vMapData);
        eEntry.settings_count = e.settings.size();
        pSettings = std::copy(e.settings.begin(), e.settings.end(), pSettings);
    }

    return TRUE;
}

// or whatever container type you want...
BOOL ReadEntries(TCHAR* memName, std::vector<Entry> &entries)
{
    entries.clear();

    std::unique_ptr<std::remove_pointer<HANDLE>::type, decltype(&CloseHandle)> uMapFile( OpenFileMapping(FILE_MAP_READ, FALSE, memName), &CloseHandle );
    HANDLE hMapFile = uMapFile.get();
    if (!hMapFile) {
        return FALSE;
    }

    std::unique_ptr<void, decltype(&UnmapViewOfFile)> uMapData( MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0), &UnmapViewOfFile );
    BYTE *vMapData = (BYTE*) uMapData.get();
    if (!vMapData) {
        return FALSE;
    }

    DWORD *pEntryCount = (DWORD*) vMapData;
    DWORD num_entries = *pEntryCount;

    if (num_entries != 0)
    {
        entries.resize(num_entries);

        SharedMemEntry *pEntries = (SharedMemEntry*) (pEntryCount + 1);

        for (DWORD i = 0; i < num_entries; ++i)
        {
            Entry &e = entries[i];
            SharedMemEntry &eEntry = pEntries[i];

            e.id = eEntry.id;
            e.type = eEntry.type;
            e.count = eEntry.count;

            e.values.reserve(eEntry.values_count);
            e.settings.reserve(eEntry.settings_count);

            int *pValues = (int*) (vMapData + eEntry.values_offset);
            std::copy(pValues, pValues + eEntry.values_count, std::back_inserter(e.values));

            int *pSettings = (int*) (vMapData + eEntry.settings_offset);
            std::copy(pSettings, pSettings + eEntry.settings_count, std::back_inserter(e.settings));
        }
    }

    return TRUE;
}

Either way, make sure you provide some kind of synchronization between DumpEntries() and ReadEntries() , such as with shared events from CreateEvent() , so that ReadEntries() does not try to read from the memory while DumpEntries() is still writing to it, and vice versa.

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