Moving from HIDMaestro 1.x to 2.0.0¶
Programs built on HIDMaestro 1.x must move to 2.0.0. A 2.0.0 program installs its driver over any older one, whatever runs on it, and refuses nothing because a 1.x program is in the way. A running 1.x program still removes every HIDMaestro device on the machine at its InstallDriver and its first create, 2.0.0 controllers included, so a machine works only once every program on it is on 2.0.0.
This page lists what a program built on v1.12.0 meets when it moves. The design notes give the reasons: programs side by side and every device as data. Programs Side by Side and the Profile System describe the same changes on this site.
Before you start¶
HIDMaestroProfileCheck, in the release ZIP beside the profile extractor, loads a profile file or directory as 2.0.0 does and prints every refusal with its JSON path. Run it over your own profiles first.HMLayoutJsonOptions.Defaultis the source-generated context's options, so it is read-only and reads and writes the SDK's own types only. Code that changed a setting on it, to add a converter or change the naming policy, throwsInvalidOperationException. Copy it withnew JsonSerializerOptions(HMLayoutJsonOptions.Default)to change a copy.- The SDK publishes as NativeAOT and as a trimmed single-file app. A 1.x app published that way loaded none of the 336 profiles.
- Creates, deploys and the machine-wide locks need an elevated process and throw
UnauthorizedAccessExceptionwithout one. Two calls return instead:RemoveAllVirtualControllers()andRemoveAllVirtualControllers(bool)do nothing, andInstallDriverreturnsAlreadyCurrentwhen its fast path finds this build's driver recorded and both packages in the store.
Profile JSON¶
Profiles load strictly. A key the schema lacks, a key written twice, a value a check refuses, a removed spelling and an id already loaded each refuse the file, where 1.x dropped an unknown key without a word, let the second of two keys win and skipped a file that did not parse. A load never throws for a file's content: HMContext.ProfileLoadIssues lists every refused file, and every profile that loaded with a model problem, with the file, the JSON path and the reason. Files load in ordinal path order, and a repeated id refuses the later file and names the earlier one, where 1.x skipped it without a word, so a user's edited copy of a catalog profile needs an id of its own. Check ProfileLoadIssues after a load to see why a profile is missing.
A profile written for 1.x that left out a key and got a behavior from code now states it, so no such profile changes its bytes unseen. Each refusal below is an InvalidOperationException at create, before anything machine-wide, that names the profile, the field's JSON path and the value that keeps 1.x's bytes.
- A
uint16-triggeroruint16-pressurefield needsmax. 1.x wrote a full pull or press as 32767: add"max": 32767for that, or"max": 65535for the full width. - An
int16-axis,int16-padorint16-pad-or-stickfield of a Y semantic needsinvert. 1.x negated every int16 Y: add"invert": truefor that, or"invert": falsefor a wire that grows downward. - The
ds3-batterytype is gone. Write it asuint8-level-mapwithlevels[1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5],chargingValue238 andfullValue239. - An output field refuses
min,maxanddpad,inverton any type butuint8-axis, andcenteronint16-le, since its decoder ignores them. - A parameter on a type that ignores it is refused:
minandmaxon a type with no normalized value,inverton a type with no axis,scaleon anything but anint16-lefield of a calibrated source, andminequal tomax. - The device model is checked when a file loads and again at create: the top-level report fields, the ports, the protocol, the report programs and the WinUSB block. A profile built or changed in code is refused at create with
HMProfileException, derived fromInvalidOperationException, which carriesProfileId,File,JsonPathandReason. It replaces theArgumentExceptiona profile with no descriptor threw and theHMLayoutValidationExceptiona layout that did not fit its descriptor threw, which the create no longer throws.HMProfileBuilder.Buildthrows it for its own errors. A button name the SDK cannot express fails the create, where it failed the first submit. Every catalog profile compiles. usbConfiguration.protocolmoved toprotocolat the profile's top level and is refused at its old place, with the path that says where it went.steamFirmwareStampis refused, with its replacement named: apatchwith akeyedHexFilevalue source.nativeDescriptor, and acommentinsideextendedReport, are mapped keys now, so a copy of a catalog profile that carries them loads.- A profile with no feature answers, protocol or presentation of its own takes them from the model it names with
deviceModelFrom, or from the identity defaults, the gates 1.x kept in code turned into data: the Switch Pro (057E:2009) over USB and over Bluetooth,sixaxisCompatible, Sony's VID 054C by product and connection, and VID 045E with "xbox" in the id, name or product string, which keeps an XUSB companion. A profile'sxinputblock, ordriverModeas its alias, states the presentation itself."deviceModel": "none"takes no model.HMProfile.HasDeviceModel,DeviceModelSourceandDeviceModelProblemreport what a profile got, andHMDeviceExtractor.Extractwrites thedeviceModelFromits identity gives. - A descriptor hex string with a character that is not a hex digit, or an odd number of digits, refuses the file at load, and a profile built in code at create with
HMProfileException.HMProfile.GetDescriptorBytesandHMProfile's layout properties throwFormatExceptionnaming the profile and its descriptor field. An odd count used to load a descriptor a byte short. inputReportSizeis the input report's whole length, its ID byte included.dataReportIdnames the input report a profile's data frames fill when the SDK's choice is not the one the device sends.- A custom USB/IP profile 1.x accepted can be refused for descriptor checks 1.x did not make: one transfer type per endpoint across alternate settings, an isochronous endpoint in one setting only, a structured endpoint matching within its own interface and setting, and the low-speed limits.
busSpeed"low"is now a low-speed device, with Windows' low-speed poll table and packet limits, where 1.x treated it as high speed.
New keys: min, max, invert, center on int16-le, scale with negate and repeat, dpad: "resolved", "#n" in buttons, the uint8-level-map type, clock: "accumulate" with divisor and holdWhile, the packed-uint output type, and at the top level axisCenters, deviceModelFrom, deviceModel, per-entry featureStubs tables, valueSources, protocol, stateBlock, inputReports, liveFeatureReports, stream, xinput, bluetoothTransport and bluetoothVendorIdSource. Since v2.0.2, triggerThreshold on a button-mask. The Profile System page describes each.
Bytes on the wire¶
- Every normalized value goes to the wire as
min + RoundHalfEven(v' x (max - min))in double, where 1.x truncated or multiplied in float. A value can arrive one count higher than under 1.x. An unsigned axis of 2^n - 1 counts rests one count higher (0..255 at 128, 0..65535 at 32768). XInput thumbs rest at 0 and 0, and every XInput trigger byte arrives as passed, where 252 of 256 arrived one lower. - A stick the program leaves out of
Axesrests at its center. 1.x rested a 0..255 or 0..65535 stick at its minimum, full left or up. Other axes keep their old rest. - A NaN in
Axeswrites the axis's resting value on every path and never reads the key a slot falls back to. 1.x wrote whatever each encoding gave a NaN: XInput X full left and Y full up, the codec's and the Switch 2 Pro's sticks at a corner. amazon-luna-usbcenters its sticks at 0x7F andamazon-luna-bleat 0x7FFF, where SDL reads zero.steam-controller-2's report 0x42 timestamp counts elapsed microseconds, where it added 4000 per report whatever the time, and holds still while the host has its IMU off.- 21 catalog profiles repeat their last frame at rest at their device's period, 18 of which sent nothing at rest under 1.x: the DualSense family at 1, 1.25 or 4 ms, the DualShock 4 USB family and
steam-deckat 4 ms,openffboardat 101 ms and five Thrustmaster wheels at 250 ms. A consumer that counts frames sees them. ps-movepresents the descriptor a real Move serves over USB: a Game Pad with three top-level collections, so Windows creates three HID children where it made one, and its HID child's usage hardware ID becomesUP:0001_U:0005. Its input report is the Move's own. Cross isHMButton.A, CircleB, SquareX, TriangleY, SelectBack, StartStart, PSGuideand MoveMisc1, and the T trigger is the left trigger. Motion goes inAccelGX/GY/GZandGyroDpsX/Y/Z, and the rawAccel*andGyro*shorts are not read. Its battery byte is 0xEE, or 0xEF withBatteryFull. Its manufacturer string is "Sony Computer Entertainment". Its descriptor declares no feature report 0x02, which drew a DualShock 4's calibration from the driver under 1.x.xbox-360-flight-stickpresents the 127-byte joystick descriptor its notes cite: 8-bit Z and Rz ahead of its 16-bit axes, a 12-byte report. Its HID child's usage hardware ID becomesUP:0001_U:0004.- A program that stored the HID child path of
ps-moveorxbox-360-flight-sticksees a new device once. - When the record of an identity's HID child is out of the tree with no driver and names HIDMaestro, as a removal vetoed under 1.x left it, a parent under
ROOTgives its child the next prefix,1&hash&1to1&hash&3, because every later child on the identity's own path stopped with problem 19. A program that stored that child's path sees a new device once. - The nine Microsoft profiles that are no XInput device, the SideWinder Force Feedback 2 and eight keyboards and mice, move their parent from
ROOT\VID_045E&PID_xxxx&IG_00toROOT\HIDClasswithroot\VID_045E&PID_xxxxand no XUSB compatible IDs, and SDL now lists them.xbox-360-wirelesspresents its HID child as 045E:02A1 and reports itself wireless in XInput, with PID 0 in its 36-byte capability reply, and its parent moves toVID_045E&PID_02A1&IG_00. A program that stored one of these ten profiles' HID child path sees a new device once. xbox-360-wheel-v1and-v2report XInput subtype 2, the wheel, where they reported 1.- A Bluetooth profile carries the hardware and compatible IDs of a real device of its transport,
BTHENUMfor classic andBTHLEDevicefor LE (bluetoothTransport), and no USB bus type. A create stamps the bus type only on the parent it made, where 1.x stamped GUID_BUS_TYPE_USB on every HIDMaestro devnode on the machine and its first child. switch-prosends its 0x30 reports on a 15 ms grid on the device clock, where the driver slept 15 ms between frames, about 15.6 ms at the default timer resolution. It sends 0x3F when its bytes change, before the handshake and after a host selects simple mode, where it streamed 0x3F every 15 ms before the handshake and sent nothing in mode 0x3F. A new profile,switch-pro-usb, is the pad on its USB descriptor, and a profile outside the catalog of 057E:2009 over USB takes its protocol.dualshock-3-full's battery byte reads 0xEE, charging, at every level and 0xEF withBatteryFull, as a DualShock 3 on USB reports, where an unset state read as a dying pad.- The nine Xbox 360 profiles whose manufacturer string was mangled read U+00A9 "Microsoft Corporation" in
HMProfile.ManufacturerString. Nothing on the wire changes. - Sixteen catalog profiles send their data-only frames as a different input report or at a different length on a UMDF2 controller:
logitech-wingman-formula-gp,gamecube-adapter-hid,joypad-gc-adapter,microsoft-designer-keyboard,genesis-nso,n64-nso,snes-nso,joycon-l,joycon-charging-grip,switch2-pro-controller,joypad-steam-input,ps3-remote,circuitpython-gamepad,pxn-vd6,thrustmaster-t16000mandvader-4-pro.HMProfile.InputReportSizeis 7 forcircuitpython-gamepad, 63 forpxn-vd6, 9 forthrustmaster-t16000mand 9 forvader-4-pro.ps3-remote,saitek-x52,thrustmaster-t80andjoypad-steam-inputpresent corrected report descriptors. ps3-remote's corrected descriptor is 69 bytes.tinyusb-generic-gamepadandnacon-bigben-ps4carried descriptors Windows' HID parser refuses, so their devices failed to start.tinyusb-generic-gamepadserves TinyUSB's gamepad descriptor: input report 2 of 12 bytes with six 8-bit axes, an 8-bit hat and 32 buttons, and no output or feature report.nacon-bigben-ps4serves the descriptor the pad serves: a 27-byte input report with X, Y, Z and Rz, L2 and R2 as buttons 7 and 8, and 8-byte vendor output and feature reports. It is now named "BigBen Game Pad for PS3", with "Bigben Interactive" and "Bigben Game Pad" as its USB strings, since 146B:0902 is that pad. Its id is unchanged.- An int16-le field with a
centerclamps its word to 0 to 65535. - Since v2.0.2
steam-deck-composite,steam-controller-compositeandsteam-controller-2read the calibrated motion fields,AccelGX/GY/GZandGyroDpsX/Y/Z, where they copied the rawAccelX/Y/ZandGyroPitch/Yaw/Roll, which no longer reach them. Their digital trigger bits set at 90 percent of travel on the Deck and the 2026 controller and at the end of travel on the 2015 controller, where they set at the first count. A touch withTouchpadFinger0PressureorTouchpadFinger1Pressureabove 0 sends that pressure, and one at 0 sends full pressure as before. A raw submit is unchanged. HMButtonbits 23 and 24 gain names,LeftTriggerClickandRightTriggerClick. A descriptor-built report maps them to buttons 24 and 25, as it mapped those bits before.- An output button mask wider than one byte reports its bits 8 and up on
OutputDecoded. SubmitMouseon a profile whose relative field excludes zero, such ascorsair-k70-rgb-mk2-low-profile, no longer splits a movement the field cannot carry without end.HidReportBuilder.InputFields: an element past a field's last usage carries that last usage, not 0, and a field's page is its usage's page. A stick added afterHidDescriptorBuilder.AddRawbytes that push, change the page and pop is on the Generic Desktop page, where it landed on the Button page.
Creates¶
CreateControllerandCreateControllerAtthrowHMResourceInUseException, derived fromInvalidOperationException, when another live controller on the machine holds the identity key, where 1.x checked only its own context. The same exception refuses a create when no transport index is free, when a v1.12.0 program starts on the index during the create, when another program made one of the index's events, and when the device of a running program, or of one whose owner cannot be known, holds the same ParentIdPrefix. 1.x removed that rival device.- A keyless controller can land on a transport index other than its slot when another program uses that index, so its default identity and device paths can differ between runs. Pass an identity key for stable paths. The slot of
CreateControllerAt(n)is n, and its transport index can differ from n.HMController.TransportIndexandSectionNamesgive the numbers and names. - While a v1.12.0 program runs, a create's transport index starts at 64. A keyless controller's paths, container id, HID serial and MAC bytes, and a keyless composite's USB serial and instance id, move with it. A keyed controller keeps its paths and container id, and its HID serial and MAC bytes move. A create with the key
index:N, N below 64, takes index N's default identity as asked, and that program's next create on index N replaces a devnode of that name. - A keyed create whose identity another program's sweep is removing waits for that sweep, up to 120 seconds, instead of failing. A keyless create waits the same way for its index's identity.
- A create waits while another program of this release registers or removes a devnode, and throws
InvalidOperationExceptionafter ten minutes. It waits up to 30 seconds for Windows' pending device installs throughCMP_WaitNoPendingInstallEvents, which Microsoft says not to call "while processing any events inside of a system-initiated callback function that is expected to return within a short amount of time", a service'sServiceMainor control handler among them, so a service should not create controllers there. - A create throws
InvalidOperationExceptionwhen a prior life of its ROOT devnode, or that devnode's record, is still there after its removal, and when a software companion's prior life is still in the tree after its removal, where it reconnected to it. A gamepad or XUSB companion that is not made fails the create, where it succeeded without it: an xinputhid controller had no other device, and an Xbox 360 controller got no XInput. The failed create's cleanup removes what it made. - A create no longer restarts a leftover
ROOT\XNACOMPOSITE\0000. - A create throws
HMPayloadMissingException, derived fromInvalidOperationException, when the build lacks the payload it needs, before any machine-wide step.InstallDriver,InstallUsbipBackendandHMVR.EnsureDriverRegisteredthrow it too, andIsUsbipBackendAvailableis false in a build without the USB/IP transport. - A context dropped without
Disposekeeps its controllers, their transport indices and their identity keys until the process exits, and a create of one of those keys in that process throwsHMResourceInUseExceptionuntil then.
Cleanup, exit and the driver¶
RemoveAllVirtualControllers()andRemoveAllVirtualControllers(bool)no longer remove the controllers of running programs, the caller's own included. WithpreserveInstall: falsethey keep the driver packages while a device is present, another program runs or the caller has a controller. They remove packages under the deploy lock and throwInvalidOperationExceptionafter waiting ten minutes for another program's deploy. A tool that wants every controller gone, such as an uninstaller or a test harness, callsRemoveAllVirtualControllers(HMCleanupScope.Everything, preserveInstall).- After
RemoveAllVirtualControllers(HMCleanupScope.Everything, ...), a controller the program still holds has no device. Its submits return and reach nothing, its output events stop,DriverVersionis null,DeliversEveryReportandReleasesOnConsumerExitare false, and it keeps its transport index until disposed. Dispose it after the sweep. - Both also remove the GameInput tables v1.11.0 left, except a table whose VID and PID a present device or any controller configuration carries, and none while an input section exists that nothing explains. They no longer remove phantom records on every call: the first of
InstallDriver, the first create andRemoveAllVirtualControllers()starts the once-per-process cleanup on a background thread and returns without waiting, and a create and theEverythingcleanup wait behind it. - A program that exits normally without disposing its controllers has their devices removed as it exits, within one 10-second wait for a create in flight and the phantom cleanup. When that wait runs out, or a device does not leave, the devices stay present until the next sweep of any program, as a killed program's do. The exit keeps the program's record while a GameInput or joy.cpl hold of its own stands.
- A
Disposeor cleanup a program calls from its ownProcessExithandler waits for another program's hold of the PnP lock within one 10-second budget the process shares. A controller it cuts short keeps its devices and its record entry, for the SDK's own exit handler when it runs after the program's, or the next sweep. - A dispose of several controllers takes about the sum of their removals, since every devnode removal holds the machine-wide PnP lock, where it took about the longest one. Each removal waits for its DEVICEINSTANCEREMOVED notification, up to 2 s more when it is late, and a HID child removed with its parent counts on the parent's.
RemoveAllVirtualControllers(HMCleanupScope.Everything, …)waits up to 2 s per removal, where it waited for none. InstallDriverno longer removes devices of running programs before deploying and never downgrades the driver. It installs the 2.0.0 driver over any older one and returnsUpgraded: a full deploy when nothing runs on the old driver, and this build's packages added beside the old ones when another program's devices or create hold it, which updates no present device.HMDriverInstallOutcomehas noKeptInUse, and nothing is refused for a holder.HMContext.LastDriverInstallsays what it did. It can throwHMPayloadMissingException,InvalidOperationExceptionafter waiting ten minutes for another program's deploy, andInvalidOperationExceptionafter waiting two minutes for another program's staging prune.- A UMDF2
CreateControllerchecks the installed driver set under the deploy lock and runs the install policy when either INF is missing or older than this build's, so a program whose packages another program uninstalled or replaced with older ones gets its own back at its next create, and a create can wait for another program's deploy. A create whose started driver does not serve protocol 4, or could not read a value the create wrote, removes its device and throwsHMDriverVersionException, whoseProblemsays which (ProtocolNotServed,ValueNotRead). A start the driver fails fails the create within one poll and names the device and its problem code. While Windows has a device installation pending, a failed start waits for it, since a device Windows flagged for reinstall fails its start until the queued install restarts it, and a wait that runs out fails the create on any problem the device shows. A USB/IP composite create takes the transport lock for the usbip-win2 install instead. - A plain HID or non-xinputhid create installs the driver on its own devnode only, so a newer SDK no longer moves another program's controller of the same profile onto its package.
InstallUsbipBackendand a process's first composite create throwInvalidOperationExceptionafter waiting ten minutes for the transport lock, when a usbip-win2 installer still runs after five minutes, and when usbip-win2's uninstall entry exists without its driver package. Since 2.0.1 that last case no longer throws: the SDK installs the host controller from its own package and never runs that entry's uninstaller.- Staging moved from
%TEMP%to%ProgramFiles%\HIDMaestro\Staging.InstallDriver, a UMDF2 create whose device needs the SWD helper,InstallUsbipBackend, a first composite create,BindWinUsband the publicDriverBuildersteps throwInvalidOperationExceptionwhen%ProgramFiles%\HIDMaestrobelongs to another program, when a staging directory that cannot be moved aside is hostile, when another program creates one while HIDMaestro creates it, when a staged file does not match its SHA-256 or changed between its repair and its use, and, in an elevated process, when the protected TEMP for pnputil or devcon cannot be made. HMContext.IsDriverInstalledanswers false when the driver store cannot be read, logs why, and throws onlyObjectDisposedException.DriverBuilder.IsDriverInstalled()throwsInvalidOperationExceptionwhen the pnputil listing fails, where it returned false.%TEMP%\HIDMaestro\hmswd_<enumerator>_<suffix>.logis no longer written, and hmswd.exe writes nohmswd_self.log. hmswd's exit code and output go intoteardown_diag.logunderHIDMAESTRO_DIAG=1, and the diagnostic logs are written as the user.- The driver changed, so the first
InstallDriver()after updating reinstalls it once.
Controllers and events¶
- An exception a handler throws on one of the SDK's threads no longer reaches the program. The SDK drops it and the next handler runs. A throwing audio handler no longer unplugs a composite controller, and a throwing
FramesReceived,StreamingChangedorHapticReceivedhandler no longer ends the process. TheInstallUsbipBackendprogress callback is isolated the same way and runs on the calling thread with no machine-wide lock held. SetHIDMAESTRO_DIAG=1to see each dropped exception in the diagnostic log. Disposewaits for a submit in progress, and a submit that races it throwsObjectDisposedException.OnSubmitLatencyMicrosruns after the submit lock is released, so two submitting threads can run it at the same time.- A handler can dispose its own controller, or call
HMContext.DisposeorDisposeControllersInParallel, without a wait on its own thread. The reader raises the rest of that packet's events on the disposed controller. HMContext.DisposeControllersInParalleldisposes every controller of the set whenperControllerCallbackthrows, runs the batch orphan sweep, and then throws oneAggregateExceptionholding each exception. In a set of two or more, controllers not yet started were left undisposed once a callback threw. With one controller the callback's exception came out bare.- On a composite,
HMUsbAudio.ControlChangedand bothStreamingChangedevents run on an audio event thread of their own, in the host's order, and can come afterDisposereturns. A newer write to a control replaces one still waiting.IsStreamingreads the device's stream state, so it can change beforeStreamingChangedis raised. Dispose waits up to 500 ms for a running audio handler. A USB/IP Dispose no longer waits up to 500 ms for its input thread. - A USB/IP persona without a protocol answers GET_REPORT(Input) with the requested report's latest bytes, or its ID and zeros before one is sent, and stalls an ID its descriptor does not declare. The Switch 2 Pro persona answers report ID 0 with its selected report, and the GameCube adapter answers the requested ID alone. 1.x answered with the last report sent whatever the ID.
- A device that builds its own reports (
HMProfile.BuildsItsOwnReports:switch-pro,switch-pro-usb,dualshock-3-fulland the Switch 2 Pro composite) takes a raw submit as its state block, verbatim and of the state block's size, and throwsArgumentExceptionfor another length. 1.x's driver read whatever bytes landed at fixed offsets of a Switch Pro raw submit, and the composite threwNotSupportedException.HMDeviceProtocol.HostPollInterval's setter throwsInvalidOperationExceptionon such a device and on one whose protocol names no input endpoint, where it took the value and did nothing.HMDeviceProtocolgainsGetRegister,CounterNamesandGetCounter. - The USB/IP server answers an import from anything but the host controller with
ST_NA. A program that drove the SDK's server with its own user-mode USB/IP client, which was never supported, no longer can.
Other APIs¶
HMVR.EnsureDriverRegisteredwrites the OpenVR path registry itself and runs no vrpathreg, so a driver registered while SteamVR runs loads at its next start. Each failure to update the path registry is anInvalidOperationExceptionwith the cause inside.HMVR.UnregisterDriverthrowsInvalidOperationExceptionwhen the registry cannot be updated, where it returned.HMVR.EnsureDriverRegisteredthrowsInvalidOperationExceptionnaming the folder when an OpenVR folder another program owns cannot be moved aside, or when a program holds a folder or file there open while the extraction writes it, where it threwIOException.HMOemNameOverride.Setneeds an elevated process.ClearandRecoverOrphansneed one only when a record is pending, and return without one when nothing is.HMController.BindWinUsbthrowsTimeoutExceptionwhen the registry ledger's lock, or v1.12.0's mutex beside it, stays held for 30 s, scaled,InvalidOperationExceptionwhen the WinUSB lock stays held for two minutes, andObjectDisposedExceptionwhen the controller is disposed during the bind. An uninstall-grade cleanup that keeps a WinUSB package notes it inHKLM\SOFTWARE\HIDMaestroPackagesLeft, and a later program's sweep removes the key with the last noted package.DeviceManager.WaitForDeviceRemoval(id, timeout), public inHIDMaestro.Internal, waits for the DEVICEINSTANCEREMOVED notification of a devnode present at the call, up to 2 s past the devnode's leaving, where it returned once a locate failed.DeviceManager.RemoveDeviceandRemoveOrphanHidChildrentake the PnP lock, so they need an elevated process to touch a present devnode, andRemoveDevicecan throwInvalidOperationExceptionwhen another program holds the lock for ten minutes, with nothing removed.RemoveDevicecan also throw the SWD helper's staging refusal before any wait, and leaves the device in place.DriverBuilder.SignDrivers()andGenerateCatalogs()hold the deploy lock, up to ten minutes, then throwInvalidOperationException.InstallDrivers()runs the install policy and returns true only when it installed or upgraded, where it always returned true.RemoveOldDriverPackages()throwsInvalidOperationExceptionstating what holds the packages instead of removing them under running devices.FullDeploy()runs the install policy and never downgrades.EnsureExtracted()andBuildDirstage under%ProgramFiles%\HIDMaestro\Staging, check every staged file on each call and need an elevated process. They throwHMPayloadMissingExceptionwhen the build lacks the driver for the machine's architecture, andInvalidOperationExceptionafter waiting two minutes for another program's staging prune.EnsureTestCertificate()creates the certificate under the deploy lock.HMProfile.XInputandHMProfileBuilder.XInputread and set thexinputblock, and the builder gainsBluetoothTransportandBluetoothVendorIdSource.HMDeviceExtractor.Extractreads the bus from the parent devnode's compatible IDs, as hidapi and SDL do, where it read the device path. A Bluetooth device, a HIDMaestro one included, extracts asbluetoothwith itsbluetoothTransportand, for a Bluetooth SIG vendor ID,bluetoothVendorIdSource, andToJsonwrites both.HMProfileBuilder.FromProfilecopies the whole profile, its model keys anddeviceModelFromincluded, where it dropped the feature table,driverPid, the extended reports and the protocol.VidandPidon a copy of a USB/IP persona write into its USB device descriptor too. A builder given no VID or PID throwsHMProfileException, a copy keeps its source's, and an explicitVid(0)orPid(0)builds.DescriptorHex(null)clears the descriptor and the keys it governed. The builder gainsClearDeviceModel,DeviceModel,DeviceModelFrom,DriverPidandFeatureAnswers.- A copy whose descriptor is replaced,
FromProfilethenFromDescriptorBuilderor a descriptor setter with other bytes, dropsbuttonMap,triggerButtons,axisMapandtriggerModewith the other keys the old descriptor's report format governs. A program that customizes a copy that way sets them again after the descriptor. 1.x kept them, describing fields the new descriptor may not have. - A layout's
kindis read in any case. AnaxisMapusage named twice, or a name no slot takes, is refused.HMLayoutValidationException's message names camelCase keys with indexes. - New in the
HIDMaestronamespace:HMController.TransportIndex,SectionNamesandDriverVersion,HMContext.LastDriverInstallandProfileLoadIssues,HMProfileLoadIssueandHMProfileLoadIssueKind,HMProfileException,HMProfile.HasDeviceModel,DeviceModelSource,DeviceModelProblemandBuildsItsOwnReports,HMDeviceExtractor.ResolveDeviceModel,HMCleanupScopeand theRemoveAllVirtualControllers(HMCleanupScope, bool)overload,HMResourceInUseException,HMDriverInstallOutcome,HMDriverVersionExceptionwithProblemandHMDriverVersionProblem,HMBuildInfo,HMPayloadandHMPayloadMissingException, and for devices described as dataHMController.Ports,DeviceProtocolandBindWinUsb,HMPortSet,HMPort,HMPortFlags,HMPortOutputEventArgs,HMDeviceProtocolandHMWinUsbBinding. - The types in
HIDMaestro.Internalare the profile JSON's object model and the SDK's plumbing, public so a program can read and build profiles. They follow the JSON schema, so a key the schema adds, renames or removes changes them. 2.0.0 removesSteamFirmwareStampSpec,FeatureStubReport.SteamFirmwareStamp, andControllerProfile'sIsXboxBranded,UsesXusb22,UsesUpperFilter,UpperFilterName,CompanionOnlyandSixaxisCompatible(thecompanionOnlykey goes with them, andsixaxisCompatiblestill loads and picks the DualShock 3 model), and changesFeatureStubReport.Paramfromint?toAnswerParamSpec(a bare number still loads),FeatureStubReport.Notifytobool?andFeatureStubSpec.Match's default to null. It adds the spec classes of the ports, the WinUSB block, the answer tables (AnswerCrcSpec,AnswerMessageSpec,AnswerParamSpec,AnswerPatchSpec,ValueRootSpec,ValueSourceSpec) and the protocol, state block, report programs and stream (ProtocolSpecand its parts,StateBlockSpec,InputReportSpec,LiveFeatureReportSpec,StreamSpec,HIDMaestro.Internal.Programs),ScaleSpec,PackedPartSpecandPackedPieceSpec, the field membersMin,Max,Scale,Negate,Repeat,Dpad,Levels,ChargingValue,Parts,ClockandHoldWhile,ControllerProfile'sAxisCenters,XInput,BluetoothTransport,BluetoothVendorIdSource,ValueSources,DeviceModel,DeviceModelFrom,NativeDescriptor,Protocol,StateBlock,InputReports,LiveFeatureReports,StreamandBuildsItsOwnReports,ExtendedReportSpec.PortsandComment,UsbConfigurationSpec.SharedSerialandWinUsb, andHidReportBuilder.InputReports.DeviceManager.WaitForHidChildgains a three-argument form andWaitForDeviceInterfacea four-argument form beside the old ones.
Native writers¶
- Each index's configuration gains
InputReportId(REG_BINARY), which the driver checks againstInputReportByteLengthand the descriptor before it uses it, and losesDeviceInstanceId, which no driver read. A create writesVendorIdandProductIdfirst. - When several programs run, a native writer reads a controller's section names from
HMController.SectionNamesof the program it feeds, or computes them fromHMController.TransportIndex, since the transport index can differ from the slot. Global\HIDMaestroVersion<N>is a new 64-byte per-controller section the SDK and the driver both write, at protocol 4: the SDK's protocol range and version, the identity tag of the controller's token, and the driver's protocol range, version, capabilities and error word. The SDK also writes the controller's index underHKLM\SOFTWARE\HIDMaestro\Identity\<token>before any PnP work, and the driver reads it before its first start. No existing section's layout changed.- A 2.0.0 controller's configuration gains
FeatureAnswers, the compiled answer table, and the cached input report IDs. The driver reads them only under the version contract, so a native writer that wants feature answers writes the section and the identity entry as the SDK does. - InputAck's capability word can carry bit 31, the driver's ended marker. Test bits 0 and 1 one at a time, never the whole word.
- The driver's
Global\HIDMaestroStopEvent<N>andGlobal\HIDMaestroOutputEvent<N>carry a DACL that admits SYSTEM, Administrators and LocalService only, so a process that is not elevated opens neither. - The driver lets go of the index's sections, events and owner pipe when its device is surprise removed, where it held them until the removal.
- A devnode with no index, neither an identity entry nor a
ControllerIndex, or with noController<index>key holding a descriptor, fails its start with STATUS_DEVICE_CONFIGURATION_ERROR. Both drivers started such a devnode as an Xbox 360 pad onController0in 1.x.HidAttrPidis no longer read. - A 2.0.0 XUSB companion reads its answers from
XusbAnswersunder the version contract and registers its XUSB interface only when it holds them. Its INF adds no interface, as xusb22.inf adds none, so the interface exists only once the driver has started.
A 1.x program on a machine with 2.0.0¶
- Its create installs its own driver package with
UpdateDriverForPlugAndPlayDevices, which Windows applies to every present device of the hardware ID. Its controllers that are running when a 2.0.0 program installs its driver keep the driver their host loaded until their device restarts. - A 1.x controller that runs on the 2.0.0 driver keeps its input and output and loses the device behavior 1.x kept in its driver, which 2.0.0 serves only for a 2.0.0 SDK's controller: a Sony profile's calibration, pairing and firmware replies, and a Bluetooth DualSense or DualShock 4's extended report, which the 1.x SDK arms on the feature read, the Switch Pro's replies and stream, the DualShock 3's sixaxis form, and XInput through the XUSB companion, which registers no interface for it.
-
v1.8.0 and v1.8.1 must not run on a machine that runs a later release: they run no ParentIdPrefix rival scan, and a v1.8.x create of
index:Nbeside a later release'sHM_000Nparent on the same enumerator makes the machine stop at every boot (bugcheck 0xCA). Programs on those releases must move. -
Its
InstallDriverremoves every HIDMaestro device on the machine, 2.0.0 controllers included, and installs its own driver whenever the payload differs. Its first create in a session runs a cleanup that removes every device too. - While it has a controller present, 2.0.0 sweeps never remove its controllers, and 2.0.0 creates take transport indices from 64, above every index it reaches with fewer than 65 controllers alive. A 2.0.0 program sees it through its owner pipes, what the 2.0.0 driver records about them, its USB/IP imports, and, while its device's index cannot be read, the registry ledger entry its create writes. A 1.x program of only keyboards and mice whose device's index cannot be read, on an index a 2.0.0 program holds, goes unseen.
- With no controller present it leaves no sign, so its next create can take a 2.0.0 program's low index and share its sections. Its
CreateControllerAt(index)takes whatever index it is given. - A program older than v1.12.0 leaves no sign of whether it still runs, so its devices are never removed on a guess. Only
RemoveAllVirtualControllers(HMCleanupScope.Everything, preserveInstall)and a create of that device's own identity key remove them.