From 83016631b6cb877fd3b09a91a3f5d022d86c4e1a Mon Sep 17 00:00:00 2001 From: Heinz Kirste Date: Sat, 4 Apr 2026 14:48:18 -0600 Subject: [PATCH 1/2] trigger CI From ba546f2ca3ff07d409816368e1f5494a95a7d5ed Mon Sep 17 00:00:00 2001 From: Heinz Kirste Date: Sat, 4 Apr 2026 15:19:43 -0600 Subject: [PATCH 2/2] docs: add build guide and fix driver build in CI Add BUILDING.md documenting the full driver build process including prerequisites, WPP tracing workaround, stampinf INF processing, UMDF version matching, signing, installation, and troubleshooting. Fix build.yml to install the WDK so the UMDF driver project builds successfully. Add driver artifacts and headers to the upload step. Add release job that packages artifacts on version tags. Include commented signing step with instructions for adding a code signing certificate as a GitHub secret. Closes #1 --- .github/workflows/build.yml | 62 +++++++++++++++- BUILDING.md | 139 ++++++++++++++++++++++++++++++++++++ 2 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 BUILDING.md 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` |