C++ 从磁盘读取文件并将其写入共享内存
C++ Read file from disk and write it into shared memory
我的目标是实现以下目标:
我想从磁盘读取一个文件(假设它是一个图像文件)并将其写入共享内存,这样我就可以从另一个进程的共享内存中读取它。
首先,我按照 this msdn tutorial 创建了一个简单的共享内存实现来包含一个字符串。它工作正常。
然后我找到了一种从磁盘读取图像的方法。实现如下:
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
,所以我寻找一种方法来转换 std::string
我必须 LPTSTR
,这是我目前为止明白了const wchar_t*
。我这样做如下:
std::wstring widestr = std::wstring(data.begin(), data.end());
const wchar_t* widecstr = widestr.c_str();
但如果我现在检查 _tcslen(widecstr)
,结果是 4
。所以我想我尝试做的是行不通的。我还在另一个 SO 问题上找到了这句话:
Notice: A std::string is suitable for holding a 'binary' buffer, where a std::wstring is not!
(Source) 这听起来好像我无法按照我尝试的方式存储文件数据。
所以我的问题是:我只是在某处犯了错误还是我的方法有缺陷?也许我需要为 MapViewOfFile
的结果使用另一种文件类型?也许我需要将文件初始加载为另一种类型?
我不会提供完整的答案,因为我没有 MCVE at hand. However, the OP asked for more clarification and, concerning CopyMemory()
我发现了一些值得注意的事情(而且只写评论有点太长了) ).
CopyMemory()
不是专门用于内存映射文件的。它只是一个将数据从源复制到目标的函数,大小以字节为单位。
在谷歌搜索 CopyMemory()
时,我偶然发现了“CopyMemory()
vs. memcpy()
”,并在 GameDev:
上找到了一个简短的答案
Straight out of WINBASE.H
#define CopyMemory RtlCopyMemory
Then, straight out of WINNT.H
#define RtlCopyMemory(Destination,Source,Length) memcpy((Destination),(Source),(Length))
所以,我们在这里:
Defined in header <cstring>
void* memcpy( void* dest, const void* src, std::size_t count );
Copies count
bytes from the object pointed to by src
to the object pointed to by dest
. Both objects are reinterpreted as arrays of unsigned char
.
If the objects overlap, the behavior is undefined.
对于(可能)重叠 source/destination 范围的特殊情况,memcpy()
有一个 "sibling" memmove()
。在这种内存映射文件的情况下,我认为源和目标永远不会重叠。因此,memcpy()
可能没问题(甚至可能比 memmove()
更快)
因此,提供 "Memory Mapped File Access Magic" 的不是 CopyMemory()
。这已经发生在另一个函数调用中,它肯定在 OP 的源代码中,但在问题中没有提到:
Maps a view of a file mapping into the address space of a calling process.
Return Value
If the function succeeds, the return value is the starting address of the mapped view.
因此,如果成功,MapViewOfFile()
returns 指向文件映射到的内存的指针。 Read/write 访问可以像任何其他进程内存访问一样在之后完成——通过赋值运算符,通过 memcpy()
(或 CopyMemory()
),或者其他任何可以想象的方式。
最后OP的附加题答案:
how I could read the data into a string/byte-array on the "other" side where I read from the shared memory?
可以以完全相同的方式进行读取,除了指向地图视图的指针成为源而本地缓冲区成为目标。但是如何确定尺寸呢?这个问题其实更笼统:可变长度的数据占用了多少字节? C/C++中有两个典型的答案:
- 也可以存储数据大小(如
std::string
、std::vector
等)
- 或以某种方式注释数据的结尾(例如 C 字符串中的零终止符)。
在OP的具体情况下,第一个选项可能更合理。因此,有效负载数据(图像)的大小也可能存储在内存映射文件中。在 reader 方面,首先评估大小(它必须具有特定的 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);
在 reader 方面它可能看起来像这样:
/* 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, '[=13=]');
// read pay-load
CopyMemory(&data[0], pBuf + sizeof size, size);
请注意,&data[0]
提供与 data.data()
相同的地址。在 C++ 17 之前,没有 std::string::data()
的非常量版本,因此 std::string::operator[]()
的 hack 有一个非常量版本。
我的目标是实现以下目标:
我想从磁盘读取一个文件(假设它是一个图像文件)并将其写入共享内存,这样我就可以从另一个进程的共享内存中读取它。 首先,我按照 this msdn tutorial 创建了一个简单的共享内存实现来包含一个字符串。它工作正常。
然后我找到了一种从磁盘读取图像的方法。实现如下:
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
,所以我寻找一种方法来转换 std::string
我必须 LPTSTR
,这是我目前为止明白了const wchar_t*
。我这样做如下:
std::wstring widestr = std::wstring(data.begin(), data.end());
const wchar_t* widecstr = widestr.c_str();
但如果我现在检查 _tcslen(widecstr)
,结果是 4
。所以我想我尝试做的是行不通的。我还在另一个 SO 问题上找到了这句话:
Notice: A std::string is suitable for holding a 'binary' buffer, where a std::wstring is not!
(Source) 这听起来好像我无法按照我尝试的方式存储文件数据。
所以我的问题是:我只是在某处犯了错误还是我的方法有缺陷?也许我需要为 MapViewOfFile
的结果使用另一种文件类型?也许我需要将文件初始加载为另一种类型?
我不会提供完整的答案,因为我没有 MCVE at hand. However, the OP asked for more clarification and, concerning CopyMemory()
我发现了一些值得注意的事情(而且只写评论有点太长了) ).
CopyMemory()
不是专门用于内存映射文件的。它只是一个将数据从源复制到目标的函数,大小以字节为单位。
在谷歌搜索 CopyMemory()
时,我偶然发现了“CopyMemory()
vs. memcpy()
”,并在 GameDev:
Straight out of WINBASE.H
#define CopyMemory RtlCopyMemory
Then, straight out of WINNT.H
#define RtlCopyMemory(Destination,Source,Length) memcpy((Destination),(Source),(Length))
所以,我们在这里:
Defined in header
<cstring>
void* memcpy( void* dest, const void* src, std::size_t count );
Copies
count
bytes from the object pointed to bysrc
to the object pointed to bydest
. Both objects are reinterpreted as arrays ofunsigned char
.If the objects overlap, the behavior is undefined.
对于(可能)重叠 source/destination 范围的特殊情况,memcpy()
有一个 "sibling" memmove()
。在这种内存映射文件的情况下,我认为源和目标永远不会重叠。因此,memcpy()
可能没问题(甚至可能比 memmove()
更快)
因此,提供 "Memory Mapped File Access Magic" 的不是 CopyMemory()
。这已经发生在另一个函数调用中,它肯定在 OP 的源代码中,但在问题中没有提到:
Maps a view of a file mapping into the address space of a calling process.
Return Value
If the function succeeds, the return value is the starting address of the mapped view.
因此,如果成功,MapViewOfFile()
returns 指向文件映射到的内存的指针。 Read/write 访问可以像任何其他进程内存访问一样在之后完成——通过赋值运算符,通过 memcpy()
(或 CopyMemory()
),或者其他任何可以想象的方式。
最后OP的附加题答案:
how I could read the data into a string/byte-array on the "other" side where I read from the shared memory?
可以以完全相同的方式进行读取,除了指向地图视图的指针成为源而本地缓冲区成为目标。但是如何确定尺寸呢?这个问题其实更笼统:可变长度的数据占用了多少字节? C/C++中有两个典型的答案:
- 也可以存储数据大小(如
std::string
、std::vector
等) - 或以某种方式注释数据的结尾(例如 C 字符串中的零终止符)。
在OP的具体情况下,第一个选项可能更合理。因此,有效负载数据(图像)的大小也可能存储在内存映射文件中。在 reader 方面,首先评估大小(它必须具有特定的 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);
在 reader 方面它可能看起来像这样:
/* 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, '[=13=]');
// read pay-load
CopyMemory(&data[0], pBuf + sizeof size, size);
请注意,&data[0]
提供与 data.data()
相同的地址。在 C++ 17 之前,没有 std::string::data()
的非常量版本,因此 std::string::operator[]()
的 hack 有一个非常量版本。