I have written a Windows service in C# which can start other EXE files in a
new process, e.g.
protected override void OnStart(string[] args) {
Process p = new Process();
p.StartInfo =
new ProcessStartInfo(@"C:\Program files\Test\Worker.exe", "");
p.Start();
}
Worker.exe is a GUI application which does some calculations and then exits.
In addition to that I have a Windows forms application which is kind of a
'watchdog'. The purpose of it is to make sure that Worker.exe quits after a
certain period of time. To accomplish this the watchdog enumerates all
running processes with the name 'WORKER' and kills them.
Process[] workerProcesses = Process.GetProcessesByName("WORKER");
foreach (Process p in workerProcesses) {
p.Kill();
}
The problem is: How can I distinguish worker.exe processes that are started
by my Windows service application from worker.exe processes that are started
by the user (e.g. through double clicking worker.exe in Windows Explorer)? I
don't want to kill all processes with the name of 'WORKER' but only those
which were started by my Windows service.
To do this I thought it would be a good idea to enumerate all top level
windows and get the corresponding process IDs. If I found a top level window
belonging to a 'WORKER' process I would be sure that this process was started
by the user. On the other hand a WORKER process without top level window is a
process that was started by my Windows service (in this case I would kill the
worker process).
To enumerate top level windows I have a class 'Win32' and a method
'HasTopLevelWindows' in class 'WatchDog':
public class Win32 {
[DllImport("user32.dll")]
public static extern int GetWindowThreadProcessId(
int hWnd,
ref int lpdwProcessId
);
public delegate bool EnumWindowsProc(int hWnd, int lParam);
[DllImport("user32.dll")]
public static extern int EnumWindows(
EnumWindowsProc ewp, // callback procedure
int lParam
);
}
public class WatchDog {
private static int _processId;
private static bool _hasTopLevelWindows;
public static bool HasTopLevelWindows(Process p) {
_processId = p.Id;
_hasTopLevelWindows = false;
Win32.EnumWindowsProc ewp = new Win32.EnumWindowsProc(EvalWindow);
Debug.WriteLine("*** Enum of top level windows start ***");
Win32.EnumWindows(ewp, 0);
Debug.WriteLine("*** Enum end ***");
Debug.WriteLine("Process " + p.Id +
"has top level windows: " + _hasTopLevelWindows);
return _hasTopLevelWindows;
}
private static bool EvalWindow(int hWnd, int lParam) {
int processId = -1;
System.Diagnostics.Debug.WriteLine(
"hWnd = " + hWnd.ToString("x") + " processId = " + processId +
" process = " + Process.GetProcessById(processId).ProcessName);
// get process ID of top level window
Win32.GetWindowThreadProcessId(hWnd, ref processId);
if (_processId == processId) {
_hasTopLevelWindows = true;
// stop enumeration
return false;
}
else {
// continue enumeration
return true;
}
}
The problem: If I start my watchdog.exe as a GUI application it enumerates
all top level windows and also finds all process IDs as expected. If I start
it through my starter service (mentioned at the beginning) then watchdog.exe
does not find all top level windows and finding the corresponding process ID
does not work either. The list of top level windows is much shorter and
GetWindowThreadProcessId returns the same process ID for all of them. The
same code works if I start watchdog.exe by double clicking it in Explorer and
it does not work when I start watchdog.exe via Windows service.
Thanks for any ideas,
Guido
"Peter Huang" - 16 Aug 2004 12:00 GMT
Hi Guido,
You may try to check the "Allow service to interact with desktop" in the
service setting's logon tab by following the steps below.
1. run the services.msc
2. select the service and right click on it and then select properties
3. in the property dialog, select the Logon tab
4. Check local system account and then check the "Allow service to interact
with desktop"
Since the windows service is designed to run as the backgroud application,
why not start the worker.exe in the windows service directly and then
maintain the process handler or process ID, so that we can stop the process
whenever we want it to stop.
Also I do not think it is a proper method to stop the process by killing
the process which may cause resource leak. A better way is to maintain a
global synchronized object, and in the process code we can check the
synchronized object in a loop, once the synchronized obejct is siginaled,
we can exit the process actively after do the proper cleaning job.
Best regards,
Peter Huang
Microsoft Online Partner Support

Signature
Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
"Peter Huang" - 18 Aug 2004 04:44 GMT
Hi Guido,
Have you tried my suggestion?
If you have any concern on this issue, please feel free to post here.
Best regards,
Peter Huang
Microsoft Online Partner Support

Signature
Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.