License Error using Custom UI Feature in Windows SDK

Description
Trying to use custom UI feature in Windows SDK, however uiManager->HasLicense() returns SDKERR_NO_PERMISSION after it is created. The same code without custom ui will join meeting successfully.

Which Windows Meeting SDK version?
zoom-sdk-windows-5.7.6.1081

To Reproduce(If applicable)
This function is called on ZOOM_SDK_NAMESPACE::MEETING_STATUS_CONNECTING in onMeetingStatusChanged in my event handler class implementing ZOOM_SDK_NAMESPACE::IMeetingServiceEvent. I get SDKERR_NO_PERMISSION. If I remove the call to customUIInit, I can join meeting successfully. Note I’m initialising with initParam.obConfigOpts.optionalFeatures = (1 << 5);.

void customUIInit() {
    ZOOM_SDK_NAMESPACE::SDKError err;
    printf("Setting up custom UI handling\n");
    CustomUIEventHandler customUIEventHandler;
    ZOOM_SDK_NAMESPACE::ICustomizedUIMgr *uiManager;
    err = ZOOM_SDK_NAMESPACE::CreateCustomizedUIMgr(&uiManager);
    uiManager->SetEvent(&customUIEventHandler);
    printf("Custom UI handling result %d\n",err);

    // Check license allows
    err = uiManager->HasLicense();
    if (ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS != err) {
        printf("License failure for custom UI handling - %d\n",err);
        // Getting 12 = SDKERR_NO_PERMISSION - shouldn't need a license but seems to be required :(
        printLastZoomError();
        exit(-1);
    }
...

Device (please complete the following information):
Windows 10

Additional context
Logs available, not sure how to attach?

Hi @max_dev, thanks for using the dev forum.

This function used to be required a long time ago when custom UI mode was not enabled for all accounts, but now all developer accounts should be able to use a custom meeting UI. You should not need to check this value before trying to join a meeting. We will work on updating our documentation to reflect this.

Thanks!

Thanks @jon.lieblich - ok, so if I skip the license check, when I attempt to create the video container, I get SDKERR_UNINITIALIZE (see code snippet below). I’ve tried to follow all the instructions on Custom UI Feature but must be missing something.

Again, when I remove my custom UI handling, I can successfully join the meeting. Also note I’m building on mingw in windows.

Also, I note that that the license check is included in the demo_sdk_v2 under at custom_ui_mgr.cpp:486 - so I still think that maybe my license check should succeed?

    ZOOM_SDK_NAMESPACE::ICustomizedVideoContainer* videoContainer = NULL;
    err = uiManager->CreateVideoContainer(&videoContainer, hParent, rc);
    if (videoContainer == NULL) {
        // We hit this condition, err is 7 = SDKERR_UNINITIALIZE
        printf("Video container null on CreateVideoContainer: %d\n",err);
        printLastZoomError();
        exit(-1);
    }

Hi @max_dev,

This error indicates that you have not yet initialized the SDK. Before you can do anything with the SDK, you must first initialize it via ZOOM_SDK_NAMESPACE::InitSDK, and then authorize the SDK with your developer credentials via SDKAuth

Thanks!

Thanks @jon.lieblich - the SDK is initialised and authorised as far as I can tell. The code below which runs successfully - I get ZOOM_SDK_NAMESPACE::AUTHRET_SUCCESS in the onAuthenticationReturn function in my ZOOM_SDK_NAMESPACE::IAuthServiceEvent implementation. If I remove the custom UI handling code, I can successfully join the meeting.

    ZOOM_SDK_NAMESPACE::InitParam initParam;
    initParam.strWebDomain = L"https://zoom.us";
    initParam.strSupportUrl = L"https://zoom.us";
    // Request Custom UI feature - see https://marketplace.zoom.us/docs/sdk/native-sdks/windows/mastering-sdk/custom-ui-feature/
    initParam.obConfigOpts.optionalFeatures = (1 << 5); 
    initParam.enableLogByDefault = true;
    //initParam.enableGenerateDump = true;
    ZOOM_SDK_NAMESPACE::SDKError err = ZOOM_SDK_NAMESPACE::InitSDK(initParam);
    if (ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS != err) {
        printf("failed InitSDK\n");
        printLastZoomError();
        return -1; 
    }   

    ZOOM_SDK_NAMESPACE::IAuthService *authService;
    err = ZOOM_SDK_NAMESPACE::CreateAuthService(&authService);
    if (ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS != err) {
        printf("failed CreateAuthService\n");
        printLastZoomError();
        return -1;
    }
    authService->SetEvent(&zoomServiceEventHandler);
    // Auth using key and secret
    ZOOM_SDK_NAMESPACE::AuthParam authParam;
    authParam.appKey = appKey.c_str();
    authParam.appSecret = appSecret.c_str();
    err = authService->SDKAuth(authParam); // AUTHRET_KEYORSECRETWRONG

    // Do the auth
    if(ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS != err) {
        printf("Failed SDKAuth\n");
        printLastZoomError();
        return -1;
    }

Hi @max_dev,

Just to make sure I’m understanding correctly, can you clarify which “custom UI handling code” being removed results in you being able to successfully join the meeting?

Thanks!

Hi @jon.lieblich ,

So, I can:

  • remove the call to customUIInit from my onMeetingStatusChanged implementation,
  • remove the initialisation line initParam.obConfigOpts.optionalFeatures = (1 << 5);

Then I can join the meeting successfullyPreformatted text. Here’s sample code for reference. Note that I’m using mingw on windows 10.

Thanks,

Max

Output With Custom UI Handling

Starting app
Args 4
Meeting number #########
App key #########
App secret #########
Waiting for event
onAuthenticationReturn - 0
onAuthenticationReturn - AUTHRET_SUCCESS
Finished waiting for event
Creating Meeting Service
Joining the meeting
Joining meeting
Waiting for event
onLoginReturnWithReason - loginstatus 0, LoginFailReason 0
onMeetingStatusChanged - 7, result 0
MEETING_STATUS_ENDED
onMeetingStatusChanged - 1, result 0
MEETING_STATUS_CONNECTING
Setting up custom UI handling
Video container null on CreateVideoContainer: 7

Output Without Custom UI Handling

Starting app
Args 4
Meeting number #########
App key #########
App secret #########
Waiting for event
onAuthenticationReturn - 0
onAuthenticationReturn - AUTHRET_SUCCESS
Finished waiting for event
Creating Meeting Service
Joining the meeting
Joining meeting
Waiting for event
onMeetingStatusChanged - 1, result 0
MEETING_STATUS_CONNECTING
onMeetingStatusChanged - 2, result 0
MEETING_STATUS_WAITINGFORHOST

Reference Code

#include <windows.h>
#include <stdio.h>
#include <unistd.h>
#include <string>
#include "zoom_sdk.h"
#include "auth_service_interface.h"
#include "meeting_service_interface.h"
#include "setting_service_interface.h"
#include "meeting_service_components/meeting_configuration_interface.h"
#include "customized_ui/zoom_customized_ui.h"
#include "customized_ui/customized_ui_mgr.h"
#include "customized_ui/customized_video_container.h"

void customUIInit();

class CustomUIEventHandler : public ZOOM_SDK_NAMESPACE::ICustomizedUIMgrEvent, public ZOOM_SDK_NAMESPACE::ICustomizedVideoContainerEvent {
    virtual void onVideoContainerDestroyed(ZOOM_SDK_NAMESPACE::ICustomizedVideoContainer* pContainer) {}
    virtual void onShareRenderDestroyed(ZOOM_SDK_NAMESPACE::ICustomizedShareRender* pRender) {}
    virtual void onRenderUserChanged(ZOOM_SDK_NAMESPACE::IVideoRenderElement *pElement, unsigned int userid) {}
    virtual void onRenderDataTypeChanged(ZOOM_SDK_NAMESPACE::IVideoRenderElement *pElement, ZOOM_SDK_NAMESPACE::VideoRenderDataType dataType) { }
    virtual void onLayoutNotification(RECT wnd_client_rect) { }
    virtual void onVideoRenderElementDestroyed(ZOOM_SDK_NAMESPACE::IVideoRenderElement *pElement) { }
    virtual void onWindowMsgNotification(UINT uMsg, WPARAM wParam, LPARAM lParam) { }
    virtual void onSubscribeUserFail(ZOOM_SDK_NAMESPACE::ZoomSDKVideoSubscribeFailReason fail_reason, ZOOM_SDK_NAMESPACE::IVideoRenderElement* pElement) {}
};

class ZoomServiceEventHandler : public ZOOM_SDK_NAMESPACE::IAuthServiceEvent, public ZOOM_SDK_NAMESPACE::IMeetingServiceEvent {
    public:
        ZoomServiceEventHandler() {}
        virtual ~ZoomServiceEventHandler() {}
        virtual void onAuthenticationReturn(ZOOM_SDK_NAMESPACE::AuthResult ret) {
            printf("onAuthenticationReturn - %d\n",ret);
            waitForEventFlag = false;
            errorFlag = true;
            if (ret == ZOOM_SDK_NAMESPACE::AUTHRET_SUCCESS) {
                printf("onAuthenticationReturn - AUTHRET_SUCCESS\n");
                errorFlag = false;
            } else if (ret == ZOOM_SDK_NAMESPACE::AUTHRET_JWTTOKENWRONG) {
                printf("onAuthenticationReturn - AUTHRET_JWTTOKENWRONG\n");
            } else if (ret == ZOOM_SDK_NAMESPACE::AUTHRET_KEYORSECRETWRONG) {
                printf("onAuthenticationReturn - AUTHRET_KEYORSECRETWRONG\n");
            } else {
                printf("onAuthenticationReturn - OTHER ERROR\n");
            }
        }
        virtual void onLoginRet(ZOOM_SDK_NAMESPACE::LOGINSTATUS ret, ZOOM_SDK_NAMESPACE::IAccountInfo* pAccountInfo) {
            printf("onLoginRet\n");
        }
        virtual void onLoginReturnWithReason(ZOOM_SDK_NAMESPACE::LOGINSTATUS ret, ZOOM_SDK_NAMESPACE::IAccountInfo* pAccountInfo, ZOOM_SDK_NAMESPACE::LoginFailReason reason) {
            printf("onLoginReturnWithReason - loginstatus %d, LoginFailReason %d\n",ret,reason);
        }
        virtual void onLogout() {
            printf("onLogout\n");
        }
        virtual void onZoomIdentityExpired() {
            printf("onZoomIdentityExpired\n");
        }
        virtual void onZoomAuthIdentityExpired() {
            printf("onZoomAuthIdentityExpired\n");
        }
        // Meeting service
        virtual void onMeetingStatusChanged(ZOOM_SDK_NAMESPACE::MeetingStatus status, int iResult=0) {
            printf("onMeetingStatusChanged - %d, result %d\n",status,iResult);
            if (status == ZOOM_SDK_NAMESPACE::MEETING_STATUS_IDLE) {
                printf("MEETING_STATUS_IDLE\n");
            } else if (status == ZOOM_SDK_NAMESPACE::MEETING_STATUS_CONNECTING) {
                printf("MEETING_STATUS_CONNECTING\n");
                // https://marketplace.zoom.us/docs/sdk/native-sdks/windows/mastering-sdk/custom-ui-feature/
                // It is recommended to create the meeting service when you receive the onMeetingStatusChanged notification with the parameter MeetingStatus equalling ZOOM_SDK_NAMESPACE::MEETING_STATUS_CONNECTING. This allows you to work with the preview meeting function as well.
                customUIInit();
            } else if (status == ZOOM_SDK_NAMESPACE::MEETING_STATUS_WAITINGFORHOST) {
                printf("MEETING_STATUS_WAITINGFORHOST\n");
            } else if (status == ZOOM_SDK_NAMESPACE::MEETING_STATUS_INMEETING) {
                printf("MEETING_STATUS_INMEETING\n");
            } else if (status == ZOOM_SDK_NAMESPACE::MEETING_STATUS_DISCONNECTING) {
                printf("MEETING_STATUS_DISCONNECTING\n");
            } else if (status == ZOOM_SDK_NAMESPACE::MEETING_STATUS_RECONNECTING) {
                printf("MEETING_STATUS_RECONNECTING\n");
            } else if (status == ZOOM_SDK_NAMESPACE::MEETING_STATUS_FAILED) {
                printf("MEETING_STATUS_FAILED\n");
            } else if (status == ZOOM_SDK_NAMESPACE::MEETING_STATUS_ENDED) {
                printf("MEETING_STATUS_ENDED\n");
            } else if (status == ZOOM_SDK_NAMESPACE::MEETING_STATUS_UNKNOW) {
                printf("MEETING_STATUS_UNKNOW\n");
            } else if (status == ZOOM_SDK_NAMESPACE::MEETING_STATUS_LOCKED) {
                printf("MEETING_STATUS_LOCKED\n");
            } else if (status == ZOOM_SDK_NAMESPACE::MEETING_STATUS_UNLOCKED) {
                printf("MEETING_STATUS_UNLOCKED\n");
            } else if (status == ZOOM_SDK_NAMESPACE::MEETING_STATUS_IN_WAITING_ROOM) {
                printf("MEETING_STATUS_IN_WAITING_ROOM\n");
            } else {
                printf("UNKNOWN\n");
            }
        }
        virtual void onMeetingStatisticsWarningNotification (ZOOM_SDK_NAMESPACE::StatisticsWarningType type) {
            printf("onMeetingStatusChanged\n");
        }
        virtual void onMeetingParameterNotification(const ZOOM_SDK_NAMESPACE::MeetingParameter* meeting_param) {
            printf("onMeetingParameterNotification\n");
        }
        bool waitForEvent() {
            printf("Waiting for event\n");
            MSG msg;
            int bRet;
            waitForEventFlag = true;
            while (((bRet = GetMessage(&msg, nullptr, 0, 0)) != 0)&&(waitForEventFlag)) {
                //printf("Got message in windows event handler\n");
                if (bRet == -1) {
                    //printf("Error event\n");
                    // handle the error and possibly exit
                } else {
                    //printf("Not error event\n");
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
            }
            printf("Finished waiting for event\n");
            return errorFlag;
        }
        
    private:
        bool waitForEventFlag;
        bool errorFlag;
};

ZoomServiceEventHandler zoomServiceEventHandler;

void customUIInit() {
    // Custom UI stuff
    ZOOM_SDK_NAMESPACE::SDKError err;
    printf("Setting up custom UI handling\n");
    CustomUIEventHandler customUIEventHandler;
    ZOOM_SDK_NAMESPACE::ICustomizedUIMgr *uiManager;
    err = ZOOM_SDK_NAMESPACE::CreateCustomizedUIMgr(&uiManager);
    if (ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS != err) {
        printf("CreateCustomizedUIMgr failure - %d\n",err);
        exit(-1);
    }
    uiManager->SetEvent(&customUIEventHandler);
    HWND hParent = ::CreateWindowA("STATIC","dummy",WS_VISIBLE,0,0,200,200,NULL,NULL,NULL,NULL);
    ::SetWindowTextA(hParent,"Dummy Window!");
    RECT rc;
    GetWindowRect(hParent, &rc);
    rc.bottom = rc.bottom - 10;
    rc.top = rc.top + 10;
    rc.right = rc.right - 10;
    rc.left = rc.left + 10;

    ZOOM_SDK_NAMESPACE::ICustomizedVideoContainer* videoContainer = NULL;
    err = uiManager->CreateVideoContainer(&videoContainer, hParent, rc);
    if (videoContainer == NULL) {
        // We hit this condition, err is 7 = SDKERR_UNINITIALIZE
        printf("Video container null on CreateVideoContainer: %d\n",err);
        exit(0);
    }
    videoContainer->SetEvent(&customUIEventHandler);
    videoContainer->Show();
}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR , int nCmdShow) {
    AllocConsole();
    freopen("CONOUT$", "w", stdout);
    printf("Starting app\n");
    int argCount;
    LPWSTR *szArgList = CommandLineToArgvW(GetCommandLineW(), &argCount);
    printf("Args %d\n",argCount);
    if (argCount != 4) {
        printf("Usage: winpoc.exe <meeting id> <app key> <app secret>\n");
        return 0;
    }
    std::wstring meetingNumber = szArgList[1];
    std::wstring appKey = szArgList[2];
    std::wstring appSecret = szArgList[3];
    printf("Meeting number %ls\n",meetingNumber.c_str());
    printf("App key %ls\n",appKey.c_str());
    printf("App secret %ls\n",appSecret.c_str());
    ZOOM_SDK_NAMESPACE::InitParam initParam;
    initParam.strWebDomain = L"https://zoom.us";
    initParam.strSupportUrl = L"https://zoom.us";
    // Request Custom UI feature - see https://marketplace.zoom.us/docs/sdk/native-sdks/windows/mastering-sdk/custom-ui-feature/
    initParam.obConfigOpts.optionalFeatures = (1 << 5);
    initParam.enableLogByDefault = true;
    //initParam.enableGenerateDump = true;
    ZOOM_SDK_NAMESPACE::SDKError err = ZOOM_SDK_NAMESPACE::InitSDK(initParam);
    if (ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS != err) {
        printf("failed InitSDK\n");
        return -1;
    }
    ZOOM_SDK_NAMESPACE::IAuthService *authService;
    err = ZOOM_SDK_NAMESPACE::CreateAuthService(&authService);
    if (ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS != err) {
        printf("failed CreateAuthService\n");
        return -1;
    }
    authService->SetEvent(&zoomServiceEventHandler);
    // Auth using key and secret
    ZOOM_SDK_NAMESPACE::AuthParam authParam;
    authParam.appKey = appKey.c_str();
    authParam.appSecret = appSecret.c_str();
    err = authService->SDKAuth(authParam); // AUTHRET_KEYORSECRETWRONG
    // Do the auth
    if(ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS != err) {
        printf("Failed SDKAuth\n");
        return -1;
    }
    // Creating auth thread
    if (zoomServiceEventHandler.waitForEvent()) {
        printf("Error!!\n");
        return -1;
    }
    // Get meeting service - this is working
    printf("Creating Meeting Service\n");
    ZOOM_SDK_NAMESPACE::IMeetingService* meetingService;
    err = ZOOM_SDK_NAMESPACE::CreateMeetingService(&meetingService);
    if(ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS != err) {
        printf("failed GetMeetingService\n");
        return -1;
    }
    meetingService->SetEvent(&zoomServiceEventHandler);

    // Get setting service - this is working
    if (meetingService->GetMeetingConfiguration())
        meetingService->GetMeetingConfiguration()->SetSharingToolbarVisibility(false);
    ZOOM_SDK_NAMESPACE::ISettingService* _setting_service;
    ZOOM_SDK_NAMESPACE::CreateSettingService(&_setting_service);
    if (!_setting_service) {
        printf("failed CreateSettingService\n");
        return -1;
    }

    printf("Joining the meeting\n");
    ZOOM_SDK_NAMESPACE::tagJoinParam joinParam;
    joinParam.userType = ZOOM_SDK_NAMESPACE::SDK_UT_WITHOUT_LOGIN;
    ZOOM_SDK_NAMESPACE::JoinParam4WithoutLogin& withoutloginParam = joinParam.param.withoutloginuserJoin;
    withoutloginParam.meetingNumber = _wtoi64(meetingNumber.c_str());
    withoutloginParam.vanityID = NULL;
    withoutloginParam.userName = L"My PoC";
    withoutloginParam.psw = L"#######";
    withoutloginParam.hDirectShareAppWnd = NULL;
    withoutloginParam.customer_key = NULL;
    withoutloginParam.webinarToken = NULL;
    withoutloginParam.isDirectShareDesktop = false;
    withoutloginParam.isVideoOff = false;
    withoutloginParam.isAudioOff = false;
    printf("Joining meeting\n");
    err = meetingService->Join(joinParam);
    if (err != ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS) {
        printf("Error on join meeting %d\n",err);
        return -1;
    }
    if (zoomServiceEventHandler.waitForEvent()) {
        printf("Error!!\n");
        return -1;
    }
    printf("Waiting ...\n");
    sleep(20);
    printf("Finished app\n");
    return 0;
}

Hi @max_dev,

There is a lot of extraneous code/information in the snippet and log output you’ve provided. Let’s try to distill this information down to just the usages of the SDK, when they happen, and the return values of each one.

Thanks!

Hi @jon.lieblich - hmm, I think the code I included is pretty much the minimal use of the SDK to join a meeting with custom UI - all code I’ve provided relates directly to SDK usage (I’ve pared it back a little further to 180 lines below). I wanted to show that I can successfully join the meeting without custom UI feature, but when I start to use the custom UI feature, it fails to create a video container with the error SDKERR_UNINITIALIZE. The only difference in code being the call to the customUIInit function (and also SDK initialisation for custom ui in initParam.obConfigOpts.optionalFeatures = (1 << 5);).

Let me know if I can provide any further info that would help. Thanks!

#include <windows.h>
#include <stdio.h>
#include <unistd.h>
#include <string>
#include "zoom_sdk.h"
#include "auth_service_interface.h"
#include "meeting_service_interface.h"
#include "setting_service_interface.h"
#include "meeting_service_components/meeting_configuration_interface.h"
#include "customized_ui/zoom_customized_ui.h"
#include "customized_ui/customized_ui_mgr.h"
#include "customized_ui/customized_video_container.h"

void customUIInit();

class CustomUIEventHandler : public ZOOM_SDK_NAMESPACE::ICustomizedUIMgrEvent, public ZOOM_SDK_NAMESPACE::ICustomizedVideoContainerEvent {
    virtual void onVideoContainerDestroyed(ZOOM_SDK_NAMESPACE::ICustomizedVideoContainer* pContainer) {}
    virtual void onShareRenderDestroyed(ZOOM_SDK_NAMESPACE::ICustomizedShareRender* pRender) {}
    virtual void onRenderUserChanged(ZOOM_SDK_NAMESPACE::IVideoRenderElement *pElement, unsigned int userid) {}
    virtual void onRenderDataTypeChanged(ZOOM_SDK_NAMESPACE::IVideoRenderElement *pElement, ZOOM_SDK_NAMESPACE::VideoRenderDataType dataType) { }
    virtual void onLayoutNotification(RECT wnd_client_rect) { }
    virtual void onVideoRenderElementDestroyed(ZOOM_SDK_NAMESPACE::IVideoRenderElement *pElement) { }
    virtual void onWindowMsgNotification(UINT uMsg, WPARAM wParam, LPARAM lParam) { }
    virtual void onSubscribeUserFail(ZOOM_SDK_NAMESPACE::ZoomSDKVideoSubscribeFailReason fail_reason, ZOOM_SDK_NAMESPACE::IVideoRenderElement* pElement) {}
};

class ZoomServiceEventHandler : public ZOOM_SDK_NAMESPACE::IAuthServiceEvent, public ZOOM_SDK_NAMESPACE::IMeetingServiceEvent {
    public:
        ZoomServiceEventHandler() {}
        virtual ~ZoomServiceEventHandler() {}
        virtual void onLoginRet(ZOOM_SDK_NAMESPACE::LOGINSTATUS ret, ZOOM_SDK_NAMESPACE::IAccountInfo* pAccountInfo) {}
        virtual void onLoginReturnWithReason(ZOOM_SDK_NAMESPACE::LOGINSTATUS ret, ZOOM_SDK_NAMESPACE::IAccountInfo* pAccountInfo, ZOOM_SDK_NAMESPACE::LoginFailReason reason) {}
        virtual void onLogout() {}
        virtual void onZoomIdentityExpired() {}
        virtual void onZoomAuthIdentityExpired() {}
        virtual void onAuthenticationReturn(ZOOM_SDK_NAMESPACE::AuthResult ret) {
            printf("onAuthenticationReturn - %d\n",ret);
            waitForEventFlag = false;
            errorFlag = true;
            if (ret == ZOOM_SDK_NAMESPACE::AUTHRET_SUCCESS) errorFlag = false;
        }
        // Meeting service
        virtual void onMeetingStatusChanged(ZOOM_SDK_NAMESPACE::MeetingStatus status, int iResult=0) {
            if (status == ZOOM_SDK_NAMESPACE::MEETING_STATUS_CONNECTING) {
                printf("MEETING_STATUS_CONNECTING\n");
                // https://marketplace.zoom.us/docs/sdk/native-sdks/windows/mastering-sdk/custom-ui-feature/
                // It is recommended to create the meeting service when you receive the onMeetingStatusChanged notification with the parameter MeetingStatus equalling ZOOM_SDK_NAMESPACE::MEETING_STATUS_CONNECTING. This allows you to work with the preview meeting function as well.
                customUIInit();
            } else if (status == ZOOM_SDK_NAMESPACE::MEETING_STATUS_WAITINGFORHOST) {
                printf("MEETING_STATUS_WAITINGFORHOST\n");
            } else {
                printf("Meeting status %d\n",status);
            }
        }
        virtual void onMeetingStatisticsWarningNotification (ZOOM_SDK_NAMESPACE::StatisticsWarningType type) {}
        virtual void onMeetingParameterNotification(const ZOOM_SDK_NAMESPACE::MeetingParameter* meeting_param) {}
        bool waitForEvent() {
            MSG msg;
            int bRet;
            waitForEventFlag = true;
            while (((bRet = GetMessage(&msg, nullptr, 0, 0)) != 0)&&(waitForEventFlag)) {
                if (bRet == -1) {
                } else {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
            }
            return errorFlag;
        }
    private:
        bool waitForEventFlag;
        bool errorFlag;
};

ZoomServiceEventHandler zoomServiceEventHandler;

void customUIInit() {
    // Custom UI stuff
    ZOOM_SDK_NAMESPACE::SDKError err;
    printf("Setting up custom UI handling\n");
    CustomUIEventHandler customUIEventHandler;
    ZOOM_SDK_NAMESPACE::ICustomizedUIMgr *uiManager;
    err = ZOOM_SDK_NAMESPACE::CreateCustomizedUIMgr(&uiManager);
    if (ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS != err) {
        printf("CreateCustomizedUIMgr failure - %d\n",err);
        exit(-1);
    }
    uiManager->SetEvent(&customUIEventHandler);
    HWND hParent = ::CreateWindowA("STATIC","dummy",WS_VISIBLE,0,0,200,200,NULL,NULL,NULL,NULL);
    ::SetWindowTextA(hParent,"Dummy Window!");
    RECT rc;
    GetWindowRect(hParent, &rc);
    rc.bottom = rc.bottom - 10;
    rc.top = rc.top + 10;
    rc.right = rc.right - 10;
    rc.left = rc.left + 10;

    ZOOM_SDK_NAMESPACE::ICustomizedVideoContainer* videoContainer = NULL;
    err = uiManager->CreateVideoContainer(&videoContainer, hParent, rc);
    if (videoContainer == NULL) {
        // We hit this condition, err is 7 = SDKERR_UNINITIALIZE
        printf("Video container null on CreateVideoContainer: %d\n",err);
        exit(0);
    }
    videoContainer->SetEvent(&customUIEventHandler);
    videoContainer->Show();
}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR , int nCmdShow) {
    AllocConsole();
    freopen("CONOUT$", "w", stdout);
    printf("Starting app\n");
    int argCount;
    LPWSTR *szArgList = CommandLineToArgvW(GetCommandLineW(), &argCount);
    printf("Args %d\n",argCount);
    if (argCount != 4) {
        printf("Usage: winpoc.exe <meeting id> <app key> <app secret>\n");
        return 0;
    }
    std::wstring meetingNumber = szArgList[1];
    std::wstring appKey = szArgList[2];
    std::wstring appSecret = szArgList[3];
    printf("Meeting number %ls\n",meetingNumber.c_str());
    printf("App key %ls\n",appKey.c_str());
    printf("App secret %ls\n",appSecret.c_str());
    ZOOM_SDK_NAMESPACE::InitParam initParam;
    initParam.strWebDomain = L"https://zoom.us";
    initParam.strSupportUrl = L"https://zoom.us";
    // Request Custom UI feature - see https://marketplace.zoom.us/docs/sdk/native-sdks/windows/mastering-sdk/custom-ui-feature/
    //initParam.obConfigOpts.optionalFeatures = (1 << 5);
    initParam.enableLogByDefault = true;
    ZOOM_SDK_NAMESPACE::SDKError err = ZOOM_SDK_NAMESPACE::InitSDK(initParam);
    if (ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS != err) return -1;
    ZOOM_SDK_NAMESPACE::IAuthService *authService;
    err = ZOOM_SDK_NAMESPACE::CreateAuthService(&authService);
    if (ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS != err) return -1;
    authService->SetEvent(&zoomServiceEventHandler);
    // Auth using key and secret
    ZOOM_SDK_NAMESPACE::AuthParam authParam;
    authParam.appKey = appKey.c_str();
    authParam.appSecret = appSecret.c_str();
    err = authService->SDKAuth(authParam); // AUTHRET_KEYORSECRETWRONG
    // Do the auth
    if(ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS != err) return -1;
    if (zoomServiceEventHandler.waitForEvent()) return -1;
    // Get meeting service - this is working
    printf("Creating Meeting Service\n");
    ZOOM_SDK_NAMESPACE::IMeetingService* meetingService;
    err = ZOOM_SDK_NAMESPACE::CreateMeetingService(&meetingService);
    if(ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS != err) return -1;
    meetingService->SetEvent(&zoomServiceEventHandler);

    // Get setting service - this is working
    if (meetingService->GetMeetingConfiguration()) meetingService->GetMeetingConfiguration()->SetSharingToolbarVisibility(false);
    ZOOM_SDK_NAMESPACE::ISettingService* _setting_service;
    ZOOM_SDK_NAMESPACE::CreateSettingService(&_setting_service);
    if (!_setting_service) return -1;
    printf("Joining the meeting\n");
    ZOOM_SDK_NAMESPACE::tagJoinParam joinParam;
    joinParam.userType = ZOOM_SDK_NAMESPACE::SDK_UT_WITHOUT_LOGIN;
    ZOOM_SDK_NAMESPACE::JoinParam4WithoutLogin& withoutloginParam = joinParam.param.withoutloginuserJoin;
    withoutloginParam.meetingNumber = _wtoi64(meetingNumber.c_str());
    withoutloginParam.vanityID = NULL;
    withoutloginParam.userName = L"My PoC";
    withoutloginParam.psw = L"XXXXX";
    withoutloginParam.hDirectShareAppWnd = NULL;
    withoutloginParam.customer_key = NULL;
    withoutloginParam.webinarToken = NULL;
    withoutloginParam.isDirectShareDesktop = false;
    withoutloginParam.isVideoOff = false;
    withoutloginParam.isAudioOff = false;
    printf("Joining meeting\n");
    err = meetingService->Join(joinParam);
    if (err != ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS) return -1;
    if (zoomServiceEventHandler.waitForEvent()) return -1;
    printf("Waiting ...\n");
    sleep(20);
    printf("Finished app\n");
    return 0;
}

Hi @jon.lieblich - let me know if there’s any other information I can provide to progress this. I have zoom sdk logs as well if it is of any help? Thanks

Hi @max_dev,

No updates on this yet, but yes if you could provide the SDK logs that may be helpful. You can’t upload the logs directly to the dev forum, so you can either host them on file sharing service or submit them in a ticket through the developer support site. :slightly_smiling_face:

Thanks!