首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 組合語言

Win32 組合語言 GUI 程式設計:以對話框為主視窗的兩種實戰架構模式

     編輯:組合語言

前言:為何選擇對話框作為主視窗?

傳統視窗程式需手動建立子控制項、處理 Tab 鍵焦點切換、灰色背景繪製等瑣碎邏輯。對話框內建對話框管理器,自動處理鍵盤導航 (Tab/Enter/Esc)、控制項建立/銷毀、背景色協調 (預設 `COLOR_BTNFACE`)、訊息分發等「管家事務」。開發者只需專注於資料交互 (取得/設定控制項內容) 與業務邏輯。本文示範兩種將對話框提升為主視窗的架構:

  1. 模式一:註冊視窗類 (`DLGWINDOWEXTRA`),建立無模式對話框,自行跑訊息迴圈並呼叫 `IsDialogMessage`。
  2. 模式二:直接呼叫 `DialogBoxParam` 建立模式對話框,無視窗類、無訊息迴圈,完全由系統管理器驅動。

一、核心 API 快速參考

API用途關鍵參數
`CreateDialogParam`建立無模式對話框`hInstance, 模板名, 父視窗, 回調, 參數` → 傳回 `HWND`
`DialogBoxParam`建立模式對話框 (阻塞)同上,但不返回直到 `EndDialog`
`IsDialogMessage`讓管理器預處理鍵盤訊息`hDlg, &msg` → `TRUE` 已處理,`FALSE` 需自行 `Translate/Dispatch`
`SendDlgItemMessage`向控制項發送訊息`hDlg, ID, Msg, wParam, lParam`
`Get/SetDlgItemText`取得/設定編輯框文字簡化版 `SendDlgItemMessage` (WM_GET/SETTEXT)
`CheckDlgButton` / `IsDlgButtonChecked`核取方塊/單選鈕狀態
`EndDialog`結束模式對話框`hDlg, 結果碼`

二、模式一:註冊視窗類 + 無模式對話框 + 手動訊息泵

1. 視窗類註冊關鍵點

wc.cbWndExtra = DLGWINDOWEXTRA   ; 必須!告知系統此類關聯對話框模板
wc.lpszClassName = "DLGCLASS"    ; 必須與 .rc 中 CLASS "DLGCLASS" 一致
wc.hbrBackground = COLOR_BTNFACE+1 ; 系統灰色背景刷

`DLGWINDOWEXTRA` 讓系統在視窗結構額外預留空間儲存對話框專用資料 (如控制項 ID 對應表)。

2. 資源腳本 關鍵片段

MyDialog DIALOG 10, 10, 205, 60
STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK
CAPTION "Our First Dialog Box"
CLASS "DLGCLASS"          ; 關鍵:綁定視窗類
BEGIN
    EDITTEXT IDC_EDIT, 15,17,111,13, ES_AUTOHSCROLL | ES_LEFT
    DEFPUSHBUTTON "Say Hello", IDC_BUTTON, 141,10,52,13
    PUSHBUTTON "E&xit", IDC_EXIT, 141,26,52,13, WS_GROUP
END
  • 座標單位為對話框單位 (非像素),系統依字型自動換算。
  • `DS_CENTER`:螢幕置中;`DS_3DLOOK`:立體外觀;`WS_VISIBLE`:建立即顯示。

3. 程式進入點與訊息泵

invoke CreateDialogParam, hInstance, ADDR DlgName, NULL, NULL, NULL
mov hDlg, eax
invoke ShowWindow, hDlg, SW_SHOWNORMAL
invoke UpdateWindow, hDlg
invoke GetDlgItem, hDlg, IDC_EDIT
invoke SetFocus, eax          ; 初始焦點給編輯框

.WHILE TRUE
    invoke GetMessage, ADDR msg, NULL, 0, 0
    .BREAK .IF !eax
    invoke IsDialogMessage, hDlg, ADDR msg
    .IF eax == FALSE
        invoke TranslateMessage, ADDR msg
        invoke DispatchMessage, ADDR msg
    .ENDIF
.ENDW

關鍵:`IsDialogMessage` 必須在 `Translate/Dispatch` 之前調用,否則 Tab/Enter 導航失效。

4. 視窗程序 `WndProc` 處理邏輯

  • `WM_COMMAND` 區分選單 (`lParam=0`) 與控制項通知 (`lParam≠0`, `HIWORD(wParam)` 為通知碼)。
  • 選單項:`IDM_GETTEXT` → `GetDlgItemText` → `MessageBox`;`IDM_CLEAR` → `SetDlgItemText NULL`;`IDM_EXIT` → `DestroyWindow`。
  • 按鈕通知 `BN_CLICKED`:`IDC_BUTTON` → 寫入預設字串;`IDC_EXIT` → 模擬選單退出。
  • 未處理訊息 → `DefWindowProc` (因已註冊視窗類)。

三、模式二:模式對話框 + 純回調函數 (無視窗類、無訊息泵)

1. 入口點極度精簡

start:
    invoke GetModuleHandle, NULL
    mov hInstance, eax
    invoke DialogBoxParam, hInstance, ADDR DlgName, NULL, ADDR DlgProc, NULL
    invoke ExitProcess, eax

`DialogBoxParam` 內部建立視窗、跑模態迴圈、處理 `WM_INITDIALOG`~`WM_CLOSE` 全生命週期,直到 `EndDialog` 返回。

2. 對話框程序 `DlgProc` 範本

DlgProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    .IF uMsg == WM_INITDIALOG
        invoke GetDlgItem, hWnd, IDC_EDIT
        invoke SetFocus, eax
        mov eax, TRUE
        ret
    .ELSEIF uMsg == WM_CLOSE
        invoke SendMessage, hWnd, WM_COMMAND, IDM_EXIT, 0
    .ELSEIF uMsg == WM_COMMAND
        mov eax, wParam
        .IF lParam == 0          ; 選單
            .IF ax == IDM_GETTEXT
                invoke GetDlgItemText, hWnd, IDC_EDIT, ADDR buffer, 512
                invoke MessageBox, NULL, ADDR buffer, ADDR AppName, MB_OK
            .ELSEIF ax == IDM_CLEAR
                invoke SetDlgItemText, hWnd, IDC_EDIT, NULL
            .ELSEIF ax == IDM_EXIT
                invoke EndDialog, hWnd, NULL
            .ENDIF
        .ELSE                    ; 控制項通知
            mov edx, wParam
            shr edx, 16
            .IF dx == BN_CLICKED
                .IF ax == IDC_BUTTON
                    invoke SetDlgItemText, hWnd, IDC_EDIT, ADDR TestString
                .ELSEIF ax == IDC_EXIT
                    invoke SendMessage, hWnd, WM_COMMAND, IDM_EXIT, 0
                .ENDIF
            .ENDIF
        .ENDIF
    .ELSE
        mov eax, FALSE
        ret
    .ENDIF
    mov eax, TRUE
    ret
DlgProc endp

關鍵差異對照表

特性模式一 (無模式+類)模式二 (模態+回調)
視窗類註冊需要 (`RegisterClassEx`)不需要
訊息迴圈手寫 (`GetMessage` + `IsDialogMessage`)系統內建 (阻塞)
回調簽名標準 `WndProc` (回傳 `LRESULT`)`DlgProc` (回傳 `TRUE/FALSE`)
未處理訊息`DefWindowProc``FALSE` (勿呼叫 `DefWindowProc`)
圖示/游標視窗類 `hIcon`/`hCursor` 生效需 `WM_INITDIALOG` 中 `WM_SETICON` 設定
選單綁定視窗類 `lpszMenuName`模板 `MENU IDR_MENU1` (整數 ID)
適用場景主視窗、需與其他視窗並存、非阻塞精靈、設定對話框、模態輸入、工具箱

四、資源腳本進階技巧

  • 控制項 ID 定義:在 `.rc` 與 `.asm` 共用 `resource.h` 或同步定義 `equ`,避免硬編碼。
  • 對話框單位換算:1 對話框單位 ≈ 1/4 字元寬 × 1/8 字元高 (系統字型)。設計時用資源編輯器視覺化拖曳最直觀。
  • WS_GROUP / WS_TABSTOP:`WS_GROUP` 標示群組首項 (如 Radio Button 群);`WS_TABSTOP` (預設大多控制項有) 控制 Tab 順序。
  • 版本資源:加入 `VS_VERSION_INFO` 讓檔案屬性頁顯示版本/著作權。

五、常見坑點與最佳實踐

  1. 焦點設定時機:`WM_CREATE` 時子控制項尚未建立,`GetDlgItem` 失敗。必須在 `UpdateWindow` 後或 `WM_INITDIALOG` 中設定。
  2. 模態對話框不得呼叫 `DestroyWindow`:必須用 `EndDialog`,否則資源洩漏且迴圈不退出。
  3. 回傳值慣例:`DlgProc` 處理過回傳 `TRUE`,未處理回傳 `FALSE`;`WndProc` 處理過回傳 `0` (多數情況),未處理交給 `DefWindowProc`。
  4. 64 位元移植:`WPARAM`/`LPARAM`/`LRESULT` 擴展為 64 位,MASM64 下改用 `QWORD`、`R8`~`R15` 傳參,結構對齊變 8 位元組。
  5. 資源編譯:`rc.exe /r dialog.rc` 生成 `.res`,`link /SUBSYSTEM:WINDOWS dialog.obj dialog.res`。

結語

對話框作為主視窗,以「內建管理器」替換「手工管家」,大幅降低 GUI 開發認知負荷。模式一適合需共存多視窗、自訂非客戶區繪製的主程式框架;模式二則是模態交互的極致精簡。實戰中常混用:主視窗採模式一,彈出設定/關於/精靈採模式二。掌握兩種架構的啟動流程、訊息分發差異、資源綁定細節,即可在 Win32 組合語言中高效構建專業級桌面應用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved