mirror of
https://github.com/dorssel/usbipd-win.git
synced 2026-11-10 14:43:56 +08:00
178 lines
7.1 KiB
C#
178 lines
7.1 KiB
C#
// SPDX-FileCopyrightText: 2021 Frans van Dorsselaer
|
|
//
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
using System.ComponentModel;
|
|
using System.Runtime.CompilerServices;
|
|
using Windows.Win32;
|
|
using Windows.Win32.Devices.DeviceAndDriverInstallation;
|
|
using Windows.Win32.Foundation;
|
|
|
|
namespace Usbipd;
|
|
|
|
static class DriverTools
|
|
{
|
|
/// <summary>
|
|
/// Remove any driver for the given device.
|
|
///
|
|
/// This is a helper function that is used both when forcing the driver and when restoring the default driver.
|
|
/// </summary>
|
|
/// <returns>true if a reboot is required.</returns>
|
|
/// <exception cref="Win32Exception">On failure.</exception>
|
|
public static bool SetNullDriver(WindowsDevice device)
|
|
{
|
|
using var deviceInfoList = PInvoke.SetupDiCreateDeviceInfoList(null, default);
|
|
if (deviceInfoList.IsInvalid)
|
|
{
|
|
Tools.ThrowWin32Error(nameof(PInvoke.SetupDiCreateDeviceInfoList));
|
|
}
|
|
var deviceInfoData = new SP_DEVINFO_DATA()
|
|
{
|
|
cbSize = (uint)Unsafe.SizeOf<SP_DEVINFO_DATA>(),
|
|
};
|
|
PInvoke.SetupDiOpenDeviceInfo(deviceInfoList, device.InstanceId, default, 0, ref deviceInfoData)
|
|
.ThrowOnWin32Error(nameof(PInvoke.SetupDiOpenDeviceInfo));
|
|
PInvoke.DiInstallDevice(default, deviceInfoList, deviceInfoData, null, DIINSTALLDEVICE_FLAGS.DIIDFLAG_INSTALLNULLDRIVER, out var reboot)
|
|
.ThrowOnWin32Error(nameof(DIINSTALLDEVICE_FLAGS.DIIDFLAG_INSTALLNULLDRIVER));
|
|
return reboot;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Force the driver for the given device to the current VBoxUsb driver.
|
|
///
|
|
/// This is a helper function that is used both when forcing the driver (requires setting a NULL driver first)
|
|
/// and when updating the driver.
|
|
/// </summary>
|
|
/// <returns>true if a reboot is required.</returns>
|
|
/// <exception cref="Win32Exception">On failure.</exception>
|
|
public static bool SetVBoxDriver(WindowsDevice device)
|
|
{
|
|
using var deviceInfoList = PInvoke.SetupDiCreateDeviceInfoList(null, default);
|
|
if (deviceInfoList.IsInvalid)
|
|
{
|
|
Tools.ThrowWin32Error(nameof(PInvoke.SetupDiCreateDeviceInfoList));
|
|
}
|
|
var deviceInfoData = new SP_DEVINFO_DATA()
|
|
{
|
|
cbSize = (uint)Unsafe.SizeOf<SP_DEVINFO_DATA>(),
|
|
};
|
|
PInvoke.SetupDiOpenDeviceInfo(deviceInfoList, device.InstanceId, default, 0, ref deviceInfoData)
|
|
.ThrowOnWin32Error(nameof(PInvoke.SetupDiOpenDeviceInfo));
|
|
var deviceInstallParams = new SP_DEVINSTALL_PARAMS_W()
|
|
{
|
|
cbSize = (uint)Unsafe.SizeOf<SP_DEVINSTALL_PARAMS_W>(),
|
|
Flags = SETUP_DI_DEVICE_INSTALL_FLAGS.DI_ENUMSINGLEINF,
|
|
FlagsEx = SETUP_DI_DEVICE_INSTALL_FLAGS_EX.DI_FLAGSEX_ALLOWEXCLUDEDDRVS,
|
|
DriverPath = DriverDetails.Instance.DriverPath,
|
|
};
|
|
PInvoke.SetupDiSetDeviceInstallParams(deviceInfoList, deviceInfoData, deviceInstallParams)
|
|
.ThrowOnWin32Error(nameof(PInvoke.SetupDiSetDeviceInstallParams));
|
|
PInvoke.SetupDiBuildDriverInfoList(deviceInfoList, ref deviceInfoData, SETUP_DI_DRIVER_TYPE.SPDIT_CLASSDRIVER)
|
|
.ThrowOnWin32Error(nameof(PInvoke.SetupDiBuildDriverInfoList));
|
|
var driverInfoData = new SP_DRVINFO_DATA_V2_W()
|
|
{
|
|
cbSize = (uint)Unsafe.SizeOf<SP_DRVINFO_DATA_V2_W>(),
|
|
};
|
|
PInvoke.SetupDiEnumDriverInfo(deviceInfoList, deviceInfoData, SETUP_DI_DRIVER_TYPE.SPDIT_CLASSDRIVER, 0, ref driverInfoData)
|
|
.ThrowOnWin32Error(nameof(PInvoke.SetupDiEnumDriverInfo));
|
|
PInvoke.DiInstallDevice(default, deviceInfoList, deviceInfoData, driverInfoData, 0, out var reboot)
|
|
.ThrowOnWin32Error(nameof(PInvoke.DiInstallDevice));
|
|
|
|
// Best effort, not really a problem if this fails.
|
|
_ = device.SetFriendlyName();
|
|
|
|
return reboot;
|
|
}
|
|
|
|
public static bool ForceVBoxDriver(WindowsDevice device)
|
|
{
|
|
var reboot = false;
|
|
|
|
// First, we must set a NULL driver to clear any existing Device Setup Class.
|
|
if (SetNullDriver(device))
|
|
{
|
|
reboot = true;
|
|
}
|
|
|
|
// For some devices (Google Pixel) it takes a while before the driver can be set again.
|
|
// 200 ms seems to work, so delay for 500 ms for good measure...
|
|
Thread.Sleep(TimeSpan.FromMilliseconds(500));
|
|
|
|
if (SetVBoxDriver(device))
|
|
{
|
|
reboot = true;
|
|
}
|
|
|
|
return reboot;
|
|
}
|
|
|
|
public static bool UnforceVBoxDriver(WindowsDevice device)
|
|
{
|
|
if (!device.HasVBoxDriver)
|
|
{
|
|
// The device does not have the VBoxUsb driver installed ... we're done.
|
|
return false;
|
|
}
|
|
|
|
var reboot = false;
|
|
|
|
// First, we must set a NULL driver, just in case no default driver exists.
|
|
if (SetNullDriver(device))
|
|
{
|
|
reboot = true;
|
|
}
|
|
|
|
// For some devices (Google Pixel) it takes a while before the driver can be set again.
|
|
// 200 ms seems to work, so delay for 500 ms for good measure...
|
|
Thread.Sleep(TimeSpan.FromMilliseconds(500));
|
|
|
|
// Now we let Windows install the default PnP driver.
|
|
// We don't fail if no such driver can be found.
|
|
using var deviceInfoSet = PInvoke.SetupDiCreateDeviceInfoList(null, default);
|
|
if (deviceInfoSet.IsInvalid)
|
|
{
|
|
Tools.ThrowWin32Error(nameof(PInvoke.SetupDiCreateDeviceInfoList));
|
|
}
|
|
var deviceInfoData = new SP_DEVINFO_DATA()
|
|
{
|
|
cbSize = (uint)Unsafe.SizeOf<SP_DEVINFO_DATA>(),
|
|
};
|
|
PInvoke.SetupDiOpenDeviceInfo(deviceInfoSet, device.InstanceId, default, 0, ref deviceInfoData)
|
|
.ThrowOnWin32Error(nameof(PInvoke.SetupDiOpenDeviceInfo));
|
|
try
|
|
{
|
|
PInvoke.DiInstallDevice(default, deviceInfoSet, deviceInfoData, null, 0, out var tmpReboot)
|
|
.ThrowOnWin32Error(nameof(PInvoke.DiInstallDevice));
|
|
if (tmpReboot)
|
|
{
|
|
reboot = true;
|
|
}
|
|
}
|
|
catch (Win32Exception ex) when ((WIN32_ERROR)ex.NativeErrorCode == WIN32_ERROR.ERROR_NO_DRIVER_SELECTED)
|
|
{
|
|
// Not really an error; this just means Windows does not have a default PnP driver for it.
|
|
// The device will be listed under "Other devices" with a question mark.
|
|
}
|
|
return reboot;
|
|
}
|
|
|
|
public static bool UninstallStubDevice(WindowsDevice device)
|
|
{
|
|
using var deviceInfoSet = PInvoke.SetupDiCreateDeviceInfoList(null, default);
|
|
if (deviceInfoSet.IsInvalid)
|
|
{
|
|
Tools.ThrowWin32Error(nameof(PInvoke.SetupDiCreateDeviceInfoList));
|
|
}
|
|
var deviceInfoData = new SP_DEVINFO_DATA()
|
|
{
|
|
cbSize = (uint)Unsafe.SizeOf<SP_DEVINFO_DATA>(),
|
|
};
|
|
PInvoke.SetupDiOpenDeviceInfo(deviceInfoSet, device.InstanceId, default, 0, ref deviceInfoData)
|
|
.ThrowOnWin32Error(nameof(PInvoke.SetupDiOpenDeviceInfo));
|
|
PInvoke.DiUninstallDevice(default, deviceInfoSet, deviceInfoData, 0, out var reboot)
|
|
.ThrowOnWin32Error(nameof(PInvoke.DiUninstallDevice));
|
|
|
|
return reboot;
|
|
}
|
|
}
|