在 Linux 上为 Windows 开发

作者:Joey Bernard

和大多数阅读《Linux Journal》的人一样,我是 Linux、GNU 和开源一切事物的狂热爱好者。我在我所有的个人机器上运行 Linux,在上面编程、玩耍,并尽可能地向他人宣传。但是,很大一部分编程工作涉及到为华盛顿州雷德蒙德的一家公司的操作系统编写应用程序。

为了我的工作,我不得不为 Microsoft Windows 平台编写一些较小的应用程序。由于执行速度是一个问题,我将不得不使用 C 语言,直接使用 Win32 API 来编写它们。我突然想到,如果我要使用像 C 这样的标准语言,我或许可以在我舒适的 Linux 家园中进行开发。

本文是关于完全在 Linux 环境中开发 Windows 应用程序的简短指南。我简要介绍了 Windows 编程,并逐步介绍了编译和测试示例程序。我还讨论了 Wine,以简化将 Windows 源代码移植到 Linux 的过程。

Win32 编程

对于我们这些在 UNIX 风格进程抽象的健康营养中长大的人来说,Windows 模型可能显得完全是异端邪说。Windows 模型是一个抢占式、多任务、多线程、消息传递操作系统。我在这里仅限于 NT 及其衍生产品,2000 和 XP。所有进程都被操作系统视为线程。这使得进程上下文比 UNIX 类操作系统中使用的传统重量级进程模型稍微轻一些。然而,作为这种一切皆线程模型的结果,一切都位于全局内存地址空间中。有了正确的权限和正确的地址,一个程序可以篡改另一个程序的位。

另一个后果是内核创建的数据结构不位于任何固定地址。这意味着用户程序有责任在使用任何全局数据结构(例如图形上下文)之前锁定关联的内存。您还必须记住在使用完这些结构后解锁它们,否则它们可能会导致内存碎片。

清单 1(可从《Linux Journal》FTP 站点获得,请参阅在线资源部分)是一个基本的 Hello World 程序。其中大部分是样板代码,只有 switch 语句中的部分才真正有趣。对于一个基本程序来说,这似乎是相当多的代码,但这就是使用低级 API 的问题。在 Linux 上进行比较的一个好例子是使用 Xt 为 X 编写代码。

Windows GUI 程序不是从 main() 函数开始,而是从 WinMain() 开始。您的程序的所有初始化都在此函数中完成。此初始化的一部分包括定义主窗口的窗口类并为其关联回调函数。接下来,创建主窗口并在桌面上显示它。然后控制权传递给消息循环,回调函数处理发送到主窗口的消息。

winprog.org(请参阅资源)提供了一个很好的 Windows 程序编写快速入门。该网站的作者提供了一个很好的 FAQ 和一个相当不错的教程,涵盖了所有基础知识。当然,Windows 编程的圣经是 Charles Petzold 撰写的巨著《Windows 编程》。如果您在这本巨著中找不到您需要的东西,您始终可以使用它来从您友好的邻居 Windows 专家那里获取信息。

交叉编译

GCC 最令人惊叹的事情之一是它已被移植到如此多不同的平台和操作系统。由此而来的一个伟大的礼物是能够在某个平台上编译二进制文件,而这些二进制文件注定要用于完全不同的平台。我经常在我的 Linux 笔记本电脑上为 Solaris 或 Windows 编译二进制文件。这是一个惊人的优势,允许在熟悉、舒适的环境中进行开发。

最纯粹的设置方法是回到源代码(请参阅资源)。这样,您可以使用精确的设置和针对您想要的确切平台编译代码。值得庆幸的是,这项工作已经完成。MinGW 项目的优秀人员维护了一个 GCC 端口,用于编译 Windows 二进制文件。这包括所有相关的文件,例如头文件。源代码和二进制 tarball 都可以在这里找到。这些程序也已打包用于基于 RPM 和基于 deb 的发行版。如果您运行的是 Debian,则可以使用 apt-get 来检索 mingw32 和 mingw32-runtime 软件包。如果您运行的是 testing 或 unstable,您还应该获取 mingw32-binutils。

GCC 中的大多数编译选项在 MinGW 中都可用,还有一些额外的选项。如果您只是编译一个程序而不使用任何额外的选项,则可以从控制台运行它。如果您想编写一个不需要 GUI 的小型、简单的程序,您就会这样做。因为这是 Windows,我们想要一个 GUI 程序,所以我们编写了上面看到的所有必需的样板代码,并添加了-mwindows选项到编译命令中。这设置了您需要的所有宏和库链接,以便编译标准的 Windows 可执行文件。如果您决定编写更复杂的 Windows 程序,该程序使用其他一些 Windows 功能,则需要显式添加您要链接的库。

在 Windows 中,您可以为您的程序定义资源。这些资源包括菜单、位图和文本字符串等项目。这些资源在单独的文件中定义,需要在链接到您的可执行文件之前单独编译。这项工作落在了 mingw-windres 程序上,该程序会创建一个对象文件,您可以随后将其链接到您的可执行文件。

要编译清单 1 中显示的简单示例程序,我们使用命令

mingw-gcc -o example1.exe example.c -mwindows

替换命令mingw-gcc与包维护者为您的包调用的编译器可执行文件。瞧,您现在拥有了一个为世界准备好的 Windows 程序。真的是这样吗?

使用 Wine 调试

对于需要为 Windows 平台编写程序的开发人员来说,Wine 是另一个巨大的福音。Wine 的开发人员所做的巨量工作是惊人的。这个出色的程序允许您从 Linux 中运行 Windows 程序。这样做的好处是,我们现在可以运行我们新编译的程序,看看它是否真的像宣传的那样工作。为此,请使用命令wine example1.exe,您应该会在桌面上看到窗口出现(图 1)。当您设置 Wine 时,您可以选择让窗口由您的窗口管理器管理、不管理或在它们自己的桌面上显示。这会影响您运行时窗口的外观。您在图 1 中看到的是一个未管理的应用程序。

Developing for Windows on Linux

图 1. 在 Wine 下以未管理方式运行的示例应用程序

如果您不够幸运,没有完美地键入您的程序,您可能需要进行一些调试以找出问题所在。Wine 在这里可以成为一个强大的资产。--debugmsg [debugchannel] 选项输出 Wine 中一个或多个调试通道的结果。可用调试通道的示例包括

  • relay:每次调用 Win32 函数时写入日志消息。

  • win:跟踪 Windows 消息。

  • all:跟踪所有消息。

除非您真的需要,否则不要使用 all。输出量很快就会让即使是最注重细节的程序员也感到不知所措。可用调试通道的完整列表可以在 Wine 站点上找到。

编译 Linux 的原生版本

我们现在有了一个很棒的、可工作的、无错误的程序,可以在 Windows 下运行。考虑到所有工作都是在 Linux 下完成的,如果我们也能让我们的程序在 Linux 下运行,那不是很好吗?Wine 项目的优秀人员再次前来救援。该项目的一部分包括 winelib,这是一个为您的 Windows 源代码提供 Linux 接口的库。为了使用此功能,您需要为您的发行版安装 wine-devel 软件包。如果您是从源代码安装的,则所需文件应该已经可用。

wine-devel 软件包中包含一个名为 winemaker 的 Perl 脚本。此脚本旨在遍历您的源文件和目录,并进行必要的更改,使其在 Linux 上针对 winelib 正确编译。它检查的项目包括文件名大小写和行尾字符。此外,它将文件路径反斜杠替换为正斜杠,并执行许多其他操作。默认情况下,它会备份它需要更改的任何源文件。它将您的项目转换为 winelib,进行各种自动更改。要编译,您只需运行

winemaker .
./configure --with-wine=/path/to/wine
make

来创建一个 Linux 可执行文件。您在上面看到的点是故意的。您传入 winemaker 可以找到它需要分析的源文件的路径;在这里,文件位于当前目录中。

在我们的例子中,我们的示例没有任何项目文件,winemaker 认为这是一个有点问题。我们可以手动完成所涉及的步骤。而不是执行mingw-gcc来编译我们的程序,我们使用winegcc使用完全相同的命令行参数。这将创建一个以 .so 结尾的文件和一个用于处理程序执行的 shell 脚本。我们现在有了我们的 Windows 源代码,它已编译并在 Linux 下运行。

结论

我希望我已经能够说服一些 Windows 开发人员,他们可以有效地在 Linux 中工作。凭借 GCC 允许编译 Windows 可执行文件,以及 Wine 在运行和调试方面提供的强大支持,在大多数情况下,没有真正的理由启动 Windows。唯一的原因是如果您最喜欢的 IDE 在 Wine 下无法正确运行,但那样您总是可以自愿修复该问题,对吗?

由于这只是一个简短的介绍,我没有介绍对 MFC 或 DLL 创建的支持。WineHQ 和 MinGW 站点更详细地讨论了这两个主题。

本文的资源: /article/7555

Joey Bernard 是加拿大 GIS 公司 CARIS 的系统架构师。他实际上从未做过任何 GIS 工作,主要只是 Oracle、UNIX 系统编程和一些 Windows 编程。

加载 Disqus 评论