diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8fd6534..459bd90 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,6 +11,8 @@ on: push: branches: - main + tags: + - 'v*' workflow_dispatch: jobs: @@ -33,6 +35,16 @@ jobs: with: msbuild-architecture: x64 + - name: Install WDK + shell: pwsh + run: | + winget install --source winget --exact --id Microsoft.WindowsWDK.10.0.26100 --accept-source-agreements --accept-package-agreements --silent + + $vsWhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" + $vsPath = & $vsWhere -latest -property installationPath + $vsInstaller = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\setup.exe" + & $vsInstaller modify --installPath $vsPath --add Microsoft.VisualStudio.Component.WDK --quiet --norestart + - name: Restore NuGet packages run: msbuild -t:restore -p:RestorePackagesConfig=true @@ -45,10 +57,58 @@ jobs: - name: Build solution run: msbuild WinUHid.sln /p:Configuration=${{ matrix.configuration }} /p:Platform=${{ matrix.platform }} /m /verbosity:minimal + # To enable driver signing, add a code signing certificate as a GitHub secret + # named SIGNING_CERTIFICATE (base64-encoded PFX) and SIGNING_PASSWORD. + # OV certificate is sufficient for x86/x64. ARM64 requires WHQL (EV certificate). + # + # - name: Sign driver + # if: matrix.configuration == 'Release' && secrets.SIGNING_CERTIFICATE + # shell: pwsh + # run: | + # $pfxBytes = [Convert]::FromBase64String("${{ secrets.SIGNING_CERTIFICATE }}") + # [IO.File]::WriteAllBytes("$env:TEMP\cert.pfx", $pfxBytes) + # $signtool = (Get-ChildItem "${env:ProgramFiles(x86)}\Windows Kits\10\bin\*\x64\signtool.exe" -Recurse | + # Sort-Object FullName -Descending | Select-Object -First 1).FullName + # $buildDir = "build\${{ matrix.configuration }}\${{ matrix.platform }}" + # & $signtool sign /f "$env:TEMP\cert.pfx" /p "${{ secrets.SIGNING_PASSWORD }}" /fd sha256 /td sha256 /tr http://timestamp.digicert.com "$buildDir\WinUHidDriver.dll" + # & $signtool sign /f "$env:TEMP\cert.pfx" /p "${{ secrets.SIGNING_PASSWORD }}" /fd sha256 /td sha256 /tr http://timestamp.digicert.com "$buildDir\WinUHid Driver\winuhiddriver.cat" + # Remove-Item "$env:TEMP\cert.pfx" + - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: WinUHid-${{ matrix.configuration }}-${{ matrix.platform }} path: | build/${{ matrix.configuration }}/${{ matrix.platform }}/WinUHid.* - WinUHid/WinUHid.h \ No newline at end of file + build/${{ matrix.configuration }}/${{ matrix.platform }}/WinUHidDriver.* + build/${{ matrix.configuration }}/${{ matrix.platform }}/WinUHid Driver/ + WinUHid/WinUHid.h + WinUHidDevs/WinUHidPS5.h + WinUHidDevs/WinUHidDevs.h + + release: + name: Create Release + needs: build + if: startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Download Release artifacts + uses: actions/download-artifact@v4 + with: + pattern: WinUHid-Release-* + + - name: Package for release + run: | + for dir in WinUHid-Release-*/; do + platform=$(basename "$dir" | sed 's/WinUHid-Release-//') + (cd "$dir" && zip -r "../WinUHid-${platform}.zip" .) + done + + - name: Create release + uses: softprops/action-gh-release@v2 + with: + files: WinUHid-*.zip + generate_release_notes: true diff --git a/BUILDING.md b/BUILDING.md new file mode 100644 index 0000000..e83ba5b --- /dev/null +++ b/BUILDING.md @@ -0,0 +1,139 @@ +# Building WinUHid from Source + +## Prerequisites + +- Visual Studio 2022 (Community or Build Tools) + - Desktop development with C++ workload + - Windows Driver Kit (WDK) component (`Microsoft.VisualStudio.Component.WDK`) +- Windows SDK 10.0.22621 or later +- Windows Driver Kit 10.0.26100 or later (`winget install Microsoft.WindowsWDK.10.0.26100`) + +## Building the User-Mode Library + +The user-mode library (`WinUHid.dll` + `WinUHidDevs.dll`) builds without any special setup: + +``` +msbuild -t:restore -p:RestorePackagesConfig=true +msbuild WinUHid.sln /p:Configuration=Release /p:Platform=x64 /m +``` + +The library can also be compiled with MinGW/GCC by including the source files directly. A WRL compatibility shim is needed for `wrl/wrappers/corewrappers.h` since MinGW doesn't ship those headers. + +## Building the UMDF Driver + +The driver project (`WinUHid Driver/`) requires the `WindowsUserModeDriver10.0` platform toolset. This is registered when you install the WDK component through the Visual Studio Installer. + +### WPP Tracing (WinUHid.tmh) + +The driver uses WPP tracing. The WPP preprocessor generates `WinUHid.tmh` during the build. If your build environment doesn't run WPP (e.g. building outside the full WDK pipeline), create a stub file at `WinUHid Driver/WinUHid.tmh`: + +```c +#pragma once +#define WPP_INIT_TRACING(...) +#define WPP_CLEANUP(...) +#define TraceEvents(level, flags, msg, ...) +``` + +This disables trace logging but the driver functions normally. + +### INF Processing (stampinf) + +The INF file contains macros (`$ARCH$`, `$UMDFVERSION$`) that must be expanded before the driver can be installed. The WDK build pipeline runs `stampinf` automatically, but if you need to process it manually: + +``` +stampinf -f "WinUHid Driver\WinUHidDriver.inf" -a amd64 -u 2.23.0 -d * -v * +``` + +The UMDF version (`-u`) must match the WDF framework on the target system. Common values: +- Windows 10 21H2: `2.23.0` +- Windows 11 22H2+: `2.33.0` + +If the version is too high for the target system, the UMDF reflector will refuse to load the driver. Check `setupapi.dev.log` for messages like "Using WDF schema version X.XX when section requires version Y.YY". + +### UMDF Version Mismatch + +If the driver installs but `WUDFHost.exe` never starts, and `setupapi.dev.log` shows a WDF schema version error, rebuild with headers matching the target OS: + +``` +WDK include path: C:\Program Files (x86)\Windows Kits\10\Include\wdf\umdf\2.23 +WDK lib path: C:\Program Files (x86)\Windows Kits\10\Lib\wdf\umdf\x64\2.23 +``` + +And set `-u 2.23.0` in stampinf. + +## Signing + +UMDF drivers don't require kernel-mode signing. Self-signed certificates work with Secure Boot enabled. + +### For local testing + +```powershell +# Create and trust a self-signed certificate +$cert = New-SelfSignedCertificate -Type CodeSigningCert -Subject "CN=WinUHid Test" -CertStoreLocation Cert:\LocalMachine\My +$store = New-Object System.Security.Cryptography.X509Certificates.X509Store("TrustedPublisher", "LocalMachine") +$store.Open("ReadWrite"); $store.Add($cert); $store.Close() + +# Sign the driver +signtool sign /a /sm /s My /n "WinUHid Test" /fd sha256 WinUHidDriver.dll + +# Create and sign the catalog +New-FileCatalog -Path $stagingDir -CatalogFilePath WinUHidDriver.cat -CatalogVersion 2.0 +signtool sign /a /sm /s My /n "WinUHid Test" /fd sha256 WinUHidDriver.cat +``` + +### For distribution + +- **x86/x64**: OV (Organization Validation) code signing certificate is sufficient +- **ARM64**: Requires WHQL submission through Microsoft's Hardware Developer Program (EV certificate + legal entity) + +## Installation + +### 1. Create the device node + +The `Root\WinUHid` device node must be created through the SetupDI API (`SetupDiCreateDeviceInfoW` + `SetupDiCallClassInstaller(DIF_REGISTERDEVICE)`). The `pnputil /add-device` command doesn't reliably work for this device type. + +See `Installer/RootDevCA/RootDevCA.cpp` for the reference implementation. + +### 2. Install the driver package + +``` +stampinf -f WinUHidDriver.inf -a amd64 -u 2.23.0 -d * -v * +pnputil /add-driver WinUHidDriver.inf /install +``` + +If `pnputil /add-driver` shows "The third-party INF does not contain digital signature information", make sure the catalog file is signed and present alongside the INF. + +### 3. Bind the driver + +After creating the device node and adding the driver package: + +```powershell +# PowerShell - requires P/Invoke for UpdateDriverForPlugAndPlayDevicesW from newdev.dll +UpdateDriverForPlugAndPlayDevicesW(NULL, "Root\WinUHid", "path\to\WinUHidDriver.inf", INSTALLFLAG_FORCE, &needReboot) +``` + +### 4. Verify + +``` +pnputil /enum-devices /class System | findstr WinUHid +``` + +Should show `Status: Started`. Confirm the device is accessible: + +```powershell +[System.IO.File]::Open("\\.\WinUHid", "Open", "ReadWrite", "ReadWrite").Close() +``` + +Note: `Test-Path '\\.\WinUHid'` returns `False` even when the device is working. Use the file handle test above instead. + +## Troubleshooting + +| Symptom | Cause | Fix | +|---------|-------|-----| +| `wdf.h` not found | WDK not installed or UMDF include path not set | Install WDK component through VS Installer | +| `WinUHid.tmh` not found | WPP preprocessor not running | Create the stub file (see above) | +| `$ARCH$` / `$UMDFVERSION$` in INF | stampinf not run | Run stampinf manually | +| Device "Started" but `\\.\WinUHid` not accessible | WUDFRd service not running | `sc start WUDFRd`, then disable/enable the device | +| Device "Started" but WUDFHost not running | UMDF version mismatch | Check `setupapi.dev.log`, rebuild with matching UMDF version | +| `error 87` from WUDFCoinstaller | `$UMDFVERSION$` not expanded in INF | Run stampinf | +| `error 259` from UpdateDriver | `$ARCH$` not expanded in INF | Run stampinf with `-a amd64` |