KTN-IT
asked on
How to add an option to terminate an infinite loop?
At my organization, I am turning old XP machines into remote desktop clients by changing the Windows shell (at HKLM\SOFTWARE\Microsoft\Wi ndows NT\CurrentVersion\Winlogon \Shell) from explorer.exe to mstsc.exe.
Below is some code that starts a process (notepad, in this case) and then waits for it to be terminated, and starts it again. This is what I want for my remote desktop clients, so when I user logs off from remote desktop, mstsc starts again to wait for another user to log in.
At some point, though, the process must be terminated, for instance, when I want to shut down or restart the computer. How would I build in some functionality that would allow an exit from the infinite loop? For instance, a certain key combination.
Below is some code that starts a process (notepad, in this case) and then waits for it to be terminated, and starts it again. This is what I want for my remote desktop clients, so when I user logs off from remote desktop, mstsc starts again to wait for another user to log in.
At some point, though, the process must be terminated, for instance, when I want to shut down or restart the computer. How would I build in some functionality that would allow an exit from the infinite loop? For instance, a certain key combination.
#include <windows.h>
void main ()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&pi, 0, sizeof(pi));
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
TCHAR sCommandLine[MAX_PATH] = TEXT("C:\\windows\\NOTEPAD.EXE");
BOOL result = 0;
result = CreateProcess(NULL, sCommandLine,NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
MSG msg;
while(1) // infinite loop
{
while (WAIT_OBJECT_0 != MsgWaitForMultipleObjects(1,&pi.hProcess,TRUE,INFINITE,QS_ALLINPUT))
{
while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) DispatchMessage(&msg);
}
result = CreateProcess(NULL, sCommandLine,NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
}
}
Maybe I'm missing a detail, but wouldn't you just change the "while(1)" to "while(condition_variable) "? Until your condition variable becomes 0, the loop will run, so you just need to determine what your exit condition will be and then set the condition_variable to zero when you want to exit.
If you shut down your computer,the process will be terminated anyway. But, in case you want to perform any cleanup actions or such, you can use 'SetConsoleCtrlHandler()' (http://msdn.microsoft.com/en-us/library/ms686016%28VS.85%29.aspx) to be notified about that, e.g.
#include <windows.h>
BOOL WINAPI CtrlHandlerRoutine(DWORD dwCtrlType)
{
switch(dwCtrlType)
{
case CTRL_SHUTDOWN_EVENT:
case CTRL_LOGOFF_EVENT:
ExitProcess(0); // also terminates loop
}
return TRUE;
}
void main ()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&pi, 0, sizeof(pi));
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
TCHAR sCommandLine[MAX_PATH] = TEXT("C:\\windows\\NOTEPAD.EXE");
SetConsoleCtrlHandler(CtrlHandlerRoutine,TRUE);
BOOL result = 0;
result = CreateProcess(NULL, sCommandLine,NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
MSG msg;
while(1) // infinite loop
{
while (WAIT_OBJECT_0 != MsgWaitForMultipleObjects(1,&pi.hProcess,TRUE,INFINITE,QS_ALLINPUT))
{
while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) DispatchMessage(&msg);
}
result = CreateProcess(NULL, sCommandLine,NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
}
}
ASKER
My thinking was more along the lines of kaufmed's suggestion. Some way to set a condition upon which the loop would exit. And no, kaufmed, you're not missing anything, other than the fact that I am a complete C++ newbie, and I don't know how to do much.
jkr, from what I read in the link you provided, 'SetConsoleCtrlHandler()' would just notify me when the program was forcibly terminated, due to Ctrl-C or something. I'm looking for something slightly more graceful than that, like a key combination or something.
How could a link a condition in my soon-to-be-not-infinite loop to some keyboard input? That probably should have been my original question.
jkr, from what I read in the link you provided, 'SetConsoleCtrlHandler()' would just notify me when the program was forcibly terminated, due to Ctrl-C or something. I'm looking for something slightly more graceful than that, like a key combination or something.
How could a link a condition in my soon-to-be-not-infinite loop to some keyboard input? That probably should have been my original question.
Actually, the handler routine is to notify you of shutdown or logoff events, along with CTRL+C. Note that you can also send your own control events which you then in turn can handle. If you oprefer a condition variable though, you could
#include <windows.h>
BOOL bRunning = TRUE; // global condition
BOOL WINAPI CtrlHandlerRoutine(DWORD dwCtrlType)
{
switch(dwCtrlType)
{
case CTRL_SHUTDOWN_EVENT:
case CTRL_LOGOFF_EVENT:
bRunning = FALSE; // terminates loop
}
return TRUE;
}
void main ()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&pi, 0, sizeof(pi));
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
TCHAR sCommandLine[MAX_PATH] = TEXT("C:\\windows\\NOTEPAD.EXE");
SetConsoleCtrlHandler(CtrlHandlerRoutine,TRUE);
BOOL result = 0;
result = CreateProcess(NULL, sCommandLine,NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
MSG msg;
while(bRunning) // global condition
{
while (WAIT_OBJECT_0 != MsgWaitForMultipleObjects(1,&pi.hProcess,TRUE,INFINITE,QS_ALLINPUT))
{
while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) DispatchMessage(&msg);
}
result = CreateProcess(NULL, sCommandLine,NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
}
}
ASKER
Thanks, jkr. I like the global variable concept.
Considering that this application is actually going to be a replacement shell instead of explorer.exe, I'm not sure there will be much chance for anyone to shutdown or terminate it except with ctrl-alt-del.
Is it difficult to add some kind of wait for a user-entered key combination that would alter the global variable?
Considering that this application is actually going to be a replacement shell instead of explorer.exe, I'm not sure there will be much chance for anyone to shutdown or terminate it except with ctrl-alt-del.
Is it difficult to add some kind of wait for a user-entered key combination that would alter the global variable?
ASKER
I should also add that I've got this running now as a Windows program, not a console program. (But there aren't any Windows created.)
ASKER
Here's my code now:
#include <windows.h>
BOOL bRunning = TRUE; // global condition
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&pi, 0, sizeof(pi));
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
TCHAR sCommandLine[MAX_PATH] = TEXT("C:\\windows\\NOTEPAD.EXE");
BOOL result = 0;
result = CreateProcess(NULL, sCommandLine,NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
MSG msg;
while(bRunning)
{
while (WAIT_OBJECT_0 != MsgWaitForMultipleObjects(1,&pi.hProcess,FALSE,INFINITE,QS_ALLINPUT))
{
while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) DispatchMessage(&msg);
}
result = CreateProcess(NULL, sCommandLine,NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
}
return 0;
}
>>I should also add that I've got this running now as a Windows program
Then you can handle WM_ENDSESSION (http://msdn.microsoft.com/en-us/library/aa376889(VS.85).aspx) and WM_QUERYENDSESSION ("http://msdn.microsoft.com/en-us/library/aa376890(VS.85).aspx") the same way.
Then you can handle WM_ENDSESSION (http://msdn.microsoft.com/en-us/library/aa376889(VS.85).aspx) and WM_QUERYENDSESSION ("http://msdn.microsoft.com/en-us/library/aa376890(VS.85).aspx") the same way.
Um, actually you'll need a separate thread to monitor a process in a GUI app, since the main thread will be busy handling the messages (and then the global variable concept should be altered if stuff becomes more complex later), e.g.
#include <windows.h>
#pragma comment(lib,"user32.lib")
static char g_szClassName[] = "MyWindowClass";
static HINSTANCE g_hInst = NULL;
BOOL bRunning = TRUE; // global condition
DWORD WINAPI MonitorThread(LPVOID) {
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&pi, 0, sizeof(pi));
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
TCHAR sCommandLine[MAX_PATH] = TEXT("C:\\windows\\NOTEPAD.EXE");
BOOL result = 0;
result = CreateProcess(NULL, sCommandLine,NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
MSG msg;
while(bRunning)
{
while (WAIT_OBJECT_0 != MsgWaitForMultipleObjects(1,&pi.hProcess,FALSE,INFINITE,QS_ALLINPUT))
{
while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) DispatchMessage(&msg);
}
result = CreateProcess(NULL, sCommandLine,NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch(Message)
{
case WM_ENDSESSION:
case WM_QUERYENDSESSION:
bRunning = FALSE;
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX WndClass;
HWND hwnd;
MSG Msg;
g_hInst = hInstance;
WndClass.cbSize = sizeof(WNDCLASSEX);
WndClass.style = NULL;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = g_hInst;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = g_szClassName;
WndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&WndClass))
{
MessageBox(0, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK | MB_SYSTEMMODAL);
return 0;
}
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
"The title of my window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 320, 240,
NULL, NULL, g_hInst, NULL);
if(hwnd == NULL)
{
MessageBox(0, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK | MB_SYSTEMMODAL);
return 0;
}
ShowWindow(hwnd, SW_HIDE); // invisible window
UpdateWindow(hwnd);
while(GetMessage(&Msg, NULL, 0, 0))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
ASKER
The Windows code I posted actually worked for me, after I changed the third parameter of MsgWaitForMultipleObjects to FALSE.
I ran the code you posted above, but it didn't do anything. It just sat there. There were no errors, but notepad never came up.
So what you're saying is, if I want to monitor keystrokes, I need a separate process? do I have to have a window to do it?
I ran the code you posted above, but it didn't do anything. It just sat there. There were no errors, but notepad never came up.
So what you're saying is, if I want to monitor keystrokes, I need a separate process? do I have to have a window to do it?
Sorry, forgot the code to create the thread - that would be
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX WndClass;
HWND hwnd;
MSG Msg;
g_hInst = hInstance;
WndClass.cbSize = sizeof(WNDCLASSEX);
WndClass.style = NULL;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = g_hInst;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = g_szClassName;
WndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&WndClass))
{
MessageBox(0, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK | MB_SYSTEMMODAL);
return 0;
}
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
"The title of my window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 320, 240,
NULL, NULL, g_hInst, NULL);
if(hwnd == NULL)
{
MessageBox(0, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK | MB_SYSTEMMODAL);
return 0;
}
DWORD dwTID;
CreateThread(NULL,0,MonitorThread,NULL,0,&dwTID); // create monito thread
ShowWindow(hwnd, SW_HIDE); // invisible window
UpdateWindow(hwnd);
while(GetMessage(&Msg, NULL, 0, 0))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
ASKER
I inserted your updated code. The result is that now notepad is started once. But when I close it it does not restart.
No thoughts on the key combination monitoring idea?
No thoughts on the key combination monitoring idea?
ASKER
I'll be back on Monday to work on this.
Thanks for all your help!!!!!
Thanks for all your help!!!!!
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
OK, thanks. The code works now. However, it does the same thing that the code I posted at the beginning of this question does. How do I take advantage of the global variable to exit my loop?
Well, that is done in the following snippets from the above code:
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch(Message)
{
case WM_ENDSESSION:
case WM_QUERYENDSESSION:
bRunning = FALSE; // <------------ cause loop to exit
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
}
return 0;
}
//....
while(bRunning) // <-------- exit if variable was changed in the message handler
{
WaitForSingleObjects(pi.hProcess,INFINITE)
result = CreateProcess(NULL, sCommandLine,NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
}
ASKER
How would I send WM_QUERYENDSESSION to my app/window? Do I need the window to be visible? Or can the invisible window monitor key strokes?
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
I'm still not clear on how I can tie this in to a user key stroke combination, but I've learned a whole lot from these answers. Thank you!