Understanding Wine
DLL and Service Development in Wine
»Index
»Setup
»DLLs
»Services
»Windows
»svchost
»Links
»Notes
»Author
»Wine HQ

Other Documentation

Review the Developer Hints over at the wine wiki.

C Code (in not C++ Code)

Translating Between C++ and C

Wine is written using C. Most COM documentation assumes development using C. Careful use of structure pointers allows the emulation of object oriented programming in C. The C code:

manager->Release(ptr);

is equivalent to the C code:

manager->lpVtable->Release(manager, ptr);

The C manager object is emulated in C using a manager struct that includes the lpVtable struct for function dispatch. The C implementation explicitly looks up the function pointer for Release in lpVtable. Finally, the C function explicitly passes a self reference to emulate the C this pointer.

Defining COBJMACROS (before including a COM classes header file) enables macros to make this syntax closer to what would be used in C++. The above function call would become (assuming that the Ranager function is a method of class Class):

Multiple interfaces to an object are facilitated by a single implementation struct that contains as members a vtable for each interface provided by the object. Data members are also included in this struct. A nice example of this can be found in the clibpoard.c.

Class_Release(manager, ptr);

Unicode

Many Wine applications are developed for a unicode environment. As described at TODO, Wine developers are not able to do either of:

FooBarW("Test");
FooBarW(L"Test");

since the former generates a standard character string and the latter is not uniformly supported by the GCC compiler. That means that as wine developers we get to:

static const WCHAR testW[] = {'t','e','s','t',0};
FooBarW(testW);

Many COM functions come in two flavors. Those ending in W for (wide) unicode and those ending in A for regular ASCII strings. Be sure to use the correct copy. Of course, that is what the type checker is for…

Interface description language (IDL)

In theory oleview can be used to generate an IDL from a type library (TLB). If you are fortunate, the type library may be bundled in a DLL is in need of being rewritten. My work has not yet been this fortunate, so I have needed to write my IDLs by hand.

An IDL is compiled into useful dot c and dot h files using widl. The curious can run widl by hand to see the generated files. Most projects will simply configure the Makefile to call widl. A recommended method of doing this is:

  • Create a local IDL file (projectName_local.idl) that includes the project IDL file.

  • Add DL_I_SRCS = projectName_local.idl after your C_SRCS listing in Makefile.in.

Note that Makedepend may not properly skip over include directives that are block commented out (TODO: verify this). Beware!

The widl program is also used to generate the dlldata.c file that implements proxy support for an interface. Proxy code is only generated for object blocks in the IDL specification. Unlike MIDL, it does not generate proxy code for odl blocks.

Submitting to Wine

Do not send attach a patch containing a full valid mail header to emails. The email header causes a good deal of confusion for many systems. Do include in the email a descriptive subject stating what is being worked on and be sure that your real full name is listed as the sender.

If you are developing a new DLL (or server) it is a good idea to:

  • Write a skeleton implementation that includes a Makefile.in, main dot c file (pretty much empty), and dot spec file with stubs. TODO: Add a few more details on this.

  • Ensure that the DLL builds cleanly

  • Create and submit base patch

  • Begin developing small specific test that you confirm pass under Windows

  • Expand your DLL to pass these tests

  • Patch and submit again

  • Rinse, lather, repeat

Development Cycle

  • Write IDL

  • Write Dll skeleton

  • Add files to git repository

  • Execute make_makefile to add new code to the build process

  • Execute config.stats; make depend to update the build system

  • Compile

Component Life Cycle

Component Registration

Many projects use the common regsevr.c to handle registering and unregestring the DLL. It can be added to a project by copying over a copy of the file and updating the regsvr_coclass and regsvr_interface structures, and including the DLL specific header file.

DLL Creation and Destruction

Some modules use factory.c to implement the factory that will generate a new instance of the object to be returned by calls to DllGetClassObject (and more general calls to the factory methods of a class).

Misc.

The Windows program depends.exe can help you see the functions that a DLL exposes to the world.

A handy way to see what symbols your program exports is the nm command with the -g flag.

uuid.c is the home of many GUIDs.

COM Services

To play around with a COM service, it is good to leave regserv running in the background. This keeps the SCM active even if your application is not actively using a service.

Running Services

Begin by registering your service. Then start your service process with ./wine service. Then start the service (assuming it is registered with the SCM) using net start service

Debugging

The first line of defense when debugging a wine application is specifying a good set of flags for WINEDEBUG. For example, debugging communication to a server process can begin with:

WINEDEBUG=+file,+ole,+count ./wine dlls/bits/bits.dll.so

Problems With Wine

WaitForMultipleObjectsEx

Wine's version of WaitForMultipleObjectsEx runs into problems due to the creation of exe.so files by the Wine build system. When building a server named test_server, Wine produces the files test_server.exe.so with the actual binary and a symbolic link to winewrapper named test_sever. The latter is used so that users need not execute the program using wine test_server. Passing three different executable names to WaitForMultipleObjectsEx causes three forms of behavior:

  • test_server.exe fails since WaitForMultipleObjectsEx is unable to find the executable. However, this is what is commonly used in Windows registry entries.

  • test_sever causes WaitForMultipleObjectsEx to return an error code since the binary (remember we are looking at a symbolic link) is evaluated to be of type BINARY_UNIX_EXE. The server is actually started, but the error code is returned.

  • test_server.exe.so works without problems. But this is not compatible with Windows.

One potential solution to this problem use wine.inf to create a fake executable for it.