Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

With Win32 GUI (and MFC) you can use both method:

  1. to draw the cv::mat into dc memory context of a static control: see here
  2. or to map a cv::window as child of some static control: see cvWin2MfcControl below for a complete example

In both cases you have to define a static control that will holds the image from opencv.

Some note:

May be your code is incomplete but the grabbing thread will be never stopped because stop (pointer to StopGrab) variable is never set to true.

In addiction if you will set in StopThread this is a race condition because 2 threads can access to StopGrab. In general to stop the thread you should use an event or a mutex, critical section to protect shared variables. In this case you can use atomic<bool> that is lock free than faster than mutex or critical section.

For you and other user here is correct code using method 2 and atomic<bool>:

#include <opencv2/opencv.hpp>

#define ID_GRABWIN    PB_QUIT + 1000

LRESULT CALLBACK SmplProc(HWND hWnd, UINT iMessage, UINT wParam, LONG lParam);
int OnGrab(void);
DWORD StopThread(LPDWORD lpdwParam);
DWORD GrabThread(LPDWORD lpdwParam);

static HANDLE HStopThread, HStopEvent, HThread;
static HINSTANCE hInst;
static HWND SmplHwnd, GrabHwnd;
static HWND HStop, HGrab, HQuit;

#include <atomic> 
std::atomic<bool>StopGrab; //needed to protect the grab loop on/off flag

const char * WIN_NAME_CV = "OCV Image Display";

/** \brief Map an OpenCV window over a MFC/Win32 control
    \param parentWnd the handle of MFC/Win32 static control
*/
bool cvWin2MfcControl(HWND parentWnd)
{
    cv::namedWindow(WIN_NAME_CV, CV_WINDOW_KEEPRATIO); // create an OpenCV win
    HWND hWnd = (HWND)cvGetWindowHandle(WIN_NAME_CV);  // get the handle of OpenCV Window
    if (!hWnd) return false;

    HWND hParent = ::GetParent(hWnd);
    ::SetParent(hWnd, parentWnd);   //move the OpenCV window to the new parent (your static control)
    ::ShowWindow(hParent, SW_HIDE); //hide the old parent window

    //resize the OpenCV window to fit your static control
    RECT parentRect;
    ::GetClientRect(parentWnd, &parentRect);
    cvResizeWindow(WIN_NAME_CV, parentRect.right, parentRect.bottom);

    return true;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
    TCHAR       SmplClassName[] = L"Sample";
    WNDCLASS    SmplClass;
    MSG         msg;

    hInst = hInstance;
    if (!hPrevInstance)
    {
        SmplClass.style = CS_HREDRAW | CS_VREDRAW;
        SmplClass.lpfnWndProc = (WNDPROC)SmplProc;
        SmplClass.cbClsExtra = 0;
        SmplClass.cbWndExtra = 0;
        SmplClass.hInstance = hInstance;
        SmplClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        SmplClass.hCursor = LoadCursor(NULL, IDC_ARROW);
        SmplClass.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH);
        SmplClass.lpszMenuName = 0;
        SmplClass.lpszClassName = SmplClassName;
        if (!RegisterClass(&SmplClass))
            return (0);
    }
    SmplHwnd = CreateWindow(SmplClassName, L"LLGrab", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 1180, 740, NULL, NULL, hInstance, NULL);

    // Create a static control where you want to draw the OpenCV frame (relative to parent win)
    RECT rcDlg;
    int border = 10;
    int btnWidth = 80, btnHeight = 40,btnx,btny;
    ::GetClientRect(SmplHwnd, &rcDlg);
    rcDlg.left = border;
    rcDlg.top = border;
    rcDlg.bottom -= (2 * border); //height
    rcDlg.right -= (btnWidth + 3 * border);  //width

    if (!(GrabHwnd = CreateWindow(L"Static", L"Frame", SS_BITMAP | WS_CHILD | WS_VISIBLE | WS_BORDER, 
        rcDlg.left, rcDlg.top, rcDlg.right - rcDlg.left, rcDlg.bottom - rcDlg.top, 
        SmplHwnd, NULL, hInstance, NULL)))
        return (FALSE);

    cvWin2MfcControl(GrabHwnd);

    // Draw the buttons out of the area used to draw the frame
    btnx = rcDlg.right + border;
    btny = rcDlg.top;
    if (!(HGrab = CreateWindow(L"Button", L"Grab", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_BORDER, btnx, btny, btnWidth, btnHeight, SmplHwnd, (HMENU)PB_GRAB, hInstance, NULL)))
        return (FALSE);
    btny += btnHeight + border;

    if (!(HStop = CreateWindow(L"Button", L"Stop", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_BORDER, btnx, btny, btnWidth, btnHeight, SmplHwnd, (HMENU)PB_STOP, hInstance, NULL)))
        return (FALSE);
    btny += btnHeight + border;

    if (!(HQuit = CreateWindow(L"Button", L"Quit", BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_BORDER, btnx, btny, btnWidth, btnHeight, SmplHwnd, (HMENU)PB_QUIT, hInstance, NULL)))
        return (FALSE);
    btny += btnHeight + border;

    EnableWindow(HStop, FALSE);
    EnableWindow(HGrab, TRUE);
    EnableWindow(HQuit, TRUE);

    ShowWindow(SmplHwnd, SW_SHOW);
    UpdateWindow(SmplHwnd);

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    WaitForSingleObject(HStopThread, INFINITE);
    return (int)(msg.wParam);
}

LRESULT CALLBACK SmplProc(HWND hWnd, UINT iMessage, UINT wParam, LONG lParam)
{
    WORD wmID;
    switch (iMessage)
    {
    case WM_COMMAND:
        wmID = LOWORD(wParam);
        switch (wmID)
        {
        case PB_QUIT:
            PostQuitMessage(0);
            break;
        case PB_GRAB:
            OnGrab();
            break;
        case PB_STOP:
            SetEvent(HStopEvent);
            break;
        }
        break;
    case WM_DESTROY:
        SetEvent(HStopEvent);
        PostQuitMessage(0);
    default:
        return DefWindowProc(hWnd, iMessage, wParam, lParam);
        break;

    }
    return 0;
}

int OnGrab(void)
{
    DWORD dwThreadId;

    HStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!HStopEvent)
        return 0;
    HStopThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)StopThread, (LPWORD)&HStopEvent, 0, &dwThreadId);
    if (!HStopThread)
        return 0;
    StopGrab.store(false);
    HThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)GrabThread, NULL, 0, &dwThreadId);
    if (HThread == NULL)
        return 0;
    return 0;
}

DWORD GrabThread(LPDWORD lpdwParam)
{
    cv::VideoCapture captures;
    captures.open(0);
    cv::Mat frame;

    EnableWindow(HStop, TRUE);
    EnableWindow(HGrab, FALSE);
    EnableWindow(HQuit, FALSE);
    while (!StopGrab.load())
    {
        captures >> frame;
        imshow(WIN_NAME_CV, frame); //display video here
        //cvWaitKey(1); not needed 
    }
    EnableWindow(HStop, FALSE);
    EnableWindow(HGrab, TRUE);
    EnableWindow(HQuit, TRUE);
    return 0;
}

DWORD StopThread(LPDWORD lpdwParam)
{
    DWORD dwResult;
    HANDLE event = *((HANDLE*)lpdwParam);
    dwResult = WaitForSingleObject(event, INFINITE);
    StopGrab.store(true);
    if (dwResult != WAIT_FAILED)
    {
        CloseHandle(event);
        event = NULL;
    }
    return 0;
}

With Win32 GUI (and MFC) you can use both method:one of below:

  1. to draw the cv::mat cv::Mat into dc memory context of a static control: see here
  2. or to map a cv::window as child of some static control: see cvWin2MfcControl cvWin2MfcControl below for a complete example

In both cases you have to define a static control that will holds the image from opencv.

Some note:note about thread control:

  • May be your code is incomplete but the grabbing thread will be never stopped because stop (pointer to StopGrab) variable is never set to true.

  • In addiction if you will set Doing StopGrab=true in StopThread this is you will have a race condition because 2 threads can access to StopGrab. In general general, to stop the a thread you should use an event or a mutex, control var under a mutex or a critical section to protect the var from a shared variables. access. In this case you can use atomic<bool> that is lock free than and faster than mutex or critical section.

For you and other user user, here is correct code using 2nd method 2 and atomic<bool>:atomic<bool>:

#include <opencv2/opencv.hpp>

#define ID_GRABWIN    PB_QUIT + 1000
#include <atomic> 
std::atomic<bool>StopGrab; //needed to protect the grab loop on/off flag

HANDLE HStopThread, HStopEvent, HThread;
HINSTANCE hInst;
HWND SmplHwnd, GrabHwnd;
HWND HStop, HGrab, HQuit;
const char * WIN_NAME_CV = "OCV Image Display";

LRESULT CALLBACK SmplProc(HWND hWnd, UINT iMessage, UINT wParam, LONG lParam);
int OnGrab(void);
DWORD StopThread(LPDWORD lpdwParam);
DWORD GrabThread(LPDWORD lpdwParam);

static HANDLE HStopThread, HStopEvent, HThread;
static HINSTANCE hInst;
static HWND SmplHwnd, GrabHwnd;
static HWND HStop, HGrab, HQuit;

#include <atomic> 
std::atomic<bool>StopGrab; //needed to protect the grab loop on/off flag

const char * WIN_NAME_CV = "OCV Image Display";
OnGrabClick(void);  

/** ------------------------------------------------------------------------------------------
    \brief Map an OpenCV window over a MFC/Win32 control
    \param parentWnd the handle of MFC/Win32 static control
*/
bool cvWin2MfcControl(HWND parentWnd)
{
    cv::namedWindow(WIN_NAME_CV, CV_WINDOW_KEEPRATIO);  // create an OpenCV win
    HWND hWnd cvWnd = (HWND)cvGetWindowHandle(WIN_NAME_CV);  // get the handle of OpenCV Window
    if (!hWnd) (!cvWnd) return false;

    HWND hParent = ::GetParent(hWnd);
    ::SetParent(hWnd, hOldParent = ::GetParent(cvWnd);
    ::SetParent(cvWnd, parentWnd);   //move   // move the OpenCV window to the new parent (your static control)
    ::ShowWindow(hParent, ::ShowWindow(hOldParent, SW_HIDE); //hide // hide the old parent window

    //resize // Resize the OpenCV window to fit your static control
    RECT parentRect;
    ::GetClientRect(parentWnd, &parentRect);
    cvResizeWindow(WIN_NAME_CV, parentRect.right, parentRect.bottom);
     return true;
}
 //------------------------------------------------------------------------------------------
DWORD GrabThread(LPDWORD lpdwParam)
{
    cv::VideoCapture captures;
    captures.open(0);
    if (!captures.isOpened())
            return -1;
    cv::Mat frame;
    EnableWindow(HStop, TRUE);
    EnableWindow(HGrab, FALSE);
    EnableWindow(HQuit, FALSE);
    while (!StopGrab.load())
    {
        captures >> frame;
        cv::imshow(WIN_NAME_CV, frame); //display video here
        //cvWaitKey(1); not needed 
    }
    EnableWindow(HStop, FALSE);
    EnableWindow(HGrab, TRUE);
    EnableWindow(HQuit, TRUE);
    return 0;
}
//------------------------------------------------------------------------------------------
DWORD StopThread(LPDWORD lpdwParam)
{
    DWORD dwResult;
    HANDLE event = *((HANDLE*)lpdwParam);
    dwResult = WaitForSingleObject(event, INFINITE);
    StopGrab.store(true);
    if (dwResult != WAIT_FAILED)
    {
        CloseHandle(event);
        event = NULL;
    }
    return 0;
}
//----M A I N ----------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
    TCHAR       SmplClassName[] = L"Sample";
    WNDCLASS    SmplClass;
    MSG         msg;

    hInst = hInstance;
    if (!hPrevInstance)
    {
        SmplClass.style = CS_HREDRAW | CS_VREDRAW;
        SmplClass.lpfnWndProc = (WNDPROC)SmplProc;
        SmplClass.cbClsExtra = 0;
        SmplClass.cbWndExtra = 0;
        SmplClass.hInstance = hInstance;
        SmplClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        SmplClass.hCursor = LoadCursor(NULL, IDC_ARROW);
        SmplClass.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH);
        SmplClass.lpszMenuName = 0;
        SmplClass.lpszClassName = SmplClassName;
        if (!RegisterClass(&SmplClass))
            return (0);
    }
    SmplHwnd = CreateWindow(SmplClassName, L"LLGrab",  WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
            1180, 740, NULL, NULL, hInstance, NULL);

    // Create a the static control where you want to draw the OpenCV frame (relative to parent win)
    RECT rcDlg;
    int border = 10;
    int btnWidth = 80, btnHeight = 40,btnx,btny;
    ::GetClientRect(SmplHwnd, &rcDlg);
    rcDlg.left = border;
    rcDlg.top = border;
    rcDlg.bottom -= (2 * border); //height
    rcDlg.right -= (btnWidth + 3 * border);  //width

    if (!(GrabHwnd = CreateWindow(L"Static", L"Frame", SS_BITMAP | WS_CHILD | WS_VISIBLE | WS_BORDER, 
        rcDlg.left, rcDlg.top, rcDlg.right - rcDlg.left, rcDlg.bottom - rcDlg.top, 
        SmplHwnd, NULL, hInstance, NULL)))
        return (FALSE);

    cvWin2MfcControl(GrabHwnd);

    // Draw the buttons out of the area used to draw the frame
    btnx = rcDlg.right + border;
    btny = rcDlg.top;
    if (!(HGrab = CreateWindow(L"Button", L"Grab", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_BORDER, WS_BORDER,
                    btnx, btny, btnWidth, btnHeight, SmplHwnd, (HMENU)PB_GRAB, hInstance, NULL)))
        return (FALSE);
    btny += btnHeight + border;

    if (!(HStop = CreateWindow(L"Button", L"Stop", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_BORDER,  btnx, btny, btnWidth, btnHeight, SmplHwnd, (HMENU)PB_STOP, hInstance, NULL)))
        return (FALSE);
    btny += btnHeight + border;

    if (!(HQuit = CreateWindow(L"Button", L"Quit", BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_BORDER,  btnx, btny, btnWidth, btnHeight, SmplHwnd, (HMENU)PB_QUIT, hInstance, NULL)))
        return (FALSE);
    btny += btnHeight + border;

    EnableWindow(HStop, FALSE);
    EnableWindow(HGrab, TRUE);
    EnableWindow(HQuit, TRUE);

    ShowWindow(SmplHwnd, SW_SHOW);
    UpdateWindow(SmplHwnd);

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    WaitForSingleObject(HStopThread, INFINITE);
    return (int)(msg.wParam);
}
 //------------------------------------------------------------------------------------------
LRESULT CALLBACK SmplProc(HWND hWnd, UINT iMessage, UINT wParam, LONG lParam)
{
    WORD wmID;
    switch (iMessage)
    {
    case WM_COMMAND:
        wmID = LOWORD(wParam);
        switch (wmID)
        {
        case PB_QUIT:
            PostQuitMessage(0);
            break;
        case PB_GRAB:
            OnGrab();
OnGrabClick();
            break;
        case PB_STOP:
            SetEvent(HStopEvent);
            break;
        }
        break;
    case WM_DESTROY:
        SetEvent(HStopEvent);
        PostQuitMessage(0);
    default:
        return DefWindowProc(hWnd, iMessage, wParam, lParam);
        break;
     }
    return 0;
}
 //------------------------------------------------------------------------------------------
int OnGrab(void)
OnGrabClick(void)
{
    DWORD dwThreadId;

    HStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!HStopEvent)
        return 0;
    HStopThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)StopThread, (LPWORD)&HStopEvent, 0, &dwThreadId);
    if (!HStopThread)
        return 0;
    StopGrab.store(false);
    HThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)GrabThread, NULL, 0, &dwThreadId);
    if (HThread == NULL)
        return 0;
    return 0;
}

DWORD GrabThread(LPDWORD lpdwParam)
{
    cv::VideoCapture captures;
    captures.open(0);
    cv::Mat frame;

    EnableWindow(HStop, TRUE);
    EnableWindow(HGrab, FALSE);
    EnableWindow(HQuit, FALSE);
    while (!StopGrab.load())
    {
        captures >> frame;
        imshow(WIN_NAME_CV, frame); //display video here
        //cvWaitKey(1); not needed 
    }
    EnableWindow(HStop, FALSE);
    EnableWindow(HGrab, TRUE);
    EnableWindow(HQuit, TRUE);
    return 0;
}

DWORD StopThread(LPDWORD lpdwParam)
{
    DWORD dwResult;
    HANDLE event = *((HANDLE*)lpdwParam);
    dwResult = WaitForSingleObject(event, INFINITE);
    StopGrab.store(true);
    if (dwResult != WAIT_FAILED)
    {
        CloseHandle(event);
        event = NULL;
    }
    return 0;
}

With Win32 GUI (and MFC) you can use one of below:

  1. to draw the cv::Mat into dc memory context of a static control: see here
  2. or to map a cv::window as child of some static control: see cvWin2MfcControl below for a complete example

In both cases you have to define a static control that will holds the image from opencv.

Some note about thread control:

  • May be your code is incomplete but the grabbing thread will be never stopped because stop (pointer to StopGrab) variable is never set to true.

  • Doing StopGrab=true in StopThread you will have a race condition because 2 threads can access to StopGrab. In general, to stop a thread you should use an event or a control var under a mutex or a critical section to protect the var from a shared access. In this case you can use atomic<bool> that is lock free and faster than mutex or critical section.

For you and other user, here is correct code using 2nd method and atomic<bool>:.

EDIT: because atomic<bool> is C++11 and isn't available in VS2008, a very simple class that defines a protected shared variable has been provided

#include <opencv2/opencv.hpp>
#if __cplusplus >= 199711L //is C++11 available ?
    // Use std::atomic<bool> because is lock free
    #include <atomic> 
<atomic>
    std::atomic<bool>StopGrab; //needed to protect the grab loop on/off flag
 #else 
    // With old C++ compiler we create a very simple class to define
    // a protected shared variable using CriticalSection
    template<class T>  class AtomicSimple
    {
    public:
        AtomicSimple(){
            InitializeCriticalSection(&CriticalSection);
        };
        AtomicSimple(T newVal) {
            InitializeCriticalSection(&CriticalSection); 
            store(newVal);
        }
        ~AtomicSimple(){
            DeleteCriticalSection(&CriticalSection);
        };
        T load()
        {
            T curVal;
            EnterCriticalSection(&CriticalSection);
            curVal = val;
            LeaveCriticalSection(&CriticalSection);
            return curVal;
        }
        void store(T newVal)
        {
            EnterCriticalSection(&CriticalSection);
            val = newVal;
            LeaveCriticalSection(&CriticalSection);
            }
    private:
        T val;
        CRITICAL_SECTION CriticalSection;
    };
    AtomicSimple<bool>StopGrab; //needed to protect the grab loop on/off flag
#endif
HANDLE HStopThread, HStopEvent, HThread;
HINSTANCE hInst;
HWND SmplHwnd, GrabHwnd;
HWND HStop, HGrab, HQuit;
const char * WIN_NAME_CV = "OCV Image Display";

LRESULT CALLBACK SmplProc(HWND hWnd, UINT iMessage, UINT wParam, LONG lParam);
int OnGrabClick(void);  

/** ------------------------------------------------------------------------------------------
    \brief Map an OpenCV window over a MFC/Win32 control
    \param parentWnd the handle of MFC/Win32 static control
*/
bool cvWin2MfcControl(HWND parentWnd)
{
    cv::namedWindow(WIN_NAME_CV, CV_WINDOW_KEEPRATIO);  // create an OpenCV win
    HWND cvWnd = (HWND)cvGetWindowHandle(WIN_NAME_CV);  // get the handle of OpenCV Window
    if (!cvWnd) return false;

    HWND hOldParent = ::GetParent(cvWnd);
    ::SetParent(cvWnd, parentWnd);     // move the OpenCV window to the new parent (your static control)
    ::ShowWindow(hOldParent, SW_HIDE); // hide the old parent window

    // Resize the OpenCV window to fit your static control
    RECT parentRect;
    ::GetClientRect(parentWnd, &parentRect);
    cvResizeWindow(WIN_NAME_CV, parentRect.right, parentRect.bottom);
    return true;
}
//------------------------------------------------------------------------------------------
DWORD GrabThread(LPDWORD lpdwParam)
{
    cv::VideoCapture captures;
    captures.open(0);
    if (!captures.isOpened())
            return -1;
    cv::Mat frame;
    EnableWindow(HStop, TRUE);
    EnableWindow(HGrab, FALSE);
    EnableWindow(HQuit, FALSE);
    while (!StopGrab.load())
    {
        captures >> frame;
        cv::imshow(WIN_NAME_CV, frame); //display video here
        //cvWaitKey(1); not needed 
    }
    EnableWindow(HStop, FALSE);
    EnableWindow(HGrab, TRUE);
    EnableWindow(HQuit, TRUE);
    return 0;
}
//------------------------------------------------------------------------------------------
DWORD StopThread(LPDWORD lpdwParam)
{
    DWORD dwResult;
    HANDLE event = *((HANDLE*)lpdwParam);
    dwResult = WaitForSingleObject(event, INFINITE);
    StopGrab.store(true);
    if (dwResult != WAIT_FAILED)
    {
        CloseHandle(event);
        event = NULL;
    }
    return 0;
}
//----M A I N ----------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
    TCHAR       SmplClassName[] = L"Sample";
    WNDCLASS    SmplClass;
    MSG         msg;

    hInst = hInstance;
    if (!hPrevInstance)
    {
        SmplClass.style = CS_HREDRAW | CS_VREDRAW;
        SmplClass.lpfnWndProc = (WNDPROC)SmplProc;
        SmplClass.cbClsExtra = 0;
        SmplClass.cbWndExtra = 0;
        SmplClass.hInstance = hInstance;
        SmplClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        SmplClass.hCursor = LoadCursor(NULL, IDC_ARROW);
        SmplClass.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH);
        SmplClass.lpszMenuName = 0;
        SmplClass.lpszClassName = SmplClassName;
        if (!RegisterClass(&SmplClass))
            return (0);
    }
    SmplHwnd = CreateWindow(SmplClassName, L"LLGrab", 
            WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT,
            1180, 740, NULL, NULL, hInstance, NULL);

    // Create the static control where you want to draw the OpenCV frame (relative to parent win)
    RECT rcDlg;
    int border = 10;
    int btnWidth = 80, btnHeight = 40,btnx,btny;
    ::GetClientRect(SmplHwnd, &rcDlg);
    rcDlg.left = border;
    rcDlg.top = border;
    rcDlg.bottom -= (2 * border); //height
    rcDlg.right -= (btnWidth + 3 * border);  //width

    if (!(GrabHwnd = CreateWindow(L"Static", L"Frame", SS_BITMAP | WS_CHILD | WS_VISIBLE | WS_BORDER, 
        rcDlg.left, rcDlg.top, rcDlg.right - rcDlg.left, rcDlg.bottom - rcDlg.top, 
        SmplHwnd, NULL, hInstance, NULL)))
        return (FALSE);

    cvWin2MfcControl(GrabHwnd);

    // Draw the buttons out of the area used to draw the frame
    btnx = rcDlg.right + border;
    btny = rcDlg.top;
    if (!(HGrab = CreateWindow(L"Button", L"Grab", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_BORDER,
                    btnx, btny, btnWidth, btnHeight, SmplHwnd, (HMENU)PB_GRAB, hInstance, NULL)))
        return (FALSE);
    btny += btnHeight + border;

    if (!(HStop = CreateWindow(L"Button", L"Stop", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_BORDER, 
                    btnx, btny, btnWidth, btnHeight, SmplHwnd, (HMENU)PB_STOP, hInstance, NULL)))
        return (FALSE);
    btny += btnHeight + border;

    if (!(HQuit = CreateWindow(L"Button", L"Quit", BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_BORDER, 
                    btnx, btny, btnWidth, btnHeight, SmplHwnd, (HMENU)PB_QUIT, hInstance, NULL)))
        return (FALSE);
    btny += btnHeight + border;

    EnableWindow(HStop, FALSE);
    EnableWindow(HGrab, TRUE);
    EnableWindow(HQuit, TRUE);

    ShowWindow(SmplHwnd, SW_SHOW);
    UpdateWindow(SmplHwnd);

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    WaitForSingleObject(HStopThread, INFINITE);
    return (int)(msg.wParam);
}
//------------------------------------------------------------------------------------------
LRESULT CALLBACK SmplProc(HWND hWnd, UINT iMessage, UINT wParam, LONG lParam)
{
    WORD wmID;
    switch (iMessage)
    {
    case WM_COMMAND:
        wmID = LOWORD(wParam);
        switch (wmID)
        {
        case PB_QUIT:
            PostQuitMessage(0);
            break;
        case PB_GRAB:
            OnGrabClick();
            break;
        case PB_STOP:
            SetEvent(HStopEvent);
            break;
        }
        break;
    case WM_DESTROY:
        SetEvent(HStopEvent);
        PostQuitMessage(0);
    default:
        return DefWindowProc(hWnd, iMessage, wParam, lParam);
        break;
    }
    return 0;
}
//------------------------------------------------------------------------------------------
int OnGrabClick(void)
{
    DWORD dwThreadId;

    HStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!HStopEvent)
        return 0;
    HStopThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)StopThread, (LPWORD)&HStopEvent, 0, &dwThreadId);
    if (!HStopThread)
        return 0;
    StopGrab.store(false);
    HThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)GrabThread, NULL, 0, &dwThreadId);
    if (HThread == NULL)
        return 0;
    return 0;
}