HOWTO: Debugging a service

Last reviewed: October 10, 1997
Article ID: Q98890
The information in this article applies to:
  • Microsoft Win32 Application Programming Interface (API) included with: - Microsoft Windows NT versions 3.51, 4.0

SUMMARY

The steps in this article illustrate how to debug a service under Windows NT using WinDbg and the interactive debugger included with Microsoft Visual C++ 4.x. WinDbg ships with the Win32 Software Development Kit (SDK). For illustration purposes, these procedures use the SERVICE sample, which is built with debugging information by default. This sample is located in:

   Mstools\Samples\Win32\Winnt\Service

There are two techniques for debugging a service. The first technique involves adding a DebugBreak() statement to the service's code and letting the Just-in-time(JIT) debugging feature of Windows NT spawn the debugger when the service executes the DebugBreak(). The second technique involves attaching the debugger to the service while it is running.

MORE INFORMATION

Preparation

  1. Build the sample.

  2. Install the Simple service with the following command:

          simple -install
    

    You receive a message indicating whether you were successful.

  3. Use the Control Panel's Services application to start the Simple service or you can also use:

          sc start simpleservice
    

    Sc.exe is located in Mstools\Bin.

  4. This step is needed for Technique 2: Use PView to get the process ID (PID) for the Simple service. For example, if PView shows the process as simple(0xD5), the PID is 0xD5. If you're using Microsoft Visual C++ 4.X, convert the PID from hexadecimal to decimal. For example, 0xD5 is 213 in decimal.

Technique 1

To specify Microsoft Visual C++ 4.X as your Just-in-time debugger:

  1. On the tools menu, click options.

  2. Click the debug tag, and select the Just-in-time debugging option.

To specify Windbg as your Just-in-time debugger, please refer to the following article in the Microsoft Knowledge Base:

   ARTICLE-ID: Q103861
   TITLE     : Choosing the Debugger That the System Will Spawn

If the service is running in any account other than the LocalSystem, the DebugBreak() technique will not work correctly. If the service account belongs to the administrator's group, the following error message appears when you are running Microsoft Visual C++ 4.X on Windows NT 4.0:

   Runtime error!

   Program: <MSDevDir\bin>\MSDEV.exe

   abnormal program termination

This error does not occur on Windows NT 3.51.

If the spawned debugger is Windbg, the debugger will appear to have correctly attached to the service but you will notice a painting problem with the application.

If the service account does not belong to the administrator's group, the following error message appears on both Microsoft Visual C++ 4.X and Windbg:

   Initialization of the dynamic link library
   <system>\system32\USER32.dll failed.  The process is
   terminating abnormally.

These errors occur because the service account does not have the proper security access to the interactive windowstation and desktop. The easiest solution to the problems is to apply a NULL dacl to the interactive windowstation and desktop, "winsta0\\default" such that you can debug a service that is running in an account other than the LocalSystem.

Sample Code

The following sample code applies a NULL dacl to the interactive windowstation and desktop objects. This application should be executed before debugging the service. Once the debugging session has been completed, the DACLs for the interactive windowstation and desktop objects can be reset by logging off and then logging on again.

   #include <windows.h>
   #include <stdio.h>

   void main(void)
   {
        HDESK                hdesk   = NULL;
        HWINSTA              hwinsta = NULL;
        SECURITY_DESCRIPTOR  sd;
        SECURITY_INFORMATION si      = DACL_SECURITY_INFORMATION;

        __try
             {
             //
             // Obtain a handle to the interactive windowstation.
             //
             hwinsta = OpenWindowStation("winsta0", FALSE, WRITE_DAC);
             if (hwinsta == NULL)
                  __leave;

             //
             // Obtain a handle to the interactive desktop.
             //
             hdesk = OpenDesktop("default", 0, FALSE,
                   WRITE_DAC | DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS);
             if (hdesk == NULL)
                  __leave;

             //
             // Create a null dacl.
             //
             if (!InitializeSecurityDescriptor(&sd,
                  SECURITY_DESCRIPTOR_REVISION))
                  __leave;

             if (!SetSecurityDescriptorDacl(&sd, TRUE, (PACL) NULL, FALSE))
                  __leave;

             //
             // Apply NULL dacl to the windowstation and desktop objects.
             //
             if (!SetUserObjectSecurity(hwinsta, &si, &sd))
                  __leave;

             if (!SetUserObjectSecurity(hdesk, &si, &sd))
                  __leave;
             }
        __finally
             {
             if (hdesk != NULL)
                  CloseDesktop(hdesk);

             if (hwinsta != NULL)
                  CloseWindowStation(hwinsta);
             }
   }

If you are using Windbg as your Just-in-time debugger, you need to either include the path of the service's debug symbols in the system's environment variables or make a call to SetCurrentDirectory() in the service so that the debugger is able to find the debug symbols for the service.

  1. Follow steps 1-5 in the "Preparation" section of this article. Include a DebugBreak() statement in the service's code that is before line 223 in Simple.c.

  2. Once the service has been started, a system dialog box appears. Choose the cancel button to debug the service. The system does now spawn the debugger.

  3. Press the F5 key (a go command) to debug the service.

  4. Follow steps 4-8 in the "Debugging a Service with WinDbg" or steps 3-6 in the "Debugging a Service with MSVC++". Note that if you are using WinDbg step 4 will break on line 245 instead of line 256.

Technique 2

Debugging a Service with WinDbg:

  1. Follow steps 1-5 in the "Preparation" section of this article.

  2. At a command prompt, go to the directory containing the sample executable and type:

          start WinDbg
    

    to start WinDbg in its own command shell.

  3. Open a command window in WinDbg and type:

          .attach <PID>
    

  4. In WinDbg, on the File menu, click Open, and open the source file (Simple.c).

  5. Set breakpoints at lines 223, 245, 256, and 271.

  6. Type "g" (a go command) in the WinDbg command window or press the F5 key to restart after the thread that WinDbg uses to do the .attach terminates.

  7. At the command prompt, start the client by typing:

          client [-pipe <pipename>] [-string <string>]
    

    For example: client -pipe \\.\pipe\simple -string "franki"

  8. Press the F5 key (a go command) to debug the service. The breakpoint hit will be on line 256. Press the F5 key again to go to the next breakpoint. Keep pressing F5 until line 223 waits again for a client to connect. Try connecting another client and repeat the same steps.

Debugging a Service with MSVC++:

  1. Follow steps 1-5 in the "Preparation" section of this article.

  2. At a command prompt, go to the directory containing the sample and type:

          MSDEV /P <PID>
    

    where <PID> is the Process ID value you retrieved in step 4.

  3. In MSVC++, on the File menu, click Open, and open the source file (Simple.c).

  4. Set breakpoints at lines 223, 245, 256, and 271. The lines will change color at this point.

  5. At the command prompt, start the client by typing:

          client [-pipe <pipename>] [-string <string>]
    

    For example: client -pipe \\.\pipe\simple -string "franki"

  6. Press the F5 key (a go command) to debug the service. The breakpoint hit will be on line 256. Press the F5 key again to go to the next breakpoint. Keep pressing F5 until line 223 waits again for a client to connect. Try connecting another client and repeat the same steps.

Exiting Windbg or MSVC++ will kill the service, which must be restarted manually either with the Control Panel or SC.EXE.

NOTE: The "System Account" and the "LocalSystem Account" are the same account.

Keywords          : BseDebug BseSync kbtool
Version           : 3.51 4.0
Platform          : NT WINDOWS
Issue type        : kbhowto


================================================================================


THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Last reviewed: October 10, 1997
© 1998 Microsoft Corporation. All rights reserved. Terms of Use.