C++ c++;WINAPI结构的共享内存数组
我正在尝试使用WINAPI通过共享命名内存共享结构数组。我能够创建和管理共享内存,但当尝试共享结构数组时,数组的大小在读取时始终为0 下面是我编写的测试代码,它应该写入/读取10个条目的数组,但即使这样也失败了。然而,我的目标是写/读一个动态结构数组,其中包含2个动态数组以及它们目前已经包含的信息 我知道我不应该在进程之间共享指针,因为它们可能指向随机值。因此,我使用new为数组分配内存 这就是我到目前为止所做的: 在两个进程中共享:C++ c++;WINAPI结构的共享内存数组,c++,arrays,windows,shared-memory,C++,Arrays,Windows,Shared Memory,我正在尝试使用WINAPI通过共享命名内存共享结构数组。我能够创建和管理共享内存,但当尝试共享结构数组时,数组的大小在读取时始终为0 下面是我编写的测试代码,它应该写入/读取10个条目的数组,但即使这样也失败了。然而,我的目标是写/读一个动态结构数组,其中包含2个动态数组以及它们目前已经包含的信息 我知道我不应该在进程之间共享指针,因为它们可能指向随机值。因此,我使用new为数组分配内存 这就是我到目前为止所做的: 在两个进程中共享: #define MEMSIZE 90024 typede
#define MEMSIZE 90024
typedef struct {
int id;
int type;
int count;
} Entry;
extern HANDLE hMapObject;
extern void* vMapData;
std::vector<Entry> entries;//collection of entries
BOOL DumpEntries(TCHAR* memName) {//Returns true, writing 10 entries
int size = min(10, entries.size());
Entry* eArray = new Entry[size];
for (int i = 0; i < size; i++) {
eArray[i] = entries.at(i);
}
::hMapObject = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, MEMSIZE, memName);
if (::hMapObject == NULL) {
return FALSE;
}
::vMapData = MapViewOfFile(::hMapObject, FILE_MAP_ALL_ACCESS, 0, 0, MEMSIZE);
if (::vMapData == NULL) {
CloseHandle(::hMapObject);
return FALSE;
}
CopyMemory(::vMapData, eArray, (size * sizeof(Entry)));
UnmapViewOfFile(::vMapData);
//delete[] eArray;
return TRUE;
}
BOOL ReadEntries(TCHAR* memName, Entry* entries) {//Returns true reading 0 entries
HANDLE hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, memName);
if (hMapFile == NULL) {
return FALSE;
}
Entry* tmpEntries = (Entry*)(MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 10 * sizeof(Entry)));
if (tmpEntries == NULL) {
CloseHandle(hMapFile);
return FALSE;
}
entries = new Entry[10];
for (int i = 0; i < 10; i++) {
entries[i] = tmpEntries[i];
}
UnmapViewOfFile(tmpEntries);
CloseHandle(hMapFile);
return TRUE;
}
过程1:
#define MEMSIZE 90024
typedef struct {
int id;
int type;
int count;
} Entry;
extern HANDLE hMapObject;
extern void* vMapData;
std::vector<Entry> entries;//collection of entries
BOOL DumpEntries(TCHAR* memName) {//Returns true, writing 10 entries
int size = min(10, entries.size());
Entry* eArray = new Entry[size];
for (int i = 0; i < size; i++) {
eArray[i] = entries.at(i);
}
::hMapObject = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, MEMSIZE, memName);
if (::hMapObject == NULL) {
return FALSE;
}
::vMapData = MapViewOfFile(::hMapObject, FILE_MAP_ALL_ACCESS, 0, 0, MEMSIZE);
if (::vMapData == NULL) {
CloseHandle(::hMapObject);
return FALSE;
}
CopyMemory(::vMapData, eArray, (size * sizeof(Entry)));
UnmapViewOfFile(::vMapData);
//delete[] eArray;
return TRUE;
}
BOOL ReadEntries(TCHAR* memName, Entry* entries) {//Returns true reading 0 entries
HANDLE hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, memName);
if (hMapFile == NULL) {
return FALSE;
}
Entry* tmpEntries = (Entry*)(MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 10 * sizeof(Entry)));
if (tmpEntries == NULL) {
CloseHandle(hMapFile);
return FALSE;
}
entries = new Entry[10];
for (int i = 0; i < 10; i++) {
entries[i] = tmpEntries[i];
}
UnmapViewOfFile(tmpEntries);
CloseHandle(hMapFile);
return TRUE;
}
所以我的问题是,我做错了什么?我在两个进程之间共享同一个结构,为要写入的条目分配新内存,并复制大小为
10*sizeof(条目)的内存代码>。当尝试阅读时,我也尝试阅读10*sizeof(条目)编码>字节并将数据强制转换为条目*
。有什么我遗漏的吗?欢迎提供所有帮助。基于粗略检查,此代码似乎试图将包含std::string的结构映射到共享内存中,以供其他进程使用
不幸的是,这场冒险在开始之前就注定了。即使您正确地传递了数组长度,我希望另一个进程会立即崩溃,只要它嗅到另一个进程试图映射到共享内存段的std::string
std::string
s是非平凡类。std::string
维护指向保存实际字符串数据的缓冲区的内部指针;在堆上分配缓冲区
您知道sizeof(std::string)
不会改变,无论字符串包含五个字符,还是“战争与和平”的全部内容,对吗?停下来想一想,在存储std::string
所需的几个字节内,这是怎么可能的
一旦你想一想,就会非常清楚为什么将一个进程的std::string
s映射到一个共享内存段,然后再尝试由另一个进程获取它们是行不通的
唯一可以实际映射到共享内存或从共享内存映射到共享内存的是;虽然可以不使用聚合,但在某些情况下也是如此。恐怕问题只存在于\u ARRAYSIZE
宏中。我无法在MSDN中找到它,但我在其他页面中找到了\u countof
或ARRAYSIZE
的参考。所有这些都定义为sizeof(array)/sizeof(array[0])
。问题在于,它只对定义为条目[10]
的真正数组有意义,而对指向这样一个数组的指针则没有意义。从技术上讲,当您声明:
Entry* entries;
sizeof(entries)
是指针大小的sizeof(Entry*)
。它小于结构的大小,因此整数除法的结果是。。。0!
无论如何,当前代码中还有其他问题。通过共享内存交换可变大小数组的正确方法是使用一个辅助结构,该结构包含一个大小,并且数组本身声明为不完整:
你可以这样扔:
BOOL DumpEntries(TCHAR* memName) {//Returns true, writing 10 entries
int size = min(10, entries.size());
EntryArray* eArray = (EntryArray *) malloc(sizeof(EntryArray) + size * sizeof(Entry));
for (int i = 0; i < size; i++) {
eArray->entries[i] = entries.at(i);
}
eArray->size = size;
::hMapObject = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, MEMSIZE, memName);
if (::hMapObject == NULL) {
return FALSE;
}
::vMapData = MapViewOfFile(::hMapObject, FILE_MAP_ALL_ACCESS, 0, 0, MEMSIZE);
if (::vMapData == NULL) {
CloseHandle(::hMapObject);
return FALSE;
}
CopyMemory(::vMapData, eArray, (sizeof(EntryArray) + size * sizeof(Entry)));
UnmapViewOfFile(::vMapData);
free(eArray);
return TRUE;
}
size_t ReadEntries(TCHAR* memName, Entry*& entries) {//Returns the number of entries or -1 if error
HANDLE hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, memName);
if (hMapFile == NULL) {
return -1;
}
EntryArray* eArray = (EntryArray*)(MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 10 * sizeof(Entry)));
if (eArray == NULL) {
CloseHandle(hMapFile);
return -1;
}
entries = new Entry[10]; // or even entries = new Entry[eArray->size];
for (int i = 0; i < 10; i++) { // same: i<eArray->size ...
entries[i] = eArray->entries[i];
}
UnmapViewOfFile(eArray);
CloseHandle(hMapFile);
return eArray.size;
}
最后但并非最不重要的一点是,反斜杠\
是字符串中的一个特殊引号字符,它必须引用自身。所以你应该写。TEXT(“Global\\Entries”)
我对你的代码做了一些修改:
过程1:
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;
}
过程2:
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;
}
变化:
- 在映射内存时,我没有使用静态大小(MEMSIZE),而是精确计算所需的内存
- 我把一个“头”放到内存映射中,一个DWORD用于发送到缓冲区中处理2个条目
- 您的ReadEntries定义错误,我将其修复为将条目*更改为条目**
注:
- 在进程2调用ReadEntries之前,需要关闭进程1中的::hMapObject句柄
- 在使用进程2中为ReadEntries返回的内存项之前,需要先删除它
- 此代码仅适用于同一windows用户,如果您想与用户进程(例如)通信服务,则需要在CreateFileMapping过程中处理SECURITY_ATTRIBUTES成员
是否将数组长度和元素存储在共享内存中?标记为.NET或其他什么,它不是标准C++,它是微软C++或只是使用POSIX套接字API。也许work@JakubKaszycki:它不是.net,但只有Winapi…_ARRAYSIZE(entries)将扩展为sizeof(entries)/sizeof(*(entries))和sizeof(entries)==0(对于您的情况),这就是为什么_ARRAYSIZE(entries)==0。您只能获取固定大小数组的大小。嘿,谢谢您的评论。我并不是真的想分享std::string
,因为我使用的是TCHAR*
,我只是把它作为例子添加到OP中。关于共享命名内存,我最担心的是能够真正共享std::vector
或包含点的数组。或者更好,能够将点(x,y)的集合从一个进程发送到另一个进程。一维整数数组也不错。更新了问题谢谢您的帮助,如何处理条目
结构中的动态数组?我将很快对此进行测试。这看起来很有希望,稍后会尝试并让您知道。当创建文本(“Local\\Entries”)
内存时,我应该能够在同一台计算机上的不同进程中读取内存,对吗?当试图在读取时创建本地内存时,它找不到内存并打印“未找到文件”。文本(“全局\\条目”)适用于所有进程,文本(“本地\\条目”)适用于同一用户名空间中的进程。这一点在这里得到了更好的解释:到目前为止进展顺利,但还有一个小问题。这个
void main()
{
Entry* entries;
DWORD number_of_entries;
if(ReadEntries(TEXT("Global\\Entries", &entries, number_of_entries) && number_of_entries > 0)
{
// do something
}
delete entries;
}