C# WinUSB не может вызвать CloseHandle на интерфейсе
Я пытаюсь освободить дескриптор интерфейса USB с помощью CloseHandle. Исключение, которое я получаю:
System.Runtime.InteropServices.SEHException (0x80004005): внешний компонент выдал исключение. в Device.Net.APICalls.CloseHandle(SafeFileHandle hObject) в Usb.Net.Windows.UsbInterface.Dispose() в C:\GitRepos\Device.Net\src\Usb.Net\Windows\UsbInterface.cs: строка 23
в Usb.Net.Windows.WindowsUsbDevice.Dispose() в C:\GitRepos\Device.Net\src\Usb.Net\Windows\WindowsUsbDevice.cs: строка 131
Я пытаюсь сделать это в методе Dispose моего класса.
Изменить фон: причина, по которой я пытаюсь это сделать, заключается в том, что мой код дает сбой при втором запуске. Согласно этой статье, я должен вызвать CloseHandle на дескрипторе устройства, которое я создал с помощью CreateFile. В любом случае это сделано в моем коде, потому что дескриптор устройства удаляется, потому что это SafeFileHandle. Но кто-то сказал мне, что мне нужно вызвать CloseHandle на дескрипторе для интерфейса. Я не знаю, правда ли это или нет. Я пытаюсь сделать это, чтобы исключить возможность того, что не вызов CloseHandle является причиной ошибки. Основываясь на других комментариях и исследованиях, я теперь считаю, что это было ошибкой и достаточно вызова WinUsb_Free. Это правильно?
Ответ Ханса Пассанта, приведенный ниже, говорит мне, чтобы я удалил вызов CloseHandle, но, как я уже отмечал в комментариях, оригинальный код (в основном) никогда не вызывал CloseHandle. Конечно, удаление вызова будет работать, но это не вопрос. Вопрос, приведенный ниже: как происходит выпуск интерфейса USB с WinUSB API? , Это просто для вызова WinUsb_Free? Это то, что вся информация, которую я имею, заставляет меня верить.
Это оригинальный метод утилизации до того, как я задал этот вопрос. У него нет вызова CloseHandle.
public void Dispose()
{
if (_IsDisposed) return;
_IsDisposed = true;
var isSuccess = WinUsbApiCalls.WinUsb_Free(Handle);
WindowsDeviceBase.HandleError(isSuccess, "Interface could not be disposed");
}
Из WindowsUsbInterface ( https://github.com/MelbourneDeveloper/Device.Net/blob/9ebc122a2755dda2824c6eda961d092f2f6e83b5/src/Usb.Net/Windows/WindowsUsbDevice.cs#L122)
public override void Dispose()
{
if (_IsDisposing) return;
_IsDisposing = true;
try
{
foreach (var usbInterface in _UsbInterfaces)
{
usbInterface.Dispose();
}
_UsbInterfaces.Clear();
_DeviceHandle?.Dispose();
_DeviceHandle = null;
base.Dispose();
}
catch (Exception ex)
{
Logger.Log("Error disposing of device", ex, nameof(WindowsUsbDevice));
}
_IsDisposing = false;
}
Из UsbInterface ( https://github.com/MelbourneDeveloper/Device.Net/blob/9ebc122a2755dda2824c6eda961d092f2f6e83b5/src/Usb.Net/Windows/UsbInterface.cs#L18):
public void Dispose()
{
var isSuccess = WinUsbApiCalls.WinUsb_Free(Handle);
WindowsDeviceBase.HandleError(isSuccess, "Interface could not be disposed");
isSuccess = APICalls.CloseHandle(Handle);
WindowsDeviceBase.HandleError(isSuccess, "Interface could not be disposed");
}
Определение вызова API ( https://github.com/MelbourneDeveloper/Device.Net/blob/CloseHandle/src/Device.Net/Windows/APICalls.cs):
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(SafeFileHandle hObject);
Я также попытался просто избавиться от дескриптора, потому что это SafeFileHandle, но метод Dispose для SafeFileHandle выдает мне то же сообщение об ошибке. Это наводит меня на мысль, что метод Dispose SafeFileHandle, вероятно, также вызывает CloseHandle, и возникает та же проблема.
Другие люди упоминали, что я должен использовать IntPtr вместо SafeFileHandle. Итак, я попытался использовать IntPtr в интерфейсе для CloseHandle, но проблема та же:
Вот определение WinUsb_Initialize
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_Initialize(SafeFileHandle DeviceHandle, out IntPtr InterfaceHandle);
Как происходит выпуск интерфейса USB с WinUSB API? Есть ли что-то, что мне нужно сделать по-другому в C#? Почему эта ошибка возникла?
2 ответа
Я думаю, что ответ на этот вопрос заключается в том, что нет необходимости вызывать CloseHandle через интерфейс USB. Согласно методу Dispose вверху этой страницы, для освобождения интерфейса достаточно вызова WinUsb_Free. CloseHandle нужно вызывать только для освобождения дескриптора устройства, которое было создано CreateFile.
public void Dispose()
{
if (_IsDisposed) return;
_IsDisposed = true;
var isSuccess = WinUsbApiCalls.WinUsb_Free(Handle);
WindowsDeviceBase.HandleError(isSuccess, "Interface could not be disposed");
}
Эта статья довольно ясно об этом.
- CloseHandle, чтобы освободить дескриптор, созданный CreateFile, как описано в шаге 1.
- WinUsb_Free для освобождения дескриптора интерфейса WinUSB для устройства, который возвращается WinUsb_Initialize.
Ганс Пассант также рекомендовал:
Удалите вызов CloseHandle() в его методе Dispose()
Также от Ганса Пассанта:
Тип 2-го аргумента неверен, функция не возвращает дескриптор kernel32, поэтому его перенос в SafeFileHandle некорректен. Это непрозрачный дескриптор, WINUSB_INTERFACE_HANDLE в нативном объявлении API, обычно указатель под капотом. Есть только один правильный способ закрыть его, вы должны вызвать WinUsb_Free().
Это напрямую не касается вопроса, который я задавал, но это справедливо. Причина, по которой я не смог вызвать Dispose() для дескриптора, возвращенного из WinUsb_Initialize, как указывает Ганс, состоит в том, что при этом вызову будет закрыт CloseHandle, а 2-й аргумент, возвращаемый WinUsb_Initialize, не является дескриптором kernel32, поэтому CloseHandle() просто победил ' не работает ни на что. Это просто приводит к тому, что, похоже, нет никаких указаний на то, что необходимо вызывать CloseHandle на интерфейсе. Поэтому я считаю, что проблема, с которой я сталкиваюсь (отдельная проблема), не имеет ничего общего с тем, чтобы не вызывать CloseHandle. Кажется, это проблема самой прошивки, и производитель, кажется, подтвердил это. Более подробная информация впереди.
Примечание. Если я не прав, скажите, пожалуйста, почему я не прав, и укажите пример использования CloseHandle для закрытия ручки на интерфейсе USB.
CloseHandle() завершается ошибкой, когда дескриптор не является правильным дескриптором kernel32 или дескриптор уже закрыт. Покопавшись в исходном коде github, я обнаружил, с чего началась эта проблема:
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_Initialize(SafeFileHandle DeviceHandle,
out SafeFileHandle InterfaceHandle);
Отредактировано, чтобы соответствовать и сделать проблему более заметной. Тип 2-го аргумента неверен, функция не возвращает дескриптор kernel32, поэтому его перенос в SafeFileHandle некорректен. Это непрозрачный дескриптор, WINUSB_INTERFACE_HANDLE в нативном объявлении API, обычно указатель под капотом. Есть только один правильный способ закрыть его, вы должны вызвать WinUsb_Free(). Код делает так, но вызов CloseHandle также некорректен и обречен на неудачу. Вызов CloseHandle(), предоставленный SafeFileHandle, также завершится неудачно, вы, вероятно, еще не зашли так далеко.
Измените тип аргумента на IntPtr
, Это требует нескольких других изменений кода, прежде всего в классе UsbInterface. Аналогично измените его тип свойства Handle на IntPtr. Удалите вызов CloseHandle() в его методе Dispose(). Написание своего собственного производного от SafeHandle класса, чтобы обернуть его другим способом, вы бы затем переопределили ReleaseHandle() для вызова WinUsb_Free().