如何在国外窗口中实现“teamviewer quickconnect”式按钮?

时间:2016-03-30 09:32:11

标签: winapi

我想在外国Windows的标题栏中添加一个按钮,就像Teamviewer使用Quickconnect功能一样,或者Chrome在右上角有一个用于切换用户的按钮。

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

的重复

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

1 个答案:

答案 0 :(得分:1)

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

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

enter image description here

第一个项目(HookDLL)是一个dll项目,第二个(Running app)是一个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项目依赖于在项目中运行应用程序项目 - >项目依赖项:

enter image description here

注意: 1)我不使用NC油漆代码,因为并非总是有效,如果windows缓冲非客户区域,则擦除自定义NC油漆按钮

2)在64位环境中,你需要为32位应用程序运行32位挂钩,并为64位应用程序运行其他挂钩

3)当你连接到另一个PROCCESS时,你不能调试你的HOOK,我建议你在你的应用程序和线程中使用windows调试它,并在工作时在另一个进程中测试它

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

5)这一行

b = (HWND)1;

我将它用于&#34;解决&#34;多线程问题,我建议你做更好的代码(同步)这种情况

如何运作:

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

代码流程

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