Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

System.AccessViolationException when copying data to portable device after installing windows 10 creators update 1703

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.

like image 365
Tatiana Avatar asked May 15 '17 13:05

Tatiana


2 Answers

--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

like image 195
veletron Avatar answered Oct 02 '22 00:10

veletron


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();
like image 33
ben hopkins Avatar answered Oct 01 '22 22:10

ben hopkins