如何使用cl编译Windows API程序?

10

我正在尝试使用Windows SDK命令提示符编译一个简单的C Windows API程序。

这是程序的摘录:

#include <Windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{

[...]

    RegisterClass(&wc);
    hwnd = CreateWindow("test", NULL, 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);

[...]

当我使用以下方式编译它时:

cl test.c
在Windows SDK命令提示符中,出现了许多类似以下的链接器错误:
test.obj : error LNK2019: unresolved external symbol __imp_CreateWindowExA referenced in function WinMain
test.obj : error LNK2019: unresolved external symbol __imp_RegisterClassA referenced in function WinMain
2个回答

26

至少有两个问题。

  1. 链接器告诉你存在“未解析的外部符号”。这意味着它找不到你尝试调用的函数的定义。在这种情况下,有两个这样未定义的函数:CreateWindowExARegisterClassA

    显然,这些函数的定义不在你的代码中,而是在Windows API库中,因此你需要告诉链接器可以在哪里找到这些定义。

    SDK提供了存根(*.lib)文件,其中包含链接器使用的信息,以便它可以在运行时在Windows DLL中找到正确的函数定义。你需要指示链接器可以在哪里找到这些*.lib文件。

    有几种不同的策略可以实现:

    1. 简单(尽管不可移植)的方法是在源文件中插入一个#pragma语句,指示编译器留下链接器可以识别的注释。例如:

      #pragma comment(lib, "user32")
      

      自动链接到user32.lib,它是user32.dll的存根文件。

    2. 或者,您可以通过命令行将参数传递给cl.exe。但是,如果您没有使用MSBuild或某种makefile,这很快就会变得非常复杂。在这种情况下,您需要修改命令行(至少):

      cl test.c user32.lib
      
      这两个选项都假设您将Windows SDK目录添加到了路径中。我相信安装程序会自动为您完成这一步骤,但我不能确定。如果未完成或者已从路径中删除这些文件,则需要在命令行中使用完整限定路径来引用 *.lib 文件。
      阅读可能的编译器选项文档是一个很好的开始。或者更好的选择是,如果您不熟悉Windows编程,可以使用像Visual Studio这样的开发环境,它会自动为您组织好所有东西。一旦您理解了发生了什么,就可以查看Visual Studio运行的命令行,并逐个分析其中的内容。

    3. 下一个问题是您在编译时没有定义Unicode,并且因为ANSI是默认值,Windows头文件中的所有宏都被解析为调用SDK函数的带有A后缀的版本。这可能不是您想要的结果。自十年前起,Windows已经完全支持Unicode,所有新应用程序都应该构建为Unicode。 Unicode版本在名称后面附加了W后缀。

      同样,您可以通过向源文件添加行或向命令行添加参数来显式地指示编译器使用Unicode。
      在这种情况下,最简单的方法可能只是添加:
       #define UNICODE
      

      #include <windows.h>之前将其添加到源文件的顶部。就像我们在上面看到的一样,从Visual Studio环境中,除非您明确更改项目设置以针对其他内容,否则UNICODE会自动为您定义。


1
当我使用#define UNICODE时,是否需要更改WinMain?我读到了一些关于这方面的内容,但如果我只是使用WinMain,编译器不会抱怨。 - AndreKR
1
@AndreKR:是的,正确的定义是在 <tchar.h> 中定义的 _tWinMainWinMain 是 ANSI 函数的名称;wWinMain 是 Unicode 函数的名称。只要将 lpCmdLine 参数声明为 LPTSTR 类型(字符串是从 ANSI 到 Unicode 改变的唯一内容,并且通过使用此宏,编译时会自动确定正确的字符串类型),它们就是相同的,这就是为什么编译器不会抱怨的原因。很抱歉这很令人困惑;在低级别上编程 Windows 意味着你必须处理多年的向后兼容性。 - Cody Gray

18

这些函数位于user32.lib中,您需要将其提供给cl工具。

cl test.c ""C:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib\user32.lib"
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.307
Copyright (C) Microsoft Corporation.  All rights reserved.

test.c
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:test.exe
test.obj
"C:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib\user32.lib"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接