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

This tutorial modifies the count DLL described in the COM DLL tutorial. The first version creates server running as a stand alone executable to provide count objects. The second version makes a few more modifications to provide count objects through a Windows service application.

This tutorial demonstrates the creation of standalone COM servers and how to register COM components from a Windows service application.

Source code for this tutorial is available from the tutorial's introduction.

Synopsis

  • Relation to COM DLLs

  • Standalone COM Component Servers

  • Building a Proxy / Stub

  • Testing a Standalone COM Component Server

  • Registering COM Components from a Window Service

  • Testing a Windows Service

Relation to COM DLLs

Similarities to COM DLLs

COM services have many similarities to COM DLLs. The primary distinction lies in methods used by COM to interface with a service.

The same IUnknown and IClassFactory interfaces are used as the COM DLL. Further, the implementation of the core service and a shared header file can be identical to that used in the DLL. The following files from earlier count example can be used without any changes:

  • count.c

  • count_private.h

  • factory.c

  • iunknown.c

  • count_local.idl

Note
Key Points
  • I lied. They are used with almost no changes. Wine uses different macros for debugging DLLs and Wine programs. DLLs use debugging macros such as TRACE and ERR. Wine programs, such as we are describing in this tutorial, prefix these debugging macros with WINE_ resulting in WINE_TRACE and WINE_ERR. Finally, one or two other debugging macros differ in more notorious ways. Diff the source code from the tutorial for details.

Differences from COM DLLs

Unlike COM DLLs using DllRegisterServer and DllUnregisterServer to register and unregister the DLL, COM services are executed using the /regserver or /unregserver options to add or remove them from the registry.

Rather than DllMain and DllGetClassObject used by COM DLLs, a COM service uses a traditional main function. Stand alone servers registers COM objects when this main function is executed, while Windows services use the main function to register the service with the Service Control Manager (SCM).

Standalone COM Component Servers

A service without SCM is very easy to write. An example of the count service written in this manner is in $WINEPATH/programs/count_server/count_server.c.

This replaces $WINEPATH/dlls/count/count_main.c from the prior tutorial. The main function simply parses command line options to either: register the server, unregister the server (only a stub implementation), or default to registering the class object by calling CoInitialize followed by CoRegisterClassObject.

Note
Key Points
  • CoInitialize must be called before CoRegisterClassObject to initialize the underlying COM infrastructure that is used when registering a new class object.

  • An object is registered with COM by providing it with an instance of the IClassFactory interface for the object via a call to CoRegisterClassObject.

Makefile Generation

The Makefile.in has a few minor differences from the DLL counterpart. The standalone count server's is located in $WINEPATH/programs/count_server/Makefile.in.

Note
Key Points
  • Remember that the new Makefile.in must be added to the build system for it to become active. Users of git accomplish this using git add on the new Makefile.in,then running ./tools/make_makefiles, and finally ./config.status; make depend. The standalone count server can then be built using make -C $WINEPATH/programs/count_server

Building and Registering a Proxy / Stub

A standalone COM server doesn't run in the same process as the client application. Special communication channels are required for the server and client to interact. The widl compiler in Wine (or midl in Windows) generates source code to marshal and unmarshal function calls between the client and the server. This generated code is compiled into a proxy / stub used to interface a client with a server. Only a Makefile is needed for the proxy / stub since the source code is generated. The Makefile.in used by Wine to generate count proxy is located in $WINEPATH/dlls/count_proxy/Makefile.in.

Note
Key Points
  • No C_SRCS are mentioned since all source files are generated.

  • Generation of interface identifiers and proxy file are requested through the definition of IDL_I_SRCS and IDL_P_SRCS variables.

  • Proxy is implemented in dlldata.c generated by WIDL. The build system knows to build this since dlldata.c is specified as a required EXTRA_OBJS. Rules for making dlldata.o and the corresponding dlldata.c are explicitly added to the Makefile.in.

  • Again, the new Makafile.in must be added the build system. Hopefully you recognize the trend and know what to do from here to build the proxy.

  • The widl compiler does not currently (Oct. 2007) generate source code from IDL using the odl description for interfaces. Changing these to object results in the correct code being generated.

A client program to test the standalone server needs to be developed. This test client changes a single line from the test client used for the count DLL. In particular this client passes in CLSCTX_LOCAL_SERVER in the CoCreateInstance call to specify that a local service running outside the client process is desired. This is in contrast to the DLL test client that specifies CLSCTX_INPROC to request a server that will run in the same process as the client. This modified client is located in $WINEPATH/dlls/count_proxy/tests/count_server_test.c. And the corresponding Makefile in $WINEPATH/dlls/count_proxy/tests/Makefile.in.

Be sure to add the Makefile.in to Wine's build system and build the test client for testing.

Testing a Standalone COM Component Server

The registry must be updated with entries for the count proxy, OLE proxies used by COMs underlying communication channels, and the count server:

cd $WINEPATH
$WINEPATH/programs/regsvr32/regsvr32 count_proxy
$WINEPATH/programs/regsvr32/regsvr32 ole32
$WINEPATH/wine programs/count_server/count_server.exe.so /regserver
Note
Key Points
  • Servers are executed with the /regserver argument to register with the registry. The regsvr32 utility is used to add DLLs to the registry.

  • The registry entries for the count DLL and the count server interfere with each other. The count DLL declares an InProcServer32 entry for the count interface associated with the count.dll. The count service declares an InProcServer32 entry for the count interface associated with the count_proxy.dll. Registering one after the other slightly overwrites the old registry entry.

The count server is manually started using:

$WINEPATH/wine programs/count_server/count_server.exe.so

The service will appear to do nothing since it is waiting for connections and does not normally generate output. After the count server is running the server can be tested (from a second terminal) by running:

$WINEPATH/wine dlls/count_proxy/tests/count_proxy_test.exe.so

Registering COM Components from a Window Service

A service with SCM support requires only minor changes to the stand alone service. The count service is in $WINEPATH/programs/count_service/count_service.c.

The main function calls StartServiceCtrlDispatcher to register the server as a service that is controlled by the SCM. It passes a NULL terminated list of server functions that will execute when the SCM starts the service. For the count service this function is ServiceMain. The ServiceMain function creates the stop_event event to monitor the status of the service, registers a control handler, and finally starts the count server by calling StartCount. The StartCount function initializes COM, initializes the security settings of the service thread created by the SCM, and finally starts the count server by calling CoRegisterClassObject.

Note
Key Points
  • Either COM should be initialized with the COINIT_MULTITHREADED flag (as done in the example code) or the ServiceMain function needs to handle incoming messages. A good discussion of this is provided by an external Single-Threaded Apartment article.

Makefile Generation

A Makefile.in for the count service is available in $WINEPATH/programs/count_service/Makefile.in.

Other than renaming a single file, this is identical to the Makefile.in used for the standalone count server. And yes, remember to integrate this into Wine's build system.

Testing a Windows Service

The same proxy / stub and test client are used to test the count service and the standalone test server. The testing procedure is slightly different since the count service is controlled through the SCM. Registry updating is similar to that used for the standalone count server:

$WINEPATH/programs/regsvr32/regsvr32 count_proxy
$WINEPATH/programs/regsvr32/regsvr32 ole32
$WINEPATH/wine programs/count_service/count_service.exe.so /regserver
Note
TODO
  • The count service currently (Oct. 2007) requires additional entries to be added to the registry. They can be added using regedit:

    $WINEPATH/programs/regedit/regedit $WINEPATH/programs/count_service/count.reg
    

    The $WINEPATH/programs/count_service/count.reg script adds the following entries to the registry:

    REGEDIT4
    
    [HKEY_CLASSES_ROOT\AppID\count_service]
    "AppID"="{72f5782a-867e-11dc-8314-0800200c9a66}"
    
    [HKEY_CLASSES_ROOT\AppID\{72f5782a-867e-11dc-8314-0800200c9a66}
    "LocalService"="count_service"
    
    [HKEY_CLASSES_ROOT\CLSID\{55E9B534-76AA-11DC-8B8E-F6CC56D89593}
    "AppID"="{72f5782a-867e-11dc-8314-0800200c9a66}"
    

Continue by running a long running Wine process to keep Wine's service manager running through the testing procedure. I like to use the regedit in interactive mode. Leave this running while testing your service. Note that regedit is not needed for this test, but is simply a convenient means to start and keep active Wine's service server.

$WINEPATH/wine programs/regedit/regedit.exe.so

The count service is then manually started (in a different terminal) using:

$WINEPATH/wine programs/count_service/count_service.exe.so

The service must then be started. From another terminal run:

$WINEPATH/programs/net/net start count_service

Finally, the service can be tested (from another terminal) by running:

$WINEPATH/wine dlls/count_proxy/tests/count_proxy_test.exe.so