模拟全局热键

Posted

技术标签:

【中文标题】模拟全局热键【英文标题】:Simulate global hotkey 【发布时间】:2016-02-28 14:32:39 【问题描述】:

您好,我正在使用 Visual c++ 我有一个工作程序,除了一件事我希望它能够使用单独的屏幕截图程序。我安装了单独的截图程序,它有一个注册的热键来截图,我希望我的程序来做。

我用 FindWindow 尝试了 keybd_event 和 SendInput,它在记事本上运行良好,但它的屏幕截图程序运行最小化或隐藏,我知道 SetForegroundWindow 的问题。

有没有办法让它像我刚刚在键盘上按下的所有窗口一样的 Windows 系统,就像真正的热键一样?它的屏幕截图程序可以使用真正的热键正常工作。

【问题讨论】:

【参考方案1】:

也许键盘挂钩可以解决问题?

example 1 example 2 example 3

并不完美,但它们非常全球化

【讨论】:

好的,我可以了解钩子,但这可以按下按键还是只听?是其他程序有热键,我想我的程序按下它。 看来我明白了。您想模拟击键,以便外部程序截取屏幕截图,对吗?好吧,那不是您遇到问题的热键。顺便说一句,请考虑以下几点: * 从您自己的代码中获取截图非常容易 * 您可以将自己的热键与对上述程序的某种管道调用结合起来 但是无论如何,您现在想要的解决方案是 SendInput() .是的,您已经尝试过了,但如果实施得当,应该可以工作。【参考方案2】:

这是一个全局热键的源代码。它已设置为侦听 CTRL + y 组合键。一旦 CTRL + y 被触发,它就会抓取屏幕截图。

要关闭全局热键,只需按 CTRL + q

要隐藏控制台窗口并保持热键在后台运行,请按 CTRL + w

#define _WIN32_WINNT 0x0400
#pragma comment( lib, "user32.lib" )

#include <iostream>
#include <windows.h>
#include <stdio.h>

HHOOK hKeyboardHook;
__declspec(dllexport) LRESULT CALLBACK KeyboardEvent (int nCode, WPARAM wParam, LPARAM lParam);
void MessageLoop();
DWORD WINAPI my_HotKey(LPVOID lpParm);
int toggleVisibility = 1;

int screenResolutionX = GetSystemMetrics(SM_CXSCREEN);
int screenResolutionY = GetSystemMetrics(SM_CYSCREEN);
POINT startCoord,endCoord;
void grabScreenshot(POINT a, POINT b);

/*********************************************
***                                        ***
***                                        ***
**********************************************/


int main(int argc, char** argv)

    /* uncomment to hide console window */
    //ShowWindow(FindWindowA("ConsoleWindowClass", NULL), false);

    HANDLE hThread;
    DWORD dwThread;

    printf("\n  s c r e e n s h o t   H O T K E Y    \n\n");
    printf("press  CTRL-y  for  screenshot \n");
    printf("press  CTRL-w  to hide or make console window visible  \n");
    printf("press  CTRL-q  to quit  \n");
    printf("\n\n");

    hThread = CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)   my_HotKey, (LPVOID) argv[0], NULL, &dwThread);

    if (hThread) return WaitForSingleObject(hThread,INFINITE);
    else return 1;



/*********************************************
***                                        ***
***                                        ***
**********************************************/


__declspec(dllexport) LRESULT CALLBACK KeyboardEvent (int nCode, WPARAM wParam, LPARAM lParam)
    DWORD SHIFT_key=0;
    DWORD CTRL_key=0;
    DWORD ALT_key=0;


    if  ( (nCode == HC_ACTION) &&   ((wParam == WM_SYSKEYDOWN) ||  (wParam == WM_KEYDOWN) ) ) 
        KBDLLHOOKSTRUCT hooked_key =    *((KBDLLHOOKSTRUCT*)lParam);
        DWORD dwMsg = 1;
        dwMsg += hooked_key.scanCode << 16;
        dwMsg += hooked_key.flags << 24;
        char lpszKeyName[1024] = 0;

        int i = GetKeyNameText(dwMsg,   (lpszKeyName+1),0xFF) + 1;

        int key = hooked_key.vkCode;

        SHIFT_key = GetAsyncKeyState(VK_SHIFT);
        CTRL_key  = GetAsyncKeyState(VK_CONTROL);
        ALT_key   = GetAsyncKeyState(VK_MENU);

        //printf("%c",key);

        if ( (key >= 'A') && (key <= 'Z') || (key >= 'a') && (key <= 'z') || (key >= '0') && (key <= '9') ) 

            if  (GetAsyncKeyState(VK_SHIFT)>= 0) key +=32;

            /*********************************************
            ***   Hotkey scope                         ***
            ***   do stuff here                        ***
            **********************************************/

            if ( (CTRL_key !=0) && (key == 'y') || (key == 'Y') ) 

               CTRL_key=0;

               // grab a screenshot

                startCoord.x=0;
                startCoord.y=0;

                endCoord.x=screenResolutionX;
                endCoord.y=screenResolutionY;

                ShowWindow(FindWindowA("ConsoleWindowClass", NULL), false);
                Sleep(1000);
                grabScreenshot(startCoord,endCoord);

                ShowWindow(FindWindowA("ConsoleWindowClass", NULL), true);

                printf("\nThe Screenshot is in the Clipboard  \n\n");
            

            //******************************************************

            if ( (CTRL_key !=0) && (key == 'q') ||  (key == 'Q') ) 
                MessageBox(NULL, "\n\n\n\nShutting down\n\nPress  OK   to close\n\n", "                                     H O T K E Y                                            ", MB_OK);
               PostQuitMessage(0);
            

            //******************************************************

            if ( (CTRL_key !=0) && (key == 'w') ||  (key == 'W') ) 

                toggleVisibility = - toggleVisibility;

                if (toggleVisibility >0 ) 
                    ShowWindow(FindWindowA("ConsoleWindowClass", NULL), true);
                
                else
                    ShowWindow(FindWindowA("ConsoleWindowClass", NULL), false);
                
            

            SHIFT_key = 0;CTRL_key = 0; ALT_key = 0;
        
    
    return CallNextHookEx(hKeyboardHook,    nCode,wParam,lParam);



/*********************************************
***                                        ***
***                                        ***
**********************************************/

void MessageLoop()

    MSG message;
    while (GetMessage(&message,NULL,0,0))
        TranslateMessage( &message );
        DispatchMessage( &message );
    


/*********************************************
***                                        ***
***                                        ***
**********************************************/


DWORD WINAPI my_HotKey(LPVOID lpParm)
    HINSTANCE hInstance = GetModuleHandle(NULL);
    if (!hInstance) hInstance = LoadLibrary((LPCSTR) lpParm);
    if (!hInstance) return 1;

    hKeyboardHook = SetWindowsHookEx (  WH_KEYBOARD_LL, (HOOKPROC) KeyboardEvent,   hInstance,  NULL    );
    MessageLoop();
    UnhookWindowsHookEx(hKeyboardHook);
    return 0;



/*********************************************
***                                        ***
***                                        ***
**********************************************/


void grabScreenshot(POINT a, POINT b)

    // copy screen to bitmap
    HDC     hScreen = GetDC(NULL);
    HDC     hDC     = CreateCompatibleDC(hScreen);
    HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, abs(b.x-a.x), abs(b.y-a.y));
    HGDIOBJ old_obj = SelectObject(hDC, hBitmap);
    BOOL    bRet    = BitBlt(hDC, 0, 0, abs(b.x-a.x), abs(b.y-a.y), hScreen, a.x, a.y, SRCCOPY);

    // save bitmap to clipboard
    OpenClipboard(NULL);
    EmptyClipboard();
    SetClipboardData(CF_BITMAP, hBitmap);
    CloseClipboard();

    // clean up
    SelectObject(hDC, old_obj);
    DeleteDC(hDC);
    ReleaseDC(NULL, hScreen);
    DeleteObject(hBitmap);

【讨论】:

以上是关于模拟全局热键的主要内容,如果未能解决你的问题,请参考以下文章

全局拦截热键,但保持原始行为

Cocoa NSStatusBar 全局热键

替换全局热键

在 golang 中实现全局热键?

使用全局热键调用 powershell 函数

WPF中的全局热键?