| <chapter id="bindlls"> |
| <title id="bindlls.title">Using Linux libraries as 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> |
| There is a well-defined interface available and there are several |
| Linux solutions that are available for it. |
| (The ODBC interface in WINE) |
| </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: |
| --> |