VerifyVersionInfo 正確檢查最低版本需求GetProcAddress 動態判斷 API 可用性VersionCheck 方案,Debug/x86VersionCheckNativeBroken 為啟動專案_tWinMain 中 GetVersionEx 硬比對 5.1 (XP)

VersionCheckNativeBroken\Release\VersionCheckNativeBroken.exe
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 宏建立條件遮罩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 永遠載入進程,可直接 GetModuleHandleLoadLibrary 取得模組句柄GetProcAddress 回傳 NULL 表示函數不存在 → 優雅降級/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 版本上無縫運行,而非被版本號綁架。