Search code examples
c#kernel32

Why Does WriteFile Return False With Invalid Handle?


I'm randomly encountering the WriteFile(..) method returning false. When I call GetLastError() the value returned is 6. Does anyone know what is causing this issue? Here is some of my code:

[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool WriteFile(IntPtr hFile, byte[] lpBuffer,
    uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten,
    IntPtr lpOverlapped);

public void Connect()
{
    IntPtr m_WriteHandleToUsbDevice = CreateFile(m_DevicePathName,
        GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING,
        0, IntPtr.Zero).DangerousGetHandle();
}

public void SendCommand()
{
    if (!WriteFile(m_WriteHandleToUsbDevice, command, bytesToWrite, out bytesToWrite, IntPtr.Zero))
    {
        uint lastError = GetLastError(); // lastError = 6
    }
}

I can send many commands successfully, but it eventually fails at some point. I've tried many different signatures of the WriteFile method but none seem to get around this issue (most just swapping the last parameter for the System.Threading.NativeOverlapped struct). A lot of similar issues were caused by an event property of the NativeOverlapped struct not being initialized, but I've tried that with no success. Below is one of the alternate signatures and logic I've tried:

[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool WriteFile(IntPtr hFile, byte[] lpBuffer,
    uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten,
    [In] ref NativeOverlapped lpOverlapped);

public void SendCommand()
{
    var ewh = new EventWaitHandle(false, EventResetMode.AutoReset);
    var unusedOutVariable = new NativeOverlapped {EventHandle = ewh.SafeWaitHandle.DangerousGetHandle()};
    if (!WriteFile(m_WriteHandleToUsbDevice, command, bytesToWrite, out bytesToWrite, IntPtr.Zero))
    {
        uint lastError = GetLastError(); // lastError = 6
    }
}

Solution

  • Using DangerousGetHandle() does not keep the file open. During the next garbage collector pass, the SafeHandle object will be detected as unreachable and its finalizer will be scheduled. The finalizer will close the handle. Then your other code begins to fail. Why did you think the word Dangerous was in the function name?

    Save the SafeHandle instance in your class member data rather than the IntPtr.