3

I have a Windows Service that monitors a COM port connected to a vendors hardware. This is a very busy piece of hardware that is constantly polling other devices on the wire (this is a twisted-pair RS485 "network"). My software needs to emulate X number of hardware devices on this wire, so I've got a multi-threaded thing going on with a multi-tiered state machine to keep track of where the communications protocol is at any moment.

Problem is with a Windows Service (this is my first one, BTW) is that you need some debugging to let you know if stuff is working properly. When I was first developing this state machine/multi-thread code I had a windows form with a RichTextBox that displayed the ASCII chars going back-n-forth on the line. Seems like I can't really have that GUI niceness with a service. I tried opening a form in the service via another program that sent the service messages that are received via the OnCustomCommand() handler but it didn't seem to work. I had "Allow service to interact with desktop" checked and everything. I was using the Show() and Hide() methods of my debug form.

I guess I don't need to see all of the individual characters going on the line but man that sure would be nice (I think I really need to see them :-) ). So does anyone have any crazy ideas that could help me out? I don't want to bog down the system with some IPC that isn't meant for the voluminous amount of data that is sure to come through. It will only be very short-term debugging though, just confirmation that the program, the RS485-to-USB dongle, and hardware is all working.

Kelly Beard
  • 621
  • 1
  • 7
  • 18

5 Answers5

4

Use OutputDebugString to write to the debugging buffer and then use DebugView to watch it. If you're running on Windows XP or earlier, then you can use PortMon to see the raw bytes going through the serial port. The advantage over a log file is that there's very little overhead, particularly when you're not watching it. You can even run DebugView from another machine and monitor your service remotely.

Don Kirkby
  • 41,771
  • 21
  • 173
  • 252
  • I must be doing something wrong, because it's not working. If I understand correctly, I should be able to just dump Debug.WriteLine() statements wherever and run this program but I'm not getting anything. – Kelly Beard May 11 '11 at 19:06
  • I think I have it now, but I have to use Debug.WriteLine(). – Kelly Beard May 11 '11 at 19:40
2

I dunno if it will work for you, but I always build my services with a extra Main that build them as console app to get debug output.

Edit:

Some example:

class Worker : ServiceBase
{

#if(RELEASE)
        /// <summary>
        /// The Main Thread where the Service is Run.
        /// </summary>
        static void Main()
        {
            ServiceBase.Run(new Worker());
        }
#endif

#if(DEBUG)
        public static void Main(String[] args)
        {
            Worker worker = new Worker();
            worker.OnStart(null);
            Console.ReadLine();
            worker.OnStop();
        }
#endif

        // Other Service code
}
sra
  • 23,629
  • 7
  • 52
  • 88
1

You could write the output to a log file and then use another application to watch that file. This question about "tail" outlines several options for watching log files with windows.

Community
  • 1
  • 1
msergeant
  • 4,701
  • 3
  • 23
  • 25
0

What I usually do when working on a Windows Service is to create it so that it can be run either as a service, or as a plain old command-line application. You can easily check whether you are running as a service by checking Environment.UserInteractive. If this property is true, then you are running from the command line. If the property is false, then you are running as a service. Add this code to Program.cs, and use it where you would normally call ServiceBase.Run(servicesToRun)

/// <summary>Runs the provided service classes.</summary>
/// <param name="servicesToRun">The service classes to run.</param>
/// <param name="args">The command-line arguments to pass to the service classes.</param>
private static void RunServices(IEnumerable<ServiceBase> servicesToRun, IEnumerable args)
{
    var serviceBaseType = typeof(ServiceBase);
    var onStartMethod = serviceBaseType.GetMethod("OnStart", BindingFlags.Instance | BindingFlags.NonPublic);
    foreach (var service in servicesToRun)
    {
        onStartMethod.Invoke(service, new object[] { args });
        Console.WriteLine(service.ServiceName + " started.");
    }

    Console.WriteLine("Press any key to exit.");
    Console.ReadKey();

    var onStopMethod = serviceBaseType.GetMethod("OnStop", BindingFlags.Instance | BindingFlags.NonPublic);
    foreach (var service in servicesToRun)
    {
        onStopMethod.Invoke(service, null);
        Console.WriteLine(service.ServiceName + " stopped.");
    }
}

Now you can debug your service, set breakpoints, anything you want. When you run your application, you'll get a console window, appropriate for displaying console messages, and it will stay open until you hit a key.

Mel
  • 2,057
  • 18
  • 22
0

I'm answering my own question here. I tried a couple of suggestions here but here's what I ended up doing...

I created a Windows Form application with a single Button and RichTextBox. This application constructed a NamedPipeServerStream on it's end. The Button's job was to send either "debug on" (command 128) or "debug off" (129) to the Windows Service. The initial value was "debug off". When the button was clicked, a command of 128 was sent to the Windows Service to turn debugging on. In the Windows Service this triggered an internal variable to be true, plus it connected to the Form application with a NamedPipeClientStream and started sending characters with a BinaryWriter as they were received or sent on the COM port. On the Form side, a BackgroundWorker was created to WaitForConnection() on the pipe. When it got a connection, a BinaryReader.ReadString() was used to read the data off of the pipe and shoot it to the RichTextBox.

I'm almost there. I'm breaking my pipe when I click the debug button again and a subsequent click doesn't correctly redo the pipe. All in all I'm happy with it. I can post any code if anyone is interested. Thanks for the responses!

Kelly Beard
  • 621
  • 1
  • 7
  • 18