c++windowsqtmtpwpd

WPD API - Cannot transfer content from device (SSCCE included)


I need to enumerate the contents of an MTP android device and transfer files from the device to a Windows PC.

Following the guide found here Transferring Content from a Device to a PC I attempted to transfer a simple text document to a specified directory. However, the output file is always empty, even when the returned value states that X number of bytes have been written.

I created an SSCCE using only the recommended Windows API calls to easily reproduce the issue. I used the API documentation and the official sample project for reference.

main.cpp

Qt project file

To test, I have an android phone connected via MTP with a folder on the root called testFolder and inside there is a file testfile.txt with the text hello world. Upon running this application, the output states that 11 bytes have been written however the file is empty. Please try it yourself?

I can't seem to understand why the file is always empty. Any ideas?

Thanks for your time.


Solution

  •   hr = SHCreateStreamOnFile(strOriginalFileName, STGM_CREATE|STGM_WRITE, &finalFileStream);
    

    It usually helps to recognize common coding patterns. Whenever you create a file then you must always close the file. In the C language you have fopen(), you have to call fclose(). In the winapi you have CreateFile(), you have to call CloseHandle(). Even in runtime environments where a lot of the resource management is automagic, like .NET, when you create a FileStream then you have to call Close() or use the using statement.

    The code does nothing to explicitly close finalFileStream.

    That has consequences, you found one. Short of a memory leak, the implementation of SHCreateStreamOnFile does not know when to flush any buffered but not yet written data to the file. So you end up with a file with no content.

    The contract for any interface pointer like IStream is that you always have to explicitly call its Release() function. Note that the code forgets to do this consistently for all the interface pointers it uses. Not the only problem, when you call CoInitialize/Ex() then you have to call CoUninitialize(). Just use the Golden Rule, you always have to cleanup explicitly in code like this.

    Fix:

      hr = StreamCopy(finalFileStream, ...);
      if (FAILED(hr)) saysomething(hr);          // Forgotten in original code
      finalFileStream->Release();                // Now it is good.
    

    You can use a smart-pointer type in C++ like CComPtr<> to get it done automagically.