11

For those of you who do not know what I am talking about: http://www.teamviewer.com/images/presse/quickconnect_en.jpg

Teamviewer overlays that button on all windows to allow you to quickly share a window with someone else. I would like any ideas on implementing something similar -- if you have example code, even better (specifically, the button -- not the sharing). I am interested in C++ and QT... but I would be interested in good solutions in other languages/libraries if there are any.

Thanks.

Daniel Placek
  • 765
  • 5
  • 16
  • So after some additional searching, I found this: http://stackoverflow.com/questions/7566205/can-i-draw-something-on-window-that-does-not-belongs-to-me-using-opengl It seems relevant, but I am not quite sure what to make of it in reference to this... – Daniel Placek Aug 17 '12 at 04:05
  • That question is not related, the OS's window manager handles frame painting - not the application that resides in the window. – cmannett85 Aug 17 '12 at 07:07
  • 1
    The program [eXtra Buttons](http://www.xtrabuttons.com/) also does that, unfortunatly it's closed source so no clue to how they do it. I noticed it has to be running for the buttons to work (I believe it also applies to TeamViewer, right?), so it might really be injecting code into every running process. Also found [this discussion thread](http://www.autoitscript.com/forum/topic/98316-adding-a-button-to-any-window-titlebar/), though I lack knowledge to understand the method shown (and do not dare to try it myself...). – mgibsonbr Aug 26 '12 at 18:33
  • mgibsonbr: That software does exactly what I am trying to do. Interestingly, if you read the comments on the front page -- there are a lot of people claiming that it crashes other applications. That doesn't necessarily mean anything, but it supports the theory that it works by injecting code into other processes... – Daniel Placek Aug 26 '12 at 23:33
  • 1
    The button is inserted and removed by tv_w32.exe or tv_x64.exe depending on architecture(try killing the process once the button is inserted)... – UIlrvnd Sep 01 '12 at 22:23

1 Answers1

7

To draw buttons or other stuff in foreign windows, you need to inject code into the foreign processes. Check the SetWindowsHookEx method for that:

You most probably want to install a hook for WH_CALLWNDPROCRET and watch out for the WM_NCPAINT message. This would be the right place to draw your button. However, I'm not really sure, if you can place a window within a Non-Client-Area, so in the worst case, you'd have to paint the button "manually".

Just call this from your main application (or from within a DLL)

SetWindowsHookEx(WH_CALLWNDPROCRET, myCallWndRetProc, hModule, 0);

Note that myCallWndRetProc must reside within a DLL and hModule is the Module-HANDLE for this DLL.

Your myCallWndRetProc could look like:

LRESULT CALLBACK myCallWndRetProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode == HT_ACTION) {
        CWPRETSTRUCT* cwpret = (CWPRETSTRUCT*)lParam;
        if (cwpret->message == WM_NCPAINT) {
            // The non-client area has just been painted.
            // Now it's your turn to draw your buttons or whatever you like
        }
    }
    return CallNextHookEx(0, nCode, wParam, lParam);
}

When starting with your implementation, I'd suggest, you just create a simple dialog application and hook your own process only:

SetWindowsHookEx(WH_CALLWNDPROCRET, myCallWndRetProc, NULL, GetCurrentThreadId());

Installing a global hook injects the DLL into all processes, which makes debugging pretty hard, and your DLL may be write-protected while it's in use.

Mark
  • 6,033
  • 1
  • 19
  • 14
  • Any suggestions on how to: a. Intercept the WM_NCPAINT message... would this require hooking WindowProc() via IAT or something? b. Manual Painting? I assume via win32 API... any suggestions on where to start with this? – Daniel Placek Aug 31 '12 at 03:52
  • 1
    No, you don't need IAT hooking or something like that. You just have to install a WindowsHook: SetWindowsHookEx(WH_CALLWNDPROCRET, myCallWndRetProc, hModule, 0); Note that myCallWndRetProc must reside within a DLL and hModule is the Module-HANDLE for this DLL. Your myCallWndRetProc could look like `LRESULT CALLBACK myCallWndRetProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode == HT_ACTION) { CWPRETSTRUCT* cwpret = (CWPRETSTRUCT*)lParam; if (cwpret->message == WM_NCPAINT) { } } }` Edit: Wow, I'll add that to the original comment. – Mark Sep 01 '12 at 15:13
  • 1
    Thanks Mark, great detailed answer. Searching on some of the things you pointed out has led me to some other great resources as well. For anyone else interested in this, check out: http://stackoverflow.com/questions/99623/how-to-draw-in-the-nonclient-area – Daniel Placek Sep 01 '12 at 23:58
  • @Mark, Where do I find HT_ACTION value? – GSP Mar 30 '16 at 08:58
  • Be very careful if you decide to implement this feature. [Here's what](http://stackoverflow.com/q/38277815/843732) it was causing in our unrelated program when it **was not** implemented correctly by the TeamViewer people. – c00000fd Jul 09 '16 at 06:34