Win32 无法从共享内存获取数据
Win32 Can't get data from shared memory
我能够创建共享内存对象,并使用 MSDN 中的指南打开它。
第一个进程创建它并保持打开状态。
第二个进程输入一个字符串。
然后第一个进程将尝试恢复该字符串并显示它,但我似乎什么也得不到。一直是空的,虽然写的部分好像设置的很正确。
我像这样将字符串写入内存:
int MemoryMapper::Write(const std::string& data) {
m_pBuffer = (LPCTSTR)MapViewOfFile(m_OpenHandle, FILE_MAP_ALL_ACCESS, 0, 0, m_BufferSize);
if (m_pBuffer == NULL)
{
std::cerr << m_DebugErrorTitle << "Write(): " << MM_ERROR_MAPPING_FAILED << " {" << GetLastError() << "}" << std::endl;
Close();
return 0;
}
const char* cdata = _CharFromString(data);
int size = (lstrlen(cdata) * sizeof(const char*));
CopyMemory((PVOID)m_pBuffer, cdata, size);
m_WrittenSize += size;
if (m_Debug > 1) { std::cout << m_DebugTitle << "Wrote " << size << " bytes." << std::endl; }
return size;
}
然后我是这样读的:
int MemoryMapper::Read(std::string& data) {
m_pBuffer = (LPCTSTR) MapViewOfFile(m_OpenHandle, FILE_MAP_ALL_ACCESS, 0, 0, m_BufferSize);
if (m_pBuffer == NULL)
{
std::cerr << m_DebugErrorTitle << "Read(" << m_MemoryName << "): " << MM_ERROR_MAPPING_FAILED << " {" << GetLastError() << "}" << std::endl;
Close();
return 0;
}
MessageBox(NULL, m_pBuffer, TEXT("TEST MESSAGE"), MB_OK);
int size = (lstrlen(m_pBuffer) * sizeof(const char*));
UnmapViewOfFile(m_pBuffer);
return size;
}
m_pBuffer 是一个 LPCTSTR,m_BufferSize 是 1024。
为对象指定的名称在两端相同。我已经确保创建和 opening/closing 部分有效。
第二个进程写入'8312.000000,8312.000000',根据代码一共92个字节。
reader 的缓冲区为空。
我做错了什么?
我尝试了各种数据类型,char、const char、string、tchar - 结果相同。
8312.000000,8312.000000
长度为 23 个字符。
std::string::c_str()
returns 一个空终止的 char*
指针。 lstrlen()
returns 最多但不包括空终止符的字符数。
Write()
是将字符串长度乘以 sizeof(const char*)
,在 32 位进程中为 4(在 64 位进程中为 8)。 Write()
超出了 data
的范围并试图将 23 * 4 = 92
字节复制到 m_pBuffer
。 cdata
保证指向包含最大 24
字节(23 个字符 + 1 个空终止符)的缓冲区,因此 Write()
正在进入周围的内存。那就是未定义的行为,任何事情都可能发生。在您的情况下,您可能只是将额外的垃圾复制到 m_pBuffer
。 Write()
本来很容易崩溃的。
事实上,如果 data
超过 256 个字符,Write()
会崩溃,因为它会尝试将 257+ * 4 > 1024
字节复制到 m_pBuffer
- 超过MapViewOfFile()
的映射访问。
您应该将字符串长度乘以 sizeof(std::string::value_type)
,即 sizeof(char)
,它始终为 1(因此您可以省略乘法)。
Read()
有相同的 sizeof()
错误,但它也假设 m_pBuffer
在调用 lstrlen()
和 [=37= 时总是以 null 终止],但 Write()
不保证空终止符始终存在。
话虽如此,请尝试更像这样的东西:
int MemoryMapper::Write(const std::string& data)
{
// include the null terminator if there is room...
DWORD size = std::min(data.size() + 1, m_BufferSize);
char *pBuffer = (char*) MapViewOfFile(m_OpenHandle, FILE_MAP_WRITE, 0, 0, size);
if (!pBuffer)
{
DWORD errCode = GetLastError();
std::cerr << m_DebugErrorTitle << "Write(): " << MM_ERROR_MAPPING_FAILED << " {" << errCode << "}" << std::endl;
Close();
return 0;
}
CopyMemory(pBuffer, data.c_str(), size);
UnmapViewOfFile(pBuffer);
m_WrittenSize += size;
if (m_Debug > 1) {
std::cout << m_DebugTitle << "Wrote " << size << " bytes." << std::endl;
}
return size;
}
int MemoryMapper::Read(std::string& data)
{
char *pBuffer = (char*) MapViewOfFile(m_OpenHandle, FILE_MAP_READ, 0, 0, m_BufferSize);
if (!pBuffer)
{
DWORD errCode = GetLastError();
std::cerr << m_DebugErrorTitle << "Read(" << m_MemoryName << "): " << MM_ERROR_MAPPING_FAILED << " {" << errCode << "}" << std::endl;
Close();
return 0;
}
// check for a null terminator, but don't exceed the buffer...
char *terminator = std::find(pBuffer, pBuffer + m_BufferSize, '[=10=]');
std::size_t len = std::distance(pBuffer, terminator);
data.assign(pBuffer, len);
UnmapViewOfFile(pBuffer);
MessageBoxA(NULL, data.c_str(), "TEST MESSAGE", MB_OK);
// include the null terminator if it was read...
return std::min(len + 1, m_BufferSize);
}
我能够创建共享内存对象,并使用 MSDN 中的指南打开它。
第一个进程创建它并保持打开状态。 第二个进程输入一个字符串。 然后第一个进程将尝试恢复该字符串并显示它,但我似乎什么也得不到。一直是空的,虽然写的部分好像设置的很正确。
我像这样将字符串写入内存:
int MemoryMapper::Write(const std::string& data) {
m_pBuffer = (LPCTSTR)MapViewOfFile(m_OpenHandle, FILE_MAP_ALL_ACCESS, 0, 0, m_BufferSize);
if (m_pBuffer == NULL)
{
std::cerr << m_DebugErrorTitle << "Write(): " << MM_ERROR_MAPPING_FAILED << " {" << GetLastError() << "}" << std::endl;
Close();
return 0;
}
const char* cdata = _CharFromString(data);
int size = (lstrlen(cdata) * sizeof(const char*));
CopyMemory((PVOID)m_pBuffer, cdata, size);
m_WrittenSize += size;
if (m_Debug > 1) { std::cout << m_DebugTitle << "Wrote " << size << " bytes." << std::endl; }
return size;
}
然后我是这样读的:
int MemoryMapper::Read(std::string& data) {
m_pBuffer = (LPCTSTR) MapViewOfFile(m_OpenHandle, FILE_MAP_ALL_ACCESS, 0, 0, m_BufferSize);
if (m_pBuffer == NULL)
{
std::cerr << m_DebugErrorTitle << "Read(" << m_MemoryName << "): " << MM_ERROR_MAPPING_FAILED << " {" << GetLastError() << "}" << std::endl;
Close();
return 0;
}
MessageBox(NULL, m_pBuffer, TEXT("TEST MESSAGE"), MB_OK);
int size = (lstrlen(m_pBuffer) * sizeof(const char*));
UnmapViewOfFile(m_pBuffer);
return size;
}
m_pBuffer 是一个 LPCTSTR,m_BufferSize 是 1024。 为对象指定的名称在两端相同。我已经确保创建和 opening/closing 部分有效。
第二个进程写入'8312.000000,8312.000000',根据代码一共92个字节。
reader 的缓冲区为空。
我做错了什么? 我尝试了各种数据类型,char、const char、string、tchar - 结果相同。
8312.000000,8312.000000
长度为 23 个字符。
std::string::c_str()
returns 一个空终止的 char*
指针。 lstrlen()
returns 最多但不包括空终止符的字符数。
Write()
是将字符串长度乘以 sizeof(const char*)
,在 32 位进程中为 4(在 64 位进程中为 8)。 Write()
超出了 data
的范围并试图将 23 * 4 = 92
字节复制到 m_pBuffer
。 cdata
保证指向包含最大 24
字节(23 个字符 + 1 个空终止符)的缓冲区,因此 Write()
正在进入周围的内存。那就是未定义的行为,任何事情都可能发生。在您的情况下,您可能只是将额外的垃圾复制到 m_pBuffer
。 Write()
本来很容易崩溃的。
事实上,如果 data
超过 256 个字符,Write()
会崩溃,因为它会尝试将 257+ * 4 > 1024
字节复制到 m_pBuffer
- 超过MapViewOfFile()
的映射访问。
您应该将字符串长度乘以 sizeof(std::string::value_type)
,即 sizeof(char)
,它始终为 1(因此您可以省略乘法)。
Read()
有相同的 sizeof()
错误,但它也假设 m_pBuffer
在调用 lstrlen()
和 [=37= 时总是以 null 终止],但 Write()
不保证空终止符始终存在。
话虽如此,请尝试更像这样的东西:
int MemoryMapper::Write(const std::string& data)
{
// include the null terminator if there is room...
DWORD size = std::min(data.size() + 1, m_BufferSize);
char *pBuffer = (char*) MapViewOfFile(m_OpenHandle, FILE_MAP_WRITE, 0, 0, size);
if (!pBuffer)
{
DWORD errCode = GetLastError();
std::cerr << m_DebugErrorTitle << "Write(): " << MM_ERROR_MAPPING_FAILED << " {" << errCode << "}" << std::endl;
Close();
return 0;
}
CopyMemory(pBuffer, data.c_str(), size);
UnmapViewOfFile(pBuffer);
m_WrittenSize += size;
if (m_Debug > 1) {
std::cout << m_DebugTitle << "Wrote " << size << " bytes." << std::endl;
}
return size;
}
int MemoryMapper::Read(std::string& data)
{
char *pBuffer = (char*) MapViewOfFile(m_OpenHandle, FILE_MAP_READ, 0, 0, m_BufferSize);
if (!pBuffer)
{
DWORD errCode = GetLastError();
std::cerr << m_DebugErrorTitle << "Read(" << m_MemoryName << "): " << MM_ERROR_MAPPING_FAILED << " {" << errCode << "}" << std::endl;
Close();
return 0;
}
// check for a null terminator, but don't exceed the buffer...
char *terminator = std::find(pBuffer, pBuffer + m_BufferSize, '[=10=]');
std::size_t len = std::distance(pBuffer, terminator);
data.assign(pBuffer, len);
UnmapViewOfFile(pBuffer);
MessageBoxA(NULL, data.c_str(), "TEST MESSAGE", MB_OK);
// include the null terminator if it was read...
return std::min(len + 1, m_BufferSize);
}