# Quickstart (/en/realtime-media/video/get-started-sdk/windows)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

This page provides a step-by-step guide on how to create a basic Video Calling app using the Agora Video SDK.

    ## Understand the tech [#understand-the-tech-4]

    To start a Video Calling session, implement the following steps in your app:

    * **Initialize the engine**: Before calling other APIs, create and initialize an engine instance.

    * **Join a channel**: Call methods to create and join a channel.

    * **Send and receive audio and video**: All users can publish streams to the channel and subscribe to audio and video streams published by other users in the channel.

    ![Video calling workflow](https://assets-docs.agora.io/images/video-sdk/video-call.svg)

    ## Prerequisites [#prerequisites-4]

    * A camera and a microphone.
    * A valid Agora account and project. See [Agora account management](/en/realtime-media/video/manage-agora-account) for details.
    * A device running Windows 7 or higher.
    * Microsoft Visual Studio 2017 or higher with [C++ desktop development](https://devblogs.microsoft.com/cppblog/windows-desktop-development-with-c-in-visual-studio/) support and C++ 11 or above.
    * If you use C# for development, you also need the [.NET Framework](https://learn.microsoft.com/en-us/dotnet/framework/install/guide-for-developers).

    ## Set up your project [#set-up-your-project-4]

    <Tabs defaultValue="new">
      <TabsList>
        <TabsTrigger value="new">
          Create a new project
        </TabsTrigger>

        <TabsTrigger value="existing">
          Add to an existing project
        </TabsTrigger>
      </TabsList>

      <TabsContent value="new">
        The following steps outline the process to set up a new Visual Studio 2022 project on Windows 11 for implementing real-time audio and video interaction functions.

        1. In Visual Studio, select **File > New > Project** to create a new project. In the pop-up window, select **MFC application** as the project template, click **Next**, update the project name to `AgoraQuickStart`, set the project storage location, and then click **Create**.

        2. In the pop-up MFC application window, set the application type to **Dialog-based** , and set **Use MFC** to **Use MFC in a shared DLL**. Enter the generated class, set the generated class to **Dlg**, set the base class to **CDialog**, and click **Finish**.
      </TabsContent>

      <TabsContent value="existing">
        To integrate real-time audio and video interaction into your project:

        1. Launch Visual Studio 2022 and open your existing project by selecting **File > Open > Project/Solution**.

        2. Navigate to your project directory and open the `.sln` file.
      </TabsContent>
    </Tabs>

    3. Create a user-interface for your app based on your application use-case.

       A basic UI consists of the following controls:

       * A Picture Control for displaying local video
       * A Picture Control for displaying remote video
       * An Edit Control for entering a channel name
       * Buttons to join and leave a channel

       Refer to [Create a user interface](#create-a-user-interface) to get a bare bones sample layout.

    ### Install the SDK [#install-the-sdk-4]

    Install the Agora Video SDK:

    1. Download the latest Windows [SDK](/en/api-reference/sdks).

    2. Unzip and open the downloaded SDK. Copy all subfolders in `sdk/` to your solution folder. Make sure these subfolders are in the same directory as your `.sln` file.

    #### Configure the project [#configure-the-project]

    In the Solution Explorer window, right-click the project name and click **Properties** to configure the following:

    1. Go to the **C/C++ > General > Additional Include Directory** menu, click **Edit**, and in the pop-up window enter `$(SolutionDir)sdk\high_level_api\include`.
    2. Go to the **Linker > General > Additional Library Directory** menu, click **Edit**, and in the pop-up window:
       * for 64 bit Windows, enter `$(SolutionDir)sdk\x86_64`.
       * for x86 Windows, enter `$(SolutionDir)sdk\x86`.
    3. Go to the **Linker > Input > Additional Dependencies** menu, click **Edit**, and in the pop-up window:
       * for 64 bit Windows, enter `$(SolutionDir)sdk\x86_64\agora_rtc_sdk.dll.lib`.
       * for x86 Windows, enter `$(SolutionDir)sdk\x86\agora_rtc_sdk.dll.lib`.
    4. Enter the **Advanced** menu, and in **Advanced Properties**, set **Copy contents to OutDir** and **Copy C++ runtime to output directory** to `Yes`.
    5. Go to the **Build Events > Post-Build Events > Command Line** menu and enter `copy $(SolutionDir)sdk\x86_64\*.dll $(SolutionDir)$(Platform)\$(Configuration)`.
    6. Click **Apply** to save the configuration.

    ## Implement Video Calling [#implement-video-calling-4]

    This section guides you through the implementation of basic real-time audio and video interaction in your app.

    The following figure illustrates the essential steps:

    <Accordions>
      <Accordion title="Quick start sequence">
        ![Video Calling quick start sequence](https://assets-docs.agora.io/images/video-sdk/quick-start-sequence.svg)
      </Accordion>
    </Accordions>

    This guide includes [complete sample code](#complete-sample-code) that demonstrates implementing basic real-time interaction. To understand the core API calls in the sample code, review the following implementation steps.

    ### Initialize the engine [#initialize-the-engine-3]

    For real-time communication, initialize an `IRtcEngine` instance and set up event handlers to manage user interactions within the channel. Use `RtcEngineContext` to specify the [App ID](/en/realtime-media/video/manage-agora-account#get-the-app-id), and custom [event handler](#subscribe-to-video-sdk-events), then call `initialize` to initialize the engine, enabling further channel operations. Add the following code to your header and `.cpp` files:

    ```cpp
    // Declare the required variables
    IRtcEngine* m_rtcEngine = nullptr; // RTC engine instance
    CAgoraQuickStartRtcEngineEventHandler m_eventHandler; // IRtcEngineEventHandler
    ```

    ```cpp
    void CAgoraQuickStartDlg::initializeAgoraEngine() {
        // Create IRtcEngine object
        m_rtcEngine = createAgoraRtcEngine();
        // Create IRtcEngine context object
        RtcEngineContext context;
        // Input your App ID. You can obtain your project's App ID from the Agora Console
        context.appId = APP_ID;
        // Add event handler for callbacks and events
        context.eventHandler = &m_eventHandler;
        // Initialize
        int ret = m_rtcEngine->initialize(context);
        m_initialize = (ret == 0);

        if (m_initialize) {
            // Enable the video module
            m_rtcEngine->enableVideo();
        } else {
            AfxMessageBox(_T("Failed to initialize Agora RTC engine"));
        }
    }
    ```

    ### Join a channel [#join-a-channel-4]

    To join a channel, call `joinChannel`\[2/2] with the following parameters:

    * **Channel name**: The name of the channel to join. Clients that pass the same channel name join the same channel. If a channel with the specified name does not exist, it is created when the first user joins.

    * **Authentication token**: A dynamic key that authenticates a user when the client joins a channel. In a production environment, you obtain a token from a [token server](/en/realtime-media/video/build/authenticate-users/deploy-token-server) in your security infrastructure. For the purpose of this guide [Generate a temporary token](/en/realtime-media/video/manage-agora-account#generate-temporary-tokens).

    * **User ID**: A 32-bit signed integer that identifies a user in the channel. You can specify a unique user ID for each user yourself. If you set the user ID to `0` when joining a channel, the SDK generates a random number for the user ID and returns the value in the `onJoinChannelSuccess` callback.

    * **Channel media options**: Configure `ChannelMediaOptions` to define publishing and subscription settings, optimize performance for your specific use-case, and set optional parameters.

    For Video Calling, set the `channelProfile` to `CHANNEL_PROFILE_COMMUNICATION` and the `clientRoleType` to `CLIENT_ROLE_BROADCASTER`.

    ```cpp
    void CAgoraQuickStartDlg::joinChannel(const char* token, const char* channelName) {
        ChannelMediaOptions options;
        // Set the channel profile to live broadcasting
        options.channelProfile = CHANNEL_PROFILE_COMMUNICATION;
        // Set the user role to broadcaster; to set the user role as audience, keep the default value
        options.clientRoleType = CLIENT_ROLE_BROADCASTER;
        // Publish the audio stream captured by the microphone
        options.publishMicrophoneTrack = true;
        // Publish the camera track
        options.publishCameraTrack = true;
        // Automatically subscribe to all audio streams
        options.autoSubscribeAudio = true;
        // Automatically subscribe to all video streams
        options.autoSubscribeVideo = true;
        // Join the channel using the temporary token obtained from the console
        m_rtcEngine->joinChannel(token, channelName, 0, options);
    }
    ```

    ### Subscribe to Video SDK events [#subscribe-to-video-sdk-events-3]

    The Video SDK provides an interface for subscribing to channel events. To use it, create a custom event handler class by inheriting from `IRtcEngineEventHandler` and override its methods to handle real-time interaction events. Add callbacks to receive notification of users joining and leaving the channel.

    <CalloutContainer type="info">
      <CalloutDescription>
        To ensure that you receive all Video SDK events, set the engine event handler before joining a channel.
      </CalloutDescription>
    </CalloutContainer>

    ```cpp
    // Define the CAgoraQuickStartRtcEngineEventHandler class to handle callback events such as users joining and leaving the channel
    class CAgoraQuickStartRtcEngineEventHandler
        : public IRtcEngineEventHandler {
        public:
            CAgoraQuickStartRtcEngineEventHandler()
                : m_hMsgHandler(nullptr) {
            }

            // Set the handle of the message receiving window
            void SetMsgReceiver(HWND hWnd) {
                m_hMsgHandler = hWnd;
            }

            // Register onJoinChannelSuccess callback
            // This callback is triggered when a local user successfully joins a channel
            virtual void onJoinChannelSuccess(const char* channel, uid_t uid, int elapsed) {
                if (m_hMsgHandler) {
                    ::PostMessage(m_hMsgHandler, WM_MSGID(EID_JOIN_CHANNEL_SUCCESS), uid, 0);
                }
            }

            // Register onUserJoined callback
            // This callback is triggered when the remote host successfully joins the channel
            virtual void onUserJoined(uid_t uid, int elapsed) {
                if (m_hMsgHandler) {
                    ::PostMessage(m_hMsgHandler, WM_MSGID(EID_USER_JOINED), uid, 0);
                }
            }

            // Register onUserOffline callback
            // This callback is triggered when the remote host leaves the channel or is offline
            virtual void onUserOffline(uid_t uid, USER_OFFLINE_REASON_TYPE reason) {
                if (m_hMsgHandler) {
                    ::PostMessage(m_hMsgHandler, WM_MSGID(EID_USER_OFFLINE), uid, 0);
                }
            }

        private:
            HWND m_hMsgHandler;
    };
    ```

    Implement the callback functions.

    ```cpp
    LRESULT CAgoraQuickStartDlg::OnEIDJoinChannelSuccess(WPARAM wParam, LPARAM lParam) {
        // Join channel success callback
        uid_t localUid = wParam;
        return 0;
    }

    LRESULT CAgoraQuickStartDlg::OnEIDUserJoined(WPARAM wParam, LPARAM lParam) {
        // Remote user joined callback
        uid_t remoteUid = wParam;
        if (m_remoteRender) {
            return 0;
        }
        // Render remote view
        VideoCanvas canvas;
        canvas.renderMode = RENDER_MODE_TYPE::RENDER_MODE_HIDDEN;
        canvas.uid = remoteUid;
        canvas.view = m_staRemote.GetSafeHwnd();
        m_rtcEngine->setupRemoteVideo(canvas);
        m_remoteRender = true;
        return 0;
    }

    LRESULT CAgoraQuickStartDlg::OnEIDUserOffline(WPARAM wParam, LPARAM lParam) {
        // Remote user left callback
        uid_t remoteUid = wParam;
        if (!m_remoteRender) {
            return 0;
        }
        // Clear remote view
        VideoCanvas canvas;
        canvas.uid = remoteUid;
        m_rtcEngine->setupRemoteVideo(canvas);
        m_remoteRender = false;
        return 0;
    }
    ```

    ### Enable the video module [#enable-the-video-module-3]

    Call the `enableVideo` method to enable the video module.

    ```cpp
    // Enable the video module
    m_rtcEngine->enableVideo();
    ```

    ### Display the local video [#display-the-local-video-4]

    Follow these steps to set up and start the local video preview:

    1. Create a `VideoCanvas` instance and configure its properties:
       1. Set the video rendering mode.
       2. Specify the user ID (`uid`).
       3. Define the display window.
    2. Call the `setupLocalVideo` method to apply the `VideoCanvas` configuration.
    3. Call the `startPreview` method to start the local video preview.

       ```cpp
       void CAgoraQuickStartDlg::setupLocalVideo() {
           // Set local video display properties
           VideoCanvas canvas;
           // Set video to be scaled proportionally
           canvas.renderMode = RENDER_MODE_TYPE::RENDER_MODE_HIDDEN;
           // User ID
           canvas.uid = 0;
           // Video display window
           canvas.view = m_staLocal.GetSafeHwnd();
           m_rtcEngine->setupLocalVideo(canvas);
           // Preview the local video
           m_rtcEngine->startPreview();
       }
       ```

    ### Display remote video [#display-remote-video-4]

    To display the remote user's video:

    1. Define the video display properties using `VideoCanvas`.
    2. Call `setupRemoteVideo` to render the video.

       ```cpp
       void CAgoraQuickStartDlg::setupRemoteVideo(uid_t remoteUid) {
           // Set remote video display properties
           VideoCanvas canvas;
           // Set video size to be proportionally scaled
           canvas.renderMode = RENDER_MODE_TYPE::RENDER_MODE_HIDDEN;
           // Remote user ID
           canvas.uid = remoteUid;
           // You can only choose to set either view or surfaceTexture. If both are set, only the settings in view take effect.
           canvas.view = m_staRemote.GetSafeHwnd();
           m_rtcEngine->setupRemoteVideo(canvas);
           m_remoteRender = true;
           return 0;
       }
       ```

    ### Leave the channel [#leave-the-channel-1]

    When a user ends a call, or closes the client call `leaveChannel` to exit the current channel.

    To stop the local video preview and clear the view:

    1. Call `stopPreview` to stop playing the local video.
    2. Call `setupLocalVideo`, passing an empty `VideoCanvas` to reset the view.

       ```cpp
       void CAgoraQuickStartDlg::LeaveChannel() {
           if (m_rtcEngine) {
               // Stop local video preview
               m_rtcEngine->stopPreview();
               // Leave the channel
               m_rtcEngine->leaveChannel();
               // Clear local view
               VideoCanvas canvas;
               canvas.uid = 0;
               m_rtcEngine->setupLocalVideo(canvas);
               m_remoteRender = false;
           }
       }
       ```

    ### Release resources [#release-resources]

    To destroy the engine instance, call `release` :

    ```cpp
    // Release resources when the object is destroyed
    m_rtcEngine->release(true);
    m_rtcEngine = NULL;
    ```

    <CalloutContainer type="warning">
      <CalloutTitle>
        Caution
      </CalloutTitle>

      <CalloutDescription>
        After you call `Dispose`, you can no longer use any SDK methods or callbacks. To use real-time Video Calling again, you must create a new engine. For more information, see [Initialize the Engine](#initialize-the-engine).
      </CalloutDescription>
    </CalloutContainer>

    ### Complete sample code [#complete-sample-code-4]

    A complete code sample that implements the basic process of real-time interaction is presented here for your reference. Copy the sample code into your project to quickly implement the basic functions of real-time interaction.

    ### AgoraQuickStartDlg.h [#agoraquickstartdlgh]

    <Accordions>
      <Accordion title="Complete sample code for real-time Video Calling">
        ```cpp
        #pragma once
        #include
        // Import related header files
        #include
        using namespace now;
        using namespace agora::rtc;
        using namespace agora::media;
        using namespace agora::media::base;

        // Define the message ID
        #define WM_MSGID(code) (WM_USER+0x200+code)
        #define EID_JOIN_CHANNEL_SUCCESS        0x00000002
        #define EID_USER_JOINED        0x00000004
        #define EID_USER_OFFLINE        0x00000004

        // Define the CAgoraQuickStartRtcEngineEventHandler class to handle callback events such as users joining and leaving the channel
        class CAgoraQuickStartRtcEngineEventHandler
            : public IRtcEngineEventHandler {
            public:
                CAgoraQuickStartRtcEngineEventHandler()
                    : m_hMsgHandler(nullptr) {
                }

                // Set the handle of the message receiving window
                void SetMsgReceiver(HWND hWnd) {
                    m_hMsgHandler = hWnd;
                }

                // Register onJoinChannelSuccess callback
                // This callback is triggered when a local user successfully joins a channel
                virtual void onJoinChannelSuccess(const char* channel, uid_t uid, int elapsed) {
                    if (m_hMsgHandler) {
                        ::PostMessage(m_hMsgHandler, WM_MSGID(EID_JOIN_CHANNEL_SUCCESS), uid, 0);
                    }
                }

                // Register onUserJoined callback
                // This callback is triggered when the remote host successfully joins the channel
                virtual void onUserJoined(uid_t uid, int elapsed) {
                    if (m_hMsgHandler) {
                        ::PostMessage(m_hMsgHandler, WM_MSGID(EID_USER_JOINED), uid, 0);
                    }
                }

                // Register onUserOffline callback
                // This callback is triggered when the remote host leaves the channel or is offline
                virtual void onUserOffline(uid_t uid, USER_OFFLINE_REASON_TYPE reason) {
                    if (m_hMsgHandler) {
                        ::PostMessage(m_hMsgHandler, WM_MSGID(EID_USER_OFFLINE), uid, 0);
                    }
                }

            private:
                HWND m_hMsgHandler;
        };

        // CAgoraQuickStartDlg dialog box
        class CAgoraQuickStartDlg : public CDialog
        {
            // Construction
            public:
                CAgoraQuickStartDlg(CWnd* pParent = nullptr); // Standard constructor
                virtual ~CAgoraQuickStartDlg();

                // Dialog box data
            #ifdef AFX_DESIGN_TIME
                enum { IDD = IDD_AGORAQUICKSTART_DIALOG };
            #endif

                // Handle the join/leave button click event
                afx_msg void OnBnClickedBtnJoin();
                afx_msg void OnBnClickedBtnLeave();
                // Handle callback events such as user joining/user leaving
                afx_msg LRESULT OnEIDJoinChannelSuccess(WPARAM wParam, LPARAM lParam);
                afx_msg LRESULT OnEIDUserJoined(WPARAM wParam, LPARAM lParam);
                afx_msg LRESULT OnEIDUserOffline(WPARAM wParam, LPARAM lParam);

            protected:
                HICON m_hIcon;

                CEdit m_edtChannelName;

                // DDX/DDV support
                virtual void DoDataExchange(CDataExchange* pDX);
                virtual BOOL OnInitDialog();
                afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
                afx_msg void OnPaint();
                afx_msg HCURSOR OnQueryDragIcon();
                DECLARE_MESSAGE_MAP()

                std::string cs2utf8(CString str);

            private:
                IRtcEngine* m_rtcEngine = nullptr;
                CAgoraQuickStartRtcEngineEventHandler m_eventHandler;
                bool m_initialize = false;
                bool m_remoteRender = false;
        };
        ```

        ### AgoraQuickStartDlg.cpp [#agoraquickstartdlgcpp]

        ```cpp
        #include "pch.h"
        #include "framework.h"
        #include "AgoraQuickStart.h"
        #include "AgoraQuickStartDlg.h"
        #include "afxdialogex.h"
        #ifdef _DEBUG
        #define new DEBUG_NEW
        #endif

        // CAboutDlg dialog used for App "About" menu item
        class CAboutDlg : public CDialogEx {
        public:
            CAboutDlg();
            // Dialog Data
        #ifdef AFX_DESIGN_TIME
            enum {
                IDD = IDD_ABOUTBOX
            };
        #endif
        protected:
            virtual void DoDataExchange(CDataExchange* pDX);
            // DDX/DDV support
            // Implementation
        protected:
            DECLARE_MESSAGE_MAP()
        };

        CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX) {
        }

        void CAboutDlg::DoDataExchange(CDataExchange* pDX) {
            CDialogEx::DoDataExchange(pDX);
        }

        BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
        END_MESSAGE_MAP()

        // CAgoraQuickStartDlg dialog for handling main user interactions and callback events
        CAgoraQuickStartDlg::CAgoraQuickStartDlg(CWnd* pParent
            /*=nullptr*/)
            : CDialog(IDD_AGORAQUICKSTART_DIALOG, pParent) {
            m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
        }

        CAgoraQuickStartDlg::~CAgoraQuickStartDlg() {
            // When deleting the CAgoraQuickStartDlg object, release the engine and related resources
            if (m_rtcEngine) {
                m_rtcEngine->release(true);
                m_rtcEngine = NULL;
            }
        }

        void CAgoraQuickStartDlg::DoDataExchange(CDataExchange* pDX) {
            CDialog::DoDataExchange(pDX);
            // Associate controls and variables for reading and writing data to controls
            DDX_Control(pDX, IDC_EDIT_CHANNEL, m_edtChannelName);
            DDX_Control(pDX, IDC_STATIC_REMOTE, m_staRemote);
            DDX_Control(pDX, IDC_STATIC_LOCAL, m_staLocal);
        }

        BEGIN_MESSAGE_MAP(CAgoraQuickStartDlg, CDialog)
            // Declare message mappings for handling Windows messages and user events such as joining and leaving channels
            ON_WM_SYSCOMMAND()
            ON_WM_PAINT()
            ON_WM_QUERYDRAGICON()
            ON_BN_CLICKED(ID_BTN_JOIN, &CAgoraQuickStartDlg::OnBnClickedBtnJoin)
            ON_BN_CLICKED(ID_BTN_LEAVE, &CAgoraQuickStartDlg::OnBnClickedBtnLeave)
            ON_MESSAGE(WM_MSGID(EID_JOIN_CHANNEL_SUCCESS), CAgoraQuickStartDlg::OnEIDJoinChannelSuccess)
            ON_MESSAGE(WM_MSGID(EID_USER_JOINED), &CAgoraQuickStartDlg::OnEIDUserJoined)
            ON_MESSAGE(WM_MSGID(EID_USER_OFFLINE), &CAgoraQuickStartDlg::OnEIDUserOffline)
        END_MESSAGE_MAP()

        // CAgoraQuickStartDlg message handlers
        // Insert your project's App ID obtained from the Agora Console
        #define APP_ID "<YOUR_APP_ID>"
        // Insert the temporary token obtained from the Agora Console
        #define token "<YOUR_TOKEN>"

        void CAgoraQuickStartDlg::initializeAgoraEngine() {
            m_rtcEngine = createAgoraRtcEngine();
            RtcEngineContext context;
            context.appId = APP_ID;
            context.eventHandler = &m_eventHandler;

            int ret = m_rtcEngine->initialize(context);
            if (ret == 0) {
                // Enable the video module
                m_rtcEngine->enableVideo();
            } else {
                AfxMessageBox(_T("Failed to initialize Agora RTC engine"));
            }
        }

        BOOL CAgoraQuickStartDlg::OnInitDialog() {
            CDialog::OnInitDialog();

            // Add "About..." menu item to the system menu
            CMenu* pSysMenu = GetSystemMenu(FALSE);
            if (pSysMenu != nullptr) {
                CString strAboutMenu;
                strAboutMenu.LoadString(IDS_ABOUTBOX);
                if (!strAboutMenu.IsEmpty()) {
                    pSysMenu->AppendMenu(MF_SEPARATOR);
                    pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
                }
            }

            // Set the dialog icons
            SetIcon(m_hIcon, TRUE);  // Set big icon
            SetIcon(m_hIcon, FALSE); // Set small icon

            // Set the message receiver
            m_eventHandler.SetMsgReceiver(m_hWnd);

            // Initialize Agora engine
            initializeAgoraEngine();

            return TRUE; // Unless focus is set to a control, return TRUE
        }


        void CAgoraQuickStartDlg::OnSysCommand(UINT nID, LPARAM lParam) {
            if ((nID & 0xFFF0) == IDM_ABOUTBOX) {
                CAboutDlg dlgAbout;
                dlgAbout.DoModal();
            } else {
                CDialog::OnSysCommand(nID, lParam);
            }
        }

        // If you add a minimize button to the dialog box, you need the following code
        // to draw the icon. For MFC applications using the document/view model, this is done automatically by the framework
        void CAgoraQuickStartDlg::OnPaint() {
            if (IsIconic()) {
                CPaintDC dc(this);
                // Device context for drawing
                SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
                // Center the icon in the client rectangle
                int cxIcon = GetSystemMetrics(SM_CXICON);
                int cyIcon = GetSystemMetrics(SM_CYICON);
                CRect rect;
                GetClientRect(&rect);
                int x = (rect.Width() - cxIcon + 1) / 2;
                int y = (rect.Height() - cyIcon + 1) / 2;
                // Draw the icon
                dc.DrawIcon(x, y, m_hIcon);
            } else {
                CDialog::OnPaint();
            }
        }

        // This function is called by the framework to obtain the cursor when the user drags the minimized window
        HCURSOR CAgoraQuickStartDlg::OnQueryDragIcon() {
            return static_cast<HCURSOR>(m_hIcon);
        }

        std::string CAgoraQuickStartDlg::cs2utf8(CString str) {
            char szBuf[2 * MAX_PATH] = { 0 };
            WideCharToMultiByte(CP_UTF8, 0, str.GetBuffer(0), str.GetLength(), szBuf, 2 * MAX_PATH, NULL, NULL);
            return szBuf;
        }

        void CAgoraQuickStartDlg::joinChannel(const char* token, const char* channelName) {
            ChannelMediaOptions options;
            // Set the channel profile to live broadcasting
            options.channelProfile = CHANNEL_PROFILE_LIVE_BROADCASTING;
            // Set the user role to broadcaster; to set the user role as audience, keep the default value
            options.clientRoleType = CLIENT_ROLE_BROADCASTER;
            // Publish the audio stream captured by the microphone
            options.publishMicrophoneTrack = true;
            // Publish the camera track
            options.publishCameraTrack = true;
            // Automatically subscribe to all audio streams
            options.autoSubscribeAudio = true;
            // Automatically subscribe to all video streams
            options.autoSubscribeVideo = true;
            // Join the channel using the temporary token obtained from the console
            m_rtcEngine->joinChannel(token, channelName, 0, options);
        }

        void CAgoraQuickStartDlg::setupRemoteVideo() {
            // Render remote view
            VideoCanvas canvas;
            canvas.renderMode = RENDER_MODE_TYPE::RENDER_MODE_HIDDEN;
            canvas.uid = remoteUid;
            canvas.view = m_staRemote.GetSafeHwnd();
            m_rtcEngine->setupRemoteVideo(canvas);
            m_remoteRender = true;
        }

        void CAgoraQuickStartDlg::setupLocalVideo() {
            // Render local view
            VideoCanvas canvas;
            canvas.renderMode = RENDER_MODE_TYPE::RENDER_MODE_HIDDEN;
            canvas.uid = 0;
            canvas.view = m_staLocal.GetSafeHwnd();
            m_rtcEngine->setupLocalVideo(canvas);
            // Preview the local video
            m_rtcEngine->startPreview();
        }

        void CAgoraQuickStartDlg::OnBnClickedBtnJoin() {
            // Join channel
            // Get the channel name
            CString strChannelName;
            m_edtChannelName.GetWindowText(strChannelName);
            if (strChannelName.IsEmpty()) {
                AfxMessageBox(_T("Fill channel name first"));
                return;
            }

            joinChannel(token, CW2A(strChannelName));
            // Render local view
            setupLocalVideo();
        }

        void CAgoraQuickStartDlg::OnBnClickedBtnLeave() {
            // Stop local video preview
            m_rtcEngine->stopPreview();
            // Leave the channel
            m_rtcEngine->leaveChannel();
            // Clear local view
            VideoCanvas canvas;
            canvas.uid = 0;
            m_rtcEngine->setupLocalVideo(canvas);
            m_remoteRender = false;
        }

        LRESULT CAgoraQuickStartDlg::OnEIDJoinChannelSuccess(WPARAM wParam, LPARAM lParam) {
            // Join channel success callback
            uid_t localUid = wParam;
            return 0;
        }

        LRESULT CAgoraQuickStartDlg::OnEIDUserJoined(WPARAM wParam, LPARAM lParam) {
            // Remote user joined callback
            uid_t remoteUid = wParam;
            if (m_remoteRender) {
                return 0;
            }

            // Render remote view
            setupRemoteVideo(remoteUid);
            return 0;
        }

        LRESULT CAgoraQuickStartDlg::OnEIDUserOffline(WPARAM wParam, LPARAM lParam) {
            // Remote user left callback
            uid_t remoteUid = wParam;
            if (!m_remoteRender) {
                return 0;
            }
            // Clear remote view
            VideoCanvas canvas;
            canvas.uid = remoteUid;
            m_rtcEngine->setupRemoteVideo(canvas);
            m_remoteRender = false;
            return 0;
        }
        ```
      </Accordion>
    </Accordions>

    ### Create a user interface [#create-a-user-interface-4]

    To connect the sample code to your existing user interface, ensure that your UI includes the controls used to [Display the local video](#display-the-local-video) and [Display remote video](#display-remote-video).

    Alternatively, follow these steps to create a bare-bones UI for your project.

    ### Steps to create a minimalistic UI [#steps-to-create-a-minimalistic-ui]

    1. Switch the project to resource view in the right menu bar, and then open the `.Dialog` file.

    2. From **View > Toolbox**, select Add **Picture Control**, and in **Properties > Miscellaneous**, set the ID of the control to `IDC_STATIC_REMOTE`.

    3. From **View > Toolbox** , select Add **Picture Control** , and in **Properties > Miscellaneous** , set the control's ID to `IDC_STATIC_LOCAL`.

    4. To set up an input box for entering the channel name, from **View > Toolbox** , select add **Static Text** control, and change the description text to `Channel name` in the properties. Add an **Edit Control** as an input box, and in **Properties > Miscellaneous**, set the control's ID to `IDC_EDIT_CHANNEL`.

    5. To add join and leave channel buttons, open **View > Toolbox** and add two **Button** controls. In **Properties > Miscellaneous** , set the IDs to `ID_BTN_JOIN` and `ID_BTN_LEAVE`, and set the description text to **Join** and **Leave** respectively. Your user interface looks similar to the following:

       ![](https://assets-docs.agora.io/images/video-sdk/windows-quickstart-ui.png)

    Follow these steps to create a bare-bones UI for your project.

    ## Test the sample code [#test-the-sample-code-4]

    To test your app, follow these steps:

    1. In Visual Studio, select local Windows debugger to start compiling the application.

    2. Enter the name of the channel you want to join in the input box and click the **Join** button to join the channel.

       You see yourself in the local view.

    3. Use the [Web demo](https://webdemo.agora.io/basicVideoCall/index.html) to join the same channel and test the following use-cases:

       * If users on both devices join the channel as hosts, they can see and hear each other.
       * If one user joins as host and the other as audience, the host can see themselves in the local video window; the audience can see the host in the remote video window and hear the host.

    ## Reference [#reference-4]

    This section contains content that completes the information on this page, or points you to documentation that explains other aspects of this product.

    * If a firewall is deployed in your network environment, refer to [Connect with Cloud Proxy](/en/realtime-media/video/build/manage-connection-and-quality/cloud-proxy) to use Agora services normally.

    ### Next steps [#next-steps-4]

    After implementing the quickstart sample, read the following documents to learn more:

    * To ensure communication security in a test or production environment, best practice is to obtain and use a token from an authentication server. For details, see [Secure authentication with tokens](/en/realtime-media/video/build/authenticate-users/authentication-workflow).

    ### Sample project [#sample-project-4]

    Agora provides open source sample projects on [GitHub](https://github.com/AgoraIO/API-Examples/tree/main/windows/APIExample/) for your reference.

    * Download or view the [sample project](https://github.com/AgoraIO/API-Examples/tree/main/windows/APIExample/APIExample/Basic/LiveBroadcasting) for a more detailed example.
    * For a Windows C# implementation, see [this sample project](https://github.com/AgoraIO-Extensions/Agora-C_Sharp-SDK/tree/master/APIExample/src/Basic/JoinChannelVideo).

    ### API Reference [#api-reference-4]

    * [`createAgoraRtcEngine`](https://api-ref.agora.io/en/video-sdk/cpp/4.x/API/class_irtcengine.html#api_createagorartcengine)

    * [`joinChannel`](https://api-ref.agora.io/en/video-sdk/cpp/4.x/API/class_irtcengine.html#api_irtcengine_joinchannel2)

    * [`enableVideo`](https://api-ref.agora.io/en/video-sdk/cpp/4.x/API/class_irtcengine.html#api_irtcengine_enablevideo)

    * [`startPreview`](https://api-ref.agora.io/en/video-sdk/cpp/4.x/API/class_irtcengine.html#api_irtcengine_startpreview)

    * [`leaveChannel`](https://api-ref.agora.io/en/video-sdk/cpp/4.x/API/class_irtcengine.html#api_irtcengine_leavechannel)

    * [`release`](https://api-ref.agora.io/en/video-sdk/cpp/4.x/API/class_irtcengine.html#api_irtcengine_release)

    ### Frequently asked questions [#frequently-asked-questions-4]

    * [How can I fix black screen issues?](/en/api-reference/faq/quality/video_blank#black-screen-on-the-local-side)

    * [Why can't I turn on the camera?](/en/api-reference/faq/quality/video_camera)

    * [How can I listen for audience joining or leaving a channel?](/en/api-reference/faq/integration/audience_event)

    * [How can I solve channel-related issues?](/en/api-reference/faq/integration/channel)

    * [How can I set the log file?](/en/api-reference/faq/integration/set_log_file)

    ### See also [#see-also-4]

    * [SDK error codes](/en/realtime-media/video/reference/error-codes)
    * [Connection status management](/en/realtime-media/video/build/manage-connection-and-quality/connection-status-management)

    
  
      
  
      
  
      
  
      
  
      
  
      
  
      
  
