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

Windows 7 開發實戰:版本檢查正確姿勢 — 原生代碼篇

     編輯:關於.NET

實驗目標

  • 體驗相容模式「版本謊言」機制 (原生代碼有效)
  • 掌握 VerifyVersionInfo 正確檢查最低版本需求
  • 實作「功能檢測」:GetProcAddress 動態判斷 API 可用性

系統需求

  • Microsoft Visual Studio 2008 (Win 7 環境)
  • Microsoft Windows 7
  • Microsoft Windows 7 SDK

練習:原生代碼版本檢查全流程

任務 1:重現錯誤版本檢查

  1. 開啟 VersionCheck 方案,Debug/x86
  2. 設定 VersionCheckNativeBroken 為啟動專案
  3. _tWinMain 中 GetVersionEx 硬比對 5.1 (XP)
  4. 執行 → 彈出「錯誤的作業系統版本」
建置設定
版本檢查失敗

任務 2:相容模式快速修復 (僅原生代碼有效)

  1. 找到 VersionCheckNativeBroken\Release\VersionCheckNativeBroken.exe
  2. 右鍵 → 內容 → 相容性 標籤
  3. 勾選「以相容模式執行」→ 選 Windows XP (Service Pack 3)
  4. 執行 → 應用程式正常啟動 (系統回報版本為 5.1)
相容性標籤設定
> ⚠️ **警示**:這只是權宜之計,強烈建議修正程式碼邏輯。

任務 3:正確實作最低版本檢查 — VerifyVersionInfo

在 VersionCheckNativeBroken.cpp 頂部加入宣告:

BOOL TestMinimumOSRequirement();

修改 _tWinMain:

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    g_hInst = hInstance;

    // 取代 GetVersionEx 硬比對
    if (!TestMinimumOSRequirement())
    {
        MessageBox(NULL, _T("Windows XP SP2 or later is required."), _T("Wrong OS version."), MB_OK | MB_ICONERROR);
        return 1;
    }

    InitCommonControls();
    DialogBox(hInstance, MAKEINTRESOURCE(IDD_VERSIONCHECKNATIVE_DIALOG), 0, DlgMainProc);
    return 0;
}

實作 TestMinimumOSRequirement (要求 XP SP2 以上):

BOOL TestMinimumOSRequirement()
{
    OSVERSIONINFOEX osvi;
    DWORDLONG dwlConditionMask = 0;
    int op = VER_GREATER_EQUAL;

    ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    osvi.dwMajorVersion = 5;
    osvi.dwMinorVersion = 1;        // Windows XP
    osvi.wServicePackMajor = 2;     // SP2
    osvi.wServicePackMinor = 0;

    VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, op);
    VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, op);
    VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMAJOR, op);
    VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMINOR, op);

    return VerifyVersionInfo(&osvi,
        VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
        dwlConditionMask);
}
  • VER_GREATER_EQUAL:滿足「大於等於」語意
  • VER_SET_CONDITION 宏建立條件遮罩
  • 一次檢查主/次版本號 + Service Pack

任務 4:功能檢測 — 動態載入 SetWaitableTimerEx

修改 Timer.cpp 中 CTimer::Set:

步驟 1:定義函數指標型別 (檔案頂部取消註解)

typedef BOOL (WINAPI *SetWaitableTimerExProc)(
    __in HANDLE hTimer,
    __in const LARGE_INTEGER *lpDueTime,
    __in LONG lPeriod,
    __in PTIMERAPCROUTINE pfnCompletionRoutine,
    __in LPVOID lpArgToCompletionRoutine,
    __in PREASON_CONTEXT WakeContext,
    __in ULONG TolerableDelay
);

步驟 2:執行階段動態取得函數位址

HMODULE hKernel32Module = GetModuleHandle(_T("kernel32.dll")); // 已載入模組用 GetModuleHandle
if (hKernel32Module == NULL) return FALSE;

SetWaitableTimerExProc pFnSetWaitableTimerEx = (SetWaitableTimerExProc)::GetProcAddress(hKernel32Module, "SetWaitableTimerEx");

// 檢查函數是否存在
if (pFnSetWaitableTimerEx == NULL)
    return FALSE; // 落回 SetWaitableTimer

// 呼叫新版 API
if (!pFnSetWaitableTimerEx(hTimer, &liDueTime, period, NULL, NULL, &reasonContext, tolerance))
{ /* handle error */ }
  • kernel32.dll 永遠載入進程,可直接 GetModuleHandle
  • 未載入的 DLL 需用 LoadLibrary 取得模組句柄
  • GetProcAddress 回傳 NULL 表示函數不存在 → 優雅降級
  • 替代方案:延遲載入 DLL (/DELAYLOAD + __try/__except 捕捉)

編譯執行 → 視窗顯示是否使用新版計時器 API。

功能檢測結果

原生代碼版本檢查最佳實踐速查

場景推薦 API / 技巧
檢查最低版本 (含 SP)VerifyVersionInfo + OSVERSIONINFOEX + VER_SET_CONDITION
簡單檢查主/次版本GetVersionEx + dwMajorVersion > 5 || (==5 && dwMinorVersion >= 1)
檢測特定 DLL 函數是否存在GetModuleHandle / LoadLibrary + GetProcAddress + 函數指標
COM API 可用性CoCreateInstance / QueryInterface 回傳值判斷
延遲載入 DLL 簡化錯誤處理連結器 /DELAYLOAD + 結構化例外處理

總結

原生代碼享有相容模式「版本謊言」紅利,但根治之道仍在程式碼:用 VerifyVersionInfo 正確表達「最低需求」,用 GetProcAddress 直接檢測功能存在性。這樣應用程式才能在未來 Windows 版本上無縫運行,而非被版本號綁架。

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