[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6FKJ9EDH9Q5VWDF8HAJRSP0":3,"skill-same:01M6FKJ9EDH9Q5VWDF8HAJRSP0":45},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":18,"linkOnly":19,"markdownContent":20,"markdownBody":20,"frontMatter":21,"refName":22,"commitSha":23,"skillDirectory":24,"forksCount":25,"repositoryHtmlUrl":26,"repositoryHomepageUrl":27,"ownerHtmlUrl":28,"ownerBlogUrl":6,"ownerDescription":29,"updatedAt":30,"install":31,"resources":44},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows",null,"ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",67,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[5],"2026-11-10 10:12:30",false,"# Zoom Video SDK - Windows Development\n\nExpert guidance for developing with the Zoom Video SDK on Windows. This SDK enables custom video applications, raw media capture\u002Finjection, cloud recording, live streaming, and real-time transcription on Windows platforms.\n\n**Official Documentation**: https:\u002F\u002Fdevelopers.zoom.us\u002Fdocs\u002Fvideo-sdk\u002Fwindows\u002F\n**API Reference**: https:\u002F\u002Fmarketplacefront.zoom.us\u002Fsdk\u002Fcustom\u002Fwindows\u002F\n**Sample Repository**: https:\u002F\u002Fgithub.com\u002Fzoom\u002Fvideosdk-windows-rawdata-sample\n\n## Quick Links\n\n**New to Video SDK? Follow this path:**\n\n1. **[SDK Architecture Pattern](concepts\u002Fsdk-architecture-pattern.md)** - Universal 3-step pattern for ANY feature\n2. **[Session Join Pattern](examples\u002Fsession-join-pattern.md)** - Complete working code to join a session\n3. **[Windows Message Loop](troubleshooting\u002Fwindows-message-loop.md)** - **CRITICAL**: Fix callbacks not firing\n4. **[Video Rendering](examples\u002Fvideo-rendering.md)** - Display video with Canvas API\n\n**Reference:**\n- **[Singleton Hierarchy](concepts\u002Fsingleton-hierarchy.md)** - 5-level SDK navigation map\n- **[API Reference](references\u002Fwindows-reference.md)** - Methods, error codes, timing rules\n- **[Delegate Methods](references\u002Fdelegate-methods.md)** - All 80+ callback methods\n- **[Sample Applications](references\u002Fsamples.md)** - Official samples guide\n- **[windows.md](windows.md)** - Secondary overview doc (pointer-style)\n- **[SKILL.md](SKILL.md)** - Complete documentation navigation\n\n**Having issues?**\n- Callbacks not firing → [Windows Message Loop](troubleshooting\u002Fwindows-message-loop.md)\n- Build errors → [Build Errors Guide](troubleshooting\u002Fbuild-errors.md)\n- Video subscribe fails → [Video Rendering](examples\u002Fvideo-rendering.md) (subscribe in `onUserVideoStatusChanged`)\n- Quick diagnostics → [Common Issues](troubleshooting\u002Fcommon-issues.md)\n\n**Building a Custom UI?**\n- [Canvas vs Raw Data](concepts\u002Fcanvas-vs-raw-data.md) - Choose your rendering approach\n- [Raw Video Capture](examples\u002Fraw-video-capture.md) - YUV420 frame processing\n\n## SDK Overview\n\nThe Zoom Video SDK for Windows is a C++ library that provides:\n- **Session Management**: Join\u002Fleave video SDK sessions\n- **Raw Data Access**: Capture raw audio\u002Fvideo frames (YUV420, PCM)\n- **Raw Data Injection**: Send custom audio\u002Fvideo into sessions\n- **Screen Sharing**: Share screens or inject custom share sources\n- **Cloud Recording**: Record sessions to Zoom cloud\n- **Live Streaming**: Stream to RTMP endpoints (YouTube, etc.)\n- **Chat & Commands**: In-session messaging and command channels\n- **Live Transcription**: Real-time speech-to-text\n- **Subsessions**: Breakout room support\n- **Whiteboard**: Collaborative whiteboard features\n- **Annotations**: Screen share annotations\n- **C# Integration**: C++\u002FCLI wrapper for .NET applications\n\n## Prerequisites\n\n### System Requirements\n\n- **OS**: Windows 10 (1903 or later) or Windows 11\n- **Architecture**: x64 (recommended), x86, or ARM64\n- **Visual Studio**: 2019 or 2022 (Community, Professional, or Enterprise)\n- **Windows SDK**: 10.0.19041.0 or later\n- **.NET Framework**: 4.8 or later (for C# applications)\n\n### Visual Studio Workloads\n\nInstall these workloads via Visual Studio Installer:\n\n1. **Desktop development with C++**\n   - MSVC v142 or v143 compiler\n   - Windows 10\u002F11 SDK\n   - C++ CMake tools (optional)\n\n2. **.NET desktop development** (for C# applications)\n   - .NET Framework 4.8 targeting pack\n   - C++\u002FCLI support\n\n## Quick Start\n\n### C++ Application\n\n```cpp\n#include \u003Cwindows.h>\n#include \"zoom_video_sdk_api.h\"\n#include \"zoom_video_sdk_interface.h\"\n#include \"zoom_video_sdk_delegate_interface.h\"\n\nUSING_ZOOM_VIDEO_SDK_NAMESPACE\n\n\u002F\u002F 1. Create SDK object\nIZoomVideoSDK* video_sdk_obj = CreateZoomVideoSDKObj();\n\n\u002F\u002F 2. Initialize\nZoomVideoSDKInitParams init_params;\ninit_params.domain = L\"https:\u002F\u002Fzoom.us\";\ninit_params.enableLog = true;\ninit_params.logFilePrefix = L\"zoom_win_video\";\ninit_params.videoRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap;\ninit_params.shareRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap;\ninit_params.audioRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap;\n\nZoomVideoSDKErrors err = video_sdk_obj->initialize(init_params);\n\n\u002F\u002F 3. Add event listener\nvideo_sdk_obj->addListener(myDelegate);\n\n\u002F\u002F 4. Join session (IMPORTANT: set audioOption.connect = false)\nZoomVideoSDKSessionContext session_context;\nsession_context.sessionName = L\"my-session\";\nsession_context.userName = L\"Windows User\";\nsession_context.token = L\"your-jwt-token\";\nsession_context.videoOption.localVideoOn = false;\nsession_context.audioOption.connect = false;  \u002F\u002F Connect audio after join\nsession_context.audioOption.mute = true;\n\nIZoomVideoSDKSession* session = video_sdk_obj->joinSession(session_context);\n\n\u002F\u002F 5. CRITICAL: Add Windows message pump for callbacks to work\nbool running = true;\nwhile (running) {\n    \u002F\u002F Process Windows messages (required for SDK callbacks)\n    MSG msg;\n    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {\n        TranslateMessage(&msg);\n        DispatchMessage(&msg);\n    }\n    \n    \u002F\u002F Your application logic here\n    Sleep(10);\n}\n```\n\n### C# Application\n\n```csharp\nusing ZoomVideoSDK;\n\nvar sdkManager = new ZoomSDKManager();\nsdkManager.Initialize();\nsdkManager.JoinSession(\"my-session\", \"jwt-token\", \"User Name\", \"\");\n```\n\n## Key Features\n\n| Feature | Description |\n|---------|-------------|\n| **Session Management** | Join, leave, and manage video sessions |\n| **Raw Video (YUV I420)** | Capture and inject raw video frames |\n| **Raw Audio (PCM)** | Capture and inject raw audio data |\n| **Screen Sharing** | Share screens or custom content |\n| **Cloud Recording** | Record sessions to Zoom cloud |\n| **Live Streaming** | Stream to RTMP endpoints |\n| **Chat** | Send\u002Freceive chat messages |\n| **Command Channel** | Custom command messaging |\n| **Live Transcription** | Real-time speech-to-text |\n| **C# Support** | Full .NET Framework integration |\n\n## Sample Applications\n\n**Official Repository**: https:\u002F\u002Fgithub.com\u002Fzoom\u002Fvideosdk-windows-rawdata-sample\n\n| Sample | Description |\n|--------|-------------|\n| VSDK_SkeletonDemo | Minimal session join - **start here** |\n| VSDK_getRawVideo | Capture YUV420 video frames |\n| VSDK_getRawAudio | Capture PCM audio |\n| VSDK_sendRawVideo | Inject custom video (virtual camera) |\n| VSDK_sendRawAudio | Inject custom audio (virtual mic) |\n| VSDK_CloudRecording | Cloud recording control |\n| VSDK_CommandChannel | Custom command messaging |\n| VSDK_TranscriptionAndTranslation | Live captions |\n\n**See complete guide**: [Sample Applications Reference](references\u002Fsamples.md)\n\n## Critical Gotchas and Best Practices\n\n### ⚠️ CRITICAL: Windows Message Pump Required\n\n**The #1 issue that causes session joins to hang with no callbacks:**\n\nAll Windows applications using the Zoom SDK **MUST** process Windows messages. The SDK uses Windows messages to deliver callbacks like `onSessionJoin()`, `onError()`, etc.\n\n**Problem**: Without a message pump, `joinSession()` appears to succeed but callbacks never fire.\n\n**Solution**: Add this to your main loop:\n\n```cpp\nwhile (running) {\n    \u002F\u002F REQUIRED: Process Windows messages\n    MSG msg;\n    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {\n        TranslateMessage(&msg);\n        DispatchMessage(&msg);\n    }\n    \n    \u002F\u002F Your application logic\n    Sleep(10);\n}\n```\n\n**Applies to**:\n- Console applications (no automatic message pump)\n- Custom main loops\n- Applications that don't use standard WinMain\u002FWndProc\n\n**GUI applications** using WinMain with standard message loop already have this.\n\n### Audio Connection Strategy\n\n**Best Practice**: Set `audioOption.connect = false` when joining, then connect audio in the `onSessionJoin()` callback.\n\n```cpp\n\u002F\u002F During join\nsession_context.audioOption.connect = false;  \u002F\u002F Don't connect yet\nsession_context.audioOption.mute = true;\n\n\u002F\u002F In onSessionJoin() callback\nvoid onSessionJoin() override {\n    IZoomVideoSDKAudioHelper* audioHelper = video_sdk_obj->getAudioHelper();\n    if (audioHelper) {\n        audioHelper->startAudio();  \u002F\u002F Connect now\n    }\n}\n```\n\n**Why**: This pattern is used in all official Zoom samples. It separates session join from audio initialization for better reliability and error handling.\n\n### All Delegate Callbacks Must Be Implemented\n\nThe `IZoomVideoSDKDelegate` interface has 70+ pure virtual methods. **ALL must be implemented**, even if empty:\n\n```cpp\n\u002F\u002F Required even if you don't use them\nvoid onProxyDetectComplete() override {}\nvoid onUserWhiteboardShareStatusChanged(IZoomVideoSDKUser*, IZoomVideoSDKWhiteboardHelper*) override {}\n\u002F\u002F ... etc\n```\n\n**Tip**: Check the SDK version's `zoom_video_sdk_delegate_interface.h` for the complete list. The interface changes between SDK versions.\n\n### Memory Mode for Raw Data\n\nAlways use heap mode for raw data memory:\n\n```cpp\ninit_params.videoRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap;\ninit_params.shareRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap;\ninit_params.audioRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap;\n```\n\nStack mode can cause issues with large video frames.\n\n### Thread Safety\n\nSDK callbacks execute on SDK threads, not your main thread:\n- Don't perform heavy operations in callbacks\n- Don't call `cleanup()` from within callbacks\n- Use thread-safe queues for passing data to UI thread\n- Use mutexes when accessing shared state\n\n### Consult Official Samples First\n\nWhen SDK behavior is unexpected, **always check the official samples** before troubleshooting:\n\n**Local samples**:\n- `C:\\tempsdk\\Zoom_VideoSDK_Windows_RawDataDemos\\VSDK_SkeletonDemo\\` (simplest)\n- `C:\\tempsdk\\sdksamples\\zoom-video-sdk-windows-2.4.12\\Sample-Libs\\x64\\demo\\`\n\nOfficial samples show correct patterns for:\n- Message pump implementation ✓\n- Audio connection strategy ✓\n- Error handling ✓\n- Memory management ✓\n\n## Video Rendering - Two Approaches\n\nThe Zoom SDK provides **two different ways** to render video. Choose based on your needs.\n\n### ? Canvas API (Recommended for Most Use Cases)\n\n**Best for**: Standard applications, clean video quality, ease of implementation\n\nThe SDK renders video directly to your HWND. **No YUV conversion needed**.\n\n```cpp\n\u002F\u002F Subscribe to a user's video with Canvas API\nIZoomVideoSDKCanvas* canvas = user->GetVideoCanvas();\nif (canvas) {\n    ZoomVideoSDKErrors ret = canvas->subscribeWithView(\n        hwnd,                                    \u002F\u002F Your window handle\n        ZoomVideoSDKVideoAspect_PanAndScan,     \u002F\u002F Fit to window, may crop\n        ZoomVideoSDKResolution_Auto              \u002F\u002F Let SDK choose best resolution\n    );\n    \n    if (ret == ZoomVideoSDKErrors_Success) {\n        \u002F\u002F SDK is now rendering directly to your window!\n    }\n}\n\n\u002F\u002F Unsubscribe when done\ncanvas->unSubscribeWithView(hwnd);\n```\n\n**Advantages**:\n- ✅ **Best quality** - SDK uses optimized, hardware-accelerated rendering\n- ✅ **No artifacts** - Professional video quality\n- ✅ **Simple code** - 3 lines to subscribe\n- ✅ **Better performance** - No CPU-intensive YUV conversion\n- ✅ **Automatic scaling** - SDK handles window resizing\n- ✅ **Aspect ratio** - Built-in aspect ratio handling\n\n**Example from official .NET sample**:\n```cpp\n\u002F\u002F Self video preview\nIZoomVideoSDKCanvas* canvas = myself->GetVideoCanvas();\ncanvas->subscribeWithView(selfVideoHwnd, aspect, resolution);\n\n\u002F\u002F Remote user video\nIZoomVideoSDKCanvas* remoteCanvas = remoteUser->GetVideoCanvas();\nremoteCanvas->subscribeWithView(remoteVideoHwnd, aspect, resolution);\n```\n\n**Video Aspect Options**:\n- `ZoomVideoSDKVideoAspect_Original` - Letterbox\u002Fpillarbox, no cropping\n- `ZoomVideoSDKVideoAspect_FullFilled` - Fill window, may crop edges\n- `ZoomVideoSDKVideoAspect_PanAndScan` - Smart crop to fill window\n- `ZoomVideoSDKVideoAspect_LetterBox` - Show full video with black bars\n\n**Resolution Options**:\n- `ZoomVideoSDKResolution_90P`\n- `ZoomVideoSDKResolution_180P`\n- `ZoomVideoSDKResolution_360P` - Good balance\n- `ZoomVideoSDKResolution_720P` - HD quality\n- `ZoomVideoSDKResolution_1080P`\n- `ZoomVideoSDKResolution_Auto` - Let SDK decide (recommended)\n\n### ? Raw Data Pipe (Advanced Use Cases)\n\n**Best for**: Custom video processing, effects, recording, computer vision\n\nYou receive raw YUV420 frames and handle rendering yourself.\n\n```cpp\n\u002F\u002F 1. Create a delegate to receive frames\nclass VideoRenderer : public IZoomVideoSDKRawDataPipeDelegate {\npublic:\n    void onRawDataFrameReceived(YUVRawDataI420* data) override {\n        int width = data->GetStreamWidth();\n        int height = data->GetStreamHeight();\n        \n        char* yBuffer = data->GetYBuffer();\n        char* uBuffer = data->GetUBuffer();\n        char* vBuffer = data->GetVBuffer();\n        \n        \u002F\u002F Convert YUV420 to RGB and render\n        ConvertYUVToRGB(yBuffer, uBuffer, vBuffer, width, height);\n        RenderToWindow(rgbBuffer, width, height);\n    }\n    \n    void onRawDataStatusChanged(RawDataStatus status) override {\n        \u002F\u002F Handle video on\u002Foff\n    }\n};\n\n\u002F\u002F 2. Subscribe to raw data\nIZoomVideoSDKRawDataPipe* pipe = user->GetVideoPipe();\nVideoRenderer* renderer = new VideoRenderer();\npipe->subscribe(ZoomVideoSDKResolution_720P, renderer);\n```\n\n**YUV420 to RGB Conversion** (ITU-R BT.601):\n```cpp\nvoid ConvertYUV420ToRGB(char* yBuffer, char* uBuffer, char* vBuffer, \n                        int width, int height) {\n    for (int y = 0; y \u003C height; y++) {\n        for (int x = 0; x \u003C width; x++) {\n            int yIndex = y * width + x;\n            int uvIndex = (y \u002F 2) * (width \u002F 2) + (x \u002F 2);\n            \n            int Y = (unsigned char)yBuffer[yIndex];\n            int U = (unsigned char)uBuffer[uvIndex];\n            int V = (unsigned char)vBuffer[uvIndex];\n            \n            \u002F\u002F YUV to RGB conversion\n            int C = Y - 16;\n            int D = U - 128;\n            int E = V - 128;\n            \n            int R = (298 * C + 409 * E + 128) >> 8;\n            int G = (298 * C - 100 * D - 208 * E + 128) >> 8;\n            int B = (298 * C + 516 * D + 128) >> 8;\n            \n            \u002F\u002F Clamp to [0, 255]\n            R = (R \u003C 0) ? 0 : (R > 255) ? 255 : R;\n            G = (G \u003C 0) ? 0 : (G > 255) ? 255 : G;\n            B = (B \u003C 0) ? 0 : (B > 255) ? 255 : B;\n            \n            \u002F\u002F Store RGB (BGR format for Windows)\n            rgbBuffer[yIndex * 3 + 0] = (unsigned char)B;\n            rgbBuffer[yIndex * 3 + 1] = (unsigned char)G;\n            rgbBuffer[yIndex * 3 + 2] = (unsigned char)R;\n        }\n    }\n}\n```\n\n**Render with GDI**:\n```cpp\nvoid RenderToWindow(unsigned char* rgbBuffer, int width, int height) {\n    HDC hdc = GetDC(hwnd);\n    \n    BITMAPINFO bmi = {};\n    bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);\n    bmi.bmiHeader.biWidth = width;\n    bmi.bmiHeader.biHeight = -height;  \u002F\u002F Negative for top-down\n    bmi.bmiHeader.biPlanes = 1;\n    bmi.bmiHeader.biBitCount = 24;     \u002F\u002F 24-bit RGB\n    bmi.bmiHeader.biCompression = BI_RGB;\n    \n    RECT rect;\n    GetClientRect(hwnd, &rect);\n    \n    StretchDIBits(hdc,\n        0, 0, rect.right, rect.bottom,  \u002F\u002F Destination\n        0, 0, width, height,              \u002F\u002F Source\n        rgbBuffer, &bmi,\n        DIB_RGB_COLORS, SRCCOPY);\n    \n    ReleaseDC(hwnd, hdc);\n}\n```\n\n**Disadvantages**:\n- ⚠️ **CPU intensive** - YUV conversion can cause frame drops\n- ⚠️ **Artifacts** - Manual rendering may show tearing\u002Fartifacts\n- ⚠️ **Complex** - More code to maintain\n- ⚠️ **Performance** - Slower than Canvas API\n\n**Use Raw Data When**:\n- Adding video filters\u002Feffects\n- Recording to custom formats\n- Computer vision processing\n- Custom compositing\n- Streaming to non-standard outputs\n\n### Self Video vs Remote Users\n\n**Self Video** (your own camera):\n\n**Option A: Canvas API**\n```cpp\nIZoomVideoSDKSession* session = sdk->getSessionInfo();\nIZoomVideoSDKUser* myself = session->getMyself();\nIZoomVideoSDKCanvas* canvas = myself->GetVideoCanvas();\ncanvas->subscribeWithView(selfVideoHwnd, aspect, resolution);\n```\n\n**Option B: Video Preview** (for self only)\n```cpp\nIZoomVideoSDKVideoHelper* videoHelper = sdk->getVideoHelper();\nvideoHelper->startVideo();  \u002F\u002F Start transmission\n\n\u002F\u002F For preview rendering\nvideoHelper->startVideoCanvasPreview(selfVideoHwnd, aspect, resolution);\n```\n\n**Remote Users** (other participants):\n\n**Canvas API** (recommended):\n```cpp\n\u002F\u002F In onUserJoin callback\nvoid onUserJoin(IZoomVideoSDKUserHelper*, IVideoSDKVector\u003CIZoomVideoSDKUser*>* userList) {\n    for (int i = 0; i \u003C userList->GetCount(); i++) {\n        IZoomVideoSDKUser* user = userList->GetItem(i);\n        IZoomVideoSDKCanvas* canvas = user->GetVideoCanvas();\n        canvas->subscribeWithView(userVideoHwnd, aspect, resolution);\n    }\n}\n```\n\n### Event-Driven Subscription Pattern\n\n⚠️ **CRITICAL**: Video subscription must be **event-driven** and **manual**.\n\n**Key Events**:\n\n1. **`onSessionJoin`** - Subscribe to self video\n2. **`onUserJoin`** - Subscribe to new remote users\n3. **`onUserVideoStatusChanged`** - Re-subscribe when video turns on\u002Foff\n4. **`onUserLeave`** - Unsubscribe and cleanup\n\n**Complete Pattern**:\n\n```cpp\nclass MainFrame : public IZoomVideoSDKDelegate {\nprivate:\n    std::map\u003CIZoomVideoSDKUser*, IZoomVideoSDKCanvas*> subscribedUsers_;\n    HWND videoWindow_;\n    \npublic:\n    void onSessionJoin() override {\n        \u002F\u002F Start your own video\n        IZoomVideoSDKVideoHelper* videoHelper = sdk->getVideoHelper();\n        videoHelper->startVideo();\n        \n        \u002F\u002F Subscribe to self video\n        IZoomVideoSDKUser* myself = sdk->getSessionInfo()->getMyself();\n        SubscribeToUser(myself);\n    }\n    \n    void onUserJoin(IZoomVideoSDKUserHelper*, \n                    IVideoSDKVector\u003CIZoomVideoSDKUser*>* userList) override {\n        \u002F\u002F Get current user to exclude self\n        IZoomVideoSDKUser* myself = sdk->getSessionInfo()->getMyself();\n        \n        for (int i = 0; i \u003C userList->GetCount(); i++) {\n            IZoomVideoSDKUser* user = userList->GetItem(i);\n            \n            \u002F\u002F IMPORTANT: Only subscribe to REMOTE users!\n            if (user != myself) {\n                SubscribeToUser(user);\n            }\n        }\n    }\n    \n    void onUserVideoStatusChanged(IZoomVideoSDKVideoHelper*, \n                                  IVideoSDKVector\u003CIZoomVideoSDKUser*>* userList) override {\n        IZoomVideoSDKUser* myself = sdk->getSessionInfo()->getMyself();\n        \n        for (int i = 0; i \u003C userList->GetCount(); i++) {\n            IZoomVideoSDKUser* user = userList->GetItem(i);\n            if (user != myself) {\n                \u002F\u002F Re-subscribe when video status changes\n                SubscribeToUser(user);\n            }\n        }\n    }\n    \n    void onUserLeave(IZoomVideoSDKUserHelper*, \n                    IVideoSDKVector\u003CIZoomVideoSDKUser*>* userList) override {\n        for (int i = 0; i \u003C userList->GetCount(); i++) {\n            IZoomVideoSDKUser* user = userList->GetItem(i);\n            UnsubscribeFromUser(user);\n        }\n    }\n    \n    void onSessionLeave() override {\n        \u002F\u002F Cleanup all subscriptions\n        for (auto& pair : subscribedUsers_) {\n            IZoomVideoSDKCanvas* canvas = pair.second;\n            if (canvas) {\n                canvas->unSubscribeWithView(videoWindow_);\n            }\n        }\n        subscribedUsers_.clear();\n    }\n    \nprivate:\n    void SubscribeToUser(IZoomVideoSDKUser* user) {\n        if (!user || subscribedUsers_.find(user) != subscribedUsers_.end())\n            return;\n            \n        IZoomVideoSDKCanvas* canvas = user->GetVideoCanvas();\n        if (canvas) {\n            ZoomVideoSDKErrors ret = canvas->subscribeWithView(\n                videoWindow_,\n                ZoomVideoSDKVideoAspect_PanAndScan,\n                ZoomVideoSDKResolution_Auto\n            );\n            \n            if (ret == ZoomVideoSDKErrors_Success) {\n                subscribedUsers_[user] = canvas;\n            }\n        }\n    }\n    \n    void UnsubscribeFromUser(IZoomVideoSDKUser* user) {\n        auto it = subscribedUsers_.find(user);\n        if (it != subscribedUsers_.end()) {\n            IZoomVideoSDKCanvas* canvas = it->second;\n            if (canvas) {\n                canvas->unSubscribeWithView(videoWindow_);\n            }\n            subscribedUsers_.erase(it);\n        }\n    }\n};\n```\n\n**Key Points**:\n- ✅ Subscribe in response to events (onUserJoin, onUserVideoStatusChanged)\n- ✅ Always exclude current user from remote subscriptions\n- ✅ Unsubscribe on onUserLeave\n- ✅ Clean up all subscriptions on onSessionLeave\n- ✅ Track subscriptions in a map for lifecycle management\n\n### ⚠️ Screen Share Subscription (DIFFERENT from Video!)\n\n**CRITICAL**: Screen share subscription uses `IZoomVideoSDKShareAction` from the callback, NOT `user->GetShareCanvas()`!\n\n```cpp\n\u002F\u002F WRONG - This won't work for remote screen shares!\nuser->GetShareCanvas()->subscribeWithView(hwnd, ...);\n\n\u002F\u002F CORRECT - Use IZoomVideoSDKShareAction from onUserShareStatusChanged callback\nvoid onUserShareStatusChanged(IZoomVideoSDKShareHelper* pShareHelper,\n                               IZoomVideoSDKUser* pUser,\n                               IZoomVideoSDKShareAction* pShareAction) {\n    if (!pShareAction) return;\n    \n    ZoomVideoSDKShareStatus status = pShareAction->getShareStatus();\n    \n    if (status == ZoomVideoSDKShareStatus_Start || \n        status == ZoomVideoSDKShareStatus_Resume) {\n        \u002F\u002F Subscribe to the share using Canvas API\n        IZoomVideoSDKCanvas* shareCanvas = pShareAction->getShareCanvas();\n        if (shareCanvas) {\n            shareCanvas->subscribeWithView(shareWindow_, \n                ZoomVideoSDKVideoAspect_Original);\n        }\n    }\n    else if (status == ZoomVideoSDKShareStatus_Stop) {\n        \u002F\u002F Unsubscribe when share stops\n        IZoomVideoSDKCanvas* shareCanvas = pShareAction->getShareCanvas();\n        if (shareCanvas) {\n            shareCanvas->unSubscribeWithView(shareWindow_);\n        }\n    }\n}\n```\n\n**Why is share different from video?**\n- **Video**: Each user has one video stream → use `user->GetVideoCanvas()`\n- **Share**: A user can have multiple share actions (multi-share) → use `IZoomVideoSDKShareAction*` from callback\n- The `IZoomVideoSDKShareAction` object represents a specific share stream and contains the share status, type, and rendering interfaces\n\n**See also**: [Screen Share Subscription Example](examples\u002Fscreen-share-subscription.md)\n\n### Multi-User Video Layout\n\nFor multiple participants, you need **one HWND per user**:\n\n```cpp\n\u002F\u002F Create separate windows\u002Fpanels for each user\nHWND selfVideoWindow = CreateWindow(...);   \u002F\u002F Your video\nHWND user1Window = CreateWindow(...);       \u002F\u002F User 1's video\nHWND user2Window = CreateWindow(...);       \u002F\u002F User 2's video\n\n\u002F\u002F Subscribe each user to their own window\nmyself->GetVideoCanvas()->subscribeWithView(selfVideoWindow, ...);\nuser1->GetVideoCanvas()->subscribeWithView(user1Window, ...);\nuser2->GetVideoCanvas()->subscribeWithView(user2Window, ...);\n```\n\n**Layout Strategies**:\n- Grid layout (2x2, 3x3)\n- Gallery view (scrollable)\n- Active speaker (large) + thumbnails\n- Picture-in-picture\n\n### Common Video Issues\n\n| Issue | Cause | Solution |\n|-------|-------|----------|\n| Video not showing | Not calling `startVideo()` | Call `videoHelper->startVideo()` in `onSessionJoin` |\n| Artifacts\u002Ftearing | Using Raw Data Pipe | Switch to Canvas API |\n| Poor performance | YUV conversion on UI thread | Use Canvas API or move conversion to worker thread |\n| Video freezes | Not processing Windows messages | Add message pump to main loop |\n| Can't see self | Subscribing to wrong user | Use `session->getMyself()` for self video |\n| Seeing self in remote list | Not excluding self | Check `if (user != myself)` before subscribing |\n\n## Complete Documentation Library\n\nThis skill includes comprehensive guides organized by category:\n\n### Core Concepts (Start Here!)\n- **[SDK Architecture Pattern](concepts\u002Fsdk-architecture-pattern.md)** - Universal 3-step pattern for ANY feature\n- **[Singleton Hierarchy](concepts\u002Fsingleton-hierarchy.md)** - 5-level navigation guide\n- **[Canvas vs Raw Data](concepts\u002Fcanvas-vs-raw-data.md)** - Choose your rendering approach\n\n### Complete Examples\n- **[Session Join Pattern](examples\u002Fsession-join-pattern.md)** - JWT auth + session join with full code\n- **[Video Rendering](examples\u002Fvideo-rendering.md)** - Canvas API video display\n- **[Screen Share Subscription](examples\u002Fscreen-share-subscription.md)** - View remote screen shares (DIFFERENT from video!)\n- **[Raw Video Capture](examples\u002Fraw-video-capture.md)** - YUV420 frame capture\n- **[Raw Audio Capture](examples\u002Fraw-audio-capture.md)** - PCM audio capture\n- **[Send Raw Video](examples\u002Fsend-raw-video.md)** - Virtual camera (inject custom video)\n- **[Send Raw Audio](examples\u002Fsend-raw-audio.md)** - Virtual mic (inject custom audio)\n- **[Cloud Recording](examples\u002Fcloud-recording.md)** - Cloud recording control\n- **[Command Channel](examples\u002Fcommand-channel.md)** - Custom command messaging\n- **[Transcription](examples\u002Ftranscription.md)** - Live transcription\u002Fcaptions\n\n### UI Framework Integration\n- **[Win32 Native](examples\u002Fdotnet-winforms\u002FREADME.md#option-1-win32-native-c---direct-sdk)** - Direct SDK usage with Canvas API (best performance)\n- **[WinForms (.NET)](examples\u002Fdotnet-winforms\u002FREADME.md#option-2-winforms-c--ccli-wrapper)** - C++\u002FCLI wrapper + Raw Data Pipe\n- **[WPF (.NET)](examples\u002Fdotnet-winforms\u002FREADME.md#option-3-wpf-c--ccli-wrapper)** - C++\u002FCLI wrapper + BitmapSource conversion\n- **[Production Quality Guidelines](examples\u002Fdotnet-winforms\u002FREADME.md#production-quality-review)** - Checklist and common issues\n\n### C++\u002FCLI Wrapper Patterns (Wrapping ANY Native Library)\n- **[Complete Guide](examples\u002Fdotnet-winforms\u002FREADME.md#ccli-wrapper-patterns-for-net-integration)** - 8 patterns for native→.NET interop\n- **[Pattern 1: Basic Structure](examples\u002Fdotnet-winforms\u002FREADME.md#pattern-1-basic-wrapper-structure)** - Project setup, class layout\n- **[Pattern 2: void* Pointers](examples\u002Fdotnet-winforms\u002FREADME.md#pattern-2-opaque-void-pointers)** - Hide native types\n- **[Pattern 3: gcroot Callbacks](examples\u002Fdotnet-winforms\u002FREADME.md#pattern-3-gcrootT-for-nativemanaged-callbacks)** - Native→Managed events\n- **[Pattern 4: IDisposable](examples\u002Fdotnet-winforms\u002FREADME.md#pattern-4-destructor--finalizer-idisposable)** - Cleanup pattern\n- **[Pattern 5: Strings](examples\u002Fdotnet-winforms\u002FREADME.md#pattern-5-string-conversion)** - String^ ↔ wstring\u002Fstring\n- **[Pattern 6: Arrays](examples\u002Fdotnet-winforms\u002FREADME.md#pattern-6-arraybuffer-conversion)** - pin_ptr, Marshal::Copy\n- **[Pattern 7: Threading](examples\u002Fdotnet-winforms\u002FREADME.md#pattern-7-thread-marshaling-native-thread--ui-thread)** - UI thread dispatch\n- **[Pattern 8: LockBits](examples\u002Fdotnet-winforms\u002FREADME.md#pattern-8-lockbits-for-fast-image-manipulation)** - Fast image conversion\n- **[Common Errors](examples\u002Fdotnet-winforms\u002FREADME.md#common-wrapper-errors)** - Troubleshooting\n\n### Troubleshooting\n- **[Windows Message Loop](troubleshooting\u002Fwindows-message-loop.md)** - **CRITICAL**: Why callbacks don't fire\n- **[Build Errors](troubleshooting\u002Fbuild-errors.md)** - SDK header dependency fixes\n- **[Common Issues](troubleshooting\u002Fcommon-issues.md)** - Quick diagnostics & error codes\n\n### References\n- **[API Reference](references\u002Fwindows-reference.md)** - 5-level API hierarchy, methods, error codes\n- **[Delegate Methods](references\u002Fdelegate-methods.md)** - All 80+ callback methods\n- **[SKILL.md](SKILL.md)** - Complete navigation guide\n\n### Most Critical Issues (From Real Debugging)\n\n1. **Callbacks not firing** → Missing Windows message loop (99% of issues)\n   - See: [Windows Message Loop Guide](troubleshooting\u002Fwindows-message-loop.md)\n\n2. **Video subscribe returns error 2** → Subscribing too early\n   - See: [Video Rendering](examples\u002Fvideo-rendering.md) - Subscribe in `onUserVideoStatusChanged`\n\n3. **Abstract class errors** → Missing virtual method implementations\n   - See: [Delegate Methods](references\u002Fdelegate-methods.md)\n\n### Key Insight\n\n**Once you learn the 3-step pattern, you can implement ANY feature:**\n1. Get singleton → 2. Implement delegate → 3. Subscribe & use\n\nSee: [SDK Architecture Pattern](concepts\u002Fsdk-architecture-pattern.md)\n\n## Resources\n\n- **Official Docs**: https:\u002F\u002Fdevelopers.zoom.us\u002Fdocs\u002Fvideo-sdk\u002Fwindows\u002F\n- **API Reference**: https:\u002F\u002Fmarketplacefront.zoom.us\u002Fsdk\u002Fcustom\u002Fwindows\u002F\n- **Dev Forum**: https:\u002F\u002Fdevforum.zoom.us\u002F\n- **GitHub Samples**: https:\u002F\u002Fgithub.com\u002Fzoom\u002Fvideosdk-windows-rawdata-sample\n- **Working Sample**: `C:\\tempsdk\\zoom-video-sdk-windows-sample\\` (complete implementation)\n\n---\n\n**Need help?** Start with [SKILL.md](SKILL.md) for complete navigation.\n\n\n## Merged from video-sdk\u002Fwindows\u002FSKILL.md\n\n# Zoom Video SDK Windows - Complete Documentation Index\n\n## Quick Start Path\n\n**If you're new to the SDK, follow this order:**\n\n0. **Overview** → [windows.md](windows.md)\n1. **Read the architecture pattern** → [concepts\u002Fsdk-architecture-pattern.md](concepts\u002Fsdk-architecture-pattern.md)\n   - Universal formula: Singleton → Delegate → Subscribe\n   - Once you understand this, you can implement any feature\n\n2. **Fix build errors** → [troubleshooting\u002Fbuild-errors.md](troubleshooting\u002Fbuild-errors.md)\n   - SDK header dependencies\n   - Required include order\n\n3. **Implement session join** → [examples\u002Fsession-join-pattern.md](examples\u002Fsession-join-pattern.md)\n   - Complete working JWT + session join code\n\n4. **Fix callback issues** → [troubleshooting\u002Fwindows-message-loop.md](troubleshooting\u002Fwindows-message-loop.md)\n   - **CRITICAL**: Why callbacks don't fire without Windows message loop\n\n5. **Implement video** → [examples\u002Fvideo-rendering.md](examples\u002Fvideo-rendering.md)\n   - Canvas API (SDK-rendered) vs Raw Data Pipe\n\n6. **Troubleshoot any issues** → [troubleshooting\u002Fcommon-issues.md](troubleshooting\u002Fcommon-issues.md)\n   - Quick diagnostic checklist\n   - Error code tables\n\n---\n\n## Documentation Structure\n\n```\nvideo-sdk\u002Fwindows\u002F\n├── SKILL.md                           # Main skill overview\n├── SKILL.md                           # This file - navigation guide\n├── windows.md                          # Secondary overview doc (pointer-style)\n│\n├── concepts\u002F                          # Core architectural patterns\n│   ├── sdk-architecture-pattern.md   # Universal formula for ANY feature\n│   ├── singleton-hierarchy.md        # 5-level navigation guide\n│   └── canvas-vs-raw-data.md         # SDK-rendered vs self-rendered choice\n│\n├── examples\u002F                          # Complete working code\n│   ├── session-join-pattern.md       # JWT auth + session join\n│   ├── video-rendering.md            # Canvas API video display\n│   ├── screen-share-subscription.md  # View remote screen shares\n│   ├── raw-video-capture.md          # YUV420 raw frame capture\n│   ├── raw-audio-capture.md          # PCM audio capture\n│   ├── send-raw-video.md             # Virtual camera (inject video)\n│   ├── send-raw-audio.md             # Virtual mic (inject audio)\n│   ├── cloud-recording.md            # Cloud recording control\n│   ├── command-channel.md            # Custom command messaging\n│   ├── transcription.md              # Live transcription\u002Fcaptions\n│   └── dotnet-winforms\u002F              # UI Framework integration\n│       └── README.md                 # Win32, WinForms, WPF patterns\n│                                     # C++\u002FCLI wrapper patterns\n│                                     # Production quality guidelines\n│\n├── troubleshooting\u002F                   # Problem solving guides\n│   ├── windows-message-loop.md       # CRITICAL - Why callbacks fail\n│   ├── build-errors.md               # Header dependency fixes\n│   └── common-issues.md              # Quick diagnostic workflow\n│\n└── references\u002F                        # Reference documentation\n    ├── windows-reference.md           # API hierarchy, methods, error codes\n    ├── delegate-methods.md            # All 80+ callback methods\n    └── samples.md                     # Official samples guide\n```\n\n---\n\n## By Use Case\n\n### I want to build a video app\n1. [SDK Architecture Pattern](concepts\u002Fsdk-architecture-pattern.md) - Understand the pattern\n2. [Session Join Pattern](examples\u002Fsession-join-pattern.md) - Join sessions\n3. [Video Rendering](examples\u002Fvideo-rendering.md) - Display video\n4. [Windows Message Loop](troubleshooting\u002Fwindows-message-loop.md) - Fix callback issues\n\n### I'm getting build errors\n1. [Build Errors Guide](troubleshooting\u002Fbuild-errors.md) - SDK header dependencies\n2. [Delegate Methods](references\u002Fdelegate-methods.md) - Abstract class errors\n3. [Common Issues](troubleshooting\u002Fcommon-issues.md) - Linker errors\n\n### I'm getting runtime errors\n1. [Windows Message Loop](troubleshooting\u002Fwindows-message-loop.md) - Callbacks not firing\n2. [Common Issues](troubleshooting\u002Fcommon-issues.md) - Error code tables\n\n### I want to view screen shares\n1. [Screen Share Subscription](examples\u002Fscreen-share-subscription.md) - **DIFFERENT from video!**\n2. [SDK Architecture Pattern](concepts\u002Fsdk-architecture-pattern.md) - Event-driven pattern\n3. [Video Rendering](examples\u002Fvideo-rendering.md) - Compare with video subscription\n\n### I want to capture raw video\u002Faudio\n1. [Canvas vs Raw Data](concepts\u002Fcanvas-vs-raw-data.md) - Choose your approach\n2. [Raw Video Capture](examples\u002Fraw-video-capture.md) - YUV420 frame capture\n3. [Raw Audio Capture](examples\u002Fraw-audio-capture.md) - PCM audio capture\n4. [SDK Architecture Pattern](concepts\u002Fsdk-architecture-pattern.md) - Subscription pattern\n\n### I want to send custom video\u002Faudio (virtual camera\u002Fmic)\n1. [Send Raw Video](examples\u002Fsend-raw-video.md) - Inject custom video frames\n2. [Send Raw Audio](examples\u002Fsend-raw-audio.md) - Inject custom audio\n3. [SDK Architecture Pattern](concepts\u002Fsdk-architecture-pattern.md) - External source pattern\n\n### I want to record sessions\n1. [Cloud Recording](examples\u002Fcloud-recording.md) - Start\u002Fstop cloud recording\n2. [API Reference](references\u002Fwindows-reference.md) - Recording helper methods\n\n### I want to use live transcription\n1. [Transcription](examples\u002Ftranscription.md) - Enable live captions\n2. [Delegate Methods](references\u002Fdelegate-methods.md) - Transcription callbacks\n\n### I want custom messaging between participants\n1. [Command Channel](examples\u002Fcommand-channel.md) - Send custom commands\n2. [API Reference](references\u002Fwindows-reference.md) - Command channel methods\n\n### I want to build a Win32 native app\n1. [Win32 Integration](examples\u002Fdotnet-winforms\u002FREADME.md#option-1-win32-native-c---direct-sdk) - Direct SDK + Canvas API\n2. [Video Rendering](examples\u002Fvideo-rendering.md) - Canvas API patterns\n3. [Production Guidelines](examples\u002Fdotnet-winforms\u002FREADME.md#production-quality-review) - Best practices\n\n### I want to build a WinForms (.NET) app\n1. [WinForms Integration](examples\u002Fdotnet-winforms\u002FREADME.md#option-2-winforms-c--ccli-wrapper) - C++\u002FCLI wrapper + Raw Data\n2. [C++\u002FCLI Patterns](examples\u002Fdotnet-winforms\u002FREADME.md#ccli-wrapper-patterns-for-net-integration) - gcroot, Finalizer, LockBits\n3. [Production Guidelines](examples\u002Fdotnet-winforms\u002FREADME.md#production-quality-review) - IDisposable, thread safety\n\n### I want to build a WPF (.NET) app\n1. [WPF Integration](examples\u002Fdotnet-winforms\u002FREADME.md#option-3-wpf-c--ccli-wrapper) - C++\u002FCLI + BitmapSource\n2. [Bitmap Conversion](examples\u002Fdotnet-winforms\u002FREADME.md#2-bitmap--bitmapsource-conversion) - Freeze(), Dispatcher\n3. [Production Guidelines](examples\u002Fdotnet-winforms\u002FREADME.md#production-quality-review) - Performance optimization\n\n### I want to use C# \u002F .NET Framework (general)\n1. [.NET Integration Overview](examples\u002Fdotnet-winforms\u002FREADME.md) - **Complete C++\u002FCLI wrapper guide**\n2. [Raw Video Capture](examples\u002Fraw-video-capture.md) - YUV→RGB conversion patterns\n3. [Session Join Pattern](examples\u002Fsession-join-pattern.md) - SDK initialization flow\n\n### I want to wrap ANY native C++ library for .NET\n1. [C++\u002FCLI Wrapper Patterns](examples\u002Fdotnet-winforms\u002FREADME.md#ccli-wrapper-patterns-for-net-integration) - **Complete 8-pattern guide**\n2. [Pattern 1: Basic Structure](examples\u002Fdotnet-winforms\u002FREADME.md#pattern-1-basic-wrapper-structure) - Project setup + class layout\n3. [Pattern 3: gcroot Callbacks](examples\u002Fdotnet-winforms\u002FREADME.md#pattern-3-gcrootT-for-nativemanaged-callbacks) - Native→Managed events\n4. [Pattern 4: IDisposable](examples\u002Fdotnet-winforms\u002FREADME.md#pattern-4-destructor--finalizer-idisposable) - Cleanup pattern\n5. [Common Errors](examples\u002Fdotnet-winforms\u002FREADME.md#common-wrapper-errors) - Troubleshooting\n\n### I want to implement a specific feature\n1. [SDK Architecture Pattern](concepts\u002Fsdk-architecture-pattern.md) - **START HERE!**\n2. [Singleton Hierarchy](concepts\u002Fsingleton-hierarchy.md) - Navigate to the feature\n3. [API Reference](references\u002Fwindows-reference.md) - Method signatures\n\n---\n\n## Most Critical Documents\n\n### 1. SDK Architecture Pattern (MASTER DOCUMENT)\n**[concepts\u002Fsdk-architecture-pattern.md](concepts\u002Fsdk-architecture-pattern.md)**\n\nThe universal 3-step pattern:\n1. Get singleton (SDK, helpers, session, users)\n2. Implement delegate (event callbacks)\n3. Subscribe and use\n\n### 2. Windows Message Loop (MOST COMMON ISSUE)\n**[troubleshooting\u002Fwindows-message-loop.md](troubleshooting\u002Fwindows-message-loop.md)**\n\n99% of \"callbacks not firing\" issues are caused by missing Windows message loop.\n\n### 3. Singleton Hierarchy (NAVIGATION MAP)\n**[concepts\u002Fsingleton-hierarchy.md](concepts\u002Fsingleton-hierarchy.md)**\n\n5-level deep navigation showing how to reach every feature.\n\n---\n\n## Key Learnings\n\n### Critical Discoveries:\n\n1. **Windows Message Loop is MANDATORY**\n   - SDK uses Windows message pump for callbacks\n   - Without it, callbacks are queued but never fire\n   - See: [Windows Message Loop Guide](troubleshooting\u002Fwindows-message-loop.md)\n\n2. **Subscribe in onUserVideoStatusChanged, NOT onUserJoin**\n   - Video may not be ready when user joins\n   - Wait for video status change callback\n   - See: [Video Rendering](examples\u002Fvideo-rendering.md)\n\n3. **Two Rendering Paths**\n   - Canvas API: SDK renders to your HWND (recommended)\n   - Raw Data Pipe: You receive YUV frames (advanced)\n   - See: [Canvas vs Raw Data](concepts\u002Fcanvas-vs-raw-data.md)\n\n4. **Helpers Control YOUR Streams Only**\n   - `videoHelper->startVideo()` starts YOUR camera\n   - To see others, subscribe to their Canvas\u002FPipe\n   - See: [Singleton Hierarchy](concepts\u002Fsingleton-hierarchy.md)\n\n5. **UI Framework Integration Differs by Platform**\n   - **Win32**: Direct SDK, Canvas API (SDK renders to HWND) - best performance\n   - **WinForms**: C++\u002FCLI wrapper, Raw Data Pipe, YUV→Bitmap, InvokeRequired\n   - **WPF**: Same wrapper + Bitmap→BitmapSource, Dispatcher, Freeze()\n   - See: [UI Framework Integration](examples\u002Fdotnet-winforms\u002FREADME.md)\n\n6. **C++\u002FCLI Wrapper Patterns (for ANY native library → .NET)**\n   - `void*` pointers - hide native types from managed headers\n   - `gcroot\u003CT^>` - prevent GC from collecting managed references in native code\n   - Finalizer + Destructor - `~Class()` and `!Class()` for IDisposable cleanup\n   - `pin_ptr` + `Marshal::Copy` - array\u002Fbuffer conversion\n   - `LockBits` - 100x faster than SetPixel for image manipulation\n   - Thread marshaling - InvokeRequired (WinForms) \u002F Dispatcher (WPF)\n   - See: [C++\u002FCLI Wrapper Guide](examples\u002Fdotnet-winforms\u002FREADME.md#ccli-wrapper-patterns-for-net-integration)\n\n7. **Audio Connection Timing**\n   - Set `audioOption.connect = false` during join\n   - Call `startAudio()` in `onSessionJoin` callback\n   - See: [Production Guidelines](examples\u002Fdotnet-winforms\u002FREADME.md#production-quality-review)\n\n---\n\n## Quick Reference\n\n### \"My code won't compile\"\n→ [Build Errors Guide](troubleshooting\u002Fbuild-errors.md)\n\n### \"Callbacks never fire\"\n→ [Windows Message Loop](troubleshooting\u002Fwindows-message-loop.md)\n\n### \"Video subscription returns error 2\"\n→ [Video Rendering](examples\u002Fvideo-rendering.md) - Subscribe in onUserVideoStatusChanged\n\n### \"Abstract class error\"\n→ [Delegate Methods](references\u002Fdelegate-methods.md)\n\n### \"How do I implement [feature]?\"\n→ [SDK Architecture Pattern](concepts\u002Fsdk-architecture-pattern.md)\n\n### \"How do I navigate to [controller]?\"\n→ [Singleton Hierarchy](concepts\u002Fsingleton-hierarchy.md)\n\n### \"What error code means what?\"\n→ [Common Issues](troubleshooting\u002Fcommon-issues.md)\n\n---\n\n## Document Version\n\nBased on **Zoom Video SDK for Windows v2.x**\n\n---\n\n**Happy coding!**\n\nRemember: The [SDK Architecture Pattern](concepts\u002Fsdk-architecture-pattern.md) is your key to unlocking the entire SDK. Read it first!\n\n## Operations\n\n- [RUNBOOK.md](RUNBOOK.md) - 5-minute preflight and debugging checklist.\n",{},"main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002Fzoom\u002Fskills\u002Fwindows",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":7,"skillName":5,"commands":32,"prompt":42,"zipAvailable":43},[33,36,39],{"tool":34,"command":35},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill windows",{"tool":37,"command":38},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill windows",{"tool":40,"command":41},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill windows","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",true,[],[46,60,70,79,86,92],{"skillNo":47,"name":48,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":49,"categories":50,"keywords":56,"publishedAt":18},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[51],{"categoryNo":52,"categoryName":53,"taxonomyType":54,"taxonomyTypeDesc":55},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[57,58,59],"model","model hierarchy","model hierarchy skill",{"skillNo":61,"name":62,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":63,"categories":64,"keywords":66,"publishedAt":18},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[65],{"categoryNo":52,"categoryName":53,"taxonomyType":54,"taxonomyTypeDesc":55},[67,68,69],"MCP","zoom","zoom mcp",{"skillNo":71,"name":72,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":73,"categories":74,"keywords":75,"publishedAt":18},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[76,77,78,68],"sdk","zoom apps sdk","apps",{"skillNo":80,"name":81,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":82,"categories":83,"keywords":85,"publishedAt":18},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[84],{"categoryNo":52,"categoryName":53,"taxonomyType":54,"taxonomyTypeDesc":55},[81],{"skillNo":87,"name":88,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":89,"categories":90,"keywords":91,"publishedAt":18},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[88],{"skillNo":93,"name":94,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":95,"categories":96,"keywords":97,"publishedAt":18},"01M6FKHYEMDF3NWJ0EDFCSBHK5","webhooks","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebhooks\u002FSKILL.md",[],[94]]