使用Cygwin在Windows上进行Unix开发

原创
开发 开发工具
本文主要向读者介绍如何在Cygwin下进行程序开发。文章将首先介绍使用GCC开发控制台模式的应用程序和GUI模式的应用程序,然后阐述在Cygwin下如何调试程序,随后详细讲解在Cygwin下动态链接库的构建和使用,最后介绍资源文件的有关知识。

一、Cygwin简介

Cygwin是许多自由软件的集合,最初由Cygnus Solutions开发,用于各种版本的Microsoft Windows上,运行UNIX类系统。Cygwin的主要目的是通过重新编译,将POSIX系统(例如Linux、BSD,以及其他Unix系统)上的软件移植到Windows上。Cygwin包括了一套库,该库在Win32系统下实现了POSIX系统调用的API;还有一套GNU开发工具集(比如GCC、GDB),这样可以进行简单的软件开发;还有一些UNIX系统下的常见程序。2001年,新增了X Window System。另外还有一个名为MinGW的库,可以跟Windows本地的MSVCRT库(Windows API)一起工作。

二、在Cygwin中使用GCC

下面我们开始介绍如何在Cygwin中使用GCC开发控制台模式的应用程序和GUI模式的应用程序。

控制台模式的应用程序

使用gcc编译程序跟在UNIX操作系统之下非常相似,关于gcc的标准用法和选项可以参考其用户手册。下面是一个简单的示例:

例1:利用GCC构建Hello World

C:\> gcc hello.c -o hello.exe
C:\> hello.exe
Hello, World

C:\>

GUI模式的应用程序

Cygwin使我们可以编译出能够访问所有标准Windows32位API的程序,其中包括定义在微软公司和畅销出版物中的那些GUI函数。然而,使用GNU工具跟使用微软公司的工具构建应用程序的过程会稍有不同。绝大多数情况下,根本无需修改源代码。然而,您应该删除函数中的全部__export属性,并将其换成下面的内容:

int foo (int) __attribute__ ((__dllexport__));

int
foo (int i)

Cygwin Makefile与其他任何类UNIX的Makefile非常类似,唯一区别在于我们需要使用gcc -mwindows来把程序连接成一个图形用户界面应用程序,而非命令行应用程序。下面是一个例子:

myapp.exe : myapp.o myapp.res
	gcc -mwindows myapp.o myapp.res -o $@

myapp.res : myapp.rc resource.h
	windres $< -O coff -o $@

注意,通过利用windres可把Windows资源编译成一个COFF格式的.res文件,这样就能把您需要的所有位图、图标及其他资源放到一个目标文件中。 正常情况下,如果您省略了“-O coff”的话,它就会创建一个Windows格式的文件,但是我们只能链接COFF格式的目标文件。所以,我们吩咐windres生成COFF格式的目标文件。我们的大部分示例都假定你的链接程序能够直接处理Windows的资源文件,我们保留.res的命名约定。关于windres的更多信息请参见有关手册。下面是一个GUI模式入门之用的“Hello ,World !”程序:

/*-------------------------------------------------*/
/* hellogui.c :一个图形模式的hello world程序                    */
/*编译命令:gcc -mwindows hellogui.c -o hellogui.exe */
/*-------------------------------------------------*/
#include <windows.h>

char glpszText[1024];

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY WinMain(HINSTANCE hInstance, 
		HINSTANCE hPrevInstance,
		LPSTR lpCmdLine,
		int nCmdShow)
{
	sprintf(glpszText, 
		"Hello World\nGetCommandLine(): [%s]\n"
		"WinMain lpCmdLine: [%s]\n",
		lpCmdLine, GetCommandLine() );

	WNDCLASSEX wcex; 
 
	wcex.cbSize = sizeof(wcex);
	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = WndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName = NULL;
	wcex.lpszClassName = "HELLO";
	wcex.hIconSm = NULL;

	if (!RegisterClassEx(&wcex))
		return FALSE; 

	HWND hWnd;
	hWnd = CreateWindow("HELLO", "Hello", WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

	if (!hWnd)
		return FALSE;

	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);

	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0)) 
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hdc;
	
	switch (message) 
	{
		case WM_PAINT:
			hdc = BeginPaint(hWnd, &ps);
			RECT rt;
			GetClientRect(hWnd, &rt);
			DrawText(hdc, glpszText, strlen(glpszText), &rt, DT_TOP | DT_LEFT);
			EndPaint(hWnd, &ps);
			break;
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

#p#

三、调试Cygwin程序

如果您的程序无法正常运行,通常情况下是由于其中的bug所致,因为程序本身存在错误会导致出乎意料的结果甚至崩溃。借助于一种称为调试器的专用工具可以使得查找和修正错误变得更简单一些。就Cygwin而论,其调试器是GDB,即GNU debugger的缩写。这个工具使我们可以在一个受控环境中运行我们的程序,在这个环境中,我们可以考察程序运行过程中或其崩溃之后的状态。有时候,程序崩溃时操作系统就会把程序当掉时的内存内容转储出来,现在通常是写在一个叫core 的file 里面。在Cygwin中,这些文件通常是些常规的文本文件,所以无法直接为GDB所用。

在调试程序之前,需要对需要进行调试的程序做一些准备工作,具体来说,就是在把源程序编译成目标程序的时候为所有标志添加-g。

例2:利用-g进行编译

$ gcc -g -O2 -c myapp.c
$ gcc -g myapp.c -o myapp

这样就会在生产的目标文件中加入额外的信息来告知调试器有关行号、变量名及其他有用的信息,不过这会使得目标文件的尺寸骤增。这些额外的符号和调试信息提供了原始代码的足够信息,所以调试器在调试它们的时候会更加容易。

在Windows版本的GNUPro中,GDB具有一个全功能的图形界面。在Cygwin的Net发行版本中,GDB只能在命令行下使用。要调用GDB,只需在命令提示符下输入gdb myapp.exe即可。这时会显示一些您自己的有关文本信息,之后GDB会提示您继续输入其他命令。只要看到这个提示符,就表示gdb正在等待您输入命令,如果您键入help命令,那么就会收到您可以使用的各个命令的帮助信息,当然您也可以通过阅读“GDB User's Manual”来全面细致地了解gdb及其使用方法。

如果你的程序崩溃了,并且您想弄清它为什么崩溃的话,最好的办法是键入run来运行您的程序。等它崩溃之后,您可以键入where命令来看看它在哪里崩溃的,或者输入info locals命令来查看所有局部变量的值。此外,如果键入print命令的话,我们还可以检查单独的变量以及指向这些变量的指针。

如果您的程序做了出乎意料之外的事情,那么可以使用break命令让gdb在程序到达指定的函数或者行号的时候停止程序的运行:

例3:gdb中的中断命令

(gdb) break my_function
(gdb) break 47

现在,当我们输入run命令之后,我们的程序会在断点处停下来,这样我们就能使用其他的gdb命令来查看程序当时的状态、修改变量以及单步调试程序的各个语句。需要注意的是,我们可以给run命令附加其他的参数,以便向我们的程序提供相应的命令行参数。例如,下面的两条命令的效果是一样的:

例4:利用命令行参数进行调试

$ myprog -t foo --queue 47

$ gdb myprog
(gdb) run -t foo --queue 47

#p#

四、动态链接库的构建和使用

动态链接库(DLL)是指在程序运行时而非编译时链接进我们的程序的那些库。一个动态链接库有三部分组成:

◆导出表

◆代码和数据

◆导入库

代码和数据是我们需要编写的函数、变量等内容,它们将被合并到一起放入dll,你可以简单的理解成建立了一个硕大的目标文件。但是它们却不会放入您的.exe文件。导出表含有动态库为其他程序提供给的函数和变量,可以简单的理解成这是一个“全局”符号表,除此之外的内容都是不可见的。通常情况下,我们需要利用文本编辑程序手工建立该表,不过我们也可以利用代码中的函数表来自动生成这个导出表。dlltool程序可以根据导出符号组成的文本文件来创建动态链接库的导出部分。输入库类似于类UNIX系统中的.a程序库,但是它只包含通知操作系统应用程序跟dll交互方式(即导入方法)所需信息。这些信息可以链接到我们的.exe程序中。当然这些信息也可以利用dlltool程序来建立。

构建动态链接库

我们这里将简单介绍如何利用gcc来建立动态链接库,有关gcc建立动态库的更详尽的选项,可以参考gcc的有关文档。首先提供一个简单的示例来演示建立一个动态链接库的过程。本例中,我们的程序(myprog.exe) 由单个源文件myprog.c组成,而动态链接库(mydll.dll)的内容则由称为mydll.c的文件得到。

幸运的是,在最新的gcc和binutils的帮助下,建立动态链接库的过程非常简单。下面我们介绍编译mydll.c的具体过程。

#include <stdio.h>

int
hello()
{
  printf ("Hello World!\n");
}  

首先将mydll.c编译为目标代码,命令如下所示:

gcc -c mydll.c

然后,告诉gcc我们要构建一个共享库,命令如下所示:

gcc -shared -o mydll.dll mydll.o

就这么简单!现在,我们将这个动态链接库链接到一个简单程序上,程序代码如下所示:

int 
main ()
{
  hello ();
}

现在我们用下列命令来连接动态链接库,命令如下所示:

gcc -o myprog myprog.c -L./ -lmydll

然而,如果想要把动态链接库做成一个导出库的话,可以使用下列语法:

gcc -shared -o cyg${module}.dll \

-Wl,--out-implib=lib${module}.dll.a \

-Wl,--export-all-symbols \

-Wl,--enable-auto-import \

-Wl,--whole-archive ${old_libs} \

-Wl,--no-whole-archive ${dependency_libs}

我们的程序库的名称是${module},动态链接库的前缀为cyg,输入库的前缀为lib。Cygwin的动态链接库使用cyg作为前缀,以作为本地Windows的MinGW动态链接库的区别。${old_libs}是我们全部的目标文件,被捆绑成静态库或者一个目标文件;${dependency_libs}是需要链接的静态库,如“-lpng -lz -L/usr/local/special -lmyspeciallib”。

链接动态链接库

假设您已有一个动态链接库,并需要建立一个与Cygwin兼容的输入库,如果您有源代码的话,可以参考本文的构建动态链接库部分。如果您没有源代码或者没有可用的输入库,那么您可以在bash中创建一个.def 文件,命令如下所示:

echo EXPORTS > foo.def

nm foo.dll | grep ' T _' | sed 's/.* T _//' >> foo.def

只有动态链接库没有去除有关符号信息的情况下上述命令才能正常工作,否则,就会出现“No symbols in foo.dll”错误信息。一旦得到了.def文件,就可以从中创建一个输入库,命令如下所示:

dlltool --def foo.def --dllname foo.dll --output-lib foo.a

#p#

五、定义Windows资源

Windres能够读取Windows资源文件(*.rc),并把它转换成res格式文件或者coff格式文件。输入文件的语法和语义的同其他任何资源编译器没有任何区别,所以详情可参阅任何有关描述Windows资源格式的文献。此外,windres程序本身在Binutils手册中也有详尽的说明。下面是一个使用windres的例子:

myapp.exe : myapp.o myapp.res
	gcc -mwindows myapp.o myapp.res -o $@

myapp.res : myapp.rc resource.h
	windres $< -O coff -o $@

六、结束语

Cygwin是许多自由软件的集合,用于各种版本的Microsoft Windows上运行UNIX类系统。Cygwin的主要目的是通过重新编译,将POSIX系统上的软件移植到Windows上。Cygwin包括了一套库,该库在Win32系统下实现了POSIX系统调用的API;还有一套GNU开发工具集(比如GCC、GDB),这样可以进行简单的软件开发;还有一些UNIX系统下的常见程序。而本文则为读者介绍了如何在Cygwin下进行程序开发。我们首先介绍使用GCC开发控制台模式的应用程序和GUI模式的应用程序,然后阐述在Cygwin下如何调试程序,随后详细讲解在Cygwin下动态链接库的构建和使用,最后介绍资源文件的有关知识。

【编辑推荐】

  1. Linux编程起步 GCC基本用法
  2. 详解GCC的下载和安装
  3. 从VC++到GCC移植:谈两者语法差异
责任编辑:杨鹏飞 来源: 51CTO
相关推荐

2020-03-07 18:51:11

EclipseFedoraPHP

2022-02-09 15:29:35

Java组件编程语言

2012-11-23 14:33:06

IBMdW

2015-07-23 14:09:15

CygwinWindowsLinux

2023-11-30 15:56:54

大型语言模型人工智能

2010-05-04 08:52:13

Unix系统

2010-02-24 15:19:38

ibmdwLinux

2021-02-16 23:17:47

Windows 10Windows微软

2010-12-09 09:12:28

2009-04-14 18:50:55

Nehalem惠普intel

2020-02-18 09:45:44

云计算云平台IT

2010-11-03 15:27:39

OpenLDAPWindowsUnix系统

2010-04-30 18:38:45

Unix系统

2023-06-20 08:00:00

2009-07-16 14:22:02

Windows Emb

2010-04-29 17:51:40

Unix工具

2011-07-19 14:11:07

Windows iPhone SDK

2010-07-02 12:58:39

Meego开发

2019-09-29 09:40:20

LinuxWindowsMacOS

2023-02-09 15:32:55

配置双网卡双网卡设置
点赞
收藏

51CTO技术栈公众号