I'm new to C++, and I made myself a little program that can launch program through typing a command on the keyboard. In order to be able to launch a program whenever I want, I decided to set up a Low Level Keyboard Hook, which keep tracking key strokes and launch the specific program when the specific command was detected. The simple windows program was used to install the hook, the windows is not showed because all I need is the hook to listen in the background.
So far it works fine, however, the minor but annoying problem is I have to terminate the program through Windows Task Manager, and it's quite inconvenient. I have managed to uninstall the hook by pressing F7 key, but it seems that the windows program which is not showed is the Parent of the hook, so the hook cannot exit the windows program. While I want them both terminated through pressing a key. Hopefully I have made myself clear.
Is there any way that I could send a message from the hook to the windows program to ask it to exit? Or somehow I can terminate both of them in the hook program?
Thanks in advance.
Here is the code of the window program:
#include <windows.h>
#include "shortcut.h"
#pragma comment( lib, "libhook.dll.a") // Link Hook.lib to the project
long WINAPI WndProc(HWND hWnd, UINT wMessage, WPARAM wParam, LPARAM lParam)
{
switch(wMessage)
{
case WM_DESTROY:
InstallHook(FALSE); // Unhook
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, wMessage, wParam, lParam);
}
return 0;
}
BOOL FileExists(LPCTSTR szPath)
{
DWORD dwAttrib = GetFileAttributes(szPath);
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
MSG msg;
WNDCLASS wndclass;
HANDLE hMutex = NULL;
char szAppName[20] = "shortcut";
hMutex = CreateMutex(NULL,TRUE,szAppName); //启动多线程
int dwRet = GetLastError();
if (hMutex)
{
if (dwRet == ERROR_ALREADY_EXISTS)
{
MessageBox(NULL, "Program is already runing.", "Oops!", MB_OK | MB_ICONINFORMATION);
CloseHandle(hMutex);
return FALSE;
}
}
wndclass.style=0;
wndclass.lpfnWndProc=(WNDPROC)WndProc;
wndclass.cbClsExtra=0;
wndclass.cbWndExtra=0;
wndclass.hInstance=hInstance;
wndclass.hIcon=NULL;
wndclass.hCursor=LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH);
wndclass.lpszMenuName=NULL;
wndclass.lpszClassName=(LPSTR)szAppName;
if(!RegisterClass(&wndclass))
return FALSE;
if (!FileExists("\\ShortCuts.txt"))
{
MessageBox(NULL, "Missing file: cannot load shortcut settings file.(Shortcuts.txt)", "ERROR",MB_OK|MB_ICONINFORMATION);
exit(1);
}
if (!InstallHook(TRUE))
exit(1);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
Here is the code of the hook program:
// Hook- a project to create the DLL and LIB files.
// Microsoft Visual C++ 6.0 and above steps:
// 1. Create a new Win32 Dynamic Link - Library project.
// 2. Add hook.cpp and hook.h to the project.
// 3. There is no step 3 :-). Just build your project and you will find
// a Hook.dll and Hook.lib file in your map.
#include <windows.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <ctime>
#include <map>
#include <process.h>
using namespace std;
HHOOK hHook;
HINSTANCE ghDLLInst=0;
const char startChar = ';';
bool bChecking = false;
string cmd;
typedef map<string,string> COMMANDMAP;
COMMANDMAP mShortcut;
string logfilename="log.txt";
ofstream LOG;
__declspec(dllexport)int InstallHook(BOOL bCode);
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwFunction, LPVOID lpNot)
{
ghDLLInst=(HINSTANCE)hModule;
return TRUE;
}
DWORD WINAPI Runsystem(LPVOID lpParam)
{
WinExec((LPCSTR)lpParam, SW_SHOW);
}
string gettime()
{
time_t curTime;
struct tm *locTime;
char buf[80];
time(&curTime);
locTime=localtime(&curTime);
strftime(buf,80,"%Y-%m-%d %H:%M:%S",locTime);
string s=buf;
return s;
}
ostream& tout()
{
return LOG<< gettime()<< ": ";
}
void StartCheck()
{
bChecking=true;
cmd.clear();
}
void EndCheck()
{
bChecking=false;
cmd.clear();
}
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if ((wParam == WM_KEYDOWN) && (nCode >= HC_ACTION)) // Only record when key pressed
{
KBDLLHOOKSTRUCT *pStruct = (KBDLLHOOKSTRUCT*)lParam;
switch (pStruct->vkCode)
{
case VK_RETURN:
{
if (bChecking)
{
COMMANDMAP::iterator it;
it=mShortcut.find(cmd);
if (it!=mShortcut.end())
{
tout()<<"received command \'"<<cmd<<"\', executing \'"<<it->second.c_str()<<endl;
CreateThread(NULL, 0, Runsystem, (void*)it->second.c_str(),0,NULL);
}
else {
tout()<<"received command \'" <<cmd<<"\', no matching."<<endl;
}
}
EndCheck();
break;
}
case VK_F7:
{
InstallHook(false);
break;
}
default: // Normal keys, convert them
{
BYTE KeyboardState[256];
GetKeyboardState(KeyboardState);
WORD CharValue;
if(ToAscii(pStruct->vkCode, pStruct->scanCode,KeyboardState,&CharValue,0) > 0) // Convert to char.
{
char character=char(CharValue);
// tout()<<"received keyCode: "<<pStruct->vkCode<< " char: "<< character<<endl;
if (bChecking)
{
cmd+=character;
}
if (!bChecking && (character == startChar))
{
// tout()<<"Start checking..."<<endl;
StartCheck();
}
}
break;
}
}
}
return (int)CallNextHookEx(hHook, nCode, wParam, lParam);
}
bool readline(ifstream &fin,string &sline)
{
do
{
getline(fin,sline);
} while (!fin.eof() && ((sline[0]=='/' && sline[1]=='/') || sline.empty()));
return fin.eof()?false:true;
}
// __declspec(dllexport) means that this function must be exported to a dll file.
__declspec(dllexport)int InstallHook(BOOL bCode)
{
if(bCode)
{
// initialize shortcuts
ifstream fin;
LOG.open(logfilename.c_str(),ios_base::app);
tout()<<"Reading config file."<<endl;
fin.open("ShortCuts.txt");
if (fin)
{
string scmd,spath;
char oneline[256];
while(readline(fin,scmd)&&readline(fin,spath))
{
mShortcut[scmd]=spath;
// LOG<<scmd<<','<<spath<<endl;
}
fin.close();
tout()<<"OK, "<<mShortcut.size()<<" shortcuts loaded."<<endl;
}
else
{
tout()<<"ERROR"<<endl;
LOG.close();
exit(0);
}
hHook=(HHOOK)SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)KeyboardProc, // Start the keyboard hook.
(HINSTANCE)GetModuleHandle(NULL), NULL);
if(!hHook)
{
tout()<<"Install hook failed."<<endl;
return 0;
}
else
{
tout()<<"Install hook successful."<<endl;
return 1;
}
}
else
{
if (MessageBox(NULL,"Are you sure to exit KeyShortcut?","Exit",MB_YESNO|MB_ICONWARNING)==IDYES)
{
tout()<<"Uninstall hook successful."<<endl;
LOG.close();
return UnhookWindowsHookEx(hHook); // Unhook the keyboardhook.
}
}
}
For instance, you can use RegisterHotKey API function to set your own hotkey to a system and then handle this hotkey's message in your program(windowless)
Added:
If you want to send quit message from one process to another then your friend is PostThreadMessage(dwThreadId, WM_DESTROY, 0, 0);
Related
I have the following problem with retrieving the window handle from a specific window (title and class name are known):
There are two identical windows with different handles under two different processes, but FindWindow() can find the handle only from the newest window spawned, never from the first one.
What can be used instead? Can EnumWindows() be used to retrieve a list of windows with the same characteristics?
Use the Win32 API EnumWindows , and then check which process each window belongs to by using the Win32 API GetWindowThreadProcessId.
Here is a sample:
#include <iostream>
#include <Windows.h>
using namespace std;
BOOL CALLBACK enumProc(HWND hwnd, LPARAM) {
TCHAR buf[1024]{};
GetClassName(hwnd, buf, 100);
if (!lstrcmp(buf, L"Notepad"))
{
GetWindowText(hwnd, buf, 100);
DWORD pid = 0;
GetWindowThreadProcessId(hwnd, &pid);
wcout << buf << " " << pid << endl;
}
return TRUE;
}
int main() {
EnumWindows(&enumProc, 0);
}
If you need to check the window of each process, you can refer to this answer.
typedef struct
{
const char *name;
const char *class;
HWND handles[10];
int handlesFound;
} SearchWindowInfo;
SearchWindowInfo wi;
wi.handlesFound = 0;
wi.title = "WindowName";
wi.class = "ClassName";
BOOL CALLBACK searchWindowCallback(HWND hwnd, LPARAM lParam)
{
SearchWindoInfo *wi = (SearchWindoInfo *)lParam;
char buffer[256];
if (wi->handlesFound == 10)
return FALSE;
buffer[255] = 0;
if (wi->name)
{
int rc = GetWindowText(hwnd, buffer, sizeof(buffer)-1);
if(rc)
{
if (strcmp(wi->name, buffer) == 0)
{
wi->handles[wi->handlesFound++] = hwnd;
return TRUE;
}
}
}
if (wi->class)
{
int rc = GetClassName (hwnd, buffer, sizeof(buffer)-1);
if(rc)
{
if (strcmp(wi->class, buffer) == 0)
{
wi->handles[wi->handlesFound++] = hwnd;
return TRUE;
}
}
}
return TRUE;
}
EnumWindows(searchWindowCallback, (LPARAM)&wi);
for(int i = 0; i < wi.handlesFound; i++)
{
// yeah...
}
I have native dll for setting the windows hook and I am calling it in one of my windows application to set it to a target application. It works perfectly fine when the target application is run in admin(elevated) mode with CallWndProcHook call back triggered.
But when I run my target application in non admin(not elevated) mode then the SetWindowsHookEx is successful but the CallWndProcHook call back does not get triggered.
static LRESULT WINAPI CallWndProcHook(int nHookCode, WPARAM wParam, LPARAM lParam)
{
std::ofstream DEBUG_STRM;
DEBUG_STRM.open (FILE, std::fstream::app);
DEBUG_STRM << "\n CallWndProcHook entered";
return CallNextHookEx(g_hHook, nHookCode, wParam, lParam);
}
extern "C" __declspec( dllexport ) int _stdcall Start(long handle)
{
HWND objectHWND = (HWND)handle;
DWORD processId;
DWORD threadId;
std::ofstream DEBUG_STRM;
DEBUG_STRM.open (FILE, std::fstream::app);
DEBUG_STRM << "\n Start hooking";
threadId = GetWindowThreadProcessId(objectHWND, &processId);
HINSTANCE hInst = GetModuleHandle("textlocator.dll");
g_hHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProcHook, hInst, threadId);
if(g_hHook == NULL){
DEBUG_STRM << "\n Hook unsuccessful";
}else{
DEBUG_STRM << "\n Hook successful";
}
if (g_hHook == 0) {
return 0;
} else {
return 1;
}
DEBUG_STRM.close();
}
Any help on it ?
I am trying to hook a Notepad with dll-injection. After the exe is run and hooks the Notepad (form what I can tell successfully) and some keys are pressed, what seems to happan is that the key presses get stuck in a loop or a queue (Notepad not responding). After the exe unhooks the Notepad responds and all pressed keys appear in the text field.
exe
#include <iostream>
#include <fstream>
#include <windows.h>
#include <stdio.h>
HHOOK hHook = NULL;
HWND handle = NULL;
HMODULE dll = NULL;
HOOKPROC address = NULL;
DWORD thread_id = 0;
using namespace std;
int main(){
handle=FindWindow(NULL,L"Untitled - Notepad");
if(handle==NULL){
cout<<"Window not found"<<endl;
getchar();
return 0;
}
thread_id=GetWindowThreadProcessId(handle,NULL);
if(thread_id==0){
cout<<"ID not found"<<endl;
getchar();
return 0;
}
dll = LoadLibrary(TEXT("X:\\qt\\hook\\debug\\hook.dll"));
if(dll==NULL){
cout<<"hook.dll not found"<<endl;
getchar();
return 0;
}
address=(HOOKPROC)GetProcAddress(dll,"CallWndProc#12");
if(address==NULL){
cout<<"Address not found"<<endl;
getchar();
return 0;
}
hHook=SetWindowsHookEx(WH_KEYBOARD,address,dll,thread_id);
if(hHook==NULL){
cout<<"hook was not set"<<endl;
return 0;
}
cout<<"Program successfully hooked"<<endl;
cout<<"Press enter to unhook the function and stop the program"<<endl;
getchar();
UnhookWindowsHookEx(hHook);
return 0;
}
dll
#include "hook.h"
#include <windows.h>
#include <iostream>
#include <fstream>
using namespace std;
extern "C"{
__declspec(dllexport) LRESULT CALLBACK CallWndProc(int nCode,WPARAM wParam,LPARAM lParam){
if(nCode<0){
return CallNextHookEx(NULL,nCode,wParam,lParam);
}
ofstream file;
file.open("X:\\qt\\klog\\debug\\function.txt");
file<<"Function keyboard_hook called\n";
file.close();
return CallNextHookEx(NULL,nCode,wParam,lParam);
}
}
BOOL APIENTRY DllMain(HMODULE hDLL, DWORD Reason, LPVOID Reserved){
switch(Reason) {
case DLL_PROCESS_ATTACH: break;
case DLL_PROCESS_DETACH: break;
case DLL_THREAD_ATTACH: break;
case DLL_THREAD_DETACH: break;
}
return TRUE;
}
Adding an message loop between SetWindowsHookEx and UnhookWindowsHookEx fixed it
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
I'm having a problem when trying to run the following code:
#include "header.h"
int main()
{
id = GetCurrentProcessId();
EnumWindows(hEnumWindows, NULL);
Sleep(5000);
//MoveWindow(hThis, 450, 450, 100, 100, TRUE);
system("pause");
return 0;
}
//header.h
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <Windows.h>
using namespace std;
DWORD id = 0;
HWND hThis = NULL;
BOOL CALLBACK hEnumWindows(HWND hwnd, LPARAM lParam)
{
DWORD pid = 0;
pid = GetWindowThreadProcessId(hwnd, NULL);
if (pid == id)
{
hThis = GetWindow(hwnd, GW_OWNER);
if (!hThis)
{
cout << "Error getting window!" << endl;
}
else
{
char *buffer = nullptr;
int size = GetWindowTextLength(hThis);
buffer = (char*)malloc(size+1);
if (buffer != nullptr)
{
GetWindowText(hThis, buffer, size);
cout << pid << ":" << buffer << endl;
free(buffer);
}
}
}
return TRUE;
}
When I run this code nothing is output to the screen almost as if the program is not attached. I tried running it under a console and windows subsystem in VS2013.
According to the GetCurrentProcessId docs, the API
Retrieves the process identifier of the calling process.
GetWindowThreadProcessId, on the other hand,
Retrieves the identifier of the thread that created the specified window and, optionally, the identifier of the process that created the window.
The return value is the identifier of the thread that created the window.
So looking at your call:
pid = GetWindowThreadProcessId(hwnd, NULL);
You're actually getting back a thread ID, not a process ID. So when you compare pid to id, you're comparing a process ID and a thread ID, and that's just not going to work. Try this instead:
GetWindowThreadProcessId(hwnd, &pid);
(Note: I can't actually test whether this works, since EnumWindows requires a top-level window to enumerate and I ran this as a console app. Let me know if this answer doesn't work for you and I'll delete it.)
(As a second note, you don't need to use NULL anymore, even for WinAPI stuff like HWND. nullptr will work perfectly fine.)
I assume you're trying to find the "Main" window from the ProcessID.. In that case, this MAY help:
#include "stdafx.h"
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <Windows.h>
struct WindowHandleStructure
{
unsigned long PID;
HWND WindowHandle;
};
BOOL CALLBACK EnumWindowsProc(HWND WindowHandle, LPARAM lParam)
{
unsigned long PID = 0;
WindowHandleStructure* data = reinterpret_cast<WindowHandleStructure*>(lParam);
GetWindowThreadProcessId(WindowHandle, &PID);
if (data->PID != PID || (GetWindow(WindowHandle, GW_OWNER) && !IsWindowVisible(WindowHandle)))
{
return TRUE;
}
data->WindowHandle = WindowHandle;
return FALSE;
}
HWND FindMainWindow(unsigned long PID)
{
WindowHandleStructure data = { PID, nullptr };
EnumWindows(EnumWindowsProc, reinterpret_cast<LPARAM>(&data));
return data.WindowHandle;
}
int main()
{
HWND Window = FindMainWindow(GetCurrentProcessId());
std::wstring Buffer(GetWindowTextLength(Window) + 1, L'\0');
GetWindowText(Window, &Buffer[0], Buffer.size());
std::wcout << Buffer.c_str() << L"\n";
system("pause");
return 0;
}
I'm in the process of writing a dll-injected global keyboard hook but have run into a problem which I can't seem to get past. So far I have one program which is in charge of loading the dll and calling the SetWindowsHookEx, and ofcourse the dll to be injected.
The DLL looks like this:
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <string>
using namespace std;
INT APIENTRY DllMain(HMODULE hDLL, DWORD Reason, LPVOID Reserved) {
switch(Reason) {
case DLL_PROCESS_ATTACH:
//fprintf(file, "DLL attach function called.\n");
break;
case DLL_PROCESS_DETACH:
//fprintf(file, "DLL detach function called.\n");
break;
case DLL_THREAD_ATTACH:
//fprintf(file, "DLL thread attach function called.\n");
break;
case DLL_THREAD_DETACH:
//fprintf(file, "DLL thread detach function called.\n");
break;
}
return TRUE;
}
extern "C" __declspec(dllexport) int meconnect(int code, WPARAM wParam, LPARAM lParam) {
if (code == HC_ACTION)
{
FILE *file;
string c;
c += (char)wParam;
fopen_s(&file, "c:\\temp2.txt", "a+");
fprintf(file, c.c_str());
fclose(file);
}
return(CallNextHookEx(NULL, code, wParam, lParam));
}
The main program looks like this:
#include <Windows.h>
#include <iostream>
#include <SDKDDKVer.h>
using namespace std;
void CheckForExit(HHOOK handle);
int main()
{
/*
* Load library in which we'll be hooking our functions.
*/
HMODULE dll = LoadLibrary(L"dllinject.dll");
if (dll == NULL) {
printf("The DLL could not be found.n");
//getchar();
return -1;
}
/*
* Get the address of the function inside the DLL.
*/
HOOKPROC addr = (HOOKPROC)GetProcAddress(dll, "meconnect");
if (addr == NULL) {
printf("The function was not found.n");
//getchar();
return -1;
}
DWORD id = GetCurrentThreadId();
HHOOK handle = SetWindowsHookEx(WH_KEYBOARD, addr, dll, 0);
if (handle == NULL) {
printf("The KEYBOARD could not be hooked.n");
}
/*
* Unhook the function.
*/
system("pause");
UnhookWindowsHookEx(handle);
return 0;
}