c++winapishared-memorylptstr

C++ Read file from disk and write it into shared memory


My goal is to achieve the following:

I want to read a file from disk (let's say it's an image file) and to write it into shared memory so I can read it from the shared memory from another process. First I followed this msdn tutorial to create a simple shared memory implementation to contain a string. It works fine.

Then I found a way to read the image from disk. The implementation is as follows:

std::ifstream fin("path/to/img.png", std::ios::in | std::ios::binary);
std::ostringstream oss;
oss << fin.rdbuf();
std::string data(oss.str());

So now I have a std::string containing my data The data.length() indicates that the file I've read is successfully stored in there. In the msdn example, the type of the MapViewOfFile result is LPTSTR, so I looked for a way to cast the std::string I have to LPTSTR, which is as far I understand a const wchar_t*. I do this as follows:

std::wstring widestr = std::wstring(data.begin(), data.end());
const wchar_t* widecstr = widestr.c_str();

But if I now check _tcslen(widecstr) the result is 4. So I guess what I tried to do doesn't work. I also found this quote on another SO question:

Notice: A std::string is suitable for holding a 'binary' buffer, where a std::wstring is not!

(Source) This makes it sound like I can't store the file data the way I tried.

So my question is: Did I just did a mistake somewhere or is my approach flawed? Maybe I need to use another file type for the result of MapViewOfFile? Maybe I need to initialy load the file into another type?


Solution

  • I wouldn't provide a full-fledged answer as I don't have an MCVE at hand. However, the OP asked for more clarification and, concerning CopyMemory() I found some things worth to be noted (and it was a bit too long to write a comment only about this).

    CopyMemory() is nothing especially dedicated to memory mapped files. It's just a function to copy data to destination from source with a size in bytes.

    While googling for CopyMemory() I stumbled over "CopyMemory() vs. memcpy()" and found an as nice as short answer on GameDev:

    Straight out of WINBASE.H

    #define CopyMemory RtlCopyMemory

    Then, straight out of WINNT.H

    #define RtlCopyMemory(Destination,Source,Length) memcpy((Destination),(Source),(Length))

    So, here we are:

    std::memcpy()

    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.

    For the special case of (potentially) overlapping source/destination ranges, memcpy() has a "sibling" memmove(). In this case of memory mapped files, I don't believe that source and destination can ever overlap. So, the memcpy() might be fine (and potentially even faster than memmove().)

    So, it isn't the CopyMemory() which provides the "Memory Mapped File Access Magic". This already happened in another function call which is surely in the source code of OP but not mentioned in the question:

    MapViewOfFile()

    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.

    Hence, on success, the MapViewOfFile() returns a pointer to the memory where file has been mapped to. Read/write access can be done afterwards like any other process memory access – via assignment operator, via memcpy() (or CopyMemory()), or what ever else is imaginable.

    Finally, the answer to the add-on question of OP:

    how I could read the data into a string/byte-array on the "other" side where I read from the shared memory?

    Reading can be done in the exact same manner except that pointer to map view becomes source and local buffer becomes destination. But how to determine size? This issue is actually more general: How many bytes are occupied by data with variable length? There are two typical answers in C/C++:

    In the specific case of OP, the first option is probably more reasonable. So, the size of pay-load data (the image) might be stored in the memory mapped file as well. On the reader side, first the size is evaluated (which has to have a certain int type and hence a known number of bytes), and the size is used to copy the pay-load data.

    Hence, on writer side it could look like this:

    /* 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);
    

    On reader side it could look like this:

    /* 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);
    

    Please, note that &data[0] provides the same address like data.data(). Prior C++ 17, there is no non-const version of std::string::data(), hence the hack with std::string::operator[]() which has a non-const version.