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

This final tutorial describes compiling the count service from the prior tutorials under Windows. This tutorial assumes that basic Windows development tools are installed and ready to use. a brief overview of installing this tool chain for Windows can be found in the notes about Visual Studio.

Compilation of the count DLL or standalone count server can be accomplished by modifying the Makefiles provided in this tutorial.

Synopsis

  • Source Code Layout

  • Compiling the Count Proxy

  • Compiling the Count Proxy Test

  • Compiling the Count Service

  • Extending the Registry

  • Executing the Count Service

  • Potential Solutions to Common Problems

Source Code Layout

Copy the source code for this tutorial onto the Windows machine. Beware Windows uses custom version of libcount_proxy.def, count_private.h, and debug.h, rather than those available via Wine. Versions of these files customized for use in Windows are located in the count_tutorial/count_tutorial_for_windows/ directory of the tutorial source code.

libcount_proxy.def

This file is not distributed in the sample code since it is generated using widl. Windows users without Wine can directly copy the version listed online in this tutorial.

count_private.h

This file is located in multiple locations. All versions need to be updated for execution under Windows.

debug.h

This file is not distributed with the sample code since it is specific to Windows compilation. Windows users can directly copy the version listed online in this tutorial.

Caution
Updating libcount_proxy.def

You must manually update libcount_proxy.def before compiling the count proxy.

Note that the libcount_proxy.def file is created from count_proxy.spec using the winebuild tool using the command:

$WINEPATH/tools/winebuild/winebuild -w --def -o libcount.def --export ./count.spec

A normal build creates this file for you. Manually update the def file for use under Windows by renaming the exports to have names without decoration. Wine generated version of libcount.def:

; File generated automatically from ./count_proxy.spec; do not edit!

LIBRARY count_proxy.dll

EXPORTS
  DllCanUnloadNow@0 @1 PRIVATE
  DllGetClassObject@12 @2 PRIVATE
  DllRegisterServer@0 @3 PRIVATE
  DllUnregisterServer@0 @4 PRIVATE
  GetProxyDllInfo@8 @5

should be modified placed in count_tutorial/dlls/count_proxy/libcount.def with the following updates:

LIBRARY count_proxy.dll

EXPORTS
  DllCanUnloadNow = _DllCanUnloadNow@0 PRIVATE
  DllGetClassObject = _DllGetClassObject@12 PRIVATE
  DllRegisterServer = _DllRegisterServer@0 PRIVATE
  DllUnregisterServer = _DllUnregisterServer@0 PRIVATE
  GetProxyDllInfo = _GetProxyDllInfo@8

A corrected copy of this file is available in src\count_tutorial_for_windows\libcount.def.

Caution
Updating count_private.h

You must manually update count_private.h before compiling the count proxy.

The count_private.h header uses the offsetof macro to locate vtable offsets within the CountImpl structure. Use of this macro in Windows requires the inclusion of the stddef.h header. Update count_private.h so that the first includes look like:

#ifndef __COUNT_PRIVATE_H__
#define __COUNT_PRIVATE_H__

#include <stddef.h>
#include <stdarg.h>

#define COBJMACROS
Caution
Nonstandard version of debug.h and test.h

You must use a nonstandard version of Wine's debug.h header and a standard version of Wine's test.h.

Wine programs use macros such as WINE_DEBUG (internal to Wine) and DEBUG (general programs) to provide debugging and traces to developers. The following version of debug.h located at count_tutorial\count_tutorial_for_windows\debug.h should be copied into count_tutorial\include\wine\debug.h to provide null versions of these macros.

Wine tests use the common header file test.h located at count_tutorial\count_tutorial_for_windows\test.h that must be copied into count_tutorial\include\wine\test.h to provide the testing marcos.

Compiling the Count Proxy

The count_tutorial\count_tutorial_for_windows\Makefile.proxy should be manually copied into count_tutorial\count_proxy\Makefile. The count proxy DLL can then be built using nmake.

Compiling the Count Proxy Test

The count_tutorial\count_tutorial_for_windows\Makefile.tests should be manually copied into count_tutorial\count_proxy\tests\Makefile. The count proxy DLL can then be built using nmake.

Compiling the Count Service

The count_tutorial\count_tutorial_for_windows\Makefile.service should be manually copied into count_tutorial\count_proxy\programs\count_service\Makefile. The count proxy DLL can then be built using nmake.

Extending the Registry

A few extra registry entries are needed to run the service through the SCM. The following registry entries can be added by hand using the Windows regedit program. Do be aware that editing the Windows registry can destroy your Windows installation. Edit at your own risk.

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}"

These entries can be added by executing regedit count.reg from the count_tutorial\count_tutorial_for_windows directory containing the above count_tutorial\count_tutorial_for_windows\count.reg file.

Executing the Count Service

  1. Copy over the source for the tutorial. This creates a count_tutorial directory to work from. The examples below use the variable $TUTORIAL to represent the location that you installed the tutorial count_tutorial.

  2. Copy the Windows Makefiles into their correct locations:

    cd $TUTORIAL
    cp count_tutorial_for_windows\Makefile.proxy dlls\count_proxy\Makefile
    cp count_tutorial_for_windows\Makefile.tests dlls\count_proxy\tests/Makefile
    cp count_tutorial_for_windows\Makefile.service programs\service\Makefile
    
  3. Register the count proxy. From a command prompt execute:

    cd $TUTORIAL\dlls\count_proxy
    regsvr32 count_proxy
    
  4. Ensure that the local system account has access to the service program. An easy way to do this is via the chmod command available in Cygwin.

    cd $TUTORIAL
    chmod -R 755 count_tutorial/
    

    I would love for a Windows guru to email me with instructions on how to do the equivalent from a Windows Command Prompt.

  5. Register the service with the SCM.

    cd $TUTORIAL\programs\count_service
    count_service.exe /regserver
    
  6. Start the service from the Services utility:

    Start -> Control Panel -> Administrative Tools -> Services -> count_service -> start

    or from a command prompt using net start count_service.

  7. Execute the test client from a command prompt:

    cd $TUTORIAL\dlls\count_proxy\tests
    count_server_test.exe
    

Potential Solutions to Common Problems

Debugging output from a COM object running as a service

Message boxes provide a primitive printf form of debugging to trace the activity of a COM object exposed via a service. Easy and effective for simple tracing and examining error codes. Basic message box syntax is:

char mb_text[200];

hres = ...;
sprintf("Debugging text with printf var expansion: 0x%08X\n", hres);
MessageBox(NULL, mb_string, NULL, MB_OK);

The service must be configured to have access to interact with the desktop for this type of debugging. This is configurable via the by right clicking on the service in the Services utility, selecting the Log On tab, and checking the Allow service to interact with desktop box. Note that selecting this option can cause new sources of failure for your application. But its a start and has gotten me through some tough service startup bugs.

Remember to remove the message boxes when you have finished debugging.

Attempts to create an object by my client generates the error 0x80070005 (E_ACCESSDENIED)

Need to properly configure the security settings of the service. These settings can be adjusted using calls to CoInitializeSecurity. A permissive setting to begin working from is:

hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_NONE,
    RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);

You can also experiment with configuring the service to run as the current user. This is accomplished via the service name in the services utility, selecting Properties -> Log On -> This account, and specifying the current user account and password. Note that using CoInitializeSecurity is a better long term solution.

Attempts to create an object by my client generates the error 0x800401F0 (CO_E_NOTINITIALIZED)

This is a registry problem. Double check that the proxy / stub is registered and that the entries listed in "Extending the Registry" above are in the registry.

Calling CoCreateInstance from client into COM object provided by a service hangs

The ServiceMain (or equivalent) function of your service is probably hanging do to improper handling of incoming messages. Either the call to CoInitialize must be declared as multithreaded or the ServiceMain needs to include a message handling loop rather than blocking for a stop event.

A second source of hang is the use of message boxes when the service is not configured to interact with the desktop. Remember that (despite my advice above) services should generally not use message boxes.

Attempts to start the service result in "Access Denied" errors

One cause of this is incorrect folder / file permissions since services often run as "Local Service" rather than the user owning the service. Setting group read and execute permissions on folders / files in question may fix this problem.