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

純 Win32 SDK 手寫計算器:從訊息迴圈到四則運算完整實作

     編輯:關於VC++

專案背景與核心函式

許多開發者認為 MFC 寫計算器容易,SDK 難。其實只要掌握「建立框架 → 訊息處理」的固定模式,SDK 同樣得心應手。核心需兩個字串/數值轉換函式:

  • double strtod(const char* nptr, char** endptr):字串 → 雙精度浮點數
  • char* _gcvt(double value, int digits, char* buffer):雙精度浮點數 → 字串(digits 為有效位數)

程式架構三步驟

  1. WinMain 入口點:註冊視窗類別、建立視窗、訊息迴圈(標準樣板)。
  2. 視窗程式 WndProc:處理 WM_CREATE 建立按鈕/編輯框、WM_COMMAND 分派按鈕事件、WM_DESTROY 結束程式。
  3. 計算邏輯模組:以全域/靜態變數維護運算狀態,封裝 NumResult()(數字鍵)、EquResult()(運算鍵)兩大核心函式。

關鍵資料結構與狀態機


// 全域狀態變數
HWND hEditResult;          // 顯示用編輯框
char lpszAddItem[256];     // 目前輸入緩衝
char lpszResult1[256], lpszResult2[256]; // 運算元 1/2
char lpszOpt[2] = "N";     // 目前運算子:'N'=無, '+','-','*','/','%','S'(sqrt)
int nOptF = 0;             // 0=等待新輸入, 1=正在輸入數字
bool bDec = false;         // 是否已輸入小數點
double nResult1, nResult2; // 運算中間結果

數字鍵處理 NumResult()


void NumResult(char* NumData) {
    if (nOptF == 0) SetWindowText(hEditResult, ""); // 新運算起始,清空顯示
    int nMax = GetWindowTextLength(hEditResult) + 1;
    GetWindowText(hEditResult, lpszAddItem, nMax);  // 取得現有字串
    strcat(lpszAddItem, NumData);                   // 追加新數字/小數點

    if (strcmp(lpszOpt, "N") == 0) {                // 尚未按運算子
        strcpy(lpszResult1, lpszAddItem);
        SetWindowText(hEditResult, lpszResult1);
    } else {                                        // 已按運算子,輸入第二運算元
        strcpy(lpszResult2, lpszAddItem);
        SetWindowText(hEditResult, lpszResult2);
    }
    nOptF = 1; // 標記已輸入數字
}

運算子/等號鍵處理 EquResult()


void EquResult() {
    // 1. 取得第一運算元
    if (strcmp(lpszOpt, "N") == 0) {
        nResult1 = strtod(lpszResult1, &stop);
    } else {
        // 2. 依前一運算子執行運算
        nResult1 = strtod(lpszResult1, &stop);
        nResult2 = strtod(lpszResult2, &stop);
        switch (*lpszOpt) {
            case '+': nResult1 += nResult2; break;
            case '-': nResult1 -= nResult2; break;
            case '*': nResult1 *= nResult2; break;
            case '/': 
                if (nResult2 == 0) { MessageBox(hWnd, "除數不能為零!", "錯誤", MB_OK); break; }
                nResult1 /= nResult2; break;
            case '%': nResult1 /= 100; break;
            case 'S': // sqrt
                if (nResult1 < 0) { MessageBox(hWnd, "負數沒有平方根!", "錯誤", MB_OK); break; }
                nResult1 = sqrt(nResult1); break;
        }
    }
    // 3. 顯示結果並重置狀態
    _gcvt(nResult1, 15, lpszResult1);
    SetWindowText(hEditResult, lpszResult1);
    nOptF = 0; bDec = false; strcpy(lpszOpt, "N");
}

WM_COMMAND 分派範例


switch (LOWORD(wParam)) {
    case IDB_NUM0: if (nOptF) NumResult("0"); break;
    case IDB_NUM1: NumResult("1"); break;
    // ... IDB_NUM2 ~ IDB_NUM9 類似
    case IDB_NUMDEC: if (!bDec) { NumResult("."); bDec = true; nOptF = 1; } break;

    case IDB_OPTADD: EquResult(); strcpy(lpszOpt, "+"); break;
    case IDB_OPTSUB: EquResult(); strcpy(lpszOpt, "-"); break;
    case IDB_OPTMUL: EquResult(); strcpy(lpszOpt, "*"); break;
    case IDB_OPTDIV: EquResult(); strcpy(lpszOpt, "/"); break;
    case IDB_OPTSQRT: strcpy(lpszOpt, "S"); EquResult(); break;
    case IDB_OPTPCT:  strcpy(lpszOpt, "%"); EquResult(); break;

    case IDB_CANCLE: // 全部重置
        SetWindowText(hEditResult, "0");
        nResult1 = nResult2 = 0;
        strcpy(lpszResult1, "0"); strcpy(lpszResult2, "0");
        nOptF = 0; bDec = false; strcpy(lpszOpt, "N");
        break;
}

必要標頭檔


#include 
#include    // sqrt
#include   // _gcvt
#include  // strtod
#include  // strcmp/strcpy/strcat
#include "resource.h" // 控制項 ID 定義

心得與擴充方向

SDK 版計算器雖代碼量較大,但完全掌控視窗建立與訊息流程,適合理解 Windows 核心機制。後續可擴充:

  • 鍵盤輸入支援(WM_CHAR/WM_KEYDOWN)
  • 科學運算功能(三角函數、對數、冪運算)
  • 歷史記錄與多行顯示
  • 自訂視窗外觀(Owner-draw 按鈕、背景圖)

原始碼已整理附於專案,歡迎參考修改。

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