wine/documentation/HOWTO-winelib
2003-05-02 20:08:53 +00:00

617 lines
23 KiB
Text

WineLib HOWTO
Version 28-Dec-2000
AUTHOR:
Wilbur Dale
Lumin Software BV
Zandheuvel 52 B
4901 HW Oosterhout (NB)
The Netherlands
wilbur.dale@lumin.nl
WARNING: This HOWTO is incomplete. I expect to add to it on a weekly
basis until it is complete.
=====================================================================
Table of Contents
I. Introduction: Wine vs. WineLib
IV. File Format Conversion
V. Compiling A Simple Win32 Program
VII. DLLs
A. Windows executable and Windows DLL.
B. Windows executable and WineLib DLL.
C. WineLib executable and Windows DLL.
D. WineLib executable and WineLib DLL.
VIII. How to use MFC
A. Using a native MFC DLL
B. Compiling MFC
=====================================================================
I. Introduction: Wine vs. WineLib
WineLib provides the Win32 API to a non-Microsoft operating
system. The WineLib Win32 functions use X11 functions to perform the
actual drawing on the screen. Wine and WineLib are based on the same
set of functions that implement the Win32 API. The difference between
Wine and WineLib is the type of executable that is loaded into memory
and executed. If an executable and any associated DLLs were compiled
for x86 hardware running the Windows 95, 98, or Windows NT (TM)
operating systems, then Wine can use a special binary loader to load
the program and the libraries into memory and execute it. WineLib on
the other hand allows you to take the source for such a program and
DLLs and compile it into the native format of a x86 Unix or Linux
operating system. WineLib also allows you to partially compile the
program and DLLs into the native format. For example, if you use a DLL
from a vendor to provide some functions to your program and the vendor
does not give you source, then you can use the Windows version of the
DLL to provide the functions and compile the rest of your program in
the native form for your system. [1]
Windows compilers and linkers generate executables with a different
structure than standard compilers. Windows has two executable formats:
the NE format and the PE format. The NE executable format provides for
two entry points and the PE format provides for three entry points
while a standard executable has a single entry point. Usually, a NE or
a PE executable will use one of the entry points for your program and
the other entry points will print an error message and exit. However,
a linker can link 16 bit objects into one or both of the alternate
entry points of a NE or PE executable.
Standard compilers assume that the function main() exists. The entry
point for a standard program is constructed from the C runtime
library, initialization code for static variables in your program, the
initialization code for your classes (C++), and your function main().
On the other hand, windows compilers assume WinMain() exists. The
entry point for a windows program is constructed from the C runtime
library, initialization code for static variables in your program, the
initialization code for your classes (C++), and your function
WinMain(). [4]
Since main() and WinMain() have different type signatures (parameter
types), WineLib provides certain aids to generate code so that your
program can be compiled and run as written for windows. For example,
WineLib generates a main() to initialize the windows API, to load any
necessary DLLs and then call your WinMain(). Therefore, you need to
learn four basic operations to compile a windows program using
WineLib: compiling a simple program, compiling resources, compiling
libraries, and compiling MFC (if you will be using MFC). Each of these
skills or operations are explained in later sections of this HOWTO.
Before you start porting your windows code to WineLib, you need to
consider whether you are allowed to port your program to WineLib. As
you compile your program using WineLib, you will be combining software
from several sources and you need to ensure that the licenses for the
components are compatible. Hence, in the next section, we will examine
several legal issues.
IV. File Format Conversion
Before you can compile your program, you must deal with one major
difference between Windows and WineLib. Window sources are in DOS
format with carriage return / line feed at the end of each line of
text while WineLib files are in Unix format with only line feed at the
end of each line of text.
The main problem with the difference between Unix and DOS format
source files occurs with macro line continuation. A Unix compiler
expects a backslash (\) followed by a newline (^J) to indict that a
macro is continued on the next line. However, a file in DOS format will
have the characters backslash (\), carriage return (^M), and newline
(^J). The Unix compiler will interpret the backslash (\), carriage
return (^M), newline (^) of a file in DOS format as a quoted carriage
return and newline. The Unix compiler will think the line has ended
and the macro is completely defined. Hence, before you compile your
sources, you will need to convert you DOS format sources to Unix
format. There are several tools such as dos2unix and tr that are
available to convert the format.
FIXME: get more info on dos2unix, tr, and all other such tools and
give example commands. Until I do [3] is a good source.
FIXME: is CR/LF conversion necessary for gcc 2.95 ?
V. Compiling A Simple Win32 Program
Wine and WineLib are written in C as is the MS Win32 API; thus, if
have a program that calls only the Win32 API directly, you can compile
the program using a C compiler and link it with some of the WineLib
libraries. There are several simple examples of WineLib programs in
the directory libtest/ in the Wine source tree. We shall examine one
of these to show you how to compile a WineLib program.
The example we shall examine is hello2. If you examine hello2.c, you
will see it is a windows program that pops up a message box that says
"Hello, hello!". It can be compiled and run using a windows compiler
just like any other windows program. However, it can not be compiled
and run with a non-windows compiler. As mentioned previously, windows
programs have an entry point called WinMain(), while non-windows
compilers use an entry point of main(). Hence, we need some "glue" to
glue the main() entry point to the WinMain() in the windows program.
In WineLib, some of the glue is provided by the spec file. Spec files
are used in several places in Wine and WineLib to provide glue between
windows code and code for non-windows compilers. WineLib provides a
tool called winebuild in the tools/winebuild directory that converts a
spec file into a C file that can be compiled and linked with the
windows source files. ...
VII. DLLs
As mentioned in the introduction, Wine allows you to execute windows
executables and windows libraries under non-Microsoft operating
systems. WineLib allows you to take sources intended for the windows
operating system and to compile them to run as native executables
under a Unix/Linux operating system. With an executable and a single
library, there are four combinations in which to run the programs and
the library:
1. a Windows executable with a Windows DLL,
2. a Windows executable with WineLib DLL,
3. a WineLib executable with Windows DLL, and
4. a WineLib executable with WineLib DLL.
In this section, we will discuss each of these and discuss the steps
required to implement the executable/DLL combination.
A. Windows executable and Windows DLL
Running a windows executable with a windows DLL is not a WineLib
program: it is a Wine program. If you type
wine program.exe
and the DLL is in the search path, then the windows program should run
using the windows DLL.
FIXME: find out what is the search path.
B. Windows executable and WineLib DLL
Running a windows executable with a WineLib DLL is also accomplished
using the Wine program. The source code for the DLL is compiled into a
Unix style shared library. When the windows executable "loads" the
DLL, Wine will use the shared library (.so file) instead.
At first you may wonder why you would want to run a windows executable
with a WineLib DLL. Such a situation implies you do not have the
source for the executable, but you do have the source for the
DLL. This is backwards from what you might expect. However, I do have
an example where this situation might arise.
Codewright is a popular editor in the windows world, and the
capabilities of Codewright can be extended by using DLLs. Since
Codewright is a commercial product, you do not have the source and
must use the windows executable with Wine. If you have written a DLL
to add functionality to Codewright, you have two choices: you can
compile the DLL using a windows compiler and use both a windows
executable and a windows DLL as in case A above, or you can use
WineLib and compile the DLL as a shared library (.so file). I have no
idea if Codewright actually runs under Wine, but this is an example of
why you might decide to use a windows executable and a WineLib
DLL. Many other editors and other programs use DLLs to extend their
functionality.
In order for Wine to use the WineLib DLL, certain glue code is need to
replace the linker magic that windows compilers use. As with a simple
executable, the winebuild program uses a spec file to generate the glue
code. For example, in the spec file for the DLL will look something like
name winedll
type win32
init winedll_DllMain
1 cdecl _WINEbirthDay@4 ( str ) WINEbirthDay
2 cdecl _WINEfullName@4 ( str ) WINEfullName
The name is the name of the DLL. Since WineLib only supports win32,
the type should always be win32. The init function is the name of the
initialization function for the DLL. The initialization function for a
windows DLL is named DllMain(). You will need to rename the function
in the DLL source so there will not be any name clashes with the
DllMain() of other DLLs in you program.
The last two lines of the spec file above, provide the export
information for the DLL. For example, the line
1 cdecl _WINEbirthDay@4 ( str ) WINEbirthDay
says that the function at ordinal 1 uses the cdecl calling convention
for the parameters. The DLL export name is _WINEbirthDay@4. The
function takes a single parameter that is a string. Finally, the C
function name to be called whenever this DLL function is called is
WINEbirthday. You will need a function ordinal line for each function
in the DLL. The export name and the ordinal can be obtained from the
windows program dumpbin and the windows version of the DLL. See the
file <wine>/tools/winebuild/README for more details on the spec file
format.
During the compile process, a command like
winebuild -fPIC -o winedll.spec.c -spec winedll.spec
will be executed to create the file winedll.spec.c from information in
the file winedll.spec. The file winedll.spec.c and winedll.c are
compiled into object files and used to create the shared library.
In order for the program to run, a copy of the shared library must be in
your EXTRA_LD_LIBRARY_PATH. For example, if your wine.conf file has
the following line,
EXTRA_LD_LIBRARY_PATH=${HOME}/wine/lib
then you must copy the shared library into the directory ~/wine/lib/
and the shared library will now be in the correct search path.
Now when you type
wine program.exe
the program will load the shared library (.so).
C. WineLib executable and Windows DLL
Running a WineLib executable with a Windows DLL is accomplished
using WineLib. This situation will be common since you may have
purchased DLLs to use with you project and the DLL vendor may not give
you the source code for the DLL.
In order for WineLib to use the Windows DLL, certain glue code is
needed to replace the linker magic that windows compilers use. Part of
the glue code must be written by you. The basic idea of the glue code
is that you write a new DLL that consists of function pointers. Each
function in the DLL will consist of a call on a function pointer. For
example,
WINEDLL_ConstString WINEDLL_INTERFACE
WINEfullName( WINEDLL_ConstString handle ) {
return (* pWINEfullName) ( handle );
}
The initialization function for the DLL will use the function
LoadLibrary() to load the windows DLL and initialize the function
pointers using the function GetProcAddress().
Since Wine can use either windows DLLs or Unix shared libraries (.so),
the LoadLibrary() function call may have unexpected results if there
is a winedll.dll and a winedll.so file. Hence, the windows version of
the DLL should be named something like hiddenWinedll.dll and the
shared library should be named winedll.so. Now the shared library will
use LoadLibrary() to load the "hidden" DLL.
The shared library will need a spec file. Fortunately, it is simpler
than case B above. The spec file will look something like
name winedll
type win32
init winedll_DllMain
The name is the name of the DLL. Since WineLib only supports win32,
the type should always be win32. The init function is the name of the
initialization function for the shared library. This is the function
that will load the "hidden" DLL and initialize the function
pointers. There is no need for any function ordinals unless your
program calls functions by the ordinal.
During the compile process, a command like
winebuild -fPIC -o winedll.spec.c -spec winedll.spec
will be executed to create the file winedll.spec.c from information in
the file winedll.spec. The file winedll.spec.c and winedll.c are
compiled into object files and used to create the shared library.
Now that the shared library is compiled, you still need to compile
your program. Part of the compile process for your program will
consist of a spec file for your program. For example,
name program
mode guiexe
type win32
init WinMain
import winedll.dll
This spec file is similar to the spec file of the simple WineLib
example in part V above. The only difference is the import
specification that tells WineLib that the main program uses
winedll.dll. If this import line is not included, the "hidden" DLL
will not be loaded and the function pointers will not be initialized.
During the compile process, a command like
winebuild -fPIC -o program.spec.c -spec program.spec
will be executed to create the file program.spec.c from information in
the file program.spec. The file program.spec.c and your source code are
compiled into object files and used to create the executable.
D. WineLib executable and WineLib DLL.
Running a WineLib executable with a WineLib DLL is accomplished using
WineLib. The source for the DLL will be combined with a spec file to
generate the shared library. Likewise, the source for your program and
a spec file will be combined to create the executable. In the source
for the DLL, you should change the name of DllMain() to a name like
winedll_DllMain() so that there will not be a name clash with other
initialization functions for other DLLs.
The shared library's spec file is like case C above. The spec file
will look something like
name winedll
type win32
init winedll_DllMain
The init function is the name of the initialization function for the
shared library (what you renamed DllMain to). There is no need for any
function ordinals unless your program calls functions by the ordinal.
During the compile process, a command like
winebuild -fPIC -o winedll.spec.c -spec winedll.spec
will be executed to create the file winedll.spec.c from information in
the file winedll.spec. The file winedll.spec.c and the source code for
your DLL are compiled into object files and used to create the shared
library.
Compiling your program is exactly like case C above. For example, the
spec file for you program will look something like
name program
mode guiexe
type win32
init WinMain
import winedll.dll
During the compile process, a command like
winebuild -fPIC -o program.spec.c -spec program.spec
will be executed to create the file program.spec.c from information in
the file program.spec. The file program.spec.c and your source code are
compiled into object files and used to create the executable.
VIII. How to use MFC
A. Using a native MFC DLL
B. Compiling MFC
FIXME: to be continued.
A Windows compiler does NOT generate a fake main. Instead, the
executable file format provides for 2 (NE) or 3 (PE) entry points.
One of these is your program, the other(s) are normally filled with
stubs that print an error message and exit. It is possible to instruct
the _linker_ to link 16-bit objects into one or both of the alternate
entry points, and create a fat binary.
At the C/C++ level, your statement about WinMain() is correct. Of
course the actual entry point first inits run time lib etc, and then
calls the C/C++ level entry, but that is also true for main() in the
standard setup. It may be important to regurgitate this info here,
though, because some of the fun things that can happen with multiple
run time libs and DLLs occur at this level.
Line 86: I only need to know how compile MFC if I use it... :-)
From: Damyan Ognyanoff <Damyan@rocketmail.com>
Subject: Re: Wine MFC info request
hi,
my MFC is from VC6.0 with SP3
MFC Bulid: (form afxbld_.h)
#define _MFC_BUILD 8447
#define _MFC_USER_BUILD "8447"
#define _MFC_RBLD 0
mfcdll.rc
FILEVERSION 6,0,_MFC_BUILD,_MFC_RBLD
PRODUCTVERSION 6,0,0,0
Hints:
1. Wine include files
In some of them you will find error about '__attribute__' all kinds of
similar errors can be fixed using proper typedefs first example :
typedef BOOL (CALLBACK *DLGPROC)(HWND,UINT,WPARAM,LPARAM);
must be converted to
typedef BOOL CALLBACK (*DLGPROC)(HWND,UINT,WPARAM,LPARAM);
and the second kind is something like
TYPE* WINAPI SomeFunction(HWND param1,UINT param2);
The problem here is a TYPE* or TYPE& (in some of mfc files) the
workaround is to declare a type before:
typedef TYPE* TYPEPtr;
or
typedef TYPE& TYPERef;
and declaration will look like:
TYPEPtr WINAPI SomeFunction(HWND param1,UINT param2);
note: don't miss a 'struct' when you define struct type pointers. I
miss it and get a lot of problems compiling MFC:
>>
struct _TEB;
typedef !!!struct!!! _TEB* P_TEB;
extern inline P_TEB WINAPI NtCurrentTeb(void);
<<
Those conversions are semanticaly the same as above but g++ compile
them and generate proper code to invoke __stdcall kind of functions
in some of Wine/obj_XXX.h files: Wine/obj_base.h - there are a lot of
defines's that are used to declare a COM interfaces
#define ICOM_METHOD(ret,xfn) \
public: virtual ret (CALLBACK xfn)(void) = 0;
will be (for all of them that are related to C++ (watch #ifdef's
carefully)):
#define ICOM_METHOD(ret,xfn) \
public: virtual ret CALLBACK (xfn)(void) = 0;
and the second tip is an error when compiler stops on line like:
ICOM_DEFINE(ISomeInterfase,IUnknown)
watch method declarations above to find something like:
ICOM_METHOD1(TYPE*,MethodName, DWORD,dwParam)
and replace TYPE* with proper TYPEPtr type. In many cases You will see
void* which can be replaced simply by LPVOID.
qthere are several errors related to anonymous structs and unions but
they can be avoided with proper - #ifdef __cplusplus
This is all about Wine headers I think. If you find something that I
miss type a line of mail to me.
2. MFC
The rules are the same with some new issues:
virtual BOOL Method1(int param1, BOOL (CALLBACK *param2)
(HWND,UINT,WPARAM,LPARAM));
don't compile. I remove a function pointer declaration
outside method:
typedef BOOL CALLBACK
(*param2Type)(HWND,UINT,WPARAM,LPARAM);
virtual BOOL Method1(int param1, param2Type param2);
I didn't apply this technique to a operator new
definitions:
void* AFXAPI operator new(size_t nSize);
so i remove AFXAPI from these declarations:
I got some missed #defines from commctrl.h and I added
them form VC6.0 include.
these are my defines form Makefile which I used to
compile MFC
-DTWINE_NO_CMONIKER \ -- this is related to exclude
CMonikerFile
-D__urlmon_h__ \ -- Wine didn't have URL interfaces
-D_AFX_NO_OLEDB_SUPPORT \
-D_WIN32 \
-DNOWIN98 \ -- this is used to exclude all
unimplemented classes from commctrl
-D_AFX_PACKING \
-D_AFX_NO_DHTML_SUPPORT \
-D_AFX_NO_SOCKET_SUPPORT \
-D_AFX_NO_SYNC_SUPPORT \
-D_AFX_NO_OCX_SUPPORT \
-D_AFX_PORTABLE \
-D_AFX_OLD_EXCEPTIONS \
-D_AFX_NO_SOCKET_SUPPORT \
-D_AFX_NO_DEBUG_CRT \
-D_AFX_NO_DAO_SUPPORT \
-D_AFX_NO_OCC_SUPPORT \
-D_AFX_NO_INET_SUPPORT \
-D_AFX_NO_RICHEDIT_SUPPORT \
-D_X86_ \
-DLONGHANDLES
may be you will try to enable some of features of mfc I tested only
-D_AFX_NO_OCC_SUPPORT but got missing interfaces from Wine
in file afxcom_.h
- _CIP<_Interface, _IID>::~_CIP<_Interface, _IID>()
+ _CIP<_Interface, _IID>::~_CIP()
in file afxtempl.h
- BOOL Lookup(BASE_CLASS::BASE_ARG_KEY key,
VALUE& rValue) const
- { return BASE_CLASS::Lookup(key,
(BASE_CLASS::BASE_VALUE&)rValue); }
+ BOOL Lookup(typename BASE_CLASS::BASE_ARG_KEY
key, VALUE& rValue) const
+ { return BASE_CLASS::Lookup(key,
(typename BASE_CLASS::BASE_VALUE&)rValue); }
and all releated errors can be fixed in this way.
3. spec file
name mfc42
type win32
rsrc mfc42
10 stdcall WinMain(long long ptr long) WinMain
4. linking
use -rdynamic wnen link libmfc.so to get ARGV and
ARGC from loader
5. I didn'n build a extension dll with Wine but I suspect that there
will be some problems related to a chaining Runtime classes form MFC
to a new dll
6. build your app as a MODULE too.
7. make a loader and in it's _WinMain:
... includes are here
iint PASCAL (*winMain)(HINSTANCE,HINSTANCE,LPSTR,int) =
0;
my app uses these to manage filenames
VOID __cdecl (*_splitpath1)(LPCSTR path, LPSTR drive,
LPSTR directory, LPSTR filename, LPSTR extension ) =
NULL;
VOID __cdecl _splitpath(LPCSTR path, LPSTR drive,
LPSTR directory, LPSTR filename, LPSTR extension )
{
if (_splitpath1)
_splitpath1(path, drive, directory, filename,
extension );
}
VOID __cdecl (*_makepath1)(LPSTR path, LPCSTR drive,
LPCSTR directory, LPCSTR filename, LPCSTR extension )
= NULL;
VOID __cdecl _makepath(LPSTR path, LPCSTR drive,
LPCSTR directory, LPCSTR filename, LPCSTR extension )
{
if (_makepath1)
_makepath1(path, drive, directory, filename,
extension);
}
int PASCAL _WinMain(HINSTANCE h,HINSTANCE h1,LPSTR
lpszCmdParam,int c)
{
HINSTANCE hInstance,hins,hlib,htst,hform,himag,hexe;
int retv;
hins = LoadLibrary("CRTDLL.DLL");
_splitpath1 = GetProcAddress(hins,
"_splitpath");
_makepath1 = GetProcAddress(hins,
"_makepath");
hins = LoadLibrary("COMCTL32.DLL");
hins = LoadLibrary("COMDLG32.DLL");
hins = dlopen("libmfc42.so",2);
hlib = LoadLibrary("mfc42");
himag = dlopen("libmxformatslib.so",2);
hform = LoadLibrary("mxformatslib");
hexe = dlopen("libmxpaint.so",2);
htst = LoadLibrary("mxpaint");
winMain = GetProcAddress(hlib, "WinMain");
if (winMain)
{
retv = winMain (htst, // note the > htst
< HERE
0,
lpszCmdParam,
SW_NORMAL);
}
FreeLibrary(htst);
FreeLibrary(hform);
FreeLibrary(hlib);
dlclose(hexe);
dlclose(himag);
dlclose(hins);
return retv;
}
the spec for loader is:
name c10
mode guiexe
type win32
init _WinMain
please find attached a Makefile which i use to build
MFC
Regards
Damyan.