-
-
Notifications
You must be signed in to change notification settings - Fork 200
/
DeviceFile.cs
97 lines (89 loc) · 3.6 KB
/
DeviceFile.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
// SPDX-FileCopyrightText: 2020 Frans van Dorsselaer
//
// SPDX-License-Identifier: GPL-2.0-only
using System;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Win32.SafeHandles;
using static UsbIpServer.Interop.WinSDK;
[assembly: SupportedOSPlatform("windows")]
namespace UsbIpServer
{
sealed class DeviceFile : IDisposable
{
public DeviceFile(string fileName)
{
handle = NativeMethods.CreateFile(fileName, FileAccess.ReadWrite, FileShare.ReadWrite,
IntPtr.Zero, FileMode.Open, (FileAttributes)FileFlags.FILE_FLAG_OVERLAPPED, IntPtr.Zero);
if (handle.IsInvalid)
{
throw new Win32Exception("CreateFile");
}
if (!ThreadPool.BindHandle(handle))
{
handle.Dispose();
throw new UnexpectedResultException("ThreadPool.BindHandle() failed");
}
}
readonly SafeFileHandle handle;
Task<uint> IoControlAsync(uint ioControlCode, byte[]? input, byte[]? output, bool exactOutput = true)
{
var taskCompletionSource = new TaskCompletionSource<uint>();
var overlapped = new Overlapped();
unsafe
{
void OnCompletion(uint errorCode, uint numBytes, NativeOverlapped* nativeOverlapped)
{
if ((Win32Error)errorCode == Win32Error.ERROR_SUCCESS)
{
if (exactOutput && ((output?.Length ?? 0) != numBytes))
{
taskCompletionSource.SetException(new ProtocolViolationException($"DeviceIoControl returned {numBytes} bytes, expected {output?.Length ?? 0}"));
}
else
{
taskCompletionSource.SetResult(numBytes);
}
}
else
{
taskCompletionSource.SetException(new Win32Exception((int)errorCode, $"DeviceIoControl returned error {(Win32Error)errorCode}"));
}
Overlapped.Free(nativeOverlapped);
}
var nativeOverlapped = overlapped.Pack(OnCompletion, new object?[] { input, output });
fixed (byte* pInput = input, pOutput = output)
{
if (!NativeMethods.DeviceIoControl(handle, ioControlCode, (IntPtr)pInput, (uint)(input?.Length ?? 0),
(IntPtr)pOutput, (uint)(output?.Length ?? 0), out var bytesReturned, (IntPtr)nativeOverlapped))
{
var errorCode = (Win32Error)Marshal.GetLastWin32Error();
if (errorCode != Win32Error.ERROR_IO_PENDING)
{
OnCompletion((uint)errorCode, 0, nativeOverlapped);
}
}
}
}
return taskCompletionSource.Task;
}
public Task<uint> IoControlAsync<T>(T ioControlCode, byte[]? input, byte[]? output, bool exactOutput = true) where T : Enum
{
return IoControlAsync((uint)(object)ioControlCode, input, output, exactOutput);
}
bool IsDisposed;
public void Dispose()
{
if (!IsDisposed)
{
handle.Dispose();
IsDisposed = true;
}
}
}
}