John R. Sheets | 1828e59 | 2001-01-24 19:36:24 +0000 | [diff] [blame] | 1 | <chapter id="bindlls"> |
| 2 | <title id="bindlls.title">Dealing with binary only dlls</title> |
| 3 | <sect1 id="bindlls-intro"> |
| 4 | <title id="binary-dlls-intro.title">Introduction</title> |
| 5 | <para> |
Bill Medland | e145bde | 2001-10-02 17:48:16 +0000 | [diff] [blame] | 6 | For one reason or another you may find yourself with a Linux shared |
| 7 | library that you want to use as if it was a Windows Dll. There are |
| 8 | various reasons for this including the following: |
| 9 | <itemizedlist> |
| 10 | <listitem> |
| 11 | <para> |
| 12 | You are porting a large application that uses several third-party |
| 13 | libraries. One is available on Linux but you are not yet ready |
| 14 | to link to it directly as a Linux shared library. |
| 15 | </para> |
| 16 | </listitem> |
| 17 | <listitem> |
| 18 | <para> |
| 19 | (The ODBC interface in WINE). There is a well-defined interface |
| 20 | available and there are several Linux solutions that are |
| 21 | available for it. |
| 22 | </para> |
| 23 | </listitem> |
| 24 | </itemizedlist> |
John R. Sheets | 1828e59 | 2001-01-24 19:36:24 +0000 | [diff] [blame] | 25 | </para> |
Bill Medland | e145bde | 2001-10-02 17:48:16 +0000 | [diff] [blame] | 26 | <para> |
| 27 | The process for dealing with these situations is actually quite simple. |
| 28 | You need to write a spec file that will describe the library's |
| 29 | interface in the same format as a Dll (primarily what functions it |
| 30 | exports). Also you will want to write a small wrapper around the |
| 31 | library. We combine these to form a Wine builtin Dll that links to the |
| 32 | Linux library. |
| 33 | </para> |
| 34 | <para> |
| 35 | In this section we will look at two examples. The first example is |
| 36 | extremely simple and leads into the subject in "baby steps". The |
| 37 | second example is the ODBC interface proxy in Wine. The files to which |
| 38 | we will refer for the ODBC example are currently in the |
| 39 | <filename class="Directory">dlls/odbc32</filename> directory of the |
| 40 | Wine source. |
| 41 | </para> |
| 42 | <para> |
| 43 | The first example is based very closely on a real case (the names |
| 44 | of the functions etc. have been changed to protect the innocent). |
| 45 | A large Windows application includes a DLL that links to a third-party |
| 46 | DLL. For various reasons the third-party DLL does not work too well |
| 47 | under Wine. However the third-party DLL is also available for the |
| 48 | Linux environment. Conveniently the DLL and Linux shared library |
| 49 | export only a small number of functions and the application only uses |
| 50 | one of those. |
| 51 | </para> |
| 52 | <para> |
| 53 | Specifically, the application calls a function: |
| 54 | <programlisting> |
| 55 | signed short WINAPI MyWinFunc (unsigned short a, void *b, void *c, |
| 56 | unsigned long *d, void *e, unsigned char f, char g, |
| 57 | unsigned char *h); |
| 58 | </programlisting> |
| 59 | and the linux library exports a corresponding function: |
| 60 | <programlisting> |
| 61 | signed short MyLinuxFunc (unsigned short a, void *b, void *c, |
| 62 | unsigned short *d, void *e, char g, unsigned char *h); |
| 63 | </programlisting> |
| 64 | </para> |
John R. Sheets | 1828e59 | 2001-01-24 19:36:24 +0000 | [diff] [blame] | 65 | </sect1> |
| 66 | |
| 67 | <sect1 id="bindlls-spec"> |
| 68 | <title id="bindlls-spec.title">Writing the spec file</title> |
| 69 | <para> |
Bill Medland | e145bde | 2001-10-02 17:48:16 +0000 | [diff] [blame] | 70 | Start by writing the spec file. This file will describe the interface |
| 71 | as if it was a dll. See elsewhere for the details of the format of |
| 72 | a spec file. |
| 73 | </para> |
| 74 | <para> |
| 75 | In the simple example we want a Wine builtin Dll that corresponds to |
| 76 | the MyWin Dll. The spec file is <filename>libMyWin.spec</filename> and |
| 77 | looks like this. |
| 78 | <programlisting> |
| 79 | # |
| 80 | # File: libMyWin.spec |
| 81 | # |
| 82 | # some sort of copyright |
| 83 | # |
| 84 | # Wine spec file for the libMyWin builtin library (a minimal wrapper around the |
| 85 | # linux library libMyLinux) |
| 86 | # |
| 87 | # For further details of wine spec files see the Winelib documentation at |
| 88 | # www.winehq.com |
| 89 | |
| 90 | name MyWin |
| 91 | type win32 |
| 92 | mode dll |
| 93 | |
| 94 | 2 stdcall _MyWinFunc@32 (long ptr ptr ptr ptr long long ptr) MyProxyWinFunc |
| 95 | |
| 96 | # End of file |
| 97 | </programlisting> |
| 98 | Notice that the arguments are flagged as long even though they are |
| 99 | smaller than that. |
| 100 | </para> |
| 101 | <para> |
| 102 | In the case of the ODBC example you can see this in the file |
| 103 | <filename>odbc32.spec</filename>. |
John R. Sheets | 1828e59 | 2001-01-24 19:36:24 +0000 | [diff] [blame] | 104 | </para> |
| 105 | </sect1> |
| 106 | |
| 107 | <sect1 id="bindlls-cxx-apis"> |
| 108 | <title id="bindlls-cxx-apis.title">How to deal with C++ APIs</title> |
| 109 | <para> |
| 110 | names are mangled, how to demangle them, how to call them |
| 111 | </para> |
| 112 | </sect1> |
| 113 | |
| 114 | <sect1 id="bindlls-wrapper"> |
| 115 | <title id="bindlls-wrapper.title">Writing the wrapper</title> |
| 116 | <para> |
Bill Medland | e145bde | 2001-10-02 17:48:16 +0000 | [diff] [blame] | 117 | Firstly we will look at the simple example. The main complication of |
| 118 | this case is the slightly different argument lists. The f parameter |
| 119 | does not have to be passed to the Linux function and the d parameter |
| 120 | (theoretically) has to be converted between unsigned long * and |
| 121 | unsigned short *. Doing this ensures that the "high" bits of the |
| 122 | returned value are set correctly. |
| 123 | <programlisting> |
| 124 | /* |
| 125 | * File: MyWin.c |
| 126 | * |
| 127 | * Copyright (c) The copyright holder. |
| 128 | * |
| 129 | * Basic WINE wrapper for the linux <3rd party library> so that it can be |
| 130 | * used by <the application> |
| 131 | * |
| 132 | * Currently this file makes no attempt to be a full wrapper for the <3rd |
| 133 | * party library>; it only exports enough for our own use. |
| 134 | * |
| 135 | * Note that this is a Unix file; please don't go converting it to DOS format |
| 136 | * (e.g. converting line feeds to Carriage return/Line feed). |
| 137 | * |
| 138 | * This file should be built in a Wine environment as a WineLib library, |
| 139 | * linked to the Linux <3rd party> libraries (currently libxxxx.so and |
| 140 | * libyyyy.so) |
| 141 | */ |
| 142 | |
| 143 | #include < <3rd party linux header> > |
| 144 | #include <windef.h> /* Part of the Wine header files */ |
| 145 | |
| 146 | signed short WINAPI MyProxyWinFunc (unsigned short a, void *b, void *c, |
| 147 | unsigned long *d, void *e, unsigned char f, char g, |
| 148 | unsigned char *h) |
| 149 | /* This declaration is as defined in the spec file. It is deliberately not |
| 150 | * specified in terms of <3rd party> types since we are messing about here |
| 151 | * between two operating systems (making it look like a Windows thing when |
| 152 | * actually it is a Linux thing). In this way the compiler will point out any |
| 153 | * inconsistencies. |
| 154 | * For example the fourth argument needs care |
| 155 | */ |
| 156 | { |
| 157 | unsigned short d1; |
| 158 | signed short ret; |
| 159 | |
| 160 | d1 = (unsigned short) *d; |
| 161 | ret = <3rd party linux function> (a, b, c, &d1, e, g, h); |
| 162 | *d = d1; |
| 163 | |
| 164 | return ret; |
| 165 | } |
| 166 | |
| 167 | /* End of file */ |
| 168 | </programlisting> |
| 169 | </para> |
| 170 | <para> |
| 171 | For a more extensive case we can use the ODBC example. This is |
| 172 | implemented as a header file |
| 173 | (<filename class="HeaderFile">proxyodbc.h</filename>) and the actual |
| 174 | C source file (<filename>proxyodbc.c</filename>). Although the file |
| 175 | is quite long it is extremely simple in structure. |
| 176 | </para> |
| 177 | <para> |
| 178 | The MAIN_OdbcInit function is the function that was named in the |
| 179 | <link linkend="bindlls-spec">spec file</link> as the init function. |
| 180 | On the process attach event the function dynamically links to the |
| 181 | desired Linux ODBC library (since there are several available) and |
| 182 | builds a list of function pointers. It unlinks on the process |
| 183 | detach event. |
| 184 | </para> |
| 185 | <para> |
| 186 | Then each of the functions simply calls the appropriate Linux function |
| 187 | through the function pointer that was set up during initialisation. |
| 188 | </para> |
| 189 | </sect1> |
| 190 | |
| 191 | <sect1 id="bindlls-building"> |
| 192 | <title id="binary-dlls-building.title">Building</title> |
| 193 | <para> |
| 194 | So how dow we actually build the Wine builtin Dll? The easiest way is |
| 195 | to get Winemaker to do the hard work for us. For the simple example we |
| 196 | have two source files (the wrapper and the spec file). We also have |
| 197 | the 3rd party header and library files of course. |
| 198 | </para> |
| 199 | <para> |
| 200 | Put the two source files in a suitable directory and then use |
| 201 | winemaker to create the build framework, including configure script, |
| 202 | makefile etc. You will want to use the following options of |
| 203 | winemaker: |
| 204 | <itemizedlist> |
| 205 | <listitem> |
| 206 | <para> |
| 207 | --nosource-fix and --nogenerate-specs (requires winemaker version |
| 208 | 0.5.8 or later) to ensure that the two files are not modified. |
| 209 | (If using an older version of winemaker then make the two files |
| 210 | readonly and ignore the complaints about being unable to modify |
| 211 | them). |
| 212 | </para> |
| 213 | </listitem> |
| 214 | <listitem> |
| 215 | <para> |
| 216 | --dll --single-target MyWin --nomfc to specify the target |
| 217 | </para> |
| 218 | </listitem> |
| 219 | <listitem> |
| 220 | <para> |
| 221 | -DMightNeedSomething -I3rd_party_include -L3rd_party_lib -lxxxx |
| 222 | -lyyyy where these are the locations of the header files etc. |
| 223 | </para> |
| 224 | </listitem> |
| 225 | </itemizedlist> |
| 226 | </para> |
| 227 | <para> |
| 228 | After running winemaker I like to edit the Makefile.in to add the line |
| 229 | CEXTRA = -Wall just before the DEFINES =. |
| 230 | </para> |
| 231 | <para> |
| 232 | Then simply run the configure and make as normal (described elsewhere). |
John R. Sheets | 1828e59 | 2001-01-24 19:36:24 +0000 | [diff] [blame] | 233 | </para> |
| 234 | </sect1> |
| 235 | </chapter> |
| 236 | |
| 237 | <!-- Keep this comment at the end of the file |
| 238 | Local variables: |
| 239 | mode: sgml |
| 240 | sgml-parent-document:("wine-doc.sgml" "book" "chapter" "") |
| 241 | End: |
| 242 | --> |