为何GetProcAddress无法定位QueryInterruptTime函数?
你检查了12个相关时间函数,GetProcAddress能找到其余所有函数,唯独找不到QueryInterruptTime,函数列表如下:
GetSystemTime GetSystemTimeAdjustment NtQuerySystemTime <<该列表中唯一不在kernel32.dll的函数 SetSystemTime GetLocalTime SetLocalTime GetSystemTimeAsFileTime GetSystemTimes GetTickCount GetTickCount64 QueryPerformanceCounter QueryInterruptTime
测试代码:
#define _WIN32_WINNT 0x0601 // this line makes no difference #include <windows.h> #include <string> #include <iostream> #include <realtimeapiset.h> int main() { HMODULE hModule = GetModuleHandle("kernel32.dll"); // NtQuerySystemTime needs ntdll.dll instead FARPROC pFunc = GetProcAddress(hModule, "QueryInterruptTime"); DWORD errorMessageID = GetLastError(); LPSTR messageBuffer = nullptr; size_t size = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); std::string message(messageBuffer, size); message = std::string("getLastError ") + std::to_string(errorMessageID) + ": " + message; // Free the buffer. LocalFree(messageBuffer); std::cout << message << std::endl; ULONGLONG interruptTime; QueryInterruptTime(&interruptTime); std::cout << interruptTime << std::endl; }
编译命令:
g++ repro.cpp -lmincore a.exe
输出内容:
getLastError 127: The specified procedure could not be found.
161477110141
替换为其他任意函数时,输出变为:
getLastError 0: The operation completed successfully.
(...)
更新:将GetModuleHandle("kernel32.dll")替换为GetModuleHandle("kernelbase.dll")后问题解决,疑问:微软官方文档中说明QueryInterruptTime位于kernel32.dll的内容是否过时或有误?
原因分析
函数实际导出位置
QueryInterruptTime实际从kernelbase.dll导出,而非kernel32.dll。现代Windows系统中,微软将大量核心API的实现从kernel32迁移到kernelbase,kernel32更多作为API转发层存在,但并非所有函数都在kernel32中保留导出项,QueryInterruptTime就是其中之一。直接调用成功的原因
你编译时链接了mincore.lib,该库包含QueryInterruptTime的正确导入信息,会自动指向kernelbase.dll中的实现,因此直接调用函数时能正常执行;但手动用GetProcAddress指定kernel32.dll查找时,自然找不到该函数的导出。文档表述的简化
微软官方文档会简化表述,将kernelbase.dll中的API归到kernel32的文档下——因为kernel32是开发者传统认知中的标准系统库入口,文档不会刻意强调每个函数的实际导出模块。这种表述并非错误,而是出于易用性的简化,通过标准头文件和库链接时无需关心底层模块差异,但手动用GetProcAddress查找时必须指定正确模块。
内容的提问来源于stack exchange,提问作者hanshenrik

