Windows API의 C++ 기반 윈도우 클래스 래핑 설계

윈도우 메시지 처리 체계의 객체 지향 구현

이 문서는 Windows API를 기반으로 한 고수준의 C++ 윈도우 클래스 계층 구조를 설명합니다. 주요 목적은 원시적인 Win32 API 호출을 추상화하여, 객체 지향 방식으로 메시지 처리와 창 생성을 관리하는 것입니다. 기본 클래스인 CXWnd는 모든 윈도우 유형의 공통 기능을 제공하며, 프레임과 하위 창의 상속 구조를 형성합니다. 각 창은 자체적으로 메시지 맵을 정의하고, 해당 맵에 등록된 메시지에 대해 특정 멤버 함수를 실행하도록 구성됩니다. WndProc는 정적 함수로, 모든 창 인스턴스에서 공유되며, 윈도우 핸들로부터 해당 인스턴스를 검색해 메시지를 처리합니다. 이 과정에서 GWL_USERDATA를 사용하여 창 인스턴스 포인터를 저장하고, 이후 메시지 처리 시 이를 복원합니다. 메시지 매핑은 매크로 기반의 구조로, 컴파일 시점에 메시지-함수 매핑 정보가 배열로 생성됩니다. 예를 들어, XON_MESSAGE(WM_SIZE, OnSize)WM_SIZE 메시지가 발생했을 때 OnSize 멤버 함수를 호출하도록 설정합니다. 하위 클래스에서는 메시지 맵을 재정의하여 부모 클래스의 동작을 확장하거나 대체할 수 있습니다. 메시지 처리 순서는 자식 클래스에서부터 부모 클래스로 탐색되며, 처음으로 일치하는 항목을 찾아 그 함수를 호출한 후 반환됩니다. 만약 메시지 응답 함수가 가상 함수라면, 메시지 맵에 등록되지 않더라도 실제로는 자식 클래스의 함수가 호출됩니다. 또한 프레임 창(CXMDIFrame)과 하위 창(CXMDIView)은 각각 다른 메시지 처리 전략을 따릅니다. 프레임 창은 DefFrameProc를 사용하여 클라이언트 영역 내의 하위 창에게 메시지를 전달하고, 하위 창은 DefMDIChildProc를 통해 자신의 상태를 유지하면서 메시지를 처리합니다. 생성 과정에서도 다양한 오버로드된 Create 함수를 통해 프레임 창, 일반 창, 자식 창의 생성을 지원하며, 각각의 경우에 맞게 창 클래스 구조 및 스타일을 조정합니다. 이러한 설계는 메시지 기반의 애플리케이션 아키텍처를 강력하게 구현하면서도, 코드의 재사용성과 유지보수성을 크게 향상시킵니다.
// XWnd.h
#pragma once
#include "xobj.h"
#include "resource.h"

class CXWnd;

typedef int (CXWnd::*X_MSG_HANDLER)(HWND, UINT, WPARAM, LPARAM);

#define dim(x) (sizeof(x)/sizeof(x[0]))

enum XSignalType {
    XSig_end = 0,
    XSig_lwl,
    XSig_vv
};

struct XMsgMapEntry {
    UINT message;
    UINT code;
    UINT id;
    UINT lastId;
    UINT sig;
    X_MSG_HANDLER handler;
};

struct XMsgMap {
    XMsgMap* baseMap;
    XMsgMapEntry* entries;
};

#define XDECLARE_MESSAGE_MAP() \
    static XMsgMapEntry _messageEntries[]; \
    static XMsgMap messageMap; \
    virtual XMsgMap* GetMessageMap() const;

#define XBEGIN_MESSAGE_MAP(classname, baseclass) \
    XMsgMap* classname::GetMessageMap() const { return &classname::messageMap; } \
    XMsgMap classname::messageMap = { &baseclass::messageMap, (XMsgMapEntry*)&classname::_messageEntries }; \
    XMsgMapEntry classname::_messageEntries[] = {

#define XON_MESSAGE(msg, func) \
    { msg, 0, 0, 0, XSig_lwl, (X_MSG_HANDLER)&func },

#define XON_COMMAND(id, func) \
    { WM_COMMAND, 0, (WORD)id, (WORD)id, XSig_vv, (X_MSG_HANDLER)&func },

#define XEND_MESSAGE_MAP() \
    { 0, 0, 0, 0, XSig_end, nullptr } \
};

class CXWnd : public CXObj {
public:
    CXWnd();
    ~CXWnd();

    XDECLARE_DYNCREATE(CXWnd)

    TCHAR windowClass[256];
    TCHAR title[256];

    HINSTANCE hInstance;
    HWND hwnd;
    HMENU menu;

    virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
    virtual BOOL CreateEx(DWORD exStyle, LPCTSTR className, LPCTSTR windowName, DWORD style,
                          int x, int y, int width, int height,
                          HWND parent, HMENU menuId, HINSTANCE inst, LPVOID param);

    virtual LRESULT Create(HINSTANCE inst, HINSTANCE prevInst, LPSTR title, int cmdShow); // frame
    virtual LRESULT Create(HINSTANCE inst, HWND parent, int cmdShow); // child
    virtual LRESULT Create(HINSTANCE inst, HWND parent); // plain

    static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

    virtual int HandleMessage(HWND, UINT, WPARAM, LPARAM);
    virtual int OnCommand(HWND, UINT, WPARAM, LPARAM);
    virtual int OnNotify(HWND, UINT, WPARAM, LPARAM);

    int OnDestroy(HWND, UINT, WPARAM, LPARAM);
    int OnSize(HWND, UINT, WPARAM, LPARAM);

    virtual int OnHookMessage(HWND, UINT, WPARAM, LPARAM);

    XDECLARE_MESSAGE_MAP()
};
// XMDIFrame.h
#pragma once
#include "xwnd.h"

class CXMDIFrame : public CXWnd {
public:
    CXMDIFrame();
    ~CXMDIFrame();

    XDECLARE_DYNCREATE(CXMDIFrame)

    HWND clientHwnd;

    virtual BOOL PreCreateWindow(WNDCLASSEX& wcex);
    static BOOL CALLBACK CloseEnumProc(HWND, LPARAM);

    virtual int HandleMessage(HWND, UINT, WPARAM, LPARAM);
    virtual int OnCommand(HWND, UINT, WPARAM, LPARAM);
    virtual int OnNotify(HWND, UINT, WPARAM, LPARAM);

    int OnQueryEndSession(HWND, UINT, WPARAM, LPARAM);
    int OnClose(HWND, UINT, WPARAM, LPARAM);
    int OnDestroy(HWND, UINT, WPARAM, LPARAM);

    int OnCreate(HWND, UINT, WPARAM, LPARAM);
    int OnSize(HWND, UINT, WPARAM, LPARAM);

    virtual int Init();

    int OnNew(HWND, UINT, WPARAM, LPARAM);
    int OnAppExit(HWND, UINT, WPARAM, LPARAM);
    int OnFileClose(HWND, UINT, WPARAM, LPARAM);

    int OnCascade(HWND, UINT, WPARAM, LPARAM);
    int OnArrange(HWND, UINT, WPARAM, LPARAM);
    int OnTile(HWND, UINT, WPARAM, LPARAM);
    int OnCloseAll(HWND, UINT, WPARAM, LPARAM);
    virtual int OnAbout(HWND, UINT, WPARAM, LPARAM);

    XDECLARE_MESSAGE_MAP()
};
// XMDIView.h
#pragma once
#include "xwnd.h"

class CXMDIView : public CXWnd {
public:
    CXMDIView();
    ~CXMDIView();

    XDECLARE_DYNCREATE(CXMDIView)

    virtual LRESULT Create(HINSTANCE inst, HWND parent);

    HMENU menuInit;
    HWND clientHwnd;
    HWND frameHwnd;

    virtual BOOL PreCreateWindow(WNDCLASSEX& wcex);
    virtual BOOL PreCreateWindow(MDICREATESTRUCT& mdicreate);

    static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

    virtual int HandleMessage(HWND, UINT, WPARAM, LPARAM);
    virtual int OnCommand(HWND, UINT, WPARAM, LPARAM);
    virtual int OnNotify(HWND, UINT, WPARAM, LPARAM);

    int OnCreate(HWND, UINT, WPARAM, LPARAM);
    int OnPaint(HWND, UINT, WPARAM, LPARAM);
    int OnSize(HWND, UINT, WPARAM, LPARAM);
    int OnChar(HWND, UINT, WPARAM, LPARAM);
    int OnClose(HWND, UINT, WPARAM, LPARAM);
    int OnDestroy(HWND, UINT, WPARAM, LPARAM);
    int OnMdiActive(HWND, UINT, WPARAM, LPARAM);
    int OnExit(HWND, UINT, WPARAM, LPARAM);

    XDECLARE_MESSAGE_MAP()
};

태그: Windows API C++ Object-Oriented Design Message Mapping MDI Architecture

7월 24일 06:56에 게시됨