5

I am attempting to transfer content to portable device using this code snippet

IPortableDeviceValues values =
            GetRequiredPropertiesForContentType(fileName, parentObjectId);

        IStream tempStream;
        uint optimalTransferSizeBytes = 0;

        content.CreateObjectWithPropertiesAndData(
            values,
            out tempStream,
            ref optimalTransferSizeBytes,
            null);

        System.Runtime.InteropServices.ComTypes.IStream targetStream =
            (System.Runtime.InteropServices.ComTypes.IStream)tempStream;

        try
        {
            using (var sourceStream =
                   new FileStream(fileName, FileMode.Open, FileAccess.Read))
            {
                var buffer = new byte[optimalTransferSizeBytes];
                int bytesRead;
                do
                {
                    bytesRead = sourceStream.Read(
                        buffer, 0, (int)optimalTransferSizeBytes);
                    IntPtr pcbWritten = IntPtr.Zero;
                    if (bytesRead < (int)optimalTransferSizeBytes)
                    {
                        targetStream.Write(buffer, bytesRead, pcbWritten);
                    }
                    else
                    {
                        targetStream.Write(buffer, (int)optimalTransferSizeBytes, pcbWritten);
                    }

                } while (bytesRead > 0);
            }
            targetStream.Commit(0);
        }
        finally
        {
            System.Runtime.InteropServices.Marshal.ReleaseComObject(tempStream);
        }

When trying to execute targetStream.Write System.AccessViolationException occuried.

This is reproducible only for windows 10, creators update 1703.

Could you please tell me what I am doing wrong?

Thanks in advance.

Tatiana
  • 51
  • 1

3 Answers3

5

--Skip to the text in bold if you just want a fix!

Investigating further, the issue is in the low level native API: ISequentialStream::Write (which IStream Inherits) The MSDN page for this is: https://msdn.microsoft.com/en-us/library/windows/desktop/aa380014%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396

Note the text for the argument: pcbWritten [out] reads 'A pointer to a ULONG variable where this method writes the actual number of bytes written to the stream object. The caller can set this pointer to NULL, in which case this method does not provide the actual number of bytes written.'

MS introduced a bug into this API (as called in PortableDeviceApi.dll) --> it is no longer checking whether or not pcbWritten is NULL before attempting to read/write from this variable - rather, it ALWAYS attempts to write the No Of Bytes written into this variable - if the API is called with this variable set to NULL then it BARFS. I have proven this is the case in pure native code by changing the way the API is called:

Works:

DWORD bytesWritten
IStream->Write(objectData, bytesRead, &bytesWritten)))

Fails:

IStream->Write(objectData, bytesRead, NULL))) <-- Note the NULL

Up in .Net, IStream.Write is marshalled thus:

void Write(byte[] pv,int cb,IntPtr pcbWritten)

and the demo code (from where we all likely got our implementations!) was:

 IntPtr pcbWritten = IntPtr.Zero  //<--Eg NULL
 IStream.Write(buffer, bytesRead, pcbWritten);

Proposed Solution - Change code to:

 IntPtr pcbWritten = Marshal.AllocHGlobal(4);
 IStream.Write(buffer, bytesRead, pcbWritten);
 Marshal.FreeHGlobal(pcbWritten);

This works around the issue - Hopefully MS will fix it to avoid us all having to re-distribute our products! The entire code is contained in PortableDeviceApi.dll (also including the stream stuff), which would explain why the entire world is not moaning about this issue and why it was not found during test. NB: For multi block copies in while loop, I suspect the alloc/free can be done outside the while without issue. Should also be safe if MS does fix the bug.

Credit: Alistair Brown (in our office) for fishing about, and allocating what should not need to be allocated and thus finding the issue.

Nigel

veletron
  • 91
  • 2
1

After banging my head on this for days, calling this on another thread solved the problem:

var t = Task.Run(() => { device.TransferContentToDevice(fileName, parent.Id); });

t.Wait();
  • This is reproducible only for windows 10, creators update 1703 – Tatiana May 31 '17 at 08:44
  • I am running 1703. The project targets .net 4.5.2, has unsafe checked, and is x86. – ben hopkins May 31 '17 at 14:50
  • [Working](http://i.imgur.com/jPQnyW9.png) Having the code run in Task.Run made the native view show both the com proxy and the implementation object. Without it, it looked like this: [Not Working](http://i.imgur.com/7W8Ut8R.png) I have no idea why though. – ben hopkins May 31 '17 at 14:55
  • It worked for me, at least in one for our multiple application. Thanks a lot! – Math Jul 07 '17 at 14:14
0

I have this same issue also, it looks to be a bug/feature in the Creators Edition build 1703 of Windows 10.

The issue has been reported via the 'Visual Studio and .Net Framework' stream on Microsoft Connect: https://connect.microsoft.com/VisualStudio/Feedback/Details/3135829 (there is no specific area for Windows 10)

I would encourage anyone with this issue to head to the above and indicate that you can reproduce it also.

I have a simple repeater for it which may be downloaded here: http://scratch.veletron.com/WPD_FAIL_REPEATER.ZIP

Paul Roub
  • 36,322
  • 27
  • 84
  • 93
veletron
  • 91
  • 2
  • The issue is also covered on https://social.msdn.microsoft.com/Forums/vstudio/en-US/7f7a045d-9d9d-4ff4-b8e3-de2d7477a177/windows-10-update-1703-problem-with-wpd-and-mtp – veletron Jun 13 '17 at 13:20
  • Windows 10 has a new (I think) mechanism built in for bug reporting and upvoting: 1) Start the 'Feedback Hub' App 2) Search for '1703 windows portable device' (My bug report for this issue) 3) Pick the item 'Windows 10 Build 1703 (Creators Update) bug in Windows Portable Device IStream:Write code - writes to device fail with System.AccessViolationException' 4) Upvote and comment! – veletron Jun 15 '17 at 15:01