(六):Winelib开发组件2
话接上节!!
(二):编译资源文件:wrc
为了编译资源,你应该使用Wine资源编译器,简写为wrc,该编译器会生成一个二进制.res文件。当编译spec文件的时候,该资源文件会被winebuild使用。
winemaker生成的makefiles文件会帮你照料好。但是,如果你要编写自己的Makefile文件的话,你应该将下面的语句加上:
WRC=$(WINE_DIR)/tools/wrc/wrc
WINELIB_FLAGS=-I$(WINE_DIR)/include -DWINELIB -D_REENTRANT
WRCFLAGS = -r -L
.SUFFIXES: .rc .res
.rc.res:
$(WRC) $(WRCFLAGS) $(WINELIB_FLAGS) -o $@ $<
资源文件中你可能会遇到两个问题:
第一个问题就是C库头文件问题。WRC不知道这些头文件在哪里。如果一个RC文件,他包含头文件的话,你将会得到一个”文件找不到”的错误。下面是解决这个问题的一些方法:
- 被Winelib头文件传统上使用的解决办法就是在一个#ifndef RC_INVOKED语句上附上包含语句,RC_INVOKED是一个宏名称,是由wrc自动定义的。
- 你也可以向wrc命令中添加一个或者是多个-I选项,这样他就能找到系统文件。举个例子,你可以添加-I/usr/include -I/usr/lib/gcc-lib/i386-linux/2.95.2/include来迎合C/C++头文件。
但这就会假设你知道这些头文件在哪里,这会降低你的makefile文件对其他平台的可移植性。或者是你可以使用C/C++编译器来运行预处理。为了能够这样做,你可以简单的修改你的makefile文件如下:
.rc.res:
$(CC) $(CC_OPTS) -DRC_INVOKED -E -x c $< | $(WRC) -N $(WRCFLAGS) $(WINELIB_FLAGS) -o $@
第二个问题就是头文件可能会包含一些arc不能理解的结构。一个典型的例子就是一个返回const类型的函数。wrc期望的函数是两个标识符后跟一个左括号。如果带上const,就有三个标识符后跟一个左括号,因此wrc迷惑了(它实际上应该会忽略所有的windows资源编译器所做的事情)。解决的办法就是在#ifndef RC_INVOKED中添加一个违规语句。
在资源中使用GIF文件是有问题的。最好的结果就是,将他们转换成BMP文件或者是改变你的.res文件。
如果在你的资源文件中,你使用通用的控制/对话框的话,你需要在#include <windows.h>
后面添加一个#include <commctrl.h>
,这样wrc才能够明白控制专用标志的值。
(三):Spec文件
1:简介
在Windows中,应用和库包含了一些必要的使API能够工作的信息,例如GetProcAddress。所以说在Unix世界中,为了能够使Winelib应用和库以同样的API工作,重复这些数据结构是有必要的。
spec文件就是为了解决上面描述的语义上的差别的。他提供了从一个DLL提取出来的关于每一个API的信息,以使适当的表能够生成。后期这些信息被用于保存日志。
一个典型的spec文件看上去就像下面这样:
@ stdcall -private DllCanUnloadNow()
@ stdcall -private DllGetClassObject(ptr ptr ptr)
@ stdcall -private DllRegisterServer()
@ stdcall -private DllUnregisterServer()
2:更多细节
下面是对spec文件格式更加详细的描述。
# comment text
在’#’后面的任何数据都被认定为注释。
ORDINAL FUNCTYPE OPTIONS EXPORTNAME([ARGTYPE [ARGTYPE [...]]]) HANDLERNAME
@ stdcall CreateDesktopW(wstr wstr ptr long long ptr)
@ stdcall CM_Get_Parent(ptr long long) setupapi.CM_Get_Parent
@ cdecl -arch=x86_64 ceilf(float) MSVCRT_ceilf
这个域可以出现0次或者是多次。每一个实例定义一个函数入口点。被EXPORTNAME([ARGTYPE[ARGTYPE[…]]])定义的原型指定了用于动态链接和参数格式的可用的名称。ORDINAL被与之相对应的功能的序号所代替,或者是使用@进行自动的序号分配(仅仅用于win32)。FUNCTYPE应该是下面的一个:
- pascal
- 用于一个Win16函数
- stdcall
- 用于一个普通的Win32函数
- cdecl
- 用于使用C调用公约的Win32函数
- varargs
- 用于一个采用可变数量的参数的Win32函数
- thiscall
用于使用C++调用公约的Win32函数
ARGTYPE应该是下面的一个:
- word
- 一个16位字
- s_word
- 16位的符号字
- segptr
- 用于一个分段指针
- segstr
- 用于一个指向空结束字符串的分段指针
- long
- 一个32位的值
- ptr
- 一个线性指针
- str
- 用于指向一个空结束字符串的线性指针
- wstr
- 用于一个空结束16位编码的字符串的线性指针
- int64
- 一个64位数
- int128
- 一个128位数
- float
- 一个32位浮点数
- double
- 一个64位浮点数
对于Wine32函数word,s_word,segptr和segstr是无效的。HANDLERNAME是真实的以32位模式处理需求的Wine函数的名称。Strings应该一直会映射到str,宽字符串会映射到watr。作为一个常用的规则,他依赖于参数是IN,OUT或者是IN/OUT。
- IN: str/wstr
- OUT: ptr
- IN/OUT: str/wstr
这个是用于调试信息的。如果参数是OUT,他将不会被初始化,因此他也不会作为一个字符串被打印。
为了能够在Win16中使用一定数量的参数来声明一个函数,指定函数为无参数。参数将会在CURRENT_STACK16->args
中可用。在Win32中,在C文件中指定函数为varargs或者是以”…”作为参数声明。可以查看位于user32.spec文件中的wsprintf*函数作为一个例子。
ORDINAL stub EXPORTNAME
这个域可以出现零次后者是更多次。每一个实例定义了一个stub函数。他使得动态链接中的序号可用,如果函数被调用了,一旦遇到错误信息就会终止运行。
ORDINAL extern EXPORTNAME SYMBOLNAME
这个域可以出现零次或多次。每一个实例都定义了一个映射到一个Wine符号(变量或函数)的条目;EXPORTNAME将会指向在C代码中定义的符号SYMBOLNAME。这个类型仅仅在Win32上工作。
(四):将他们链接到一块
为了成一个可执行程序,你需要链接这些文件:你的对象文件,任何你的应用程序依赖的Windows库,例如gdi32,和你使用的其他附加库。你要链接的所有的库应该是作为.so库可用的。如何他们仅仅在.dll下是可用的,那么请想看“构建Winelib DLLs”这一节。
当你尝试链接你的可执行程序的时候,你才会发现你的当前库中是否丢失符号。在Windows上,帮你构建库的时候,链接器就会立即告诉你一个符号是否未定义。在Unix和Winelib中,不是这样的。符号会被默默的被标注为未定义,只有当你尝试去构建一个可执行程序的时候,链接器才会验证所有的符号。
当首次转换一个库到Winelib成功之前,你应该尝试把他联街道一个可执行程序中。在这个时候,你可能就会发现一些你认为已经实现的未被定义的符号。然后,进入到库源码进行修改。但是你也可能会发现丢失的符号是被定义了的,就像gdi32。这是因为你并没有将gdi32和库链接。修复他的一个方式就是链接这个可执行程序,另外一个方式就是带着gdi32来使用你的库。最好的方式就是回到你的库的Makefile中,显式的将他和gdi32链接起来。
正如你要注意到的,对于Winelib所属库来说并没有完全实现。所以如果一个程序必须要链接ole32的话,你也需要和advapi32,rpcrt4和其他库链接,即使你并不直接使用他们。
为了成一个可执行程序,你需要链接这些文件:你的对象文件,任何你的应用程序依赖的Windows库,例如gdi32,和你使用的其他附加库。你要链接的所有的库应该是作为.so库可用的。如何他们仅仅在.dll下是可用的,那么请想看“构建Winelib DLLs”这一节。
当你尝试链接你的可执行程序的时候,你才会发现你的当前库中是否丢失符号。在Windows上,帮你构建库的时候,链接器就会立即告诉你一个符号是否未定义。在Unix和Winelib中,不是这样的。符号会被默默的被标注为未定义,只有当你尝试去构建一个可执行程序的时候,链接器才会验证所有的符号。
当首次转换一个库到Winelib成功之前,你应该尝试把他联街道一个可执行程序中。在这个时候,你可能就会发现一些你认为已经实现的未被定义的符号。然后,进入到库源码进行修改。但是你也可能会发现丢失的符号是被定义了的,就像gdi32。这是因为你并没有将gdi32和库链接。修复他的一个方式就是链接这个可执行程序,另外一个方式就是带着gdi32来使用你的库。最好的方式就是回到你的库的Makefile中,显式的将他和gdi32链接起来。
正如你要注意到的,对于Winelib所属库来说并没有完全实现。所以如果一个程序必须要链接ole32的话,你也需要和advapi32,rpcrt4和其他库链接,即使你并不直接使用他们。