본문 바로가기
WIN32 API

[WIN32 API]기본 class 작업

by Junk_Seo 2018. 4. 25.
반응형

기본 class 작업

저번에 정리한 API 코드를 class화 하는 작업입니다.

일단 코드를 먼저 보도록 하겠습니다.

 

<mywin.h>

class C_MYWIN
{
private:
    HINSTANCE m_hInstance;
    HWND m_hWnd;

private:
    static C_MYWIN *m_pMyWin;

public:
    static void createWin();
    static C_MYWIN *getWin();
    static void releaseWin();

private:
    C_MYWIN();
    static LRESULT CALLBACK wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
    LRESULT CALLBACK myProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

public:
    bool init(HINSTANCE hInstance);
    void updateMsg();
    void release();
};

<mywin.cpp>

C_MYWIN *C_MYWIN::m_pMyWin = nullptr;
C_MYWIN::C_MYWIN() : m_hInstance(NULL), m_hWnd(NULL) {}

void C_MYWIN::createWin()
{
    if (!m_pMyWin)
        m_pMyWin = new C_MYWIN();
}

C_MYWIN *C_MYWIN::getWin()
{
    return m_pMyWin;
}

void C_MYWIN::releaseWin()
{
    if (m_pMyWin)
    {
        delete m_pMyWin;
        m_pMyWin = nullptr;
    }
}

bool C_MYWIN::init(HINSTANCE hInstance)
{
    // 윈도우 클래스 등록
    WNDCLASSEXW wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = wndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32APIEX));
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = 0;
    wcex.lpszClassName = L"className";
    wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    RegisterClassExW(&wcex);

    // 윈도우 생성
    m_hWnd = CreateWindowW(L"className", nullptr, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
    if (!m_hWnd)
        return false;

    // 초기화 작업은 여기에
    m_hInstance = hInstance;
    ShowWindow(m_hWnd, SW_SHOWDEFAULT);
    UpdateWindow(m_hWnd);

    return true;
}

void C_MYWIN::updateMsg()
{
    MSG msg = {};

    // 기본 메시지 루프입니다.
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

void C_MYWIN::release() {}

LRESULT C_MYWIN::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    return m_pMyWin->myProc(hWnd, message, wParam, lParam);
}

LRESULT C_MYWIN::myProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        // TODO: 여기에 hdc를 사용하는 그리기 코드를 추가합니다.
        EndPaint(hWnd, &ps);
    }
    break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    
    return 0;
}

<main>

int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: 여기에 코드를 입력합니다.
    C_MYWIN::createWin();
    C_MYWIN::getWin()->init(hInstance);
    C_MYWIN::getWin()->updateMsg();
    C_MYWIN::getWin()->release();
    C_MYWIN::releaseWin();
    
    return 0;
}

class화 작업에 싱글톤 방식을 사용하였습니다.

 

멤버 변수로 HINSTANCE 변수와 HWND 변수가 있고 여기에 값을 저장하여 사용하고 있습니다.

 

class 작업을 하면서 가장 중요한 부분은 콜백 함수를 어떻게 처리하느냐입니다.

여기선 콜백 함수가 wndProc() 함수인데, 이 콜백 함수를 단순히 클래스의 멤버 함수로 등록하여 사용할 수 없습니다. 

왜냐하면 윈도우 클래스 등록 부분에서 콜백함수의 함수 포인터 변수를 저장하게 되는데, 콜백 함수를 class 멤버 함수로 하면 class의 namespace가 생겨버리기 때문에 문제가 생기기 때문입니다.

그래서 static 키워드를 사용하여 wndProc() 함수를 static 멤버 함수로 만들었습니다. 

이렇게만 하면 static 함수인 wndProc() 콜벡 함수가 class의 멤버에 접근할 수 없는 문제가 발생합니다. 

그래서 myProc()이라는 wndProc() 함수와 똑같은 함수를 멤버 함수로 만듭니다. 

그리고 wndProc()에서 myProc()을 호출하도록 하고 myProc()에 원래 wndProc()에 있던 내용을 씁니다.

 

이렇게 되면 메시지 처리를 위해 콜백 함수인 wndProc() 함수를 호출하면 다시 myProc() 함수를 호출하고 여기에서 메시지를 처리하게 됩니다.

반응형