方向键增强



#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <shellapi.h>
#include <cstdio>
#include <iostream>


//static DWORD g_mainThreadId = 0;
static HHOOK g_hook = nullptr;
static NOTIFYICONDATA g_notifyIconData = {};
static HWND g_hWnd = nullptr;

// 双击判定时间窗
static const DWORD kDoubleTapWindowMs = 1000;
static DWORD g_lastRightTick = 0;
static DWORD g_lastLeftTick = 0;

static DWORD g_lastUpTick = 0;
static DWORD g_lastDownTick = 0;

// 退出标志
bool g_exitRequested = false;

// 退出热键判断
static bool IsDown(int vk) { return (GetAsyncKeyState(vk) & 0x8000) != 0; }
//static bool WantExitHotkey() { return IsDown(VK_CONTROL) && IsDown(VK_MENU) && IsDown('Q'); }


static void Dbg(const char* s) {
    OutputDebugStringA(s);
    std::printf("%s", s);
    std::fflush(stdout);
}






static void SendEndRespectPhysicalShift(bool wantShiftSelection) {
    bool physicalShiftDown = (GetAsyncKeyState(VK_LSHIFT) & 0x8000) || (GetAsyncKeyState(VK_RSHIFT) & 0x8000);

    // 我们是否需要“自己按下Shift”
    bool needInjectShift = wantShiftSelection && !physicalShiftDown;

    INPUT in[4] = {};
    int n = 0;

    if (needInjectShift) {
        in[n].type = INPUT_KEYBOARD;
        in[n].ki.wVk = VK_SHIFT;
        in[n].ki.dwFlags = 0;
        n++;
    }

    // END(注意:END 在 SendInput 里最好用 EXTENDEDKEY)
    in[n].type = INPUT_KEYBOARD;
    in[n].ki.wVk = VK_END;
    in[n].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
    n++;

    in[n].type = INPUT_KEYBOARD;
    in[n].ki.wVk = VK_END;
    in[n].ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP;
    n++;

    if (needInjectShift) {
        in[n].type = INPUT_KEYBOARD;
        in[n].ki.wVk = VK_SHIFT;
        in[n].ki.dwFlags = KEYEVENTF_KEYUP;
        n++;
    }

    SendInput(n, in, sizeof(INPUT));
}


static void SendHomeRespectPhysicalShift(bool wantShiftSelection) {
    bool physicalShiftDown = (GetAsyncKeyState(VK_LSHIFT) & 0x8000) || (GetAsyncKeyState(VK_RSHIFT) & 0x8000);
    bool needInjectShift = wantShiftSelection && !physicalShiftDown;

    INPUT in[4] = {};
    int n = 0;

    if (needInjectShift) {
        in[n].type = INPUT_KEYBOARD;
        in[n].ki.wVk = VK_SHIFT;
        in[n].ki.dwFlags = 0;
        n++;
    }

    in[n].type = INPUT_KEYBOARD;
    in[n].ki.wVk = VK_HOME;
    in[n].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
    n++;

    in[n].type = INPUT_KEYBOARD;
    in[n].ki.wVk = VK_HOME;
    in[n].ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP;
    n++;

    if (needInjectShift) {
        in[n].type = INPUT_KEYBOARD;
        in[n].ki.wVk = VK_SHIFT;
        in[n].ki.dwFlags = KEYEVENTF_KEYUP;
        n++;
    }

    SendInput(n, in, sizeof(INPUT));
}





static void SendCtrlEndRespectPhysicalShift(bool wantShiftSelection) {
    bool physicalShiftDown =
        (GetAsyncKeyState(VK_LSHIFT) & 0x8000) ||
        (GetAsyncKeyState(VK_RSHIFT) & 0x8000);
    bool physicalCtrlDown =
        (GetAsyncKeyState(VK_LCONTROL) & 0x8000) ||
        (GetAsyncKeyState(VK_RCONTROL) & 0x8000);

    bool needInjectShift = wantShiftSelection && !physicalShiftDown;
    bool needInjectCtrl = !physicalCtrlDown;

    // 最多:Ctrl down, Shift down, End down/up, Ctrl up, Shift up
    INPUT in[6] = {};
    int n = 0;

    if (needInjectCtrl) {
        in[n].type = INPUT_KEYBOARD;
        in[n].ki.wVk = VK_CONTROL;
        in[n].ki.dwFlags = 0;
        n++;
    }

    if (needInjectShift) {
        in[n].type = INPUT_KEYBOARD;
        in[n].ki.wVk = VK_SHIFT;
        in[n].ki.dwFlags = 0;
        n++;
    }

    // End
    in[n].type = INPUT_KEYBOARD;
    in[n].ki.wVk = VK_END;
    in[n].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
    n++;

    in[n].type = INPUT_KEYBOARD;
    in[n].ki.wVk = VK_END;
    in[n].ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP;
    n++;

    if (needInjectShift) {
        in[n].type = INPUT_KEYBOARD;
        in[n].ki.wVk = VK_SHIFT;
        in[n].ki.dwFlags = KEYEVENTF_KEYUP;
        n++;
    }

    if (needInjectCtrl) {
        in[n].type = INPUT_KEYBOARD;
        in[n].ki.wVk = VK_CONTROL;
        in[n].ki.dwFlags = KEYEVENTF_KEYUP;
        n++;
    }

    SendInput(n, in, sizeof(INPUT));
}

static void SendCtrlHomeRespectPhysicalShift(bool wantShiftSelection) {
    bool physicalShiftDown =
        (GetAsyncKeyState(VK_LSHIFT) & 0x8000) ||
        (GetAsyncKeyState(VK_RSHIFT) & 0x8000);
    bool physicalCtrlDown =
        (GetAsyncKeyState(VK_LCONTROL) & 0x8000) ||
        (GetAsyncKeyState(VK_RCONTROL) & 0x8000);

    bool needInjectShift = wantShiftSelection && !physicalShiftDown;
    bool needInjectCtrl = !physicalCtrlDown;

    INPUT in[6] = {};
    int n = 0;

    if (needInjectCtrl) {
        in[n].type = INPUT_KEYBOARD;
        in[n].ki.wVk = VK_CONTROL;
        in[n].ki.dwFlags = 0;
        n++;
    }

    if (needInjectShift) {
        in[n].type = INPUT_KEYBOARD;
        in[n].ki.wVk = VK_SHIFT;
        in[n].ki.dwFlags = 0;
        n++;
    }

    // Home
    in[n].type = INPUT_KEYBOARD;
    in[n].ki.wVk = VK_HOME;
    in[n].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
    n++;

    in[n].type = INPUT_KEYBOARD;
    in[n].ki.wVk = VK_HOME;
    in[n].ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP;
    n++;

    if (needInjectShift) {
        in[n].type = INPUT_KEYBOARD;
        in[n].ki.wVk = VK_SHIFT;
        in[n].ki.dwFlags = KEYEVENTF_KEYUP;
        n++;
    }

    if (needInjectCtrl) {
        in[n].type = INPUT_KEYBOARD;
        in[n].ki.wVk = VK_CONTROL;
        in[n].ki.dwFlags = KEYEVENTF_KEYUP;
        n++;
    }

    SendInput(n, in, sizeof(INPUT));
}



// 低级键盘钩子回调函数

static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
    if (nCode == HC_ACTION) {
        const KBDLLHOOKSTRUCT* k = reinterpret_cast<const KBDLLHOOKSTRUCT*>(lParam);

        if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)) {
            // 忽略我们自己注入的键,避免刷屏/递归
            if (k->flags & LLKHF_INJECTED) {
                return CallNextHookEx(g_hook, nCode, wParam, lParam);
            }

            // ✅ 打印:确认钩子确实收到了键
    /*        char buf[256];
            std::snprintf(buf, sizeof(buf),
                "[key] vk=%lu ctrl=%d alt=%d shift=%d\n",
                (unsigned long)k->vkCode,
                IsDown(VK_CONTROL) ? 1 : 0,
                IsDown(VK_MENU) ? 1 : 0,
                IsDown(VK_SHIFT) ? 1 : 0);
            Dbg(buf);*/

            // ✅ 退出热键:Ctrl+Alt+Q
            if (IsDown(VK_CONTROL) && IsDown(VK_MENU) && (k->vkCode == 'Q')) {
                Dbg("[exit] hotkey hit: Ctrl+Alt+Q\n");

                // ✅ 最稳:让“当前线程”的消息循环退出
                PostQuitMessage(0);

                // (可选)如果你想用 PostThreadMessage,也可以这样写并检查返回值:
                // BOOL ok = PostThreadMessageW(g_mainThreadId, WM_QUIT, 0, 0);
                // if (!ok) {
                //     DWORD e = GetLastError();
                //     char b2[128];
                //     std::snprintf(b2, sizeof(b2), "[exit] PostThreadMessage failed err=%lu\n", (unsigned long)e);
                //     Dbg(b2);
                // }

                return 1; // 吃掉热键
            }

            // 下面是你原来的 Right 双击逻辑(略:保持不变,只要放这里就行)
            if (k->vkCode == VK_RIGHT) {
                DWORD now = GetTickCount();
                DWORD delta = now - g_lastRightTick;

                if (g_lastRightTick != 0 && delta <= kDoubleTapWindowMs) {
                    bool withShift = (GetAsyncKeyState(VK_LSHIFT) & 0x8000) || (GetAsyncKeyState(VK_RSHIFT) & 0x8000);
                    SendEndRespectPhysicalShift(withShift);
                    g_lastRightTick = 0;
                    return 1;
                }
                else {
                    g_lastRightTick = now;
                }
            }


            if (k->vkCode == VK_LEFT) {
                DWORD now = GetTickCount();
                DWORD delta = now - g_lastLeftTick;

                if (g_lastLeftTick != 0 && delta <= kDoubleTapWindowMs) {
                    bool withShift = (GetAsyncKeyState(VK_LSHIFT) & 0x8000) || (GetAsyncKeyState(VK_RSHIFT) & 0x8000);
                    SendHomeRespectPhysicalShift(withShift);

                    g_lastLeftTick = 0;
                    return 1; // 吃掉第二下 Left
                }
                else {
                    g_lastLeftTick = now;
                }
            }






            // ↑↑ 双击:到文件最开头(Ctrl+Home),Shift 选中
            if (k->vkCode == VK_UP) {
                DWORD now = GetTickCount();
                DWORD delta = now - g_lastUpTick;

                if (g_lastUpTick != 0 && delta <= kDoubleTapWindowMs) {
                    bool withShift =
                        (GetAsyncKeyState(VK_LSHIFT) & 0x8000) ||
                        (GetAsyncKeyState(VK_RSHIFT) & 0x8000);

                    SendCtrlHomeRespectPhysicalShift(withShift);

                    g_lastUpTick = 0;
                    return 1; // 吃掉第二下 Up
                }
                else {
                    g_lastUpTick = now;
                }
            }

            // ↓↓ 双击:到文件最末尾(Ctrl+End),Shift 选中
            if (k->vkCode == VK_DOWN) {
                DWORD now = GetTickCount();
                DWORD delta = now - g_lastDownTick;

                if (g_lastDownTick != 0 && delta <= kDoubleTapWindowMs) {
                    bool withShift =
                        (GetAsyncKeyState(VK_LSHIFT) & 0x8000) ||
                        (GetAsyncKeyState(VK_RSHIFT) & 0x8000);

                    SendCtrlEndRespectPhysicalShift(withShift);

                    g_lastDownTick = 0;
                    return 1; // 吃掉第二下 Down
                }
                else {
                    g_lastDownTick = now;
                }
            }


        }
    }
    return CallNextHookEx(g_hook, nCode, wParam, lParam);
}






// 托盘图标右键菜单处理
void ShowTrayMenu(HWND hwnd) {
    HMENU hMenu = CreatePopupMenu();
    AppendMenu(hMenu, MF_STRING, 1, L"退出");

    POINT pt;
    GetCursorPos(&pt);
    SetForegroundWindow(hwnd);
    TrackPopupMenu(hMenu, TPM_LEFTBUTTON, pt.x, pt.y, 0, hwnd, NULL);

    DestroyMenu(hMenu);
}

// 处理消息循环的部分
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    case WM_SYSCOMMAND:
        if (wParam == SC_CLOSE) {
            // 点击关闭按钮时也退出
            g_exitRequested = true;
            return 0;
        }
        break;
    case WM_COMMAND:
        if (LOWORD(wParam) == 1) {
            g_exitRequested = true;
            return 0;
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    case WM_APP + 1:
        if (lParam == WM_RBUTTONDOWN) {
            ShowTrayMenu(hwnd);
        }
        break;
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

// 创建托盘图标
void AddTrayIcon(HWND hwnd) {
    g_notifyIconData.cbSize = sizeof(g_notifyIconData);
    g_notifyIconData.uID = 1;
    g_notifyIconData.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
    g_notifyIconData.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    g_notifyIconData.hWnd = hwnd;
    g_notifyIconData.uCallbackMessage = WM_APP + 1;
    lstrcpy(g_notifyIconData.szTip, TEXT("右键退出"));

    Shell_NotifyIcon(NIM_ADD, &g_notifyIconData);
}

void RemoveTrayIcon() {
    Shell_NotifyIcon(NIM_DELETE, &g_notifyIconData);
}

int main() {

    std::cout << "ctrl + alt + q 退出" << std::endl;

    //g_mainThreadId = GetCurrentThreadId();

    // 注册窗口类
    const char* className = "TrayAppClass";
    // 获取宽字符字符串的长度
    int len = MultiByteToWideChar(CP_ACP, 0, className, -1, NULL, 0);

    // 创建宽字符数组
    wchar_t* wideClassName = new wchar_t[len];

    // 将 char 字符串转换为宽字符字符串
    MultiByteToWideChar(CP_ACP, 0, className, -1, wideClassName, len);

    WNDCLASS wc = { 0 };
    wc.lpfnWndProc = WindowProc;
    wc.lpszClassName = wideClassName;
    wc.hInstance = GetModuleHandle(NULL);
    RegisterClass(&wc);

    g_hWnd = CreateWindowExW(
        0,
        wideClassName,
        wideClassName,
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        wc.hInstance,
        NULL
    );

    delete[] wideClassName;
    if (!g_hWnd) {
        MessageBox(NULL, TEXT("Failed to create window"), TEXT("Error"), MB_OK);
        return 1;
    }

    AddTrayIcon(g_hWnd);

    // 安装低级钩子
    g_hook = SetWindowsHookExW(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandleW(NULL), 0);
    if (!g_hook) {
        MessageBox(NULL, TEXT("Failed to install keyboard hook"), TEXT("Error"), MB_OK);
        return 1;
    }

    MSG msg;
    while (!g_exitRequested && GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    // 清理
    UnhookWindowsHookEx(g_hook);
    RemoveTrayIcon();

    return 0;
}


posted @ 2025-12-10 11:53  AngDH  阅读(0)  评论(0)    收藏  举报