Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clickthrough to desktop on a transparent form

I have a screen capturing utility on which I can rubber band an area on the desktop. I've done this is a fairly easy manner, I have a form which is the same size as the screen on which I draw a screenshot of the desktop transformed into grayscale. When the user holds down the left mouse button he/she can select an area on the form. The rectangle which the user draws is filled with TransparentColor. Once the users lifts up his/her mouse the transparent rectangle is left in place and the actual desktop is visible. Here comes my problem: On my dev PC I can actually click through this transparent rectangle and navigate around etc. while on my other PC the form responds on mouse clicks on the actual transparent rectangle.

I'm using .NET 4.0 in C#, any ideas on how I can make it actually click through to the desktop on all cases??

Thank you and much appreciated :)

like image 734
Tamas Henning Avatar asked Dec 28 '10 09:12

Tamas Henning


1 Answers

I managed to find a proper solution for this problem after looking very deeply into this. It turns out with the proper Win32 API calls it is possible to set a form "Invisible" to mouse clicks. This can be achieved by:

public const int GWL_EXSTYLE = -20;
public const uint WS_EX_LAYERED = 0x00080000;
public const uint WS_EX_TRANSPARENT = 0x00000020;

[DllImport("user32.dll", SetLastError = true)]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);

[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

[DllImport("user32.dll")]
static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);

public void SetFormTransparent(IntPtr Handle) {
    oldWindowLong = GetWindowLong(Handle, GWL_EXSTYLE);
    SetWindowLong(Handle, GWL_EXSTYLE, Convert.ToInt32(oldWindowLong | WS_EX_LAYERED | WS_EX_TRANSPARENT));
}

public void SetFormNormal(IntPtr Handle) {
    SetWindowLong(Handle, GWL_EXSTYLE, Convert.ToInt32(oldWindowLong | WS_EX_LAYERED));
}

But there is a trick to everything. You need to be careful that all clicks made on the forum will fall through the form and be sent to anything below the form. To ensure that if I click on my form e.g. on a button and I want the button clicked I did a simple trick. I have a timer in the background running every N milliseconds and analyzing the position of the Cursor. If it's above the area I want it to be, it'll set the Form to Normal via SetFormNormal() otherwise it'll be transparent.

Hope this code bit helps and people will use it.

like image 161
Tamas Henning Avatar answered Sep 18 '22 18:09

Tamas Henning