# Windows Message Loop Requirement ## Critical Issue: SDK Callbacks Not Firing ### The Problem **Symptom**: Authentication times out, callbacks never execute ``` [AUTH] Calling SDKAuth... [AUTH] Waiting for callback... [Still waiting after 30 seconds...] ERROR: Authentication timeout ``` **Root Cause**: The Zoom Windows SDK uses the **Windows message pump** to dispatch callbacks. Without processing Windows messages, callbacks are queued but never delivered. --- ## Why This Happens The SDK uses COM/Windows messaging for asynchronous operations: 1. **SDK Thread**: Receives response from Zoom servers 2. **Posts Windows Message**: To application's message queue 3. **Application Must Process**: Via `GetMessage()` or `PeekMessage()` 4. **Message Dispatched**: Callback function finally invoked **Without message processing**: Messages queue up → Never dispatched → Callbacks never fire → Timeout --- ## The Solution ### Pattern 1: Non-Blocking with PeekMessage() (Recommended) Use when you need to check conditions or implement timeouts: ```cpp bool WaitForAuthentication() { auto startTime = std::chrono::steady_clock::now(); while (!g_authenticated && !g_exit) { // CRITICAL: Process Windows messages for SDK callbacks! MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } // Check timeout auto elapsed = std::chrono::duration_cast( std::chrono::steady_clock::now() - startTime).count(); if (elapsed >= 30) { return false; // Timeout } // Small sleep to avoid CPU spinning std::this_thread::sleep_for(std::chrono::milliseconds(100)); } return g_authenticated; } ``` ### Pattern 2: Blocking with GetMessage() Use for main application loop: ```cpp int main() { // ... initialize SDK, authenticate, join meeting ... // Main message loop MSG msg; while (GetMessage(&msg, nullptr, 0, 0)) { if (msg.message == WM_QUIT) { break; } TranslateMessage(&msg); DispatchMessage(&msg); } // Cleanup CleanUPSDK(); return 0; } ``` ### Pattern 3: Hybrid Approach (Our Solution) Combines non-blocking message processing with custom exit conditions: ```cpp // During authentication wait while (!g_authenticated && !g_exit) { MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } std::this_thread::sleep_for(std::chrono::milliseconds(100)); } // Main application loop while (!g_exit) { MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) { g_exit = true; break; } TranslateMessage(&msg); DispatchMessage(&msg); } // Do other work here ProcessVideoFrames(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } ``` --- ## Common Mistakes ### ❌ Wrong: Just Sleeping ```cpp // This will NEVER work - callbacks never dispatched! while (!g_authenticated) { std::this_thread::sleep_for(std::chrono::seconds(1)); } ``` ### ❌ Wrong: Using std::condition_variable Without Messages ```cpp // Callbacks won't fire - no message processing! std::unique_lock lock(mutex); cv.wait(lock, []{ return g_authenticated; }); ``` ### ✅ Correct: Message Loop with Condition Check ```cpp while (!g_authenticated) { MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } std::this_thread::sleep_for(std::chrono::milliseconds(100)); } ``` --- ## Where Message Processing is Required You MUST process Windows messages in these scenarios: ### 1. Authentication ```cpp authService->SDKAuth(authContext); // MUST process messages while waiting while (!authenticated) { ProcessMessages(); } ``` ### 2. Joining Meeting ```cpp meetingService->Join(joinParam); // MUST process messages while waiting while (meetingStatus != IN_MEETING) { ProcessMessages(); } ``` ### 3. Main Application Loop ```cpp // MUST continuously process messages while (!exit) { ProcessMessages(); } ``` ### 4. Waiting for Any SDK Callback Any time you're waiting for: - `onAuthenticationReturn()` - `onMeetingStatusChanged()` - `onRawDataFrameReceived()` - Any other SDK callback You MUST be processing Windows messages! --- ## Debugging Tips ### How to Tell if Message Loop is Missing **Symptoms**: - Callbacks never fire - Timeouts after 10-30 seconds - No error messages from SDK - Debug output shows "waiting..." but nothing happens **Quick Test**: Add logging in your callback: ```cpp void onAuthenticationReturn(AuthResult ret) { std::cout << "CALLBACK FIRED!" << std::endl; // Does this ever print? } ``` If you never see "CALLBACK FIRED!", you're not processing messages. ### Debug Helper Function ```cpp void ProcessMessagesWithDebug() { MSG msg; int messageCount = 0; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { messageCount++; TranslateMessage(&msg); DispatchMessage(&msg); } if (messageCount > 0) { std::cout << "Processed " << messageCount << " messages" << std::endl; } } ``` --- ## PeekMessage vs GetMessage | Feature | PeekMessage | GetMessage | |---------|-------------|------------| | **Blocking** | No | Yes | | **Returns if no messages** | Immediately | Waits | | **Good for** | Timeouts, conditions | Main message loop | | **CPU Usage** | Can spin (add sleep) | Efficient | | **Flexibility** | High | Low | ### When to Use Each **PeekMessage**: - When waiting for SDK callbacks with timeout - When you need to check other conditions - When combining with other work **GetMessage**: - Main application message loop - When you want efficient CPU usage - Standard Windows application pattern --- ## Complete Example ```cpp #include #include #include #include #include #include using namespace ZOOM_SDK_NAMESPACE; bool g_authenticated = false; bool g_exit = false; class MyAuthListener : public IAuthServiceEvent { public: void onAuthenticationReturn(AuthResult ret) override { std::cout << "Auth callback received!" << std::endl; if (ret == AUTHRET_SUCCESS) { g_authenticated = true; } } // ... other required methods ... }; bool AuthenticateWithMessageLoop(IAuthService* authService, const wchar_t* jwt) { authService->SetEvent(new MyAuthListener()); AuthContext context; context.jwt_token = jwt; if (authService->SDKAuth(context) != SDKERR_SUCCESS) { return false; } // Wait for callback with message processing auto startTime = std::chrono::steady_clock::now(); while (!g_authenticated && !g_exit) { // Process Windows messages - CRITICAL! MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } // Check timeout (30 seconds) auto elapsed = std::chrono::duration_cast( std::chrono::steady_clock::now() - startTime).count(); if (elapsed >= 30) { std::cerr << "Authentication timeout" << std::endl; return false; } // Small sleep to avoid CPU spinning std::this_thread::sleep_for(std::chrono::milliseconds(100)); } return g_authenticated; } int main() { // Initialize SDK InitParam initParam; initParam.strWebDomain = L"https://zoom.us"; InitSDK(initParam); // Create auth service IAuthService* authService = nullptr; CreateAuthService(&authService); // Authenticate with message loop if (!AuthenticateWithMessageLoop(authService, L"your-jwt-token")) { std::cerr << "Authentication failed" << std::endl; return 1; } std::cout << "Authenticated successfully!" << std::endl; // Main application loop with message processing while (!g_exit) { MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) { g_exit = true; break; } TranslateMessage(&msg); DispatchMessage(&msg); } // Do other work std::this_thread::sleep_for(std::chrono::milliseconds(100)); } // Cleanup CleanUPSDK(); return 0; } ``` --- ## Why SDK Documentation Doesn't Mention This 1. **Assumed knowledge**: Windows developers are expected to know about message pumps 2. **Sample code uses it**: But in different patterns that might not be obvious 3. **Not explicitly required**: Works fine if you already have a message loop 4. **Platform-specific**: Only affects Windows SDK --- ## Key Takeaways 1. **Always process Windows messages** when waiting for SDK callbacks 2. **Use PeekMessage()** for non-blocking with timeout 3. **Use GetMessage()** for main application loop 4. **Add sleep** to avoid CPU spinning with PeekMessage() 5. **Test callbacks** to verify message loop is working 6. **This is not optional** - SDK will not work without it --- ## Related Issues - **Authentication timeout**: Usually caused by missing message loop - **Meeting join timeout**: Same issue - **No video frames**: Check message loop in main application loop - **Callbacks delayed**: Ensure message processing is frequent enough --- ## See Also - [Authentication Flow Pattern](../examples/authentication-pattern.md) - [Common Build Errors](build-errors.md) - [SDK Initialization](../SKILL.md#initialization)