For (semi)persistent information, I fully agree with Spencer. But if you need some quick runtime info for debugging/tracing purposes, you can "hijack" a commandline window and write to it.
protected override OnStart(string[] args)
{
int processId;
IntPtr ptr = GetForegroundWindow();
GetWindowThreadProcessId(ptr, out processId);
var process = Process.GetProcessById(processId);
if (String.CompareOrdinal(process.ProcessName, "cmd") == 0)
{
// Hijack an existing foreground cmd window.
AttachConsole(process.Id);
}
else
{
// Or create a new one
AllocConsole();
}
}
protected override OnStop()
{
FreeConsole();
}
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AllocConsole();
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool FreeConsole();
[DllImport("kernel32", SetLastError = true)]
static extern bool AttachConsole(int dwProcessId);
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
Then you can use the console for your output, ideally with 3rd party logging systems like log4net. In my scenario the setup looked like:
//Enable log4net logging into the console
ConsoleAppender appender = new ConsoleAppender {Layout = new SimpleLayout()};
BasicConfigurator.Configure(appender);
But that part is log4net-specific. You should be able to use even Console.Write()
, although it's much harder to set up.
What I like about this is that you can "bind" it to commandline/config parameters of the service and the 3rd party logger will just switch logging from its default location to the console, if you set it up so. No need to do any further checks in code. Also you can do this anywhere - in your dev machine, on a remote server...
Anyway, as I said in the beginning, if you don't really need realtime information, I'd stick with Spencer's advice.