| <chapter id="bindlls"> |
| <!-- FIXME: note that you can link PE DLLs to Winelib apps --> |
| <title id="bindlls.title">Building WineLib 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 |
| library that you want to use as if it were 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 |
| (e.g. the ODBC interface in Wine). |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| You have a binary only Windows application that can be extended |
| through plugins, such as a text editor or IDE. |
| </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. You combine these to form a Wine built-in Dll that links to the |
| Linux library. Then you modify the DllOverrides in the wine config |
| file to ensure that this new built-in DLL is called rather than any |
| windows version. |
| </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 library 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, int 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 were a DLL. See elsewhere for the details of the format of |
| a spec file (e.g. man winebuild). |
| </para> |
| <para> |
| In the simple example we want a Wine built-in Dll that corresponds to |
| the MyWin Dll. The spec file is <filename>MyWin.dll.spec</filename> and |
| looks something like this: |
| <programlisting> |
| # |
| # File: MyWin.dll.spec |
| # |
| # some sort of copyright |
| # |
| # Wine spec file for the MyWin.dll built-in library (a minimal wrapper around the |
| # linux library libMyLinux) |
| # |
| # For further details of wine spec files see the Winelib documentation at |
| # www.winehq.org |
| |
| 2 stdcall MyWinFunc (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. With this example we will link directly to the |
| Linux shared library whereas with the ODBC example we will load the |
| Linux shared library dynamically. |
| </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-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 |
| <literal>unsigned long *i</literal> and <literal>unsigned short *</literal>. |
| Doing this ensures that the "high" bits of the returned value are set |
| correctly. Also unlike with the ODBC example we will link directly to |
| the Linux Shared Library. |
| <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 */ |
| |
| /* 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 |
| */ |
| signed short WINAPI MyProxyWinFunc (unsigned short a, void *b, void *c, |
| unsigned long *d, void *e, int f, char g, unsigned char *h) |
| { |
| 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> |
| <function>DllMain</function> the function is used to initialize the DLL. |
| 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 initialization. |
| </para> |
| </sect1> |
| |
| <sect1 id="bindlls-building"> |
| <title id="binary-dlls-building.title">Building</title> |
| <para> |
| So how do we actually build the Wine built-in 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> |
| |
| <sect1 id="bindlls-installing"> |
| <title id="binary-dlls-installing.title">Installing</title> |
| <para> |
| So how do you install the proxy and ensure that everything connects up |
| correctly? You have quite a bit of flexibility in this area so what |
| follows are not the only options available. |
| </para> |
| <para> |
| Ensure that the actual Linux Shared Object is placed somewhere where |
| the Linux system will be able to find it. Typically this means it |
| should be in one of the directories mentioned in the /etc/ld.so.conf |
| file or somewhere in the path specified by LD_LIBRARY_PATH. If you |
| can link to it from a Linux program it should be OK. |
| </para> |
| <para> |
| Put the proxy shared object (MyWin.dll.so) in the same place as the |
| rest of the built-in DLLs. (If you used winemaker to set up your build |
| environment then running "make install" as root should do that for you) |
| Alternatively ensure that WINEDLLPATH includes the directory containing |
| the proxy shared object. |
| </para> |
| <para> |
| If you have both a Windows DLL and a Linux DLL/proxy pair then you will |
| have to ensure that the correct one gets called. The easiest way is |
| probably simply to rename the windows version so that it doesn't get |
| detected. Alternatively you could specify in the DllOverrides section |
| (or the AppDefaults\\myprog.exe\\DllOverrides section) of the config |
| file (in your .wine directory) that the built-in version be used. Note |
| that if the Windows version Dll is present and is in the same |
| directory as the executable (as opposed to being in the Windows |
| directory) then you will currently need to specify the whole path to |
| the dll, not merely its name. |
| </para> |
| <para> |
| Once you have done this you should be using the Linux Shared Object |
| successfully. If you have problems then set the WINEDEBUG=+module |
| environment variable before running wine to see what is actually happening. |
| </para> |
| </sect1> |
| |
| <sect1 id="bindlls-filenames"> |
| <title id="binary-dlls-filenames.title">Converting filenames</title> |
| <para> |
| Suppose you want to convert incoming DOS format filenames to their |
| Unix equivalent. Of course there is no suitable function in the true |
| Microsoft Windows API, but wine provides a function for just this |
| task and exports it from its copy of the kernel32 DLL. The function |
| is <function>wine_get_unix_file_name</function> (defined in winbase.h). |
| </para> |
| </sect1> |
| </chapter> |
| |
| <!-- Keep this comment at the end of the file |
| Local variables: |
| mode: sgml |
| sgml-parent-document:("winelib-user.sgml" "book" "chapter" "") |
| End: |
| --> |