| <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 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: | |
| --> |