◎ Part 4: WindowProc + WinMain (메시지 처리 & 진입점)

 // ------------ WindowProc sub ----------

WNDPROC g_OldEditProc = NULL;     // hInputEdit, hOutputEdit용 (Ctrl+A만)
WNDPROC g_OldTextAProc = NULL;    // hTextA용 (Ctrl+A + TAB → TextB 이동)
extern HWND hTextA;   // Text A 핸들
extern HWND hTextB;   // Text B 핸들

// 일반 Edit용: Ctrl+A만 처리 (InputEdit, OutputEdit에 적용)
LRESULT CALLBACK EditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (msg == WM_KEYDOWN)
    {
        if (wParam == 'A' && (GetKeyState(VK_CONTROL) & 0x8000))
        {
            SendMessage(hwnd, EM_SETSEL, 0, -1);
            return 0;
        }
    }
    return CallWindowProc(g_OldEditProc, hwnd, msg, wParam, lParam);
}

// TextA 전용: Ctrl+A + TAB → TextB로 이동
LRESULT CALLBACK TextAProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (msg == WM_KEYDOWN)
    {
        // Ctrl+A 전체 선택
        if (wParam == 'A' && (GetKeyState(VK_CONTROL) & 0x8000))
        {
            SendMessage(hwnd, EM_SETSEL, 0, -1);
            return 0;
        }

        // TAB 키 (Shift 안 눌린 경우에만 TextB로 이동)
        if (wParam == VK_TAB)
        {
            if (!(GetKeyState(VK_SHIFT) & 0x8000))  // Shift+TAB은 기본 동작 허용
            {
                SetFocus(hTextB);
                return 0;
            }
        }
    }
    return CallWindowProc(g_OldTextAProc, hwnd, msg, wParam, lParam);
}

// ------------ WindowProc ------------

LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_CREATE:
        CreateControls(hwnd);

        // 기존 Input Edit, Output Edit: Ctrl+A만
        g_OldEditProc = (WNDPROC)SetWindowLongPtr(
            hInputEdit,
            GWLP_WNDPROC,
            (LONG_PTR)EditProc
        );
        SetWindowLongPtr(                   // 반환값 저장 안 해도 되지만 일관성 위해
            hOutputEdit,
            GWLP_WNDPROC,
            (LONG_PTR)EditProc
        );

        // 별도의 Text A: Ctrl+A + TAB → Text B 이동
        g_OldTextAProc = (WNDPROC)SetWindowLongPtr(
            hTextA,
            GWLP_WNDPROC,
            (LONG_PTR)TextAProc
        );

        // Text B는 서브클래싱 안 해도 됨 (필요하면 Ctrl+A만 넣어도 OK)

        return 0;

    case WM_COMMAND:
    {
        int btnId = LOWORD(wParam);

        static char input[MAX_BUFFER] = { 0 };
        static char output[MAX_BUFFER] = { 0 };

        GetWindowTextA(hInputEdit, input, MAX_BUFFER - 1);
        output[0] = '\0'; // 이전 결과 제거

        bool converted = true;

        switch (btnId)
        {
        case IDC_TEXT_TO_HEX: textToHex(input, output, MAX_BUFFER); break;
        case IDC_HEX_TO_TEXT: hexToText(input, output, MAX_BUFFER); break;
        case IDC_DEC_TO_HEX: decToHex(input, output, MAX_BUFFER); break;
        case IDC_HEX_TO_DEC: hexToDec(input, output, MAX_BUFFER); break;
        case IDC_TEXT_TO_DEC: textToDec(input, output, MAX_BUFFER); break;
        case IDC_DEC_TO_TEXT: decToText(input, output, MAX_BUFFER); break;
        case IDC_DEC_TO_OCTAL: decToOctal(input, output, MAX_BUFFER); break;
        case IDC_OCTAL_TO_DEC: octalToDec(input, output, MAX_BUFFER); break;
        case IDC_TEXT_TO_UTF7: textToUtf7(input, output, MAX_BUFFER); break;
        case IDC_UTF7_TO_TEXT: utf7ToText(input, output, MAX_BUFFER); break;
        case IDC_HEX_TO_UCS2: hexToUcs2(input, output, MAX_BUFFER); break;
        case IDC_UCS2_TO_HEX: ucs2ToHex(input, output, MAX_BUFFER); break;
        case IDC_TEXT_TO_BINARY: textToBinary(input, output, MAX_BUFFER); break;
        case IDC_BINARY_TO_TEXT: binaryToText(input, output, MAX_BUFFER); break;
        case IDC_ESCAPE: escapeString(input, output, MAX_BUFFER); break;
        case IDC_UNESCAPE: unescapeString(input, output, MAX_BUFFER); break;
        case IDC_ENCODE_HTML: encodeHTML(input, output, MAX_BUFFER); break;
        case IDC_DECODE_HTML: decodeHTML(input, output, MAX_BUFFER); break;
        case IDC_TEXT_TO_BASE64: encodeBase64(input, output, MAX_BUFFER); break;
        case IDC_BASE64_TO_TEXT: decodeBase64(input, output, MAX_BUFFER); break;
        case IDC_HEX_TO_BASE64: hexToBase64(input, output, MAX_BUFFER); break;
        case IDC_BASE64_TO_HEX: base64ToHex(input, output, MAX_BUFFER); break;
        case IDC_IP_TO_DEC: ipToDec(input, output, MAX_BUFFER); break;
        case IDC_DEC_TO_IP: decToIp(input, output, MAX_BUFFER); break;
        case IDC_IP_TO_HEX: ipToHex(input, output, MAX_BUFFER); break;
        case IDC_HEX_TO_IP: hexToIp(input, output, MAX_BUFFER); break;
        case IDC_TEXT_TO_URL: textToUrl(input, output, MAX_BUFFER); break;
        case IDC_URL_TO_TEXT: urlToText(input, output, MAX_BUFFER); break;
        
        case IDC_REPLACE_GO:
        {
            static char findText[32] = { 0 };
            static char replaceText[32] = { 0 };

            GetWindowTextA(hTextA, findText, sizeof(findText) - 1);
            GetWindowTextA(hTextB, replaceText, sizeof(replaceText) - 1);

            replaceTextInInput(hInputEdit, findText, replaceText);
            converted = false;
            break;
        }

        case IDC_COPY_OUTPUT:
        {
            static char buf[MAX_BUFFER] = { 0 };
            GetWindowTextA(hOutputEdit, buf, MAX_BUFFER - 1);
            copyToClipboard(buf);
            converted = false;
            break;
        }

        case IDC_COPY_TO_INPUT:
        {
            static char buf[MAX_BUFFER] = { 0 };
            GetWindowTextA(hOutputEdit, buf, MAX_BUFFER - 1);
            SetWindowTextA(hInputEdit, buf);
            converted = false;
            break;
        }

        case IDC_CLEAR_ALL:
            SetWindowTextA(hTextA, "");
            SetWindowTextA(hTextB, "");
            SetWindowTextA(hInputEdit, "");
            SetWindowTextA(hOutputEdit, "");
            converted = false;
            break;

        case IDC_FILE_SELECT:
            openFileSelectDialog(hwnd, hOutputEdit);
            converted = false;  // ← 중요: 이미 함수 내부에서 출력했으므로
            break;

        default:
            converted = false;
            break;
        }

        // 핵심: 변환 버튼이면 결과 출력
        if (converted)
            SetWindowTextA(hOutputEdit, output);

        return 0;
    }

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProcW(hwnd, msg, wParam, lParam);
}

// ------------ WinMain ------------

int WINAPI WinMain(
    _In_ HINSTANCE hInst,
    _In_opt_ HINSTANCE hPrev,
    _In_ LPSTR lpCmd,
    _In_ int nCmdShow)
{
    const wchar_t CLASS_NAME[] = L"ConvToolImproved";

    WNDCLASSW wc = { 0 };
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInst;
    wc.lpszClassName = CLASS_NAME;
    wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
    wc.hCursor = LoadCursorW(NULL, IDC_ARROW);

    if (!RegisterClassW(&wc)) return 0;

    HWND hwnd = CreateWindowExW(
        0,
        CLASS_NAME,
        L"Data Conversion Tool",
        WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX & ~WS_THICKFRAME,
        CW_USEDEFAULT, CW_USEDEFAULT,
        680, 570,
        NULL, NULL, hInst, NULL);

    if (!hwnd) return 0;

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    MSG msg = { 0 };
    while (GetMessageW(&msg, NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }
    return (int)msg.wParam;
}

 

 - WindowProc: 메시지 처리

LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
    case WM_CREATE:
        CreateControls(hwnd);
        break;
  • 윈도우 생성될 때: CreateControls()를 호출해서 모든 UI 생성.
    case WM_COMMAND: {
        int btnId = LOWORD(wParam);
        char* input = (char*)malloc(MAX_BUFFER);
        char* output = (char*)malloc(MAX_BUFFER);
        if (!input || !output) {
            if (input)  free(input);
            if (output) free(output);
            break;
        }
        ZeroMemory(input, MAX_BUFFER);
        ZeroMemory(output, MAX_BUFFER);
  • 사용자가 버튼을 클릭할 때 발생.
  • btnId : 클릭된 버튼의 ID (201~228 중 하나).
  • 동적 메모리 할당 : 8KB짜리 입력/출력 버퍼.
  • ZeroMemory() : 메모리를 0으로 초기화 (쓰레기값 제거).
        GetWindowTextA(hInputEdit, input, MAX_BUFFER - 1);
  • 입력 텍스트박스에서 사용자가 입력한 텍스트를 읽기.
        switch (btnId) {
        case IDC_TEXT_TO_HEX:    textToHex(input, output, MAX_BUFFER);    break;
        case IDC_HEX_TO_TEXT:    hexToText(input, output, MAX_BUFFER);    break;
        // ... 등등
        case IDC_TEXT_TO_BASE64: encodeBase64(input, output, MAX_BUFFER); break;
        case IDC_BASE64_TO_TEXT: decodeBase64(input, output, MAX_BUFFER); break;
        // ...
        }
  • 클릭된 버튼에 따라 해당 변환 함수 호출.
  • 예: IDC_TEXT_TO_HEX면 textToHex() 함수 실행.
        case IDC_COPY_OUTPUT: {
            char* buf = (char*)malloc(MAX_BUFFER);
            if (buf) {
                GetWindowTextA(hOutputEdit, buf, MAX_BUFFER - 1);
                copyToClipboard(buf);
                free(buf);
            }
            free(input); free(output);
            return 0;
        }
  • "Copy Output to Clipboard" 버튼
    >> 출력 텍스트박스의 내용을 읽고
    >> copyToClipboard()로 클립보드에 복사
    >> 메모리 해제 후 바로 반환 (SetWindowTextA 호출 안 함).
        case IDC_COPY_TO_INPUT: {
            char* buf = (char*)malloc(MAX_BUFFER);
            if (buf) {
                GetWindowTextA(hOutputEdit, buf, MAX_BUFFER - 1);
                SetWindowTextA(hInputEdit, buf);
                free(buf);
            }
            free(input); free(output);
            return 0;
        }
  • "Copy Output to Input" 버튼
    >> 출력 내용을 입력 텍스트박스에 붙여넣기.
    >> 메모리 해제.
        case IDC_CLEAR_ALL:
            SetWindowTextA(hInputEdit, "");
            SetWindowTextA(hOutputEdit, "");
            free(input); free(output);
            return 0;
  • "Clear All" 버튼: 입력/출력 박스 모두 비우기.
        SetWindowTextA(hOutputEdit, output);

        free(input);
        free(output);
        break;
    }
  • 변환 함수 호출 후 결과를 출력 텍스트박스에 표시.
  • 메모리 해제 (malloc 되었으니 반드시 필요).
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProcW(hwnd, msg, wParam, lParam);
}
  • 윈도우가 닫힐 때 프로그램 종료.

 

 - WinMain: 진입점

int WINAPI WinMain(
    _In_ HINSTANCE hInst,
    _In_opt_ HINSTANCE hPrev,
    _In_ LPSTR lpCmd,
    _In_ int nCmdShow)
{
    const wchar_t CLASS_NAME[] = L"ConvToolImproved";
  • 프로그램의 시작점.
  • hInst : 현재 프로그램의 인스턴스 핸들.
  • nCmdShow : 윈도우를 어떻게 보여줄지 (최소화/정상/최대화).
  • CLASS_NAME : 윈도우 클래스 이름 (등록할 클래스 이름).
    WNDCLASSW wc = { 0 };
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInst;
    wc.lpszClassName = CLASS_NAME;
    wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
    wc.hCursor = LoadCursorW(NULL, IDC_ARROW);

    if (!RegisterClassW(&wc)) return 0;
  • 윈도우 클래스 정의:
    >> lpfnWndProc : 메시지 처리 함수 (WindowProc).
    >> hInstance : 인스턴스.
    >> lpszClassName : 클래스 이름.
    >> hbrBackground : 배경색 (버튼 면 색상).
    >> hCursor : 마우스 커서 (기본 화살표).
    >> RegisterClassW() : 이 클래스를 윈도우 시스템에 등록.
    HWND hwnd = CreateWindowExW(
        0,
        CLASS_NAME,
        L"Data Conversion Tool",
        WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX & ~WS_THICKFRAME,
        CW_USEDEFAULT, CW_USEDEFAULT,
        680, 480,
        NULL, NULL, hInst, NULL);

    if (!hwnd) return 0;
  • 실제 윈도우 생성
    >> CLASS_NAME : 위에서 등록한 클래스 사용
    >> L"Data Conversion Tool" : 타이틀 바에 표시될 제목
    >> WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX & ~WS_THICKFRAME :
       >>> 기본 윈도우 스타일에서
       >>> 최대화 버튼 제거 (~WS_MAXIMIZEBOX)
       >>> 크기 조절 불가능 (~WS_THICKFRAME)
    >> 680, 480 : 초기 윈도우 크기 (680×480).
    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
  • 윈도우 표시 (ShowWindow).
  • 윈도우 갱신 (UpdateWindow) - 화면에 즉시 그리기.
    MSG msg = { 0 };
    while (GetMessageW(&msg, NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }
    return (int)msg.wParam;
  • 메시지 루프 (프로그램의 핵심):
    >> GetMessageW() : 윈도우 메시지 대기 (사용자 클릭, 키 입력 등).
    >> TranslateMessage() : 키 입력을 문자로 변환.
    >> DispatchMessageW() : 메시지를 WindowProc로 전달.
    >> 루프는 WM_QUIT 메시지를 받을 때까지 계속 반복.


◎ 실행 흐름 요약

1. WinMain 시작
   ↓
2. 윈도우 클래스 등록 (WNDCLASSW)
   ↓
3. 윈도우 생성 (CreateWindowExW)
   ↓
4. 윈도우 표시 (ShowWindow)
   ↓
5. 메시지 루프 시작 (while GetMessageW)
   ├─ 사용자 입력 대기
   ├─ WindowProc에서 메시지 처리
   │  ├─ WM_CREATE : CreateControls() 호출 (UI 생성)
   │  ├─ WM_COMMAND : 버튼 클릭 감지
   │  │  ├─ 변환 함수 호출 (textToHex, hexToText 등)
   │  │  └─ 결과를 출력 텍스트박스에 표시
   │  └─ WM_DESTROY : PostQuitMessage(0)
   │
   └─ 루프 종료 (WM_QUIT 받음)
   ↓
6. 프로그램 종료

 

+ Recent posts