| <chapter id="bindlls"> | |
| <title id="bindlls.title">Dealing with binary only dlls</title> | |
| <sect1 id="bindlls-intro"> | |
| <title id="binary-dlls-intro.title">Introduction</title> | |
| <para> | |
| For one reason or another you may find yourself with a Linux shared | |
| library that you want to use as if it was a Windows Dll. There are | |
| various reasons for this including the following: | |
| <itemizedlist> | |
| <listitem> | |
| <para> | |
| You are porting a large application that uses several third-party | |
| libraries. One is available on Linux but you are not yet ready | |
| to link to it directly as a Linux shared library. | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| (The ODBC interface in WINE). There is a well-defined interface | |
| available and there are several Linux solutions that are | |
| available for it. | |
| </para> | |
| </listitem> | |
| </itemizedlist> | |
| </para> | |
| <para> | |
| The process for dealing with these situations is actually quite simple. | |
| You need to write a spec file that will describe the library's | |
| interface in the same format as a Dll (primarily what functions it | |
| exports). Also you will want to write a small wrapper around the | |
| library. We combine these to form a Wine builtin Dll that links to the | |
| Linux library. | |
| </para> | |
| <para> | |
| In this section we will look at two examples. The first example is | |
| extremely simple and leads into the subject in "baby steps". The | |
| second example is the ODBC interface proxy in Wine. The files to which | |
| we will refer for the ODBC example are currently in the | |
| <filename class="Directory">dlls/odbc32</filename> directory of the | |
| Wine source. | |
| </para> | |
| <para> | |
| The first example is based very closely on a real case (the names | |
| of the functions etc. have been changed to protect the innocent). | |
| A large Windows application includes a DLL that links to a third-party | |
| DLL. For various reasons the third-party DLL does not work too well | |
| under Wine. However the third-party DLL is also available for the | |
| Linux environment. Conveniently the DLL and Linux shared library | |
| export only a small number of functions and the application only uses | |
| one of those. | |
| </para> | |
| <para> | |
| Specifically, the application calls a function: | |
| <programlisting> | |
| signed short WINAPI MyWinFunc (unsigned short a, void *b, void *c, | |
| unsigned long *d, void *e, unsigned char f, char g, | |
| unsigned char *h); | |
| </programlisting> | |
| and the linux library exports a corresponding function: | |
| <programlisting> | |
| signed short MyLinuxFunc (unsigned short a, void *b, void *c, | |
| unsigned short *d, void *e, char g, unsigned char *h); | |
| </programlisting> | |
| </para> | |
| </sect1> | |
| <sect1 id="bindlls-spec"> | |
| <title id="bindlls-spec.title">Writing the spec file</title> | |
| <para> | |
| Start by writing the spec file. This file will describe the interface | |
| as if it was a dll. See elsewhere for the details of the format of | |
| a spec file. | |
| </para> | |
| <para> | |
| In the simple example we want a Wine builtin Dll that corresponds to | |
| the MyWin Dll. The spec file is <filename>libMyWin.spec</filename> and | |
| looks like this. | |
| <programlisting> | |
| # | |
| # File: libMyWin.spec | |
| # | |
| # some sort of copyright | |
| # | |
| # Wine spec file for the libMyWin builtin library (a minimal wrapper around the | |
| # linux library libMyLinux) | |
| # | |
| # For further details of wine spec files see the Winelib documentation at | |
| # www.winehq.com | |
| name MyWin | |
| type win32 | |
| mode dll | |
| 2 stdcall _MyWinFunc@32 (long ptr ptr ptr ptr long long ptr) MyProxyWinFunc | |
| # End of file | |
| </programlisting> | |
| Notice that the arguments are flagged as long even though they are | |
| smaller than that. | |
| </para> | |
| <para> | |
| In the case of the ODBC example you can see this in the file | |
| <filename>odbc32.spec</filename>. | |
| </para> | |
| </sect1> | |
| <sect1 id="bindlls-cxx-apis"> | |
| <title id="bindlls-cxx-apis.title">How to deal with C++ APIs</title> | |
| <para> | |
| names are mangled, how to demangle them, how to call them | |
| </para> | |
| </sect1> | |
| <sect1 id="bindlls-wrapper"> | |
| <title id="bindlls-wrapper.title">Writing the wrapper</title> | |
| <para> | |
| Firstly we will look at the simple example. The main complication of | |
| this case is the slightly different argument lists. The f parameter | |
| does not have to be passed to the Linux function and the d parameter | |
| (theoretically) has to be converted between unsigned long * and | |
| unsigned short *. Doing this ensures that the "high" bits of the | |
| returned value are set correctly. | |
| <programlisting> | |
| /* | |
| * File: MyWin.c | |
| * | |
| * Copyright (c) The copyright holder. | |
| * | |
| * Basic WINE wrapper for the linux <3rd party library> so that it can be | |
| * used by <the application> | |
| * | |
| * Currently this file makes no attempt to be a full wrapper for the <3rd | |
| * party library>; it only exports enough for our own use. | |
| * | |
| * Note that this is a Unix file; please don't go converting it to DOS format | |
| * (e.g. converting line feeds to Carriage return/Line feed). | |
| * | |
| * This file should be built in a Wine environment as a WineLib library, | |
| * linked to the Linux <3rd party> libraries (currently libxxxx.so and | |
| * libyyyy.so) | |
| */ | |
| #include < <3rd party linux header> > | |
| #include <windef.h> /* Part of the Wine header files */ | |
| signed short WINAPI MyProxyWinFunc (unsigned short a, void *b, void *c, | |
| unsigned long *d, void *e, unsigned char f, char g, | |
| unsigned char *h) | |
| /* This declaration is as defined in the spec file. It is deliberately not | |
| * specified in terms of <3rd party> types since we are messing about here | |
| * between two operating systems (making it look like a Windows thing when | |
| * actually it is a Linux thing). In this way the compiler will point out any | |
| * inconsistencies. | |
| * For example the fourth argument needs care | |
| */ | |
| { | |
| unsigned short d1; | |
| signed short ret; | |
| d1 = (unsigned short) *d; | |
| ret = <3rd party linux function> (a, b, c, &d1, e, g, h); | |
| *d = d1; | |
| return ret; | |
| } | |
| /* End of file */ | |
| </programlisting> | |
| </para> | |
| <para> | |
| For a more extensive case we can use the ODBC example. This is | |
| implemented as a header file | |
| (<filename class="HeaderFile">proxyodbc.h</filename>) and the actual | |
| C source file (<filename>proxyodbc.c</filename>). Although the file | |
| is quite long it is extremely simple in structure. | |
| </para> | |
| <para> | |
| The MAIN_OdbcInit function is the function that was named in the | |
| <link linkend="bindlls-spec">spec file</link> as the init function. | |
| On the process attach event the function dynamically links to the | |
| desired Linux ODBC library (since there are several available) and | |
| builds a list of function pointers. It unlinks on the process | |
| detach event. | |
| </para> | |
| <para> | |
| Then each of the functions simply calls the appropriate Linux function | |
| through the function pointer that was set up during initialisation. | |
| </para> | |
| </sect1> | |
| <sect1 id="bindlls-building"> | |
| <title id="binary-dlls-building.title">Building</title> | |
| <para> | |
| So how dow we actually build the Wine builtin Dll? The easiest way is | |
| to get Winemaker to do the hard work for us. For the simple example we | |
| have two source files (the wrapper and the spec file). We also have | |
| the 3rd party header and library files of course. | |
| </para> | |
| <para> | |
| Put the two source files in a suitable directory and then use | |
| winemaker to create the build framework, including configure script, | |
| makefile etc. You will want to use the following options of | |
| winemaker: | |
| <itemizedlist> | |
| <listitem> | |
| <para> | |
| --nosource-fix and --nogenerate-specs (requires winemaker version | |
| 0.5.8 or later) to ensure that the two files are not modified. | |
| (If using an older version of winemaker then make the two files | |
| readonly and ignore the complaints about being unable to modify | |
| them). | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| --dll --single-target MyWin --nomfc to specify the target | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| -DMightNeedSomething -I3rd_party_include -L3rd_party_lib -lxxxx | |
| -lyyyy where these are the locations of the header files etc. | |
| </para> | |
| </listitem> | |
| </itemizedlist> | |
| </para> | |
| <para> | |
| After running winemaker I like to edit the Makefile.in to add the line | |
| CEXTRA = -Wall just before the DEFINES =. | |
| </para> | |
| <para> | |
| Then simply run the configure and make as normal (described elsewhere). | |
| </para> | |
| </sect1> | |
| </chapter> | |
| <!-- Keep this comment at the end of the file | |
| Local variables: | |
| mode: sgml | |
| sgml-parent-document:("wine-doc.sgml" "book" "chapter" "") | |
| End: | |
| --> |