2

我的目标是实现以下目标:

我想从磁盘读取一个文件(假设它是一个图像文件)并将其写入共享内存,以便我可以从另一个进程的共享内存中读取它。首先,我按照这个 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,所以我寻找一种方法来转换std::stringI have to LPTSTR,据我所知,这是一个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 则不是!

( Source ) 这听起来好像我无法按照我尝试的方式存储文件数据。

所以我的问题是:我只是在某个地方犯了错误还是我的方法有缺陷?也许我需要为结果使用另一种文件类型MapViewOfFile?也许我需要将文件初始化加载到另一种类型?

4

1 回答 1

1

我不会提供完整的答案,因为我手头没有MCVE。但是,OP 要求进行更多澄清,并且关于CopyMemory()我发现一些值得注意的事情(仅对此发表评论有点太长了)。

CopyMemory()没有什么特别专用于内存映射文件。它只是一个将数据从源复制到目标的函数,大小以字节为单位。

在谷歌搜索时,CopyMemory()我偶然发现了“ CopyMemory()vs. ”,并在GameDevmemcpy()上找到了一个简短的答案:

直接从 WINBASE.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 );

count将字节从指向的对象复制到src指向的对象dest。两个对象都被重新解释为unsigned char.

如果对象重叠,则行为未定义。

对于(可能)重叠源/目标范围的特殊情况,memcpy()有一个“兄弟” memmove()。在这种内存映射文件的情况下,我不相信源和目标可以重叠。所以,这memcpy()可能很好(甚至可能比memmove().)

所以,它不是CopyMemory()提供“内存映射文件访问魔术”的。这已经发生在另一个函数调用中,该函数调用肯定在 OP 的源代码中,但在问题中没有提到:

MapViewOfFile()

将文件映射视图映射到调用进程的地址空间。

返回值

如果函数成功,则返回值是映射视图的起始地址。

因此,成功时,MapViewOfFile()返回一个指向文件已映射到的内存的指针。之后可以像任何其他进程内存访问一样进行读/写访问——通过赋值运算符、通过memcpy()(或CopyMemory())或其他任何可以想象的方式。

最后,OP的附加问题的答案:

我如何将数据读入从共享内存读取的“另一端”的字符串/字节数组中?

读取可以以完全相同的方式完成,除了指向地图视图的指针成为源,本地缓冲区成为目标。但是如何确定大小?这个问题其实更笼统:变长的数据占用了多少字节?C/C++中有两个典型的答案:

  • 也可以存储数据的大小(例如 in std::stringstd::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, '\0');
// read pay-load
CopyMemory(&data[0], pBuf + sizeof size, size);

请注意,&data[0]提供相同的地址,如data.data(). 在 C++ 17 之前,没有 的非常量版本std::string::data(),因此 hackstd::string::operator[]()具有非常量版本。

于 2018-05-09T06:46:59.227 回答