Я хочу выйти из цикла сообщений Windows. Как и C ++, как разорвать цикл сообщений в хуке Windows. Я придумал одно решение, которое отлично работает для приложения Window Desktop, но не работает для консольного приложения. Как такое могло случиться?
РЕДАКТИРОВАТЬ: я загружаю свои коды в https://github.com/hellohawaii3/Experiment, клонирую его и тогда вы сможете быстро воспроизвести мою проблему. Спасибо!
1. Почему я хочу это сделать?
Пишу консольное приложение. Во-первых, пользователей просят установить некоторые параметры, которые определяют поведение приложения. Затем приложение запускает цикл сообщений и отслеживает ввод с клавиатуры / мыши. Пользователи могут захотеть изменить настройку, поэтому я хочу, чтобы пользователи нажимали горячую клавишу, выходили из цикла сообщений и возвращались к началу приложения.
Если вы знаете, как реализовать эту функцию, не беспокоясь о выходе из цикла обработки сообщений, сообщите мне! Однако я также хочу знать, почему мое решение не работает для консольного приложения и хорошо работает для настольного приложения. Спасибо за вашу помощь!
2. Что я пробовал.
Я использую переменную типа bool recieve_quit. Когда на клавиатуре нажимается определенная клавиша, функция обратного вызова ловушки устанавливает для переменной значение True. Для каждого цикла получения сообщения сначала проверяйте переменную 'recieve_quit' и завершайте работу, когда переменная имеет значение False.
3. Результат моего эксперимента
Для консольного приложения переменная «recieve_quit» устанавливается правильно при нажатии определенной клавиши, однако цикл сообщений продолжается.
Для настольного приложения Windows с графическим интерфейсом я могу выйти из цикла сообщений, как и ожидалось.
4. Настройки эксперимента
Я использую VS2019, C ++, windows 10 home 1909.
Я использую dll inject, чтобы установить ловушку для своего консольного приложения.
5.Мой код
Я привожу здесь игрушечные примеры кодов, большая часть которых создается Visual Studio автоматически, не беспокойтесь, если вы думаете, что мои коды слишком замкнуты.
(а) Мое консольное приложение
Консоль:
// Console.cpp
#include <iostream>
#include "dll_func.h"
#include <windows.h>
int main()
{
MSG msg;
HHOOK hhook_tmp2 = SetWindowsHookEx(WH_KEYBOARD_LL, HandleKeyboardEvent, hDllModule, 0);
while (recieve_quit == false)
{
if (GetMessage(&msg, nullptr, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
MessageBox(NULL, TEXT("APP QUIT"), TEXT(" "), MB_OK);
}
(б) моя dll, содержащая функцию перехвата
Мой файл dll_func.h после doc https://docs.microsoft.com/en-us/cpp/build/walkthrough-creating-and-using-a-dynamic-link-library-cpp?view=msvc-160
#pragma once
#ifdef DLL1_EXPORTS
#define DLLFUNC_API __declspec(dllexport)
#else
#define DLLFUNC_API __declspec(dllimport)
#endif
#include "framework.h"
extern "C" DLLFUNC_API bool recieve_quit;
extern "C" DLLFUNC_API LRESULT CALLBACK HandleKeyboardEvent(int nCode, WPARAM wParam, LPARAM lParam);// refer to https://stackoverflow.com/a/60913531/9758790
extern "C" DLLFUNC_API HMODULE hDllModule;//or whatever name you like
Мой файл dll_func.cpp, содержащий определение функции перехвата.
#include "pch.h"
#include <windows.h>
#include "dll_func.h"
bool recieve_quit = false;
LRESULT CALLBACK HandleKeyboardEvent(int nCode, WPARAM wParam, LPARAM lParam)
{
PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)lParam;
if (wParam == WM_KEYDOWN) {
if (p->vkCode == VK_F8) {
recieve_quit = true;
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
Dllmain.cpp также немного изменяется после его создания в Visual Studio, как было предложено https://stackoverflow.com/a/60913531/9758790, чтобы получить подсказку о DLL для внедрения.
// dllmain.cpp
#include "pch.h"
#include "dll_func.h"
// refer to https://stackoverflow.com/a/60913531/9758790
HMODULE hDllModule;//or whatever name you like
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
hDllModule = hModule;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
(c) Приложение "Мой рабочий стол".
Я выбираю «Создание настольного приложения Windows» при создании решения с помощью Visual Studio. Большинство кодов автоматически генерируется VS. Коды, которые я добавил и изменил, заключены в символы *****.
// GUI.cpp
//
#include "framework.h"
#include "GUI.h"
#define MAX_LOADSTRING 100
HINSTANCE hInst;
WCHAR szTitle[MAX_LOADSTRING];
WCHAR szWindowClass[MAX_LOADSTRING];
// *********************** ADDED BY ME! ***********************
// same as dll_func.cpp
bool recieve_quit = false;
LRESULT CALLBACK HandleKeyboardEvent(int nCode, WPARAM wParam, LPARAM lParam)
{
//FILE* file;
//fopen_s(&file, "./temp0210222.txt", "a+");
//fprintf(file, "Function keyboard_hook called.n");
//fclose(file);
PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)lParam;
if (wParam == WM_KEYDOWN) {
if (p->vkCode == VK_F8) {
recieve_quit = true;
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
// *********************** END OF MY CODES ***********************
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// *********************** ADDED BY ME! ***********************
HHOOK hhook_tmp2 = SetWindowsHookEx(WH_KEYBOARD_LL, HandleKeyboardEvent, hInst, 0);
// *********************** END OF MY CODES ***********************
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_GUI, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_GUI));
MSG msg;
// main message loop:
// *********************** MODIFIED BY ME! ***********************
//while (GetMessage(&msg, nullptr, 0, 0))
//{
// if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
// {
// TranslateMessage(&msg);
// DispatchMessage(&msg);
// }
//}
while (recieve_quit == false)
{
if (GetMessage(&msg, nullptr, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
MessageBox(NULL, TEXT("APP QUIT"), TEXT(" "), MB_OK);
// *********************** END OF MODIFICATION ***********************
return (int) msg.wParam;
}
//
// Function: MyRegisterClass()
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_GUI));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_GUI);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
//
// Function: InitInstance(HINSTANCE, int)
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance;
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// Function: WndProc(HWND, UINT, WPARAM, LPARAM)
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// "About"
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
(г) Советы по воспроизведению моих результатов
Для консольного приложения: создайте новое решение консольного приложения с именем Console и скопируйте мои коды, чтобы заменить Console.cpp. Добавьте новый проект в это решение, выберите Создать новый проект - ›DLL, назовите его Dll1. Создайте dll_func.h, dll_func.c и скопируйте мои коды. Не забудьте также изменить dllmain.cpp. Задайте путь к AdditionalIncludeDirectories, добавьте dll1.lib в AdditionalDependencies. Также укажите путь к файлам dll и lib.
Для настольного приложения для Windows: создайте решение для настольного приложения для Windows, назовите его GUI. Скопируйте мои коды в GUI.cpp и просто запустите. Нажмите клавишу F8, приложение закроется и появится окно сообщения, как и ожидалось.