Windows 메시지 루프에서 벗어나고 싶습니다. C ++ 과 마찬가지로 Windows hook에서 메시지 루프를 끊는 방법 . Window Desktop Application에서는 잘 작동하지만 Console Application에서는 실패하는 하나의 솔루션을 생각해 냈습니다. 어떻게 이런 일이 일어날 수 있습니까?
편집 : https://github.com/hellohawaii3/Experiment에 내 코드를 업로드하고 복제 한 다음 내 문제를 빠르게 재현 할 수 있습니다. 감사!
1. 왜 그렇게하고 싶습니까?
콘솔 애플리케이션을 작성 중입니다. 첫째, 사용자는 앱의 동작을 결정하는 몇 가지 옵션을 설정해야합니다. 그런 다음 앱은 메시지 루프를 시작하고 키보드 / 마우스의 입력을 모니터링합니다. 사용자가 설정을 변경하고 싶을 수 있으므로 사용자가 핫키를 누르고 메시지 루프를 종료하고 응용 프로그램의 시작으로 돌아갈 수 있도록하고 싶습니다.
메시지 루프에서 벗어날 염려없이이 함수를 구현하는 방법을 알고 있다면 알려주세요! 그러나 내 솔루션이 콘솔 앱에서 실패하고 데스크톱 앱에서 잘 작동하는 이유도 알고 싶습니다. 당신의 도움을 주셔서 감사합니다!
2. 나는 무엇을 시도했다.
부울 변수 ' recieve_quit '를 사용합니다. 키보드에서 특정 키를 누르면 후크 콜백 함수에 의해 변수가 True로 설정됩니다. 메시지를받는 모든 루프에 대해 먼저 ' recieve_quit ' 변수를 확인 하고 변수가 False이면 종료합니다.
3. 실험 결과
콘솔 APP의 경우 특정 키를 누르면 ' recieve_quit ' 변수 가 올바르게 설정되지만 메시지 루프는 계속됩니다.
GUI가있는 Windows 데스크톱 앱의 경우 예상대로 메시지 루프를 종료 할 수 있습니다.
4. 실험 설정
VS2019, C ++, Windows 10 home 1909를 사용하고 있습니다.
dll 주입을 사용하여 콘솔 앱에 대한 후크를 설정합니다.
5. 내 코드
여기에서는 대부분 Visual Studio에서 자동으로 생성되는 장난감 예제 코드를 제공합니다 . 내 코드가 너무 느슨하다고 생각하더라도 신경 쓰지 마십시오 .
(a) 내 콘솔 앱
콘솔:
// 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);
}
(b) 후크 함수를 포함하는 내 DLL
doc https://docs.microsoft.com/en-us/cpp/build/walkthrough-creating-and-using-a-dynamic-link-library-cpp?view=msvc-160에 따라 내 dll_func.h 파일
#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는 https://stackoverflow.com/a/60913531/9758790 에서 제안한대로 Visual Studio에서 만든 후 약간 수정 되어 dll 주입에 대한 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) My Desktop 응용 프로그램.
Visual Studio로 솔루션을 만들 때 'Windows 데스크톱 응용 프로그램 만들기'를 선택합니다. 대부분의 코드는 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;
}
(d) 결과 재현을위한 팁
콘솔 APP의 경우 :라는 새 콘솔 응용 프로그램 솔루션 구축 콘솔 및 Console.cpp을 대체 할 내 코드를 복사합니다. 이 솔루션에 새 프로젝트를 추가하고 새 프로젝트 만들기 -> DLL을 선택하고 이름을 Dll1 로 지정 합니다. dll_func.h, dll_func.c를 만들고 내 코드를 복사합니다. dllmain.cpp도 수정하는 것을 잊지 마십시오. AdditionalIncludeDirectories의 경로를 설정하고 AdditionalDependencies에 dll1.lib를 추가합니다. 경로도 dll 및 lib 파일로 설정하십시오.
Windows 데스크톱 앱의 경우 : 지금 Windows 데스크톱 앱 솔루션을 빌드하고 이름을 GUI로 지정합니다. 내 코드를 GUI.cpp에 복사하고 실행하기 만하면됩니다. F8 키를 누르면 앱이 종료되고 예상대로 메시지 상자가 나타납니다.
이 게시물은 콘솔 앱이 키보드 메시지를 수신 하지 않는 이유와이를 처리 할 수있는 방법을 설명합니다.
콘솔의 경우 실행이 시작 GetMessage
되고 종료되지 않습니다. 후크는 알림을 수신하고 recieve_quit
. 실행이 종료 GetMessage
되지 않으므로 recieve_quit가 확인되지 않습니다.
이 답변은 콘솔 앱에서 키 누르기 처리에 관한 것입니다. https://stackoverflow.com/a/6479673/4240951
일반적으로 콘솔 앱에 숨겨진 창을 추가하거나 GetAsyncState
필요한 키를 확인 합니다.
메시지 루프가있을 때 후크를 설정할 필요가 없습니다. 다음과 같이 키 누름을 확인할 수 있습니다.
while (recieve_quit == false)
{
if (GetMessage(&msg, nullptr, 0, 0)) {
if (msg.message == WM_KEYDOWN && msg.wParam == VK_F8)
recieve_quit = true;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다