콘솔 앱에서 Windows 메시지 루프를 종료 / 중단하는 방법 및 Windows 데스크톱 앱의 종속성은 무엇입니까?

안녕 하와이

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] 삭제

에서 수정
0

몇 마디 만하겠습니다

0리뷰
로그인참여 후 검토

관련 기사

Related 관련 기사

뜨겁다태그

보관