我的目标是实现以下目标:
我想从磁盘读取文件(假设它是一个图像文件(并将其写入共享内存,以便我可以从另一个进程的共享内存中读取它。 首先,我按照此 msdn 教程创建了一个包含字符串的简单共享内存实现。它工作正常。
然后我找到了一种从磁盘读取图像的方法。实现如下:
std::ifstream fin("path/to/img.png", std::ios::in | std::ios::binary);
std::ostringstream oss;
oss << fin.rdbuf();
std::string data(oss.str());
所以现在我有一个包含我的数据的std::string
data.length()
表示我读取的文件已成功存储在那里。在msdn示例中,MapViewOfFile
结果的类型是LPTSTR
,所以我寻找一种方法来强制转换我必须LPTSTR
std::string
,据我所知,这是一个const wchar_t*
。我这样做如下:
std::wstring widestr = std::wstring(data.begin(), data.end());
const wchar_t* widecstr = widestr.c_str();
但是,如果我现在检查_tcslen(widecstr)
结果是4
.所以我想我试图做的事情不起作用。我还在另一个SO问题上找到了这句话:
注意:std::string 适合保存"二进制"缓冲区,而 std::wstring 则不适合!
(来源(这听起来好像我无法按照我尝试的方式存储文件数据。
所以我的问题是:我只是在某处犯了错误还是我的方法有缺陷?也许我需要为MapViewOfFile
的结果使用另一种文件类型?也许我需要将文件初始化为另一种类型?
我不会提供一个完整的答案,因为我手头没有 MCVE。但是,OP要求更多澄清,关于CopyMemory()
,我发现一些值得注意的事情(仅就此写评论有点太长了(。
CopyMemory()
没有什么特别专用于内存映射文件。它只是一个将数据从源复制到目标的功能,大小以字节为单位。
在谷歌搜索CopyMemory()
时,我偶然发现了"CopyMemory()
vs.memcpy()
",并在GameDev上找到了一个简短的答案:
直接出自赢底。H
#define CopyMemory RtlCopyMemory
然后,直接离开WINNT。H
#define RtlCopyMemory(Destination,Source,Length) memcpy((Destination),(Source),(Length))
所以,我们在这里:
std::memcpy()
在标头
<cstring>
中定义
void* memcpy( void* dest, const void* src, std::size_t count );
将
src
指向的对象中的count
字节复制到dest
指向的对象。这两个对象都被重新解释为unsigned char
数组。如果对象重叠,则行为未定义。
对于(可能(源/目标范围重叠的特殊情况,memcpy()
具有"同级"memmove()
。在这种情况下,内存映射文件,我认为源和目标永远不会重叠。因此,memcpy()
可能很好(甚至可能比memmove()
更快(。
因此,它不是提供"内存映射文件访问魔术"的CopyMemory()
。这已经发生在另一个函数调用中,该函数调用肯定在 OP 的源代码中,但在问题中未提及:
MapViewOfFile()
将文件映射的视图映射到调用进程的地址空间。
返回值
如果函数成功,则返回值是映射视图的起始地址。
因此,成功后,MapViewOfFile()
将返回一个指针,指向文件映射到的内存。读/写访问可以像任何其他进程内存访问一样完成 - 通过赋值运算符,通过memcpy()
(或CopyMemory()
(,或者任何其他可以想象到的。
最后,OP附加问题的答案:
如何将数据读入我从共享内存中读取的"另一"端的字符串/字节数组中?
读取可以以完全相同的方式完成,只是指向地图视图的指针成为源,本地缓冲区成为目标。但是如何确定尺寸呢?这个问题实际上更普遍:可变长度的数据占用了多少字节?C/C++ 中有两个典型的答案:
- 要么存储数据的大小(如
std::string
、std::vector
等( - 或以某种方式注释数据的结尾(例如 C 字符串中的零终止符(。
在OP的特定情况下,第一种选择可能更合理。因此,付费负载数据(图像(的大小也可能存储在内存映射文件中。在读取器方面,首先评估大小(必须具有特定的int
类型,因此必须具有已知的字节数(,并且大小用于复制付费负载数据。
因此,在编写器方面,它可能看起来像这样:
/* prior something like
* unsigned char *pBuf = MapViewOfFile(...);
* has been done.
*/
// write size:
size_t size = data.size();
CopyMemory(pBuf, (const void*)&size, sizeof size);
// write pay-load from std::string data:
CopyMemory(pBuf + sizeof size, data.data(), size);
在读者方面,它可能看起来像这样:
/* prior something like
* const unsigned char *pBuf = MapViewOfFile(...);
* has been done.
*/
// read size:
size_t size = 0;
CopyMemory((void*)&size, pBuf, sizeof size);
// In C, I had probably done: size_t size = *(size_t*)pBuf; instead...
// allocate local buffer for pay-load
std::string data(size, ' ');
// read pay-load
CopyMemory(&data[0], pBuf + sizeof size, size);
请注意,&data[0]
提供与data.data()
相同的地址。在C++ 17之前,没有非常量版本的std::string::data()
,因此std::string::operator[]()
的黑客有一个非常量版本。