mirror of
https://github.com/reactos/wine.git
synced 2024-11-29 22:50:43 +00:00
293 lines
12 KiB
Plaintext
293 lines
12 KiB
Plaintext
<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:
|
|
-->
|