- Download source - 4.4 KB (executable program compiled with .NET Framework 4.0, please compile<command: csc.exe Coc.CodeOfCooper.ServiceableConsoleProgramDemo.cs> with source code if you are using lower version)
Background
Generally, when developing a Windows service program, we may like to also include a console program for testing or debugging. Because we can't directly debug a service, we have to run a service first, then try to attach the service process quickly, otherwise we may miss the break point after we attach it, or (the third way) we have to write helpful code which allows us to attach a slower and catch up process go to the break point, we usually add, e.g., Thread.Sleep(10000)
. This article will introduce a different way to handle this problem. We merge the console program and service program together, and it is quiet easy.
Demo
In the above demo, I have shown two usages: the first one is a console program, another one is a Windows service, which can be created and deleted by sc.exe. sc.exe is a Windows system program, more info please check out here: http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/sc.mspx?mfr=true, . there is another way to created and deleted windows service by .NET Framework tool(v2.0 above), which is InstallUtil.exe(http://msdn.microsoft.com/en-us/library/50614e95(v=vs.80).aspx), our Demo program doesn't support InstallUtil yet, because we didn't include any Installer
, and Installer
is required to install by InstallUtil.exe, we need to add a Installer
for support InstallUtil.
Using the code
Very easy to implement, detaled steps are following:
- Download attached ZIP file, and unzip file to somewhere, e.g. c:\work
- Rewrite or replace the business logic class:
ComponentRunner
- Rewrite or replace the service class:
MainService
, in demo, the service just run one time, and doesn't run anymore, its behave doesn't a service, you need
to rewrite or replace it to make your service running always...
Explanation of Source Code
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Console.WriteLine("CurrentDomain_UnhandledException " + e.ExceptionObject.ToString());
}
It's a good idea to handle all unhandled exceptions here, once somewhere we forget to catch the exception, we have a final place to jot something.
Process parentProcess = ParentProcessUtilities.GetParentProcess();
bool runFromService = (parentProcess != null &&
string.Compare(parentProcess.ProcessName, "services", true) == 0);
Console.WriteLine("started from service? {0}", runFromService);
if (runFromService)
{
Environment.CurrentDirectory = AppDomain.CurrentDomain.BaseDirectory;
ServiceBase.Run(new ServiceBase[] { new MainService() });
}
else
{
new ComponentRunner().Run();
Console.WriteLine("\r\nPress any key to quit(still running)...");
Console.ReadLine();
}
We get the parent process of the current process by a utility of the process ParentProcessUtilities
,
then check is its process name equals 'services'. This is a Windows system process, it manages all service processes, you can see it and its subordinates from
the below image:
This image is captured from the tool Process Explorer which is a useful and must have tool for Windows programmers.
You can get more information here: http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx.
The next code shows the ParentProcessUtilities
class.
[StructLayout(LayoutKind.Sequential)]
public struct ParentProcessUtilities
{
internal IntPtr Reserved1;
internal IntPtr PebBaseAddress;
internal IntPtr Reserved2_0;
internal IntPtr Reserved2_1;
internal IntPtr UniqueProcessId;
internal IntPtr InheritedFromUniqueProcessId;
[DllImport("ntdll.dll")]
private static extern int NtQueryInformationProcess(IntPtr processHandle,
int processInformationClass,
ref ParentProcessUtilities processInformation,
int processInformationLength,
out int returnLength);
public static Process GetParentProcess()
{
return GetParentProcess(Process.GetCurrentProcess().Handle);
}
public static Process GetParentProcess(int id)
{
Process process = Process.GetProcessById(id);
return GetParentProcess(process.Handle);
}
public static Process GetParentProcess(IntPtr handle)
{
ParentProcessUtilities pbi = new ParentProcessUtilities();
int returnLength;
int status = NtQueryInformationProcess(handle, 0, ref pbi,
Marshal.SizeOf(pbi), out returnLength);
if (status != 0)
throw new Win32Exception(status);
try
{
return Process.GetProcessById(pbi.InheritedFromUniqueProcessId.ToInt32());
}
catch (ArgumentException)
{
return null;
}
}
}
This code use the Windows API NtQueryInformationProcess
to obtain the parent process. This code was referenced from internet, http://stackoverflow.com/questions/394816/how-to-get-parent-process-in-net-in-managed-way, this page also includes a managed way to get the parent process, but it fails when working from a service.
Enjoy the code.
History
Version 0.1, created on 2012-06-01.