【问题标题】:How to call a kernel32.dll function GetTickCount() using LoadLibrary(..) in C++如何在 C++ 中使用 LoadLibrary(..) 调用 kernel32.dll 函数 GetTickCount()
【发布时间】:2015-12-22 18:16:30
【问题描述】:

我正在寻找一个在 Windows 机器上以毫秒为单位的函数。本质上,我想调用这个 WinAPI 函数 GetTickCount(),但我被困在“使用 LoadLibrary(...) n 调用 GetTickCount() 函数”部分..

我搜索了每个论坛并用谷歌搜索了它,但到处都有人使用不完整的无法编译的代码。任何人都可以编写一个简短的示例程序来加载 kernel32.dll 并调用 GetTickCount() 以毫秒为单位显示时间吗?

请编写可编译的代码!

【问题讨论】:

  • 为什么你认为你应该打电话给LoadLibrarykernel32.dll 已经加载到每个 Windows 进程中,你永远不需要自己加载它。

标签: winapi time


【解决方案1】:

您应该做的第一件事是声明一个与导出函数兼容的函数指针类型。做到这一点至关重要,也是遇到麻烦的最大几率。仔细查看函数声明即可得出:

 typedef DWORD (WINAPI * GetTickCount_t)(void);

接下来,使用 LoadLibrary 和 GetProcessAddress 获取函数指针值。您总是必须将 GPA 的返回值转换为函数指针类型。像这样:

HMODULE hKernel = LoadLibrary(L"kernel32.dll");
assert(hKernel);
GetTickCount_t pfnGetTickCount = (GetTickCount_t)GetProcAddress(hKernel, "GetTickCount");
assert(pfnGetTickCount);

这里的故障模式是 DLL 的路径。我不必指定一个,因为 kernel32.dll 存储在 c:\windows\system32 中,该目录始终位于搜索路径中。对于您自己的 DLL,通常情况并非如此。仅将其存储在与主 EXE 相同的目录中,您可以只指定 DLL 文件名而不是完整路径。查看 SetDllDirectory() 的文档以获取背景信息。

以及导出函数的名称。在这里很容易,Windows API 函数以未修饰的名称导出。对于您自己的 DLL,通常不是这种情况,如果您没有使用 extern "C" 声明函数并使用 C++ 编译器,则可以使用前导下划线、“@nn”后缀或损坏的名称导出导出。要查看真实名称,请在 DLL 上使用 Dumpbin.exe /exports。另请注意,GetProcAddress 使用 const char*,这与使用 Unicode 字符串的 Windows API 的其余部分不同。字符串文字上没有 L 前缀。

那你叫它,这很简单:

DWORD tick = pfnGetTickCount();

如果您的函数指针类型声明错误,那么您可能会使用 AV 使程序崩溃,得到奇怪的函数结果或不平衡的堆栈。

【讨论】:

    【解决方案2】:

    您无法加载kernel32.dll,它已经加载到每个进程中。而且GetTickCount 存在于每个版本的Windows 上,所以你不需要GetProcAddress 来查看它是否存在。您只需要:

    #include <windows.h>
    #include <iostream>
    
    int main(void)
    {
        std::cout << GetTickCount() << std::endl;
    }
    

    动态加载示例(因为winmm.dll 未预加载):

    #include <windows.h>
    #include <iostream>
    
    int main(void)
    {
        HMODULE winmmDLL = LoadLibraryA("winmm.dll");
    
        if (!winmmDLL) {
            std::cerr << "LoadLibrary failed." << std::endl;
            return 1;
        }
    
        typedef DWORD (WINAPI *timeGetTime_fn)(void);
        timeGetTime_fn pfnTimeGetTime = (timeGetTime_fn)GetProcAddress(winmmDLL, "timeGetTime");
    
        if (!pfnTimeGetTime) {
            std::cerr << "GetProcAddress failed." << std::endl;
            return 2;
        }
    
        std::cout << (*pfnTimeGetTime)() << std::endl;
        return 0;
    }
    

    我已经使用 Visual Studio 2010 命令提示符成功编译并运行了这个示例,不需要特殊的编译器或链接器选项。

    【讨论】:

    • 是的,我明白了。但是你能想到如何使用 LoadLibrary..(如果我想加载另一个库)
    • @Rushil:当然可以,但是我要将您的示例更改为timeGetTime,因此讨论加载库是有意义的。
    【解决方案3】:

    您不必这样做。 Kernel32.dll 被加载到 Windows 上的每个 x86 进程中。您只需包含标头即可使其工作 - 编译器将为您加载它。

    【讨论】:

    • 它不会被加载到 x64 进程中吗?
    • @Rushil:另一个文件,也称为kernel32.dll,但在不同的目录中,被加载到 64 位进程中。
    【解决方案4】:

    [kernel32.dll]在每个进程中加载​​,因为它提供了ExitProcess函数……

    您只需添加&lt;windows.h&gt; 并致电GetTickCount

    干杯,

    【讨论】:

    • 谢谢!但是如果我必须调用另一个 dll 中的函数怎么办。你能告诉我如何调用 LoadLibrary 来完成这项工作吗?
    猜你喜欢
    • 1970-01-01
    • 2015-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多