Sep 23

汇编语言是一种最贴近机器硬件、运行效率很高的低级编译型语言。由于它与硬件紧密相关,因此它的开发完全受平台限制,所开发的程序代码基本上不具有移植性。当然,只要硬件(主要是中央处理器)相近,那就有可能在不同平台上采用同一套汇编语言编程工具进行程序开发。MSYS2 是 GCC 工具链在 MS Windows 平台上极为成功的移植!利用 MSYS2 提供的编程工具,就可以通过 C/C++/Python 等编程语言在 MS Windows 平台上进行原生程序开发。这既可以借鉴 GNU Linux 平台上优秀开源软件的成功开发经验,还可以将这些优秀的软件移植到 MS Windows 平台。不过,这里要介绍的是如何利用 MSYS2 中基于 MinGW x64 工具链提供的 GNU as 与 ld 工具进行 64 位 GNU 汇编(也即采用 AT&T 语法)程序的开发。

先来看一个示例,它主要通过指令 call 调用了标准 C 函数(比如 printf、exit 等):

    /* test.s */
    .section .rodata
msg: .asciz "Hello, MSVC Library!"
    .section .text
    .global _start
_start:
    # align RSP
    push %rbp
    mov %rsp, %rbp
    sub $0x20, %rsp

    lea msg(%rip), %rcx
    call printf

    xor %eax, %eax
    call exit

上述汇编源代码在 MSYS2 环境下的编译以及运行过程是这样的:

$ as -o test.o test.s
$ ld -o test.exe test.o -lmsvcrt
$ ./test.exe

为了正确编写 GNU 汇编源代码,除了熟悉 AT&T 汇编语法与 Amd64 指令集之外,特别需要了解 MS Windows x64 的调用协定(FASTCALL 的一种变体)、MS Windows C/C++ 运行库,请参看 https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170、https://learn.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-170。有一点要特别强调一下:微软的 C 运行时库中还存在着一些以单下划线开头并跟有标准 C 函数名的接口(比如 _printf、_exit 等),这些接口是微软实现的但并不是标准 C 函数,不同版本的 MS Windows 中的 C 运行时库(例如 msvcrt 等)可能提供了标准 C 接口,也可能提供了微软的扩展接口,或者两者兼有,或者只有其中之一。

再来看如何调用 MS Windows 的应用程序编程接口?

    /* test.s */
    .section .rodata
msg:
    .asciz "Hello, Win32 ASM World"
    len = .-msg
    .section .text
    .global _start
_start:
    # align RSP
    push %rbp
    mov %rsp, %rbp
    sub $0x20, %rsp

    # https://learn.microsoft.com/en-us/windows/console/getstdhandle
    # get handle for standard I/O
    # GetStdHandle(STD_OUTPUT_HANDLE)
    mov $-11, %rcx
    call GetStdHandle

    # https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile
    # WriteFile(hstdOut, msg, len, &bytes, 0)
    mov %rax, %rcx
    lea msg(%rip), %rdx
    mov $len, %r8
    lea -0x20(%rbp), %r9
    push $0
    call WriteFile

    # https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-exitprocess
    # exit with zero
    # ExitProcess(0)
    xor %ecx, %ecx
    call ExitProcess

上述源代码在 MSYS2 环境下的编译以及运行过程如下:

$ as -o test.o test.s
$ ld test.o -lkernel32 -o test.exe
$ ./test.exe

上面的汇编代码中用到了 Windows 应用程序编程接口中最重要的动态链接库:Kernel32.dll (包含那些用于管理内存、进程和线程的函数,例如 GetStdHandle、WriteFile、ExitProcess 等函数)。另一个值得一提的是:User32.dll(包含那些用于执行用户界面任务的函数,例如 MessageBoxA 等 函数)。如果想生成对话框程序,可试试下述汇编代码

    /* test.s */
    .section .rodata
mba_msg:
    .asciz "Hello ASM world!"
mba_title:
    .asciz "Simple Message Box"
    .section .text
    .globl _start
_start:
    # align RSP
    push %rbp
    mov %rsp, %rbp
    sub $0x20, %rsp 

    # https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messageboxa
    # display a message box
    # MessageBoxA(NULL, mba_msg, mba_title, MB_ICONINFORMATION)
    xor %ecx, %ecx
    lea mba_msg(%rip), %rdx
    lea mba_title(%rip), %r8
    mov $0x40, %r9d
    call MessageBoxA
 
    # ExitProcess(0)
    xor %ecx, %ecx
    call ExitProcess

它的编译与执行过程如下:

$ as -o test.o test.s
$ ld test.o -lkernel32 -luser32 -o test.exe
$ ./test.exe

MS Windows 的应用程序编程接口被分为两种:一种是遵循 Win32 时代的应用程序编程接口,这部分被称为本地应用程序编程(Native API);另一种则是以 .NET Framework 为基础开发的,称为 Managed API。本地应用程序编程接口(https://learn.microsoft.com/en-us/windows/win32/api/) 以二进制方式发布、供 C/C++ 程序直接调用。这些本地应用程序开发接口的名称大多以 Nt/Zw 开头,而所有系统调用都以字母 Nt 开头。换而言之,MS Windows 的系统调用是本地应用程序编程(Native API)的子集。来看一下 Linux 与 MS Windows 的应用程序编程接口对照表(https://www.cnblogs.com/UnGeek/p/2981439.html):

如果 GNU 汇编代码中调用了 Nt/Zw 开头的 MS Windows 应用程序编程接口,那么只需在使用 GNU 链接器时增加 -lntdll 选项。

实际上,GCC 工具链为 GNU 汇编语言提供了第三代系统调用指令 syscall,若想通过它调用 MS Windows 的系统调用,那就需要提前知道相应的系统调用号。然而 MS Windows 并没有提供任何相关细节,好在已有开发者对动态库 NTDLL.dll 进行了逆向工程,得到了有关每个 Windows 内核的部分系统调用号的一些非官方信息:

  • https://github.com/ikermit/11Syscalls
  • https://j00ru.vexillium.org/syscalls/nt/64/
  • http://j00ru.vexillium.org/syscalls/win32k/64/
  • https://github.com/j00ru/windows-syscalls

请注意,即使是在 MS Windows 10 的不同版本之间,MS Windows 的同一个系统调用的调用号也会发生变化。

/* NtCreateThreadEx on Windows 10 (1511) */
mov %rcx, %r10
mov $0xB4, %rax
syscall

有关 Win32 汇编的基础知识,可参考 https://www.cnblogs.com/LyShark/p/11136319.html。还有一点值得提到的是,通过 MinGW64 制作 MS Windows 上动态库(DLL)的方法,它与 Linux 稍有不同,请参考 https://nullprogram.com/blog/2021/05/31/。

Oct 7

MSYS2 简介

简单的说,MSYS2 是 MSYS 的一个升级版, 提供了 GCC 工具链。与 MSYS 最大的区别是移植了 Arch Linux 的软件包管理系统 Pacman。有了 Pacman 升级系统,安装新软件包,还有解决软件包间的依赖问题就变得简单多了。从 http://msys2.github.io/ 下载安装包并安装,安装包的文件名通常为 msys2-<架构>-<日期>.exe ,根据 CPU 架构选择最新的安装包。

MSYS2 设计目标是在 MS Windows 上运行 MinGW 工具链的,它有两套 MinGW 工具链,一个是 mingw32,一个是 mingw64。它们的功能是一样的,都是用于编译 MS Windows 上的代码(能调用 MS Windows 应用程序接口,不能调用 Unix/Linux 系统调用)的,可以生成“纯净”的 win32 和 win64 可执行文件。这些可执行文件运行时只依赖 MS Windows 系统的动态库或静态库。另外,MSYS2 自身还带了一个原生 GCC 工具链,它可用于编译 UNIX/Linux 代码,但支持的函数仅限于 POSIX 部分,其他系统调用都不支持,如 Linux 的 epoll、eventfd 等等,所以功能有限。原生 GCC 工具链可编译生成 MS Windows 平台上的可执行文件,但它们的运行必须依赖 msys2xxx.dll。开发专业应用软件,不要使用 MSYS2 的原生 GCC 工具链,它只是为了编译 MinGW 工具链而存在的,是制造工具的工具。

可以看到 MSYS2 有三个执行脚本,分别是 msys2_shell.bat、mingw32_shell.bat 和 mingw64_shell.bat,查看相应的文件内容可以看到其中的区别。实际上,三个 .bat 的区别就是对环境变量 PATH 的不同设置,mingw32_shell.bat 优先使用 msys64/mingw32 下的工具,mingw64_shell.bat 优先使用 msys64/mingw64 下的工具,而 msys2_shell.bat 两个都不使用,只用自身 msys 的工具(依赖 msys2xxx.dll)。这么做的好处是当需要编译 32 位的项目时使用 mingw32_shell.bat,64 位的项目使用 mingw64_shell.bat,各套工具互不干扰。

配置软件源

需要修改安装目录下,/etc/pacman.d 文件夹中的 mirrorlist.msys、mirrorlist.mingw64、mirrorlist.mingw32 三个文件,分别在文件开始处添加

> notepad /etc/pacman.d/mirrorlist.msys:
##中国科学技术大学开源软件镜像
Server = http://mirrors.ustc.edu.cn/msys2/MSYS2/$arch
##北京理工大学镜像
Server = http://mirror.bit.edu.cn/msys2/REPOS/MSYS2/$arch
##清华大学开源镜像
Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/msys/$arch

> notepad /etc/pacman.d/mirrorlist.mingw64:
##中国科学技术大学开源软件镜像
Server = http://mirrors.ustc.edu.cn/msys2/MINGW/x86_64
##北京理工大学镜像
Server = http://mirror.bit.edu.cn/msys2/REPOS/MINGW/x86_64
##清华大学开源镜像
Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/x86_64

> notepad /etc/pacman.d/mirrorlist.mingw32:
##中国科学技术大学开源软件镜像
Server = http://mirrors.ustc.edu.cn/msys2/MINGW/i686
##北京理工大学镜像
Server = http://mirror.bit.edu.cn/msys2/REPOS/MINGW/i686
##清华大学开源镜像
Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/i686

提高数据库访问速度

Pacman 将所有软件包的信息放在一一对应的许多小文件中。通过改善数据库访问速度,可以减少花在数据库相关任务上的时间,比如:寻找软件包、检索软件包依赖性。最安全最简单的方法是以root身份运行

$ pacman-optimize

上述命令试图将所有小文件放在磁盘上同一个物理区域,以减少磁头移动。这种方法很安全,但不一定有效。其效果取决于你的文件系统、磁盘使用率、和磁盘碎片程度。另一种更激进的方式是在优化数据库之前首先删除 cache 中所有未安装以及不使用的仓库中的包:

$ pacman -Sc && pacman-optimize

加快下载速度

如果你的包下载速度变得极慢,首先确定你用的是那些(镜像)网站而不是 MSYS2 的官方链接。其次,可以通过各种下载工具而不是 Pacman 内置的下载方式,来改善 Pacman 的下载速度。不论怎样,在做任何修改前,你必须确定拥有了最新版的Pacman:

$ pacman -Syu

对于需要更强大代理支持的用户来说,用 wget 比用 Pacman 自己的下载方式更加方便。要使用 wget,首先使用

$ pacman -S wget

安装它,然后修改 /etc/pacman.conf 并在其中的 [options] 区段将下面内容去掉注释:

$ nano -w /etc/pacman.conf
XferCommand = /usr/bin/wget -c --passive-ftp -c %u

除了将 wget 参数放在 /etc/pacman.conf 里,你也可以直接修改 wget 配置文件(全局文件是/etc/wgetrc,各个用户的文件是 $HOME/.wgetrc)。 如果对于支持断点续传的下载工具还不满足需求,那么可以使用支持多线程的断点下载工具。例如 aria2 是一个具有断点续传和分块下载功能的轻量级下载软件,支持 HTTP/HTTPS/FTP 协议。aria2 可以以多线程的方式通过 HTTP/HTTPS和FTP 协议连接镜像服务器,显著提高下载速度。先使用下述方式

$ pacman -S aria2

安装 aria2。接着修改 /etc/pacman.conf,在 [option] 段添加下列一行(如果已存在则修改之):

$ notepad /etc/pacman.conf
XferCommand = /usr/bin/aria2c --allow-overwrite=true -c --file-allocation=none --log-level=error -m2 --max-connection-per-server=2 --max-file-not-found=5 --min-split-size=5M --no-conf --remote-time=true --summary-interval=60 -t5 -d / -o %o %u

开启 pacman 的多线程下载之旅吧!

安装必要的工具链和必要的开发库

下面假设使用的是 64 位系统。如果使用的是 32 位系统,仅需后面出现的 x86_64 改为 i686。在 MS Windows 开始菜单中运行 mingw64_shell.bat 以打开命令行窗口。更新本地软件包数据库也即与软件仓库同步:

$ pacman -S --refresh

$ pacman -Sy

然后升级软件包也即更新系统:

$ pacman -S --sysupgrade

$ pacman -Su

通常,将上述命令合二为一

$ pacman -Syu

现在开始安装工具链

$ pacman -S mingw-w64-x86_64-toolchain

如果提示冲突,则使用

$ pacman -S --force mingw-w64-x86_64-toolchain

开发的各种辅助工具

$ pacman -S base-devel

安装 GTK+-3 的库

$ pacman -S --force mingw-w64-x86_64-gtk3

安装 GTK+ 快速构建工具 Glade 包

$ pacman -S --force mingw-w64-x86_64-glade3

安装 wxWidgets 的库

$ pacman -S --force mingw-w64-x86_64-wxWidgets

安装 OpenGL 库(虽然微软操作系统自带了 OpenGL 库,为了与 MSYS2 相容,选用 GLEW、FreeGLUT 库)

$ pacman -S --force mingw-w64-x86_64-glew mingw-w64-x86_64-freeglut

pacman 的常用用法

查看 pacman 工具帮助

$ pacman -h
$ pacman -S -h

列出本地 pacman 软件包数据库中的软件包清单

$ Pacman -Sl

列出所有已安装的软件包

$ pacman -Q --explicit

或者

$ pacman -Q -e

或直接

$ pacman -Q

安装新的软件包或软件组

$ pacman -S 

比如我要安装 gcc,那么执行:

$ pacman -S gcc

很多时候,我们不知道要安装软件的准确名称,这时就要先查询软件包的名称。

$ pacman -Ss 

比如我想安装 gcc 相关的软件,那么可以这样搜索

$ pacman -Ss gcc

可以列出所有的软件组

$ pacman -Q --groups

或者

$ pacman -Q -g

查看软件组

$ pacman -S -g

查看软件组包含的软件

$ pacman -Q -g 

例如

$ pacman -Q -g base-devel

查询软件包的内容

$ pacman -Q -l 

例如查询软件包 gtk+ 的内容

$ pacman -Q -l gtk+

查询软件所在的软件包或软件组

$ pacman -Q -s 

例如

$ pacman -Q -s nettle

强制安装某个包(不管是否已安装)

$ pacman -Sf nettle

更新系统但不升级某个包

$ pacman -Su --ignore mysql

删除一个软件包

$ pacman -R 

例如删掉某个包但忽略无用依赖的删除

$ pacman -R glade3

删除指定软件包,及其所有没有被其他已安装软件包使用的依赖关系:

$ pacman -Rs mysql

清理当前未被安装软件包的缓存(/var/cache/pacman/pkg):

$ pacman -Sc

清理所有下载的包与数据库

$ pacman -Scc

下载包而不安装它

$ pacman -Sw mysql
Feb 19

GTK+ 是一套跨平台的图形用户界面(Graphical User Interface)开发工具包,按 LGPL 许可协议发布的。此地介绍一下 GTK+ 开发工具包在 MS Windows 上的安装配置。

〇、预备工作

由于 GTK+ 是由 C 语言开发,因此要开发 GTK+ 应用程序,首先需要配置好 C 编译环境。在 MS Windows 平台,C 编译器的选择很多,主流的有 MSVC、GCC 的移植版本 MinGW 与 Cygwin。由于 GTK+ 是 GNU 项目,因此,这里推荐使用 GNU 的编译器,当然并不是说不能使用其他 C 编译器,这完全取决于个人喜好。MinGW 的下载与安装参考其官方网站 http://www.mingw.org 的说明。由于 MinGW 最初是 32 位,近年来,TDM-GCC 推出了 MinGW 的 64 位版本,详见其官方主页 http://tdm-gcc.tdragon.net/。至于 Cygwin,请到它的官方网站 http://www.cygwin.com/ 了解详情。在 Cygwin 与 MinGW 两者之间,一般推荐 MinGW 作为 C/C++ 编译工具链,除非需要在 MS Windows 平台上开发 Unix/Linux 应用程序。当然,在 MS Windows 平台上开发大型应用程序,少不得使用集成开发环境。集成开发环境也有很多选择,例如 MSVS、Code::Blocks、QtCreator 等。下面的介绍中使用的 Code::Blocks,请到其官网 http://codeblocks.org 下载最新版本(留意 Code::Blocks 的安装包是否已带有 MinGW)。

一、下载与配置 GTK+ 开发包

在 MS Windows 中安装 GTK+ 有多种选择,可利用 Cygwin 或 MinGW 工具自行由源码编译 GTK+ 工具包,不过要解决 GTK+ 依赖,工程相对浩大。目前,GTK+ 官方推荐利用 MSYS2(官网地址为 http://msys2.github.io)提供的包管理器 pacman 来自动完成 GTK+ 的编译与安装。这些 GTK+ 安装方法的共同特点是,安装过程都会顺带安装预备工作中提到的编译器。为了与预备工作中相衔接,这里直接利用预编译的 GTK+ 集成开发包来完成它们安装。

虽然预编译的集成开发包有些陈旧,集成开发包有些陈旧,但好处也是明显地,它包含了所有开发与运行 GTK+ 应用程序的头文件与库文件。需要 GTK+ 2 的用户,可到 http://ftp.gnome.org/pub/GNOME/binaries/ 下载 32 位的集成开发包 gtk+-bundle_2.24.10-20120208_win32.zip 或 64 位的集成开发包 gtk+-bundle_2.22.1-20101229_win64.zip;需要 GTK+ 3 的用户,可到 http://win32builder.gnome.org/ 下载 32 位的集成开发包 gtk+-bundle_3.10.4-20131202_win32.zip 或 64 位的集成开发包 gtk+-bundle_3.10.4-20131202_win64.zip。它们的安装非常简单,只需要将这些压缩包解压缩即可,一般建议将它们安装磁盘的根目录(如 C:\GTK+-2.12C:\GTK+-3.10),但不推荐安装到 MS Windows 的应用程序文件夹。为了能够使用 GTK+ 提供的 pkg-config 来配置 GCC 的编译、链接选项,还需配置环境变量 PATH。只需在 MS Windows 的文件管理器中鼠标右击 "My Computer" -> "Properties" -> "Advanced system settings" -> "Environment variables",接着双击 "System variables" 页面中的 PATH 行,将 GTK+ 目录中的 bin 路径(如 ;C:\GTK+-2.12\bin;C:\GTK+-3.10\bin)添加到原 PATH 变量的末尾。命令行操作也可完成 PATH 的设置,也即打开命令行窗口(CMD.exe),然后执行

> setx PATH "%PATH%;C:\GTK+-2.12\bin"

或者

> setx PATH "%PATH%;C:\GTK+-3.10\bin"

设置好环境变量 PATH 之后,可选择性的做下述设置

> pango-querymodules > C:\GTK+-2.12\etc\pango\pango.modules
> gdk-pixbuf-query-loaders > C:\GTK+-2.12\lib\gdk-pixbuf-2.0\2.10.0\loaders.cache
> gtk-query-immodules-2.0 > C:\GTK+-2.12\lib\gtk-2.0\2.12.11\immodules.cache

二、开发环境的测试与 Code::Blocks 的整合

在正式开始 GTK+ 开发工作之前,要确保 GTK+ 环境配置的正确性,为此还需做一些简单的测试。前述 GTK+ 集成开发包带有 GTK+ 的开发演示文档,可到相应目录查找。此地,以命令行方式演示一个由 GTK+ 2 开发简单应用的例子:

> notepad.exe hellogtk.c

#include <gtk/gtk.h>

int main(int argc, char *argv[])
{
    GtkWidget *window;
    gtk_init(&argc, &argv);
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    g_signal_connect(G_OBJECT(window), "destroy",
    G_CALLBACK(gtk_main_quit), NULL);

    gtk_widget_show(window);
    gtk_main();
    return 0;
}

现在在 MinGW Shell 中执行

> gcc hellogtk.c -o hellogtk.exe `pkg-config --cflags --libs gtk+-2.0`

一切正常的话,会在 hellogtk.c 所在目录生成一个可执行文件 hellogtk.exe,鼠标双击运行它。

要开发一个大型的 GTK+ 程序,除了使用 Makefile 管理源代码文件之外,使用集成开发环境是方便的。下面以 Code::Blocks 作为集成开发环境演示如何进行 GTK+ 的开发工作,而这仅需搞清楚如何在 Code::Blocks 中配置 GTK+ 的库文件与头文件路径,当然有很多方法达到这个目的,这里只给出针对 GTK+ 工程相关的配置方法。为此,先按下述操作新建一个 GTK+ 工程:启动 Code::Blocks,点击 "File" -> "New" -> "GTK+ Project" 创建一个 GTK+ 2 项目。下面点击该工程对应的菜单 "Project" -> "build options" 子标签。下面从简到烦介绍三种在 GTK+ 工程属性中配置 GTK+ 开发环境的方法:

1、利用 pkg-config 直接设置编译选项与链接选项

由于 pkg-config 已在 PATH 中,故而 GTK+ 的编译选项与链接选项的设置非常简捷,只需在 "Project build options "-> "Compiler settings" -> "Other Compiler options" 中增加编译选项设置 pkg-config --cflags gtk+-2.0;再在 "Project build options" -> "Linker settings" -> "Other linker options" 增加链接选项为 pkg-config --libs gtk+-2.0

2、利用 pkg-config 的输出设置编译选项与链接选项

2.1、设置编译选项。先在命令行窗口(CMD.exe) 中执行

> pkg-config --cflags gtk+-2.0
-mms-bitfields -IC:/GTK+-2.12/include/gtk-2.0 -IC:/GTK+-2.12/lib/gtk-2.0/include -IC:/GTK+-2.12/include/atk-1.0 -IC:/GTK+-2.12/include/cairo -IC:/GTK+-2.12/include/pango-1.0 -IC:/GTK+-2.12/include/glib-2.0 -IC:/GTK+-2.12/lib/glib-2.0/include -IC:/GTK+-2.12/include/libpng12

然后开启 Code::Blocks,将 -mms-bitfields -IC:/GTK+-2.12/include/gtk-2.0 -IC:/GTK+-2.12/lib/gtk-2.0/include -IC:/GTK+-2.12/include/atk-1.0 -IC:/GTK+-2.12/include/cairo -IC:/GTK+-2.12/include/pango-1.0 -IC:/GTK+-2.12/include/glib-2.0 -IC:/GTK+-2.12/lib/glib-2.0/include -IC:/GTK+-2.12/include/libpng12 拷贝到 "Project build options" -> "Compiler settings" -> "Other Compiler options";

2.2、设置链接选项。在命令行窗口(CMD.exe) 中执行

> pkg-config --libs gtk+-2.0
-LC:/GTK+-2.12/lib -lgtk-win32-2.0 -lgdk-win32-2.0 -latk-1.0 -lgio-2.0 -lgdk_pixbuf-2.0 -lpangowin32-1.0 -lgdi32 -lpangocairo-1.0 -lpango-1.0 -lcairo -lgobject-2.0 -lgmodule-2.0 -lglib-2.0 -lintl

然后将 -LC:/GTK+-2.12/lib -lgtk-win32-2.0 -lgdk-win32-2.0 -latk-1.0 -lgio-2.0 -lgdk_pixbuf-2.0 -lpangowin32-1.0 -lgdi32 -lpangocairo-1.0 -lpango-1.0 -lcairo -lgobject-2.0 -lgmodule-2.0 -lglib-2.0 -lintl 拷贝到 "Project build options" -> "Linker settings" -> "Other linker options"。

3、将搜索路径、编译选项、链接选项分开设置

3.1、设置编译选项。在 "Project build options" -> "Compiler settings" -> "Other Compiler options" 加入编译选项 -mms-bitfields

3.2、设置链接选项与库文件。在 "Project build options" -> "Linker settings" -> "Link libraries" 中将 GTK+ 2 库的所有库文件(动态库或者静态库)加入,也即点击 ADD 按钮后弹出的文件选择对话框中将 atk-1.0cairogdi32gdk-win32-2.0gdk_pixbuf-2.0gio-2.0glib-2.0gmodule-2.0gobject-2.0gtk-win32-2.0intlpango-1.0pangowin32-1.0pangocairo-1.0 逐一加入;在 "Project build options" -> "Linker settings" -> "Other linker options" 中加入链接选项 -mwindows

3.3、设置搜索目录。在 "Project build options" -> "Search directories" -> "Compiler" 子标签中加入 GTK+ 2 的头文件路径:C:\GTK+-2.12\include\atk-1.0C:\GTK+-2.12\include\cairoC:\GTK+-2.12\include\glib-2.0C:\GTK+-2.12\include\gtk-2.0C:\GTK+-2.12\include\libpng12C:\GTK+-2.12\include\pango-1.0C:\GTK+-2.12\lib\glib-2.0\includeC:\GTK+-2.12\lib\gtk-2.0\include。一般来说就这些,如果安装了新的库再加。若上一步的 "Link libraries" 已带有绝对路径,就不必设库文件的搜索路径了;如若不然,还需在 "Project build options" -> "Search directories" -> "Linker" 子标签中加入库文件路径,也即 C:\GTK+-2.12\lib

介绍第三种比较麻烦的配置方法的目的是让那些使用 MSVC 作为编译工具的用户看清楚如何配置 GTK+ 开发环境。

三、GTK+ 程序界面的快速构建工具

Glade 用来为 GTK+ 和 GNOME 程序快速地设计图形用户界面,并自动生成图形界面的 C 源代码。历史上,Glade 有三个版本,不过目前主要的是 Glade2 与 Glade3,推荐使用 Glade3。Glade2 和 Glade3 的主要区别是: Glade2 会自动生成 makefile 等文件,而 Glade3 只是用来生成界面,然后采用 Libglade 和 GtkBuilder 格式调用 .glade 文件,这样的好处是代码和界面完全分开,避免代码的改变又需要重新编译。与 GTK+ 类似,在 MS Windows 中安装 Glade 的方法很多种,如前,此地直接利用预编译的 Glade 工具包。安装方法很简单,到 http://ftp.gnome.org/pub/GNOME/binaries/ 下载安装包 glade3_3.8.1-1_win32.zip 即可,由于 Glade 是来生成布局文件的,因此无须在意是否是 32 位还是 64 位。当然,为了避免在运行时出现依赖错误,也可安装带有完整 GTK+ 库的版本,例如 glade3-3.6.7-with-GTK+.exe。若使用 Libglade 格式的布局文件,还需调用 libglade 库,为此安装 libglade 库,也即到 http://ftp.gnome.org/pub/GNOME/binaries/下载 32 位安装包 libglade-dev_2.6.4-1_win32.zip 或 64 位安装包 libglade-dev_2.6.3-1_win64.zip 即可;并假设 Glade 的安装目录均在 C:\Glade,据此设置环境变量 PKG_CONFIG_PATH,也即在命令行窗口(CMD.exe) 中执行

> setx  PKG_CONFIG_PATH "%PKG_CONFIG_PATH%;C:\Glade\lib\pkgconfig"

接着试试下述命令行

> pkg-config --modversion libglade-2.0

看是否有版本信息输出?若找不到该命令,就需要重启电脑;若有,说明系统变量设置正确。

四、GTK+ 的 C++ 开发包的安装与配置

GTKmm 便是 GTK+ 的 C++ 开发包。与 GTK+ 类似,在 MS Windows 中安装 GTKmm 有多种选择,如前,这里直接利用预编译的 GTKmm 开发包包来完成安装,不过需要注意,它是以 GTK+ 包的安装为前提,要预先配置好 GTK+ 才行。从 http://ftp.gnome.org/pub/GNOME/binaries/ 下载 32 位的 GTKmm 开发包 gtkmm-win32-devel-2.22.0-1.exe 或者 64 位的 GTKmm 开发包 gtkmm-win64-devel-2.22.0-1.exe,然后点击它们安装即可,假设安装目录是 C:\GTKmm-2.22。在使用 GTKmm 之前,还需设置系统变量,打开命令行窗口(CMD.exe) ,执行:

> setx PKG_CONFIG_PATH "%PKG_CONFIG_PATH%;C:\GTKmm-2.22\lib\pkgconfig"

在打开命令行窗口(CMD.exe)中再试试

> pkg-config --modversion gtkmm-2.4

若一切正常,可以简单测试一下 GTKmm 是否正常工作?在 MinGW Shell 中,执行下述命令行

> notepad.exe hellogtkmm.cpp

/*Gtkmm程序的基本头文件,下面用到的两个类就由它定义*/
#include <gtkmm.h>

int main (int argc, char *argv[])
{
   /* 
   每一个Gtkmm应用程序都必须有这样一个类的实例,并且必需是第一个生成的Gtk对象。
   它对环境进行必要的初始化,需要用 argc 和 argv 两个参数对它进行实例化。
   */
   Gtk::Main app(argc, argv);
   
   /* 定义一个GTK的窗口类,默认地它以可执行程序名作为窗口标题。*/
   Gtk::Window main_win;
   
   /* 显示窗口,并进入监听事件的循环状态,当窗口关闭时返回。*/
   app.run(main_win);
   
   return 0;
}

> g++ hellogtkmm.cpp -o hellogtkmm `pkg-config --cflags --libs gtkmm-2.4`
> hellogtkmm.exe

附录

MS Visual C/C++(MSVC) 是由 Mirosoft 公司在 MS Windows 操作系统开发的 C/C++ 编译器,目前对应的集成开发环境是 MS Visual Studio(MSVS)。因此,开发 GTK+,使用 MSVS 也是一个不错的选择,这里介绍一下如何利用 MSVS(或 MSVC)进行开发工作。

先来说说在命令行环境中测试 GTK+ 开发环境的方法。通常,安装完 MSVS 之后,并不会将 MSVC 的命令行路径加入到系统环境变量 PATH 之中,但 MSVS 带有配置好环境变量 PATH 的 MSVS 命令行提示窗口。因此,要使用 MSVC 命令行,可在启动 MSVS 之后调用 MSVS 命令行提示窗口,也即启动 MSVS -> "Open the Visual Studio Command Prompt",在打开的命令行窗口中试试 pkg-config 的输出 :

> pkg-config --cflags gtk+-2.0

若一切正常的话,将这些选项保存下来,并删去其中 -mms-bitfields,并在末尾增加 -Dinline= /link /libpath:C:\GTK+-2.12\lib gtk-win32-3.0.lib gobject-2.0.lib。例如编译前述 hellogtk.c,在 MSVS 命令行提示窗口中执行

> cl hellogtk.c -IC:/GTK+-2.12/include/gtk-2.0 -IC:/GTK+-2.12/lib/gtk-2.0/include -IC:/GTK+-2.12/include/atk-1.0 -IC:/GTK+-2.12/include/cairo -IC:/GTK+-2.12/include/pango-1.0 -IC:/GTK+-2.12/include/glib-2.0 -IC:/GTK+-2.12/lib/glib-2.0/include -IC:/GTK+-2.12/include/libpng12 -Dinline= /link /libpath:C:/GTK+-2.12/lib gtk-win32-3.0.lib gobject-2.0.lib

其中 "gtk-win32-3.0.lib gobject-2.0.lib" 是编译 GTK+ 应用程序最少的链接选项要求;更多的链接选项请执行

> pkg-config --libs gtk+2.0

之后,自行添加需要的 .lib 项。

跟以往一样,开发大型程序,还是使用集成开发环境比较好。不过 MSVS 并没有提供 GTK+ 工程向导,因此只能通过在 MS Visual Studio 中新建一个空的 C++ 项目,然后在该项目的工程属性中配置 GTK+ 所需要的编译、链接参数。具体来说,

1、配置头文件搜索目录:鼠标右键新建的 C++ 工程属性->配置属性->C++目录->包含目录,把下述目录 C:\GTK+-2.12\include\atk-1.0C:\GTK+-2.12\include\cairoC:\GTK+-2.12\include\glib-2.0C:\GTK+-2.12\include\gtk-2.0C:\GTK+-2.12\include\libpng12C:\GTK+-2.12\include\pango-1.0C:\GTK+-2.12\lib\glib-2.0\includeC:\GTK+-2.12\lib\gtk-2.0\include 添加进去。

2、配置库文件搜索目录,鼠标右键新建的 C++ 工程属性->配置属性->C++目录->库目录,把 C:\GTK+-2.12\lib 目录添加进去;

3、配置附加依赖项, C++ 工程属性->配置属性->链接器->输入->附加依赖项,把 atk-1.0.libcairo.libgdi32.libgdk-win32-2.0.libgdk_pixbuf-2.0.libgio-2.0.libglib-2.0.libgmodule-2.0.libgobject-2.0.libgtk-win32-2.0.libintl.libpango-1.0.libpangowin32-1.0.libpangocairo-1.0.lib 等项添加进去,注意每项要换行。

要注意,不同的 GTK+ 版本,每个链接项的版本可能不同,请根据 pkg-config 的输出自行调整。