Click through a form with transparent background in C# - c#

I have been working on RAD Studio and on that this is performed automatically if the background is fully transparent, but it seems that it isn't as simple to do in Visual Studio. I have read articles and questions of functions GetWindowLong() and SetWindowLong(), but when I try to use them myself, I get error "the name xxx does not exist in the current context".
What I tried was:
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
int initialStyle = GetWindowLong(this.Handle, -20);
SetWindowLong(this.Handle, -20, initialStyle | 0x80000 | 0x20);
}
}
}
So is there some file I have to include in order get it to work, or do I have to place the function elsewhere? I'm also curious to know why does it behave so differently in the RAD Studio and Visula Studio.

The methods you are using are not found in the standard .NET libraries, you need to invoke them through user32.dll.
[DllImport("user32.dll", EntryPoint = "GetWindowLong", CharSet = CharSet.Auto)]
private static extern IntPtr GetWindowLong32(HandleRef hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr", CharSet = CharSet.Auto)]
private static extern IntPtr GetWindowLongPtr64(HandleRef hWnd, int nIndex);
[DllImport("user32.dll", SetLastError = true)]
static extern UInt32 GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, UInt32 dwNewLong);
public static IntPtr GetWindowLong(HandleRef hWnd, int nIndex)
{
if (IntPtr.Size == 4) return GetWindowLong32(hWnd, nIndex);
else return GetWindowLongPtr64(hWnd, nIndex);
}
Calling them this way will now work correctly:
uint initialStyle = GetWindowLong(this.Handle, -20);
SetWindowLong(this.Handle, -20, initialStyle | 0x80000 | 0x20);
Make sure you are using System.Runtime.InteropServices
EDIT: I found and modified some code from another question (I will add link if I can find it, I closed the tab and can't find it), and came up with this:
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
private const int MOUSEEVENTF_LEFTDOWN = 0x02;
private const int MOUSEEVENTF_LEFTUP = 0x04;
public enum GWL
{
ExStyle = -20,
HINSTANCE = -6,
ID = -12,
STYLE = -16,
USERDATA = -21,
WNDPROC = -4
}
public enum WS_EX
{
Transparent = 0x20,
Layered = 0x80000
}
public enum LWA
{
ColorKey = 0x1,
Alpha = 0x2
}
[DllImport("user32.dll", EntryPoint = "GetWindowLong")]
public static extern int GetWindowLong(IntPtr hWnd, GWL nIndex);
[DllImport("user32.dll", EntryPoint = "SetWindowLong")]
public static extern int SetWindowLong(IntPtr hWnd, GWL nIndex, int dwNewLong);
[DllImport("user32.dll", EntryPoint = "SetLayeredWindowAttributes")]
public static extern bool SetLayeredWindowAttributes(IntPtr hWnd, int crKey, byte alpha, LWA dwFlags);
private void Form1_Click(object sender, EventArgs e)
{
base.OnShown(e);
int originalStyle = GetWindowLong(this.Handle, GWL.ExStyle);
int wl = GetWindowLong(this.Handle, GWL.ExStyle);
wl = wl | 0x80000 | 0x20;
SetWindowLong(this.Handle, GWL.ExStyle, wl);
int X = Cursor.Position.X;
int Y = Cursor.Position.Y;
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, X, Y, 0, 0);
System.Threading.Thread.Sleep(50);
SetWindowLong(this.Handle, GWL.ExStyle, originalStyle);
TopMost = true;
}
I'm not an expert on how to do this, and I'm not sure if I like that solution, but it works.
Make sure you subscribe to the event with: this.Click += Form1_Click;

Assuming Form name as form1
Set these properties to following.
form1.BackColor = Control;
form1.TransparencyKey = Control;
now you can easily click through the transparent area of the form. hope this is what you were looking for.

Shujaat Siddiqui's answer works just fine for clicking through the transparency. It will not hide the form's title though nor its borders.
To do this you may either set the Opacity=0d or chose a FormBorderStyle=none along with a Text="" and ControlBox=false;
You may also think about cutting holes into the window's region to get precise control where click will go through and where not.
In all these cases you will also have to plan for control of the form's position and size..
But, as long as you don't go for semi-tranparency of the background all can be done..

Related

A form be transparent and click thru - but still receive dragdrop events

I have a form which I overlay on another form and use transparency key to set it to transparent and click thru-able, track the other forms position and set it to parent. All works fine and happily.
What I would really like is for this transparent and click thru-able form to receive drag and drop events, but I suspect that using TransparencyKey means that all mouse events are click thru-able including drag and drop?
So far i haven't been able to google myself out of it, so wondered if anyone here would know better ?
Many thanks,
Ian.
Code Project Article on Transparent Click-through Forms (VB.NET)
Converted to C# from the article.
Win32 APIs:
GetWindowLong
SetWindowLong
SetLayeredWindowAttributes
DLL Imports
[DllImport("user32.dll", EntryPoint = "GetWindowLongA", SetLastError = true)]
private static extern int GetWindowLong(IntPtr hwnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "SetWindowLongA", SetLastError = true)]
private static extern int SetWindowLong(IntPtr hwnd, int nIndex, int dwNewLong);
[DllImport("user32.dll")]
private static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
Example
private const int GWL_EXSTYLE = -20;
private const int WS_EX_LAYERED = 0x80000;
private const int WS_EX_TRANSPARENT = 0x20;
private const int LWA_ALPHA = 2;
private const int LWA_COLOR_KEY = 1;
var style = GetWindowLong(this.Handle, GWL_EXSTYLE)
SetWindowLong(this.Handle, GWL_EXSTYLE,
style | WS_EX_LAYERED | WS_EX_TRANSPARENT)
var percent = 0.7
var alpha = 255 * percent
SetLayeredWindowAttributes(this.Handle, 0, alpha, LWA_ALPHA)

What makes this click through?

I'm trying to figure out how this code works but I just can not figure out what makes it click through.
Yes, this code is not mine since i'm trying to learn/understand it.
Assume I want the tranparancy but not the click through what needs to be changed and why?
I have been over the Windows styles pages over and over and still can not get my head around the click through part.
using System;
using System.Runtime.InteropServices;
using UnityEngine;
public class TransparentWindow : MonoBehaviour
{
[SerializeField]
private Material m_Material;
private struct MARGINS
{
public int cxLeftWidth;
public int cxRightWidth;
public int cyTopHeight;
public int cyBottomHeight;
}
[DllImport("user32.dll")]
private static extern IntPtr GetActiveWindow();
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong);
[DllImport("user32.dll")]
static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll", EntryPoint = "SetLayeredWindowAttributes")]
static extern int SetLayeredWindowAttributes(IntPtr hwnd, int crKey, byte bAlpha, int dwFlags);
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
private static extern int SetWindowPos(IntPtr hwnd, int hwndInsertAfter, int x, int y, int cx, int cy, int uFlags);
[DllImport("Dwmapi.dll")]
private static extern uint DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS margins);
const int GWL_STYLE = -16;
const uint WS_POPUP = 0x80000000;
const uint WS_VISIBLE = 0x10000000;
const int HWND_TOPMOST = -1;
void Start()
{
#if !UNITY_EDITOR // You really don't want to enable this in the editor..
int fWidth = Screen.width;
int fHeight = Screen.height;
var margins = new MARGINS() { cxLeftWidth = -1 };
var hwnd = GetActiveWindow();
SetWindowLong(hwnd, GWL_STYLE, WS_POPUP | WS_VISIBLE);
// Transparent windows with click through
SetWindowLong(hwnd, -20, 524288 | 32);//GWL_EXSTYLE=-20; WS_EX_LAYERED=524288=&h80000, WS_EX_TRANSPARENT=32=0x00000020L
SetLayeredWindowAttributes(hwnd, 0, 255, 2);// Transparency=51=20%, LWA_ALPHA=2
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, fWidth, fHeight, 32 | 64); //SWP_FRAMECHANGED = 0x0020 (32); //SWP_SHOWWINDOW = 0x0040 (64)
DwmExtendFrameIntoClientArea(hwnd, ref margins);
#endif
}
void OnRenderImage(RenderTexture from, RenderTexture to)
{
Graphics.Blit(from, to, m_Material);
}
}
This function:
SetWindowLong(hwnd, -20, 524288 | 32);
does the trick. Windows implements the rule that Mircosoft made which is that a window that is transparent to the user must be transparent to the mouse.
With the transparency bit set WS_EX_TRANSPARENT the window becomes transparent to mouse too and click passes to the painted layer behind the transparent window.
You need not understand but make use of this 'OS feature' which was probably implemented to cover for something else.
Read this article about the subject and this answer that explains the parameters

How do I get the "topmost" status of every window in c#

Actually I wrote my self a programm that sets the topmost status of a specific window to true or false.
I realised this by importing the user32.dll
[DllImport("user32.dll")]
private static extern bool SetWindowPos(...);
Now I was wondering if its possible to get the state of the window and see if the topmost is set and add this to the items I have in a Listview.
Of course I can do this in runtime, depending on the actions I performed, but my programm will not be opend all the time and I dont want to save data somewere.
What I want is to see in the listview if there was set topmost before and than set it true/false on buttonclick. Depending on its state...
So is there something like GetWindowPos to get the topmost state of a specific window ?
According to James Thorpe's comment I solved it like this.
First I added the needed dll and const
//Import DLL
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
//Add the needed const
const int GWL_EXSTYLE = (-20);
const UInt32 WS_EX_TOPMOST = 0x0008;
At this point i recognized that I allways have to add the DLL again when I want to use several functions of it. For example
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
I thought I can write it like
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
NOTE: You can not do it like this :D
After this I went to my function that refreshes my ListView and edited it like
//Getting the processes, running through a foreach
//...
//Inside the foreach
int dwExStyle = GetWindowLong(p.MainWindowHandle, GWL_EXSTYLE);
string isTopMost = "No";
if ((dwExStyle & WS_EX_TOPMOST) != 0)
{
isTopMost = "Yes";
}
ListViewItem wlv = new ListViewItem(isTopMost, 1);
wlv.SubItems.Add(p.Id.ToString());
wlv.SubItems.Add(p.ProcessName);
wlv.SubItems.Add(p.MainWindowTitle);
windowListView.Items.Add(wlv);
//Some sortingthings for the listview
//end of foreach processes
In this way I can display if the Window has a topmost status.

Hosting a process in C#.Net form disables some process's buttons

I am trying to host a .exe within my .net application (Mainly video viewing software) however certain applications do not allow me to use their menu's or some controls. Has anyone had this problem before or any idea of why it could be happening?
Here is my code to host the application:
#region Methods/Consts for Embedding a Window
[DllImport("user32.dll", EntryPoint = "GetWindowThreadProcessId", SetLastError = true,
CharSet = CharSet.Unicode, ExactSpelling = true,
CallingConvention = CallingConvention.StdCall)]
private static extern long GetWindowThreadProcessId(long hWnd, long lpdwProcessId);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
private static extern long SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", EntryPoint = "GetWindowLongA", SetLastError = true)]
private static extern long GetWindowLong(IntPtr hwnd, int nIndex);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
private static extern long SetWindowPos(IntPtr hwnd, long hWndInsertAfter, long x, long y, long cx, long cy, long wFlags);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);
[DllImport("user32.dll", EntryPoint = "PostMessageA", SetLastError = true)]
private static extern bool PostMessage(IntPtr hwnd, uint Msg, int wParam, int lParam);
private const int SWP_NOOWNERZORDER = 0x200;
private const int SWP_NOREDRAW = 0x8;
private const int SWP_NOZORDER = 0x4;
private const int SWP_SHOWWINDOW = 0x0040;
private const int WS_EX_MDICHILD = 0x40;
private const int SWP_FRAMECHANGED = 0x20;
private const int SWP_NOACTIVATE = 0x10;
private const int SWP_ASYNCWINDOWPOS = 0x4000;
private const int SWP_NOMOVE = 0x2;
private const int SWP_NOSIZE = 0x1;
private const int GWL_STYLE = (-16);
private const int WS_VISIBLE = 0x10000000;
private const int WM_CLOSE = 0x10;
private const int WS_CHILD = 0x40000000;
private const int WS_MAXIMIZE = 0x01000000;
#endregion
#region Variables
private IntPtr hostedProcessHandle;
private Process hostedProcess = null;
private ProcessStartInfo hostedPSI = new ProcessStartInfo();
#endregion
//Helper method to start a process contained within the form
private void HostProcess(string processPath)
{
//Start the process located at processPath
hostedPSI.FileName = processPath;
hostedPSI.Arguments = "";
hostedPSI.WindowStyle = ProcessWindowStyle.Maximized;
hostedProcess = System.Diagnostics.Process.Start(hostedPSI);
//Stop watch is used to calculate time out period.
Stopwatch sw = new Stopwatch();
sw.Start();
//Loop to aquire application handle. Exit loop if the time out period is past.
do
{
hostedProcessHandle = hostedProcess.MainWindowHandle;
if (sw.ElapsedMilliseconds > 10000) throw new TimeoutException();
} while (hostedProcessHandle == new IntPtr(0));
//Host the process in the forms panel.
SetParent(hostedProcessHandle, this.panel1.Handle);
SetWindowLong(hostedProcessHandle, GWL_STYLE, WS_VISIBLE + WS_MAXIMIZE);
MoveWindow(hostedProcessHandle, 10, 10, this.panel1.Width - 20, this.panel1.Height - 20, true);
}
private void CloseHostedProcess()
{
hostedProcess.Kill();
}
Here is a screen shot of my test application hosting VLC, Some of the menu's and buttons as you can see are grayed out and not working:
This is not just a problem with VLC — I see this issue when hosting other applications too.
Just an update. If i right click on VLC -> Play -> Add and play a video back manually the menu bar works again. However the video controls at the bottom are still not working! They change color when rolled over but clicking them still doesn't work!
The reason I was experiencing problems seems to be because TeamViewer was running in the background. I am currently trying to find out why this causes me issues but stopping TeamViewer's process seems to rectify my problem.

.Net correspondings of WinApi TabControl styles

Within each tab, the control centers the icon and label, placing the icon to the left of the label. In WinApi you can force the icon to the left, leaving the label centered, by specifying the TCS_FORCEICONLEFT style. You can left-align both the icon and label by using the TCS_FORCELABELLEFT style.
How can I make the same in c#?
There is similar question, but those solution does not work for me, despyte I use Edward's advice.
In class derived from TabControl (I need draggable tabs, but now while tab is dragged the alignment of its label breaks since I set the SizeMode to Fixed to get tabs the same width and properly dragging):
[DllImport("user32.dll", EntryPoint = "SetWindowLong", CharSet = CharSet.Auto)]
protected static extern bool SetWindowLong32(IntPtr ptr, int index, int value);
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Auto)]
protected static extern bool SetWindowLongPtr64(IntPtr ptr, int index, int value);
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
protected static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);
private const int GWL_STYLE = -16;
private const int TCS_FORCELABELLEFT = 0x20;
protected static bool SetWindowLong(IntPtr ptr, int index, int value)
{
if (IntPtr.Size == 4)
{
return SetWindowLong32(ptr, index, value);
}
return SetWindowLongPtr64(ptr, index, value);
}
and in TabControl constructor:
SetWindowLong(Handle, GWL_STYLE, TCS_FORCELABELLEFT);
var SWP_FRAMECHANGED = 0x20;
var SWP_NOMOVE = 0x2;
var SWP_NOSIZE = 0x1;
var SWP_NOZORDER = 0x4;
SetWindowPos(Handle, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
I'm aware it's possible use owner-drawn mode, but I don't know how can i use the system style changing only necessary elements.
If you want to just call SetWindowLong, try
[DllImport("user32.dll")]
static extern bool SetWindowLong(IntPtr ptr, int index, int value);
If you are targeting 32-/64-bit platforms, this SO answer might throw some light on how to invoke the function properly.

Categories

Resources