Skip to content

Conversation

@wysaid
Copy link
Owner

@wysaid wysaid commented Dec 7, 2025

Summary

This PR addresses crashes that occur on Windows when enumerating camera devices with buggy drivers, particularly when devices like Oculus Quest 3 VR headsets are unplugged while the system attempts to access them.

Problem

Some camera devices with faulty DirectShow drivers cause the application to crash when:

  • Enumerating all available camera devices
  • Attempting to bind to device monikers after the device has been physically disconnected

The crash occurs in IMoniker::BindToObject() calls, which can throw unhandled exceptions from poorly implemented drivers.

Solution

Core Changes

  • SEH Protection: Wrapped critical DirectShow API calls with Windows Structured Exception Handling (__try/__except)
  • Helper Functions: Extracted protected binding operations into dedicated functions:
    • tryBindMonikerToFilter(): Safe device enumeration
    • tryBindMonikerForOpen(): Safe device opening
  • Result Enum: Introduced DeviceBindResult enum for type-safe error handling (Success/Failed/Exception)
  • Compiler Support: Added CCAP_SEH_SUPPORTED macro to detect MSVC and MinGW-w64 SEH support

Code Quality

  • Removed magic numbers in favor of enum values
  • Simplified function signatures
  • Added comprehensive error logging
  • Professional naming and comments (removed issue tracking references)

Testing

  • New Test Program: tests/test_device_hotplug.cpp
    • Interactive test with two scenarios:
      1. Device enumeration during hot-plug events
      2. Opening devices that have been unplugged
    • Colored console output for clear test results
    • VS Code task for easy execution: "Run Device Hot-plug Test (Debug)"

Impact

  • ✅ Prevents application crashes from buggy drivers
  • ✅ Gracefully handles device hot-plug scenarios
  • ✅ Maintains cross-platform compatibility (MSVC, MinGW-w64, other compilers)
  • ✅ No performance impact on normal operations
  • ✅ Comprehensive test coverage for device hot-plug scenarios

Technical Details

Files Changed:

  • src/ccap_imp_windows.cpp: Core SEH implementation
  • tests/test_device_hotplug.cpp: New interactive test program
  • tests/CMakeLists.txt: Test build configuration
  • .vscode/tasks.json: Added test execution task

Compiler Compatibility:

  • MSVC: Full SEH support
  • MinGW-w64: SEH support via __SEH__ macro
  • Other compilers: Graceful degradation (no SEH protection)

Testing Instructions

  1. Build the test program:

    cmake --build build --config Debug --target ccap_device_hotplug_test
  2. Run via VS Code task: "Run Device Hot-plug Test (Debug)"

  3. Follow interactive prompts to test device enumeration and hot-plug scenarios

Related Issue

Fixes #26

Summary by CodeRabbit

  • Bug Fixes

    • Improved stability when enumerating and opening devices to avoid crashes from problematic hardware.
  • Tests

    • Added an interactive device hot-plug test to validate graceful behavior when devices are unplugged during enumeration/opening.
  • Chores

    • Added a VS Code task to run the device hot-plug test (Debug) for easier manual testing.

✏️ Tip: You can customize this high-level summary in your review settings.

wysaid and others added 3 commits December 7, 2025 22:39
…rs (#26)\n\nUse SEH to protect moniker->BindToObject during enumeration and device binding, so buggy drivers (e.g., unplugged Oculus Quest 3) don't crash the process. Log and skip problematic devices.
Copilot AI review requested due to automatic review settings December 7, 2025 17:18
@coderabbitai
Copy link

coderabbitai bot commented Dec 7, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds SEH-protected DirectShow device binding to prevent crashes during enumeration/open, a new interactive device hot‑plug test executable, corresponding CMake target and messages, and a VSCode task to run the test.

Changes

Cohort / File(s) Summary
SEH-protected device binding
src/ccap_imp_windows.cpp
Adds DeviceBindResult enum and SEH-guarded helpers tryBindMonikerToFilter and tryBindMonikerForOpen. Replaces direct BindToObject calls in findDeviceNames and open with result-based handling, logging, and adjusted enumeration control flow. Falls back to prior behavior when SEH unsupported.
Interactive test source
tests/test_device_hotplug.cpp
New interactive test implementing testDeviceEnumerationWithHotplug() and testOpenUnpluggedDevice() plus main(). Prompts user to unplug devices, enumerates/opens via ccap::Provider, and reports crash/graceful-failure outcomes with colored console output.
Build & tooling
tests/CMakeLists.txt, .vscode/tasks.json
Adds ccap_device_hotplug_test executable target (C++17), platform-specific compile options, and a Windows-only custom run target; emits user instructions. Adds VSCode task "Run Device Hot-plug Test (Debug)" with debug build dependency, platform branches, presentation settings, and $gcc problemMatcher.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Review SEH wrapper implementations and macros around CCAP_SEH_SUPPORTED for correctness and build portability.
  • Verify DeviceBindResult handling in both findDeviceNames and open preserves intended enumeration/stop semantics and logging.
  • Inspect exception-to-log mapping and resource-management (COM object release) across success/failure paths.
  • Validate platform-specific CMake flags, the new test target linkage, and the Windows custom target command correctness.
  • Check VSCode task cwd/commands for correct paths and cross-platform guards.

Poem

🐰 A rabbit hops where cameras hide,
Catching crashes on the wild device tide.
SEH shields stitch fragile drivers' fray,
Tests ask you kindly: "Unplug, then play!"
Hooray for safer lists — and binkies all day! 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main objective of the PR: preventing crashes from buggy camera drivers during Windows device enumeration, which is the core focus of the changes.
Linked Issues check ✅ Passed The PR comprehensively addresses all coding requirements from issue #26: it prevents crashes during device enumeration by wrapping DirectShow calls with SEH protection, skips problematic filter instantiation when possible, allows device enumeration to continue despite faulty drivers, and handles hot-plug scenarios robustly.
Out of Scope Changes check ✅ Passed All changes are directly scoped to addressing issue #26: SEH protection in Windows device enumeration/opening code, helper functions for safe binding, an interactive test to verify crash prevention, and corresponding build configuration. No unrelated modifications detected.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix_issue_26

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/ccap_imp_windows.cpp (1)

129-144: Consider nullifying output parameter on failure/exception.

When BindToObject fails or an exception occurs, *deviceFilter may contain an indeterminate value (if SUCCEEDED(hr) is false, COM may or may not have written to the output parameter). For safety, consider explicitly setting *deviceFilter = nullptr in the failure and exception paths.

 DeviceBindResult tryBindMonikerForOpen(IMoniker* moniker, IBaseFilter** deviceFilter) {
 #if CCAP_SEH_SUPPORTED
     __try {
 #endif
         HRESULT hr = moniker->BindToObject(0, 0, IID_IBaseFilter, (void**)deviceFilter);
         if (SUCCEEDED(hr)) {
             return DeviceBindResult::Success;
         } else {
+            *deviceFilter = nullptr;
             return DeviceBindResult::Failed;
         }
 #if CCAP_SEH_SUPPORTED
     } __except (EXCEPTION_EXECUTE_HANDLER) {
+        *deviceFilter = nullptr;
         return DeviceBindResult::Exception;
     }
 #endif
 }
tests/test_device_hotplug.cpp (2)

33-38: Unused platform-specific headers.

conio.h (Windows) and termios.h/unistd.h (non-Windows) are included but appear unused. The waitForUserInput() function uses std::getline() instead. Consider removing these includes if they're not needed, or add a comment if they're reserved for future use.

 #ifdef _WIN32
-#include <conio.h> // For _kbhit() and _getch()
-#else
-#include <termios.h>
-#include <unistd.h>
+// Platform-specific headers reserved for future non-blocking input features
 #endif

264-272: Test always opens first device regardless of user action.

The prompt asks users to note "one of the devices" and unplug it, but the test always attempts to open devices[0]. If the user unplugs a different device, the test may not exercise the intended failure path. Consider either:

  1. Clarifying the instructions to specifically unplug the first device, or
  2. Adding a prompt for users to select which device index to test
-    std::cout << "\n" << COLOR_YELLOW << "ACTION REQUIRED:\n"
-              << "  1. Note one of the devices listed above\n"
-              << "  2. Physically unplug that device\n"
+    std::cout << "\n" << COLOR_YELLOW << "ACTION REQUIRED:\n"
+              << "  1. Physically unplug the FIRST device [0] listed above\n"
+              << "     (\"" << devices[0] << "\")\n"
               << "  3. Press Enter when ready\n" << COLOR_RESET;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d4e51f and 362bf32.

📒 Files selected for processing (4)
  • .vscode/tasks.json (1 hunks)
  • src/ccap_imp_windows.cpp (3 hunks)
  • tests/CMakeLists.txt (1 hunks)
  • tests/test_device_hotplug.cpp (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/ccap_imp_windows.cpp (1)
src/ccap_imp.cpp (2)
  • reportError (193-199)
  • reportError (193-193)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Agent
  • GitHub Check: macOS Build (Release-static)
  • GitHub Check: Windows Build (Release-shared)
  • GitHub Check: Windows Build (Release-static)
  • GitHub Check: Windows Build (Debug-shared)
  • GitHub Check: Windows Build (Debug-static)
🔇 Additional comments (9)
.vscode/tasks.json (1)

902-934: LGTM!

The new VSCode task follows the established patterns in this file. The task configuration properly:

  • Handles both Windows and Unix-like environments
  • Sets appropriate working directory for the test executable
  • Uses a dedicated panel with focus for the interactive test
  • Depends on "Build Project (Debug)" to ensure the executable is built first
src/ccap_imp_windows.cpp (4)

94-104: LGTM!

The SEH detection macro correctly identifies MSVC and MinGW-w64 with SEH support. The DeviceBindResult enum provides a clean, type-safe way to distinguish between successful binding, normal failures, and SEH-caught exceptions.


107-126: LGTM!

The helper function correctly wraps the critical BindToObject call with SEH protection. The filter is properly released on success, and logging uses appropriate severity levels (info for bind failures, warning for exceptions). When SEH is not supported, the code gracefully degrades to unprotected calls.


455-461: LGTM!

The SEH-protected helper is correctly integrated. Only devices that successfully bind are added to the list, and unavailable devices (both failed bindings and exception-causing devices) are silently skipped, which is the expected behavior for enumeration.


712-736: LGTM!

The error handling logic correctly differentiates between:

  1. Success: Device opens and enumeration stops
  2. Specific device requested but failed: Reports appropriate error and stops
  3. No specific device and failure: Logs and continues to try next device

The exception case properly distinguishes between SEH-caught crashes (with specific messaging) and normal bind failures.

tests/test_device_hotplug.cpp (3)

40-100: LGTM!

The helper functions provide clean, colored console output that improves test readability. The countdown timer and device list printing are well-implemented for an interactive test.


111-230: LGTM!

The test function is well-structured with clear steps, appropriate exception handling, and helpful result analysis. Creating a new Provider instance for re-enumeration ensures fresh device discovery, which is crucial for testing the hot-plug scenario.


292-348: LGTM!

The main function provides a user-friendly interactive experience with clear visual indicators, appropriate platform warnings, and proper exception handling. The 5-second countdown gives users time to prepare before the test begins.

tests/CMakeLists.txt (1)

217-230: LGTM!

The executable configuration is appropriate for an interactive test - it links only against ccap (not gtest) and correctly sets C++17 as required. The test is intentionally excluded from the automated test suite via gtest_discover_tests.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements crash prevention for Windows camera device enumeration when dealing with buggy DirectShow drivers, particularly addressing issues with devices like Oculus Quest 3 VR headsets that can cause application crashes when unplugged during device access operations.

Key Changes:

  • Added Windows Structured Exception Handling (SEH) protection around DirectShow API calls that can throw unhandled exceptions from faulty drivers
  • Introduced DeviceBindResult enum for type-safe error handling with three states: Success, Failed, and Exception
  • Created an interactive test program to verify crash prevention during device hot-plug scenarios

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
src/ccap_imp_windows.cpp Implements SEH protection with helper functions tryBindMonikerToFilter() and tryBindMonikerForOpen() to safely bind device monikers; adds CCAP_SEH_SUPPORTED macro for compiler compatibility detection
tests/test_device_hotplug.cpp New interactive test program with ANSI colored output to manually verify crash prevention during device enumeration and opening after hot-unplug events
tests/CMakeLists.txt Adds build configuration for the new interactive test executable and custom target for manual execution
.vscode/tasks.json Adds VS Code task to run the device hot-plug test with dedicated panel presentation

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Fix typo: run_device_hotplug_testg_test -> run_device_hotplug_test
- Remove unused platform-specific headers (conio.h, termios.h, unistd.h)
- Add <algorithm> header to fix Linux compilation error with std::find
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
tests/test_device_hotplug.cpp (2)

251-266: Test always opens devices[0] regardless of user's choice.

The instructions tell the user to note and unplug "one of the devices," but the test always attempts to open devices[0]. If the user unplugs a different device, the test may pass incorrectly.

Consider prompting the user to enter the device index they unplugged:

     std::cout << "\n" << COLOR_YELLOW << "ACTION REQUIRED:\n"
               << "  1. Note one of the devices listed above\n"
               << "  2. Physically unplug that device\n"
-              << "  3. Press Enter when ready\n" << COLOR_RESET;
+              << "  3. Enter the device index you unplugged (or press Enter for 0): " << COLOR_RESET;
 
-    waitForUserInput();
+    std::string input;
+    std::getline(std::cin, input);
+    size_t deviceIndex = 0;
+    if (!input.empty()) {
+        try {
+            deviceIndex = std::stoul(input);
+            if (deviceIndex >= devices.size()) {
+                printWarning("Invalid index, using device 0.");
+                deviceIndex = 0;
+            }
+        } catch (...) {
+            printWarning("Invalid input, using device 0.");
+        }
+    }
 
     printInfo("Attempting to open the first device from the previous enumeration...");
+    std::cout << "  Target device: [" << deviceIndex << "] " << devices[deviceIndex] << "\n";
 
     ccap::Provider testProvider;
     bool openResult = false;
 
     try {
         // Try to open the first device from our enumerated list
         // If it was unplugged, this should fail gracefully, not crash
-        openResult = testProvider.open(devices[0], false);
+        openResult = testProvider.open(devices[deviceIndex], false);

286-286: Unused argc and argv parameters may trigger compiler warnings.

If these parameters are intentionally unused, consider suppressing the warning:

-int main(int argc, char** argv) {
+int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {

Or simply:

-int main(int argc, char** argv) {
+int main() {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 362bf32 and 3ba395d.

📒 Files selected for processing (2)
  • tests/CMakeLists.txt (1 hunks)
  • tests/test_device_hotplug.cpp (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/CMakeLists.txt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: Build (Release-clang-shared)
  • GitHub Check: Build (Release-clang-static)
  • GitHub Check: Build Fedora (Release-static)
  • GitHub Check: Build (Debug-clang-shared)
  • GitHub Check: Build (Debug-gcc-static)
  • GitHub Check: Build Fedora (Debug-shared)
  • GitHub Check: Build (Release-gcc-static)
  • GitHub Check: Windows Build (Debug-shared)
  • GitHub Check: Windows Build (Release-shared)
  • GitHub Check: Windows Build (Release-static)
🔇 Additional comments (4)
tests/test_device_hotplug.cpp (4)

34-95: LGTM!

The helper functions are well-organized with consistent naming, appropriate use of constexpr for color codes, and correct use of size_t for vector indexing. The countdown timer using std::this_thread::sleep_for is platform-independent.


105-224: LGTM!

The test function is well-structured with clear step-by-step flow, comprehensive exception handling, and informative user prompts. Creating a new Provider instance for re-enumeration ensures fresh device discovery.


302-307: LGTM!

Good addition of platform-specific guidance. The #ifndef _WIN32 check appropriately warns non-Windows users while still allowing them to run the test for basic functionality verification.


313-329: LGTM!

The test orchestration is well-structured with proper exception handling, clear flow between test scenarios, and appropriate return codes for success/failure cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enumerating devices on Windows may crash when camera is unplugged

2 participants