Github Repo: https://github.com/JeffersonQin/Max4Min

Introduction

看到这个轻小说标题一样的标题就可以知道这个项目必定不是在干什么正事。因为作者是学生,所以需要精通 Alt + tab / Win + Down 之类的技巧。然而,Windows 10 2004 以后 Alt + Tab 就开始抽风(可能会跳到莫名其妙的进程),所以只能放弃转而使用 Win + Down。而 Win + Down 也会带来问题,那就是在最大化时需要按两下快捷键,对于我这种懒人无疑是灾难性的。所以,写这个。

功能如题。主要使用 win32api 的 SetWindowsHookEx , WH_CBT 完成。

Screenshots

技术难点的介绍

用 C++ 实现 Hook

//Initialized Data to be shared with all instance of the dll
#pragma data_seg("Shared")
HINSTANCE g_hInstance = NULL;
HHOOK g_hCBTHook = NULL;
#pragma data_seg()
// Initialised data End of data share
#pragma comment(linker,"/section:Shared,RWS")


BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        {
            g_hInstance = hModule;
            break;
        }
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

#ifdef __cplusplus
extern "C"
{
#endif


static LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode == HCBT_MINMAX && 
        lParam == SW_MAXIMIZE && 
        (GetAsyncKeyState(VK_SHIFT) & 0x8000)) {
        HWND hwnd = (HWND)wParam;
        HMONITOR hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
        MONITORINFO monitorInfo;
        monitorInfo.cbSize = sizeof(MONITORINFO);
        GetMonitorInfo(hMonitor, (LPMONITORINFO)&monitorInfo);
        RECT area = monitorInfo.rcWork;
        SetWindowPos(hwnd, HWND_TOP, area.left, area.top, area.right - area.left, area.bottom - area.top, SWP_SHOWWINDOW);
        return 1;
    }
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}


bool InstallCBTHook()
{
    if (!g_hCBTHook)
    {
        g_hCBTHook = SetWindowsHookEx(WH_CBT, (HOOKPROC)CBTProc, g_hInstance, 0);

        if (g_hCBTHook)
        {
            DEBUG(OutputDebugStringA("Hook CBT succeed\n"));
            return true;
        }
        else
        {
            DEBUG({
                DWORD dwError = GetLastError();
                char szError[MAX_PATH];
                _snprintf_s(szError, MAX_PATH, "Hook CBT failed, error = %u\n", dwError);
                OutputDebugStringA(szError);
            });
        }
    }

    return false;
}

bool UninstallCBTHook()
{
    if (g_hCBTHook)
    {
        UnhookWindowsHookEx(g_hCBTHook);
        g_hCBTHook = NULL;
        DEBUG(OutputDebugStringA("Uninstall CBT Hook\n"));
    }
    return true;
}

参考:https://github.com/yinkaisheng/MyLiteSpy ,顺便维护了一下这个项目提了个 PR ( :, https://github.com/yinkaisheng/MyLiteSpy/pull/2

关于 Hook 过程的本身

我们知道,32 位进程只能对 32 位的进程进行 Hook,64 位亦然。下面是我的解决方案:

  • 分别编译 Hook / Unhook 的 32 位和 64 位
  • 判断是否是 64 位系统,若是,则都 Hook / Unhook
  • 由于 Hook / Unhook 是写成死循环(也不能说是死循环,一直在等待消息传入,不占用资源),所以没法退出,所以 Unhook 的时候顺便 taskkill 一下

Hook, Unhook 的代码:

#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")

#include <iostream>
#include "../Max4MinCore/HookHandlerProc.h"

int main()
{
    if (!InstallCBTHook()) {
        std::cerr << "Install failed!" << std::endl;
    }
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {}
    return 0;
}
#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")

#include <iostream>
#include "../Max4MinCore/HookHandlerProc.h"

int main()
{
    if (!UninstallCBTHook()) {
        std::cerr << "Uninstall failed!" << std::endl;
    }
    return 0;
}
最后修改:2021 年 10 月 04 日 11 : 56 AM
真的不买杯奶茶嘛....qwq