【问题标题】:How to implement "teamviewer quickconnect"-like button into foreign window?如何在外国窗口中实现类似“teamviewer quickconnect”的按钮?
【发布时间】:2016-03-30 09:32:10
【问题描述】:

我想在外部窗口的标题栏中放置一个按钮,就像 Teamviewer 使用 Quickconnect 功能所做的那样,或者 Chrome 在右上角有一个用于切换用户的按钮。

我知道这是How is Teamviewers Quickconnect button accomplished?的重复

我只是想知道是否有可能获得一个工作示例或一个指向实现此功能的开源程序的链接。那里给出的答案对我来说相当先进。如,我应该如何“挂钩”和“拦截” WM_NCPAINT 消息等等。

【问题讨论】:

    标签: winapi


    【解决方案1】:

    这是我能开发的最简单的例子:

    你需要Visual Studio,添加2个项目到解决方案中:

    第一个项目(HookDLL)是一个 dll 项目,第二个(正在运行的应用程序)是一个 win32 控制台项目

    在 main.cpp(在项目运行应用程序中)添加:

    __declspec(dllimport) void RunHook();
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        RunHook();
        return 0;
    }
    

    在 dllmain button.cpp(在 HookDLL 项目中)添加以下代码:

    #include <Windows.h>
    #include <stdio.h>
    
    HINSTANCE hinstDLL; 
    HHOOK hhook_wndproc; 
    HWND b = NULL;
    HBRUSH blue_brush = NULL, yellow_brush, red_brush;
    int button_status = 0;
    
    LRESULT CALLBACK DefaultWindowProc(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
    {
        switch(Msg)
        {
        case WM_CREATE:
            if(!blue_brush)
            {
                blue_brush = CreateSolidBrush(RGB(0, 0, 255));
                yellow_brush = CreateSolidBrush(RGB(255, 255, 0));
                red_brush = CreateSolidBrush(RGB(255, 0, 0));
    
            }
            break;
        case WM_PAINT:
            {
                HBRUSH b;
                PAINTSTRUCT ps;
                HDC hdc = BeginPaint(hwnd, &ps);
                switch(button_status)
                {
                case 0:
                    b = blue_brush;
                    break;
                case 1:
                    b = yellow_brush;
                    break;
                default:
                    b = red_brush;
                }
                FillRect(hdc, &ps.rcPaint, b);
                EndPaint(hwnd, &ps);
            }
            return 0;
        case WM_MOUSEMOVE:
            if(button_status == 0)
            {
                SetTimer(hwnd, 1, 100, NULL);
                button_status = 1;
                InvalidateRect(hwnd, NULL, false);
            }
            return 0;
        case WM_TIMER:
            {
                POINT pt;
                GetCursorPos(&pt);
                if(button_status == 1 && WindowFromPoint(pt) != hwnd)
                {
                    KillTimer(hwnd, 1);
                    button_status = 0;
                    InvalidateRect(hwnd, NULL, false);
                }
            }
            return 0;
        case WM_MOUSELEAVE:
            button_status = 0;
            InvalidateRect(hwnd, NULL, false);
            return 0;
        case WM_LBUTTONDOWN:
            button_status = 2;
            InvalidateRect(hwnd, NULL, false);
            return 0;
        case WM_LBUTTONUP:
            if(button_status == 2) MessageBox(GetParent(hwnd), "teamviewer like button clicked", "Message", MB_OK);
            button_status = 1;
            InvalidateRect(hwnd, NULL, false);
            return 0;
        }
        return DefWindowProc(hwnd, Msg, wParam, lParam);
    }
    
    void InitButton(HWND parent, int xPos, int yPos)
    {
        WNDCLASS wc;
    
        wc.style = 0;
        wc.lpfnWndProc = DefaultWindowProc;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hInstance = hinstDLL;
        wc.hIcon = NULL;
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
        wc.hbrBackground = NULL;
        wc.lpszMenuName = NULL;
        wc.lpszClassName = "DEFAULT_CLASS";
        RegisterClass(&wc);
        b = CreateWindowEx(WS_EX_TOOLWINDOW, "DEFAULT_CLASS", NULL, WS_BORDER | WS_POPUP | WS_VISIBLE, xPos, yPos, 20, 20, parent, NULL, hinstDLL, NULL);
    }
    
    LRESULT WINAPI HookCallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
        if(nCode >= 0 && lParam != 0)
        { 
            CWPRETSTRUCT *msg = (CWPRETSTRUCT*)lParam;
            if(!IsWindow(msg->hwnd) || (GetWindowLong(msg->hwnd, GWL_STYLE) & WS_CHILD) != 0) return CallNextHookEx(hhook_wndproc, nCode, wParam, lParam);
            switch(msg->message)
            {
            case WM_SHOWWINDOW:
                if(!b && msg->wParam != 0)
                {
                    b = (HWND)1;// see NOTES 5
                    RECT a;
                    GetWindowRect(msg->hwnd, &a);
                    InitButton(msg->hwnd, a.right - 150, a.top);
                }
                break;
            case WM_SIZE:
                if(GetParent(b) == msg->hwnd)
                {
                    RECT a;
                    GetWindowRect(msg->hwnd, &a);
                    SetWindowPos(b, 0, a.right - 150, a.top, 0, 0, SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_NOZORDER);
                }
                break;
            case WM_SIZING: 
            case WM_MOVING:
                if(GetParent(b) == msg->hwnd)
                {
                    RECT* lprc = (LPRECT) msg->lParam;
                    SetWindowPos(b, 0, lprc->right - 150, lprc->top, 0, 0, SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_NOZORDER);
                }
            }
        }
        return CallNextHookEx(hhook_wndproc, nCode, wParam, lParam);
    }
    
    __declspec(dllexport) void RunHook()
    {
        hhook_wndproc = SetWindowsHookEx(WH_CALLWNDPROCRET, HookCallWndProc, hinstDLL, 0);
        char aux[10];
        gets_s(aux);
        UnhookWindowsHookEx(hhook_wndproc);
    }
    
    BOOL APIENTRY DllMain( HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
    {
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
            hinstDLL = hModule;
            break;
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
        }
        return TRUE;
    }
    

    现在,让 dll 项目依赖于在 project->project dependencies 中运行的 app 项目:

    注意事项: 1) 我不使用 NC 绘制代码,因为并非总是有效,如果 Windows 缓冲区非客户端区域,则删除自定义的 NC 绘制按钮

    2) 在 64 位环境下,您需要为 32 位应用程序运行一个 32 位挂钩,为 64 位应用程序运行其他挂钩

    3) 当你的钩子连接到另一个进程时,你不能调试你的钩子,我建议你在你的应用程序和线程中使用一个窗口来调试它,并在工作时在另一个进程中测试它的后期

    4) 为了简单起见,我使用类似按钮的方法

    5) 这一行

    b = (HWND)1;
    

    我用它来“解决”一个多线程问题,我建议你在这个案例中编写更好的代码(同步)

    这是如何工作的:

    • 运行应用程序
    • 开始安装挂钩时
    • 打开任何其他应用程序(相同的 32/64 位,请参阅注 2)
    • 您必须在标题栏左侧看到一个蓝色按钮
    • 点击它会看到一个消息框
    • 对于完成挂钩:只需在控制台窗口按 ENTER

    代码流程:

    • 运行的应用程序只需在 dll 中调用 RunHook() 过程,然后 dll 完成工作
    • dll 中的 RunHook() 启动挂钩 HookCallWndProc(全局)
    • HookCallWndProc 捕获所需的消息并使用 InitButton() 过程创建窗口按钮
    • DefaultWindowProc 处理按钮消息

    【讨论】:

      猜你喜欢
      • 2013-02-25
      • 2014-06-10
      • 2010-10-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多