Can I send a ctrl-C (SIGINT) to an application on Windows? - windows

I have (in the past) written cross-platform (Windows/Unix) applications which, when started from the command line, handled a user-typed Ctrl-C combination in the same way (i.e. to terminate the application cleanly).
Is it possible on Windows to send a Ctrl-C/SIGINT/equivalent to a process from another (unrelated) process to request that it terminate cleanly (giving it an opportunity to tidy up resources etc.)?

I have done some research around this topic, which turned out to be more popular than I anticipated. KindDragon's reply was one of the pivotal points.
I wrote a longer blog post on the topic and created a working demo program, which demonstrates using this type of system to close a command line application in a couple of nice fashions. That post also lists external links that I used in my research.
In short, those demo programs do the following:
Start a program with a visible window using .Net, hide with pinvoke, run for 6 seconds, show with pinvoke, stop with .Net.
Start a program without a window using .Net, run for 6 seconds, stop by attaching console and issuing ConsoleCtrlEvent
Edit: The amended solution from #KindDragon for those who are interested in the code here and now. If you plan to start other programs after stopping the first one, you should re-enable CTRL+C handling, otherwise the next process will inherit the parent's disabled state and will not respond to CTRL+C.
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AttachConsole(uint dwProcessId);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern bool FreeConsole();
[DllImport("kernel32.dll")]
static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine, bool Add);
delegate bool ConsoleCtrlDelegate(CtrlTypes CtrlType);
// Enumerated type for the control messages sent to the handler routine
enum CtrlTypes : uint
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GenerateConsoleCtrlEvent(CtrlTypes dwCtrlEvent, uint dwProcessGroupId);
public void StopProgram(Process proc)
{
//This does not require the console window to be visible.
if (AttachConsole((uint)proc.Id))
{
// Disable Ctrl-C handling for our program
SetConsoleCtrlHandler(null, true);
GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0);
//Moved this command up on suggestion from Timothy Jannace (see comments below)
FreeConsole();
// Must wait here. If we don't and re-enable Ctrl-C
// handling below too fast, we might terminate ourselves.
proc.WaitForExit(2000);
//Re-enable Ctrl-C handling or any subsequently started
//programs will inherit the disabled state.
SetConsoleCtrlHandler(null, false);
}
}
Also, plan for a contingency solution if AttachConsole() or the sent signal should fail, for instance sleeping then this:
if (!proc.HasExited)
{
try
{
proc.Kill();
}
catch (InvalidOperationException e){}
}

The closest that I've come to a solution is the SendSignal 3rd party app. The author lists source code and an executable. I've verified that it works under 64-bit windows (running as a 32-bit program, killing another 32-bit program), but I've not figured out how to embed the code into a windows program (either 32-bit or 64-bit).
How it works:
After much digging around in the debugger I discovered that the entry point that actually does the behavior associated with a signal like ctrl-break is kernel32!CtrlRoutine. The function had the same prototype as ThreadProc, so it can be used with CreateRemoteThread directly, without having to inject code. However, that's not an exported symbol! It's at different addresses (and even has different names) on different versions of Windows. What to do?
Here is the solution I finally came up with. I install a console ctrl handler for my app, then generate a ctrl-break signal for my app. When my handler gets called, I look back at the top of the stack to find out the parameters passed to kernel32!BaseThreadStart. I grab the first param, which is the desired start address of the thread, which is the address of kernel32!CtrlRoutine. Then I return from my handler, indicating that I have handled the signal and my app should not be terminated. Back in the main thread, I wait until the address of kernel32!CtrlRoutine has been retrieved. Once I've got it, I create a remote thread in the target process with the discovered start address. This causes the ctrl handlers in the target process to be evaluated as if ctrl-break had been pressed!
The nice thing is that only the target process is affected, and any process (even a windowed process) can be targeted. One downside is that my little app can't be used in a batch file, since it will kill it when it sends the ctrl-break event in order to discover the address of kernel32!CtrlRoutine.
(Precede it with start if running it in a batch file.)

I guess I'm a bit late on this question but I'll write something anyway for anyone having the same problem.
This is the same answer as I gave to this question.
My problem was that I'd like my application to be a GUI application but the processes executed should be run in the background without any interactive console window attached. I think this solution should also work when the parent process is a console process. You may have to remove the "CREATE_NO_WINDOW" flag though.
I managed to solve this using GenerateConsoleCtrlEvent() with a wrapper app. The tricky part is just that the documentation is not really clear on exactly how it can be used and the pitfalls with it.
My solution is based on what is described here. But that didn't really explain all the details either and with an error, so here is the details on how to get it working.
Create a new helper application "Helper.exe". This application will sit between your application (parent) and the child process you want to be able to close. It will also create the actual child process. You must have this "middle man" process or GenerateConsoleCtrlEvent() will fail.
Use some kind of IPC mechanism to communicate from the parent to the helper process that the helper should close the child process. When the helper get this event it calls "GenerateConsoleCtrlEvent(CTRL_BREAK, 0)" which closes down itself and the child process. I used an event object for this myself which the parent completes when it wants to cancel the child process.
To create your Helper.exe create it with CREATE_NO_WINDOW and CREATE_NEW_PROCESS_GROUP. And when creating the child process create it with no flags (0) meaning it will derive the console from its parent. Failing to do this will cause it to ignore the event.
It is very important that each step is done like this. I've been trying all different kinds of combinations but this combination is the only one that works. You can't send a CTRL_C event. It will return success but will be ignored by the process. CTRL_BREAK is the only one that works. Doesn't really matter since they will both call ExitProcess() in the end.
You also can't call GenerateConsoleCtrlEvent() with a process groupd id of the child process id directly allowing the helper process to continue living. This will fail as well.
I spent a whole day trying to get this working. This solution works for me but if anyone has anything else to add please do. I went all over the net finding lots of people with similar problems but no definite solution to the problem. How GenerateConsoleCtrlEvent() works is also a bit weird so if anyone knows more details on it please share.

Somehow GenerateConsoleCtrlEvent() return error if you call it for another process, but you can attach to another console application and send event to all child processes.
void SendControlC(int pid)
{
AttachConsole(pid); // attach to process console
SetConsoleCtrlHandler(NULL, TRUE); // disable Control+C handling for our app
GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); // generate Control+C event
}

Edit:
For a GUI App, the "normal" way to handle this in Windows development would be to send a WM_CLOSE message to the process's main window.
For a console app, you need to use SetConsoleCtrlHandler to add a CTRL_C_EVENT.
If the application doesn't honor that, you could call TerminateProcess.

Here is the code I use in my C++ app.
Positive points :
Works from console app
Works from Windows service
No delay required
Does not close the current app
Negative points :
The main console is lost and a new one is created (see FreeConsole)
The console switching give strange results...
// Inspired from http://stackoverflow.com/a/15281070/1529139
// and http://stackoverflow.com/q/40059902/1529139
bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent)
{
bool success = false;
DWORD thisConsoleId = GetCurrentProcessId();
// Leave current console if it exists
// (otherwise AttachConsole will return ERROR_ACCESS_DENIED)
bool consoleDetached = (FreeConsole() != FALSE);
if (AttachConsole(dwProcessId) != FALSE)
{
// Add a fake Ctrl-C handler for avoid instant kill is this console
// WARNING: do not revert it or current program will be also killed
SetConsoleCtrlHandler(nullptr, true);
success = (GenerateConsoleCtrlEvent(dwCtrlEvent, 0) != FALSE);
FreeConsole();
}
if (consoleDetached)
{
// Create a new console if previous was deleted by OS
if (AttachConsole(thisConsoleId) == FALSE)
{
int errorCode = GetLastError();
if (errorCode == 31) // 31=ERROR_GEN_FAILURE
{
AllocConsole();
}
}
}
return success;
}
Usage example :
DWORD dwProcessId = ...;
if (signalCtrl(dwProcessId, CTRL_C_EVENT))
{
cout << "Signal sent" << endl;
}

A solution that I have found from here is pretty simple if you have python 3.x available in your command line. First, save a file (ctrl_c.py) with the contents:
import ctypes
import sys
kernel = ctypes.windll.kernel32
pid = int(sys.argv[1])
kernel.FreeConsole()
kernel.AttachConsole(pid)
kernel.SetConsoleCtrlHandler(None, 1)
kernel.GenerateConsoleCtrlEvent(0, 0)
sys.exit(0)
Then call:
python ctrl_c.py 12345
If that doesn't work, I recommend trying out the windows-kill project:
Choco: https://github.com/ElyDotDev/windows-kill
Node: https://github.com/ElyDotDev/node-windows-kill

void SendSIGINT( HANDLE hProcess )
{
DWORD pid = GetProcessId(hProcess);
FreeConsole();
if (AttachConsole(pid))
{
// Disable Ctrl-C handling for our program
SetConsoleCtrlHandler(NULL, true);
GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); // SIGINT
//Re-enable Ctrl-C handling or any subsequently started
//programs will inherit the disabled state.
SetConsoleCtrlHandler(NULL, false);
WaitForSingleObject(hProcess, 10000);
}
}

Thanks to jimhark's answer and other answers here, I found a way to do it in PowerShell:
$ProcessID = 1234
$MemberDefinition = '
[DllImport("kernel32.dll")]public static extern bool FreeConsole();
[DllImport("kernel32.dll")]public static extern bool AttachConsole(uint p);
[DllImport("kernel32.dll")]public static extern bool GenerateConsoleCtrlEvent(uint e, uint p);
public static void SendCtrlC(uint p) {
FreeConsole();
if (AttachConsole(p)) {
GenerateConsoleCtrlEvent(0, p);
FreeConsole();
}
AttachConsole(uint.MaxValue);
}'
Add-Type -Name 'dummyName' -Namespace 'dummyNamespace' -MemberDefinition $MemberDefinition
[dummyNamespace.dummyName]::SendCtrlC($ProcessID)
What made things work was sending the GenerateConsoleCtrlEvent to the desired process group instead of sending it to all processes that share the console of the calling process and then AttachConsole back to the current process' parent's console.

Yes. The https://github.com/ElyDotDev/windows-kill project does exactly what you want:
windows-kill -SIGINT 1234

It should be made crystal clear because at the moment it isn't.
There is a modified and compiled version of SendSignal to send Ctrl-C (by default it only sends Ctrl+Break). Here are some binaries:
(2014-3-7) : I built both 32-bit and 64-bit version with Ctrl-C, it's called SendSignalCtrlC.exe and you can download it at: https://dl.dropboxusercontent.com/u/49065779/sendsignalctrlc/x86/SendSignalCtrlC.exe https://dl.dropboxusercontent.com/u/49065779/sendsignalctrlc/x86_64/SendSignalCtrlC.exe -- Juraj Michalak
I have also mirrored those files just in case:
32-bit version: https://www.dropbox.com/s/r96jxglhkm4sjz2/SendSignalCtrlC.exe?dl=0
64-bit version: https://www.dropbox.com/s/hhe0io7mcgcle1c/SendSignalCtrlC64.exe?dl=0
Disclaimer: I didn't build those files. No modification was made to the compiled
original files. The only platform tested is the 64-bit Windows 7. It is recommended to adapt the source available at http://www.latenighthacking.com/projects/2003/sendSignal/ and compile it yourself.

In Java, using JNA with the Kernel32.dll library, similar to a C++ solution. Runs the CtrlCSender main method as a Process which just gets the console of the process to send the Ctrl+C event to and generates the event. As it runs separately without a console the Ctrl+C event does not need to be disabled and enabled again.
CtrlCSender.java - Based on Nemo1024's and KindDragon's answers.
Given a known process ID, this consoless application will attach the console of targeted process and generate a CTRL+C Event on it.
import com.sun.jna.platform.win32.Kernel32;
public class CtrlCSender {
public static void main(String args[]) {
int processId = Integer.parseInt(args[0]);
Kernel32.INSTANCE.AttachConsole(processId);
Kernel32.INSTANCE.GenerateConsoleCtrlEvent(Kernel32.CTRL_C_EVENT, 0);
}
}
Main Application - Runs CtrlCSender as a separate consoless process
ProcessBuilder pb = new ProcessBuilder();
pb.command("javaw", "-cp", System.getProperty("java.class.path", "."), CtrlCSender.class.getName(), processId);
pb.redirectErrorStream();
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
Process ctrlCProcess = pb.start();
ctrlCProcess.waitFor();

I found all this too complicated and used SendKeys to send a CTRL-C keystroke to the command line window (i.e. cmd.exe window) as a workaround.

A friend of mine suggested a complete different way of solving the problem and it worked for me. Use a vbscript like below. It starts and application, let it run for 7 seconds and close it using ctrl+c.
'VBScript Example
Set WshShell = WScript.CreateObject("WScript.Shell")
WshShell.Run "notepad.exe"
WshShell.AppActivate "notepad"
WScript.Sleep 7000
WshShell.SendKeys "^C"

// Send [CTRL-C] to interrupt a batch file running in a Command Prompt window, even if the Command Prompt window is not visible,
// without bringing the Command Prompt window into focus.
// [CTRL-C] will have an effect on the batch file, but not on the Command Prompt window itself -- in other words,
// [CTRL-C] will not have the same visible effect on a Command Prompt window that isn't running a batch file at the moment
// as bringing a Command Prompt window that isn't running a batch file into focus and pressing [CTRL-C] on the keyboard.
ulong ulProcessId = 0UL;
// hwC = Find Command Prompt window HWND
GetWindowThreadProcessId (hwC, (LPDWORD) &ulProcessId);
AttachConsole ((DWORD) ulProcessId);
SetConsoleCtrlHandler (NULL, TRUE);
GenerateConsoleCtrlEvent (CTRL_C_EVENT, 0UL);
SetConsoleCtrlHandler (NULL, FALSE);
FreeConsole ();

SIGINT can be send to program using windows-kill, by syntax windows-kill -SIGINT PID, where PID can be obtained by Microsoft's pslist.
Regarding catching SIGINTs, if your program is in Python then you can implement SIGINT processing/catching like in this solution.

Based on process id, we can send the signal to process to terminate forcefully or gracefully or any other signal.
List all process :
C:\>tasklist
To kill the process:
C:\>Taskkill /IM firefox.exe /F
or
C:\>Taskkill /PID 26356 /F
Details:
http://tweaks.com/windows/39559/kill-processes-from-command-prompt/

Related

Execution abnormalities depending on process creation (ShellExecute vs CreateProcess)

We're running a Windows Service which is responsible for monitoring a set of processes. The service is basically just responsible for (a) checking if the defined jobs are running, and (b) starting the jobs if they are not.
The service is created via the following command (sc: https://technet.microsoft.com/en-us/library/bb490995.aspx):
sc create "My Service" binPath= C:\heyoo\myservice.exe type= own start= auto error= normal
sc start "SCF Service"
One of the jobs the service is responsible for creating is 'Camera.exe'. Camera.exe retrieves a video feed from the connected cameras (FireWire 1394), and does some processing on them.
A week ago, the service was rewritten from using ShellExecute to use CreateProcess so it is able to better monitor the defined jobs (as it gets the HANDLE to the process).
ShellExecute call (old method):
bool Execute()
{
int result = (int)ShellExecute(NULL, "open", "C:\\bin\\Camera.exe", NULL, NULL, SW_SHOWDEFAULT);
return result > 32;
}
CreateProcess call (new method):
// Called with Execute("C:\\bin\\Camera.exe", "");
bool Execute(std::string prog, std::string args)
{
std::string cmd = std::string(prog) + " " + args;
char *path = new char[cmd.length()+1];
strcpy(path, cmd.c_str());
STARTUPINFO si = {0};
si.cb = sizeof(STARTUPINFO);
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
DWORD creationFlags = REALTIME_PRIORITY_CLASS;
BOOL result = CreateProcess(NULL, path, NULL, NULL, FALSE, creationFlags, NULL, NULL, &si, &pi);
delete[] path;
if (result) {
SetProcInfo(pi);
}
return result;
}
With the new CreateProcess method, we noticed that (A) the network systematically fails after a certain interval, and (B) the images retrieved from the cameras contain invalid timestamps (correct timestamps are crucial to us).
A frequently takes down the entire network connection, and requires a reboot to get back online. B causes the processing of images to fail, as we are highly dependent on valid timestamps.
The problems (A & B) does only arise when Service.exe is run as a service. When running Service.exe or Camera.exe from the command line, none of the problems occurr.
Today I removed the CreateProcess calls from the service (went back to ShellExecute), and the problems disappeared again. What am I doing wrong with the API call?
DWORD creationFlags = REALTIME_PRIORITY_CLASS;
This is the most obvious difference. When you call ShellExecute, the process will be created with normal priority. The documentation for real time priority says:
Process that has the highest possible priority. The threads of a real-time priority class process preempt the threads of all other processes, including operating system processes performing important tasks. For example, a real-time process that executes for more than a very brief interval can cause disk caches not to flush or cause the mouse to be unresponsive.
You really don't want to do this!
Pass 0 as the creation flags. The documentation says:
If none of the priority class flags is specified, the priority class defaults to NORMAL_PRIORITY_CLASS unless the priority class of the creating process is IDLE_PRIORITY_CLASS or BELOW_NORMAL_PRIORITY_CLASS. In this case, the child process receives the default priority class of the calling process.
For what it is worth, you can obtain a process handle by using ShellExecuteEx rather than ShellExecute. In fact you should always prefer ShellExecuteEx to ShellExecute since the latter cannot report errors properly. Even so, you are creating a new process, and so CreateProcess is the right function for that task.

Why does WaitForSingleObject() after CreateProcess() show the AppStarting cursor?

I've written a simple launcher that looks up the actual program and then executes it. This works fine, but there is one issue: Windows displays the "pointer with hourglass" cursor for about give seconds. This does not happen if I launch the program directly.
I tried setting sinfo.dwFlags = STARTF_FORCEOFFFEEDBACK but this did not help. I guess that's because it's my program that somehow needs to disable the cursor and the program I start does that fine (it creates a proper window etc.).
This is the relevant code from my application. I do not have any threads or any other fancy stuff (the sole purpose of the program is to launch another program and pass on the exit code to its caller in case it cares about it):
STARTUPINFO sinfo = {0};
PROCESS_INFORMATION pinfo = {0};
if (!CreateProcess(program, buf, NULL, NULL, FALSE, 0, NULL, NULL, &sinfo, &pinfo)) {
Fail("Could not launch Vim");
}
if (WaitForSingleObject(pinfo.hProcess, INFINITE) == WAIT_FAILED) {
Fail("WaitForSingleObject");
}
Apparently Windows expects a Windows program to process messages and does not consider it fully started before that point, which is why it shows the appstarting cursor.
Adding the following code to process a single dummy message solved the problem:
MSG msg;
PostMessage(NULL, WM_NULL, 0, 0);
GetMessage(&msg, NULL, 0, 0);
Your program is showing the hour-glass icon because your UI thread is hanging as a result of being stalled on the WaitForSingleObject call. Remember, WaitForSingleObject does not return until after the child process exits. You could substitute the Wait call with a Sleep and get the same effect.
You have some options:
Use MsgWaitForMultipleObjects instead of WaitForSingleObject. This is the more formal way to pump messages and wait at the same time instead of the PostMessage/GetMessage thing you suggest in your own answer.
Do the Wait call on another thread. When the thread returns, it PostMessage something to your UI thread to indicate it is done.
In both of the above cases, the UI will become responsive to clicks and input events while the child process is still happening. You will need to code your UI thread to handle this as appropriate (if it's warranted).

What makes SwitchDesktop not work right after a user unlocks it session?

I have a program that switches desktop and start a new process on it. When the process exits, the parent process restores the original desktop.
For testing purposes, I put a button in a plain win32 app that triggers the switch. It works, and closing the launched process (notepad), I go back to the original desktop.
In that same program, I have called WTSRegisterSessionNotification to receive a notification when a session is unlocked (WTS_SESSION_UNLOCK). I receive it.
But when I try to switch desktops in WTS_SESSION_UNLOCK message handler, SwitchDesktop fails and GetLastError is 0. The documentation says that last error is usually not set by SwitchDesktop.
Funny enough, if I put my call to switch desktop in a for loop, it works on the 5th iteration.
In short, this does not work :
case WM_WTSSESSION_CHANGE:
if(wParam == WTS_SESSION_UNLOCK)
{
SwitchDesktop(a_valid_desktop_handle);
}
break;
But this ugly hack works :
case WM_WTSSESSION_CHANGE:
if(wParam == WTS_SESSION_UNLOCK)
{
for(int i=0; i<10; ++i)
{
if(SwitchDesktop(a_valid_desktop_handle))
{
//This will work when i == 5, maybe 6.
break;
}
}
}
break;
Setting a timer (to exit the message loop) also works, but it is just a more convoluted form of loop with regards to this problem. SwitchDesktop will work on after a handfull of WM_TIMER messages. It looks like constant time, although I did not measure it.
MSDN documentation for SwitchDesktop mentions that this will fail with a custom Userinit process, which I use. But getting the name of the current desktop just before the switch :
wchar_t name[512];
GetUserObjectInformation(GetThreadDesktop(GetCurrentThreadId()), UOI_NAME, name, sizeof(name)/sizeof(*name), 0);
OutputDebugString(name);
Gives me default all the time. And since GetLastError is 0, not 5 (access denied) I am pretty sure the secure desktop is gone before I receive the WTS_SESSION_UNLOCK notification.
I known I can't switch desktop while the screen is locked, but is there a "grace period" after the desktop is unlocked in which I can't call SwitchDesktop ?
When the desktop gets locked, it switches to another desktop which is reserved for this purpose. It is quite possible that when you receive the message, that desktop is still in control and you're not allowed to switch because you're not running in the current desktop.
I can't test it right now but I would put the call to SwitchDesktop not on WTS_SESSION_UNLOCK but on WTS_CONSOLE_CONNECT. From what I gather WTS_SESSION_UNLOCK occurs first and then your get WTS_CONSOLE_CONNECT which would correspond to what you see with the "constand time"...
SwitchDesktop fails (with error 0) because (accordingly to MSDN) it belongs to a window station not (yet) visible. There is no user notification I know of that says "the HWINSTA become visible".

CreateProcess with WaitForSingleObject performance degradation

Everyone probably knows the code to run a program and wait for it to end:
CreateProcess(...
WaitForSingleObject(Process.hProcess, INFINITE
It was used several times by myself. But recently I found that this call when it launches a program with a multimedia playback has worse performance than the same process being executed from a general file manager (Windows XP). That's ok with CPU consumption of my (parent) process, but while playing the fragment there are unexpected little stops.
I made a little change to something like:
CreateProcess ...
do {
Sleep(100);
Res = WaitForSingleObject(Process.hProcess, 10);
} while (Res == WAIT_TIMEOUT);
And it helped. Now the child process plays the fragment without problems.
So what's wrong with the first fragment and is it documented somewhere? As I see from the tests the second "wait" is more "relaxed" than the first one, but the first one doesn't eat CPU at least formally
If this code is running on a UI thread, you will cause performance problems with other processes that (directly or indirectly) send messages to your window(s), since you do not run the message loop while you are waiting for the child process. Neither Sleep() nor WaitForSingleObject() will process messages.
Windows Explorer (the file manager) will not suffer this problem because it:
Does not keep an open handle to processes it launches at the user's request (I think this is more likely, since Explorer needs neither to know when the process exits or its exit code), or
Does not wait on any open process handles it may keep from its children, and importantly
Always makes sure to run a message loop while it waits on handles. This is very important in any process that makes use of COM, which Explorer uses heavily.
Instead of calling WaitForSingleObject(), you can call MsgWaitForMultipleObjects(). If you specifiy QS_ALLINPUT for the dwWaitMask parameter, MsgWaitForMultipleObjects will return either when your event is signaled or when there is input in the thread's message queue. If MsgWaitForMultipleObjects() returned because a message is available, you can process it and resume waiting:
MSG msg;
DWORD reason = WAIT_TIMEOUT;
while (WAIT_OBJECT_0 != reason) {
reason = MsgWaitForMultipleObjects(1, &hChildProcess, FALSE, INFINITE, QS_ALLINPUT);
switch (reason) {
case WAIT_OBJECT_0:
// Your child process is finished.
break;
case (WAIT_OBJECT_0 + 1):
// A message is available in the message queue.
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
// Note that if your main message loop does additional processing
// (such as calling IsDialogMessage() for modeless dialogs)
// you will want to do those things here, too.
}
break;
}
}

How can I update a win32 app gui while it is waiting for another program to finish?

I am currently using CreateProcess/WaitForSingleObject from within a win32 GUI app to launch a small GUI app that deals with software licensing issues. This all works fine, but it essentially hangs the "parent" app while it waits for the licensing app to finish its work. During this time, updates to the parent app do not occur and it ends up with ugly white squares if the utility app window is moved.
Also, for some strange reason, while the utility app is running, if I copy something from within that app to the clipboard, it HANGS. I haven't figured out why yet, but it only happens if I am waiting for the app to finish from within the parent app.
So I'm thinking that if I can cause the parent app to handle its events while waiting for my other app to finish, it might solve both problems.
So, is there a replacement for CreateProcess/WaitForSingleObject that also handles UI updates?
Your parent process appears to hang because the WaitForSingleObject() call blocks your thread until the handle you pass into the call is signaled.
Your child process likely hangs during the copy-to-clipboard operation because it is, as a part of that operation, sending a message either specifically to the parent process's window or to all top-level windows. The message loop in your parent process's thread is not running, because it is blocked waiting until the child process exits, so the message is never processed and the child process remains blocked.
Instead of calling WaitForSingleObject(), you can call MsgWaitForMultipleObjects(). If you specifiy QS_ALLINPUT for the dwWaitMask parameter, MsgWaitForMultipleObjects will return either when your event is signaled or when there is input in the thread's message queue. If MsgWaitForMultipleObjects() returned because a message is available, you can process it and resume waiting:
MSG msg;
DWORD reason = WAIT_TIMEOUT;
while (WAIT_OBJECT_0 != reason) {
reason = MsgWaitForMultipleObjects(1, &hChildProcess, FALSE, INFINITE, QS_ALLINPUT);
switch (reason) {
case WAIT_OBJECT_0:
// Your child process is finished.
break;
case (WAIT_OBJECT_0 + 1):
// A message is available in the message queue.
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
// Note that if your main message loop does additional processing
// (such as calling IsDialogMessage() for modeless dialogs)
// you will want to do those things here, too.
}
break;
}
}
You could put the WaitForSingleObject call in a loop and use a relatively small value for the dwMilliseconds parameter.
The condition to exit the loop is when the WaitForSingleObject call returns WAIT_OBJECT_0.
In the loop you have to examine the message queue to see if there are any that you must process. How you handle this is really up to you an it depends on your typical needs.
// Assuming hYourWaitHandle is the handle that you're waiting on
// and hwnd is your window's handle, msg is a MSG variable and
// result is a DWORD variable
//
// Give the process 33 ms (you can use a different value here depending on
// how responsive you wish your app to be)
while((result = WaitForSingleObject(hYourWaitHAndle, 33)) == WAIT_TIMEOUT)
{
// if after 33 ms the object's handle is not signaled..
// we examine the message queue and if ther eare any waiting..
// Note: see PeekMessage documentation for details on how to limit
// the types of messages to look for
while(PeekMessage(&msg, hwnd, 0, 0, PM_NOREMOVE))
{
// we process them..
if(GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
// if you're here it means WaitForSingleObject returned WAIT_OBJECT_0, so you're done
// (but you should always check and make sure it really is WAIT_OBJECT_0)
if(result != WAIT_OBJECT_0)
{
// This should not be.. so react!
}
I suggest you can handle this as follows:
Parent application does CreateProcess, and then returns immediately instead of waiting for a response or for the utility app to finish
Because the parent applicatin has returned, it can handle other Window messages (e.g. WM_PAINT)
When the utility app finishes, it notifies the parent application (e.g. using PostMessage and RegisterWindowMessage APIs)
Parent application handles positive notification received via PostMessage
Parent application may also have a Windows timer (WM_TIMER) running, so that it knows if the utility app is killed before it send its notification
You can get a hanging problem if the app you are spawning causes a sendmessage broadcast, either explicit or implicit. This is clipped from my website:
The problem arises because your application has a window but isn't pumping messages. If the spawned application invokes SendMessage with one of the broadcast targets (HWND_BROADCAST or HWND_TOPMOST), then the SendMessage won't return to the new application until all applications have handled the message - but your app can't handle the message because it isn't pumping messages.... so the new app locks up, so your wait never succeeds.... DEADLOCK.
I don't do clipboard code, but if that causes the situation above (believeable), then you'll deadlock. You can:
put the launch of the secondary application into a little thread
use a timeout and spin around a PeekMessage loop (yuck)
use the MsgWaitForMultipleObjects API.
No preference is implied by that ordering... I'm assuming you don't create the spawned application yourself, in which case you could use IPC to get around this issue, as ChrisW suggested.
You should create a thread that does only the following:
call CreateProcess() to run the other app
call WaitForSingleObject() to wait for the process to finish - since this is a background thread your app will not block
call CloseHandle() on the process handle
call PostMessage() with a notification message for your main thread
Now you only need to make sure that your main application has its GUI disabled to prevent reentrancy problems, possible by showing a modal dialog that informs the user that the other app is running and needs to be dealt with first. Make sure that the dialog can not be closed manually, and that it closes itself when it receives the posted notification message from the background thread. You can put the whole thread creation into this dialog as well, and wrap everything in a single function that creates, shows and destroys the dialog and returns the result your external application produces.

Resources