This document describes the camera hardware pipeline exactly as implemented in src/core/MyForm_Camera.cpp and the MyForm members that drive it. It is descriptive only — no optimizations are proposed.
SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT)— present devices only.- For each device: read
SPDRP_CLASS. Keep onlyCameraorImage. - Read
SPDRP_DEVICEDESC(friendly name) and instance ID. - Return
vector<CameraDeviceInfo>.
DIGCF_PRESENTis used here. The toggle functions useDIGCF_ALLCLASSESwithoutDIGCF_PRESENTso they can still act on a device node that is currently not present (e.g., disabled).
Priority (see MyForm_Config.cpp):
- Live
selectedInstanceIdifpreferCurrentSelectionand non-empty. - Saved device from
config.txt(device=). - (Only when
!preferCurrentSelection) live selection again. - Scan and pick first instance ID containing
MI_00(RGB/IR sub-device). - Else first scanned camera.
- Else
false.
A device counts as disabled if either:
SPDRP_CONFIGFLAGS & CONFIGFLAG_DISABLEDis set, orCM_Get_DevNode_Statusreturns problem codeCM_PROB_DISABLED(22).
This dual check is the ground truth used by all verification.
- Resolve target; if none → log
…_NoTarget, returnfalse. lastToggleTime = DateTime::Now.- If already disabled → set
cameraExpectedDisabled=true, log…_AlreadyDisabled, returntrue. SetCameraHardwareStateVerified(target, false, retryOnFailure).VerifyCameraHardwareState(target, true).cameraExpectedDisabled = result; log…_Resultwith elevated/integrity/setup/cfgmgr/stage; returnresult && verified.
Symmetric. Uses RecoverCameraHardware(target, cycleDevice) instead of the direct verified set, and logs …_Result / …_AlreadyEnabled.
if target empty -> false
shouldBeDisabled = !enable
if Verify(target, shouldBeDisabled) -> true // already correct, no churn
for attempt 0..2:
ToggleCameraHardware(target, enable) // SetupAPI
if Verify(target, shouldBeDisabled): RecordTick; return true
ToggleCameraHardwareCfgMgr(target, enable) // CfgMgr
if Verify(target, shouldBeDisabled): RecordTick; return true
if reinitializeOnMismatch:
ToggleCameraHardware(target, !enable)
ToggleCameraHardwareCfgMgr(target, !enable)
Sleep(250)
Sleep(250)
// final attempt
ToggleCameraHardware(target, enable)
verified = Verify(target, shouldBeDisabled)
if verified: RecordTick
return verified
SetupDiGetClassDevs(DIGCF_ALLCLASSES).- Match instance ID (exact or
_wcsicmp). SP_PROPCHANGE_PARAMSwithDIF_PROPERTYCHANGE,DICS_ENABLE/DICS_DISABLE,DICS_FLAG_GLOBAL.SetupDiSetClassInstallParamsthenSetupDiCallClassInstaller.- Records diagnostic stage
10→14(or12/13on failure,15if not found).
LocateCameraDevInst(resolves DEVINST).CM_Enable_DevNode/CM_Disable_DevNode(devInst, CM_DISABLE_UI_NOT_OK).CM_Reenumerate_DevNode(devInst, 0).- Records stage
20→23.
- Up to 3 attempts,
Sleep(100)between, checkingGetCameraHardwareDisabledState == shouldBeDisabled.
SetCameraHardwareStateVerified(target, true, false)(ensure enabled).- If
cycleDevice:Sleep(350)SetCameraHardwareStateVerified(target, false, false)(disable)Sleep(900)SetCameraHardwareStateVerified(target, true, false)(enable) — OR-accumulatedSleep(500)SetCameraHardwareStateVerified(target, true, false)(enable) — OR-accumulated
ScanSystemCameras() → RecoverCameraHardware(each, cycleDevices).
| Step | Sleep | Cumulative contribution |
|---|---|---|
VerifyCameraHardwareState |
100 ms × up to 3 | ≤ 300 ms per verify call |
SetCameraHardwareStateVerified per attempt |
250 ms (+250 if reinitialize) | up to ~2.5 s worst case |
RecoverCameraHardware cycle |
350 + 900 + 500 | 1750 ms extra |
WndProc suspend |
500 ms | post-disable |
WndProc resume |
1000 ms | pre-enable |
A single lock event can therefore take ~1 s (disable + 500 ms). A startup "restore + enable" with cycling can take several seconds. These timings are intentional in the original v2.0 to allow the Windows device stack to settle; they are preserved exactly in the extraction.
All API failures are captured into the four g_last* globals (g_lastSetupApiError, g_lastConfigManagerResult, g_lastHardwareToggleStage, plus the toggle tick). The Disable/EnableTargetCameraHardware functions read these (zeroing them via InterlockedCompareExchange) and embed them in the …_Result diagnostic log line. No error is surfaced to the user via MessageBox; logging is the only diagnostic channel.
On isSystemEnding, the destructor calls DisableTargetCameraHardware(true) (leave disabled). On a normal close, the destructor calls EnableTargetCameraHardware(false) (leave enabled). See docs/LIFECYCLE.md.
src/watchdog/CameraFailsafe (90 s poll → 10 s confirm) and src/watchdog/RecoveryLoopFailsafe (5 s startup check → 30 s poll → 5 s retry) detect unexpected Disabled while ExpectedEnabled and recover by calling this pipeline — RecoverCameraHardware(target, false) (enable-only, no cycle) + VerifyCameraHardwareState(target, false) — bounded to 3 attempts with cooldown. They never call ToggleCameraHardware/ToggleCameraHardwareCfgMgr directly and never disable. Full detail: docs/watchdog/.