How do I get the "topmost" status of every window in c# - 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.

Related

How to Display a Non-Windows App Inside a Windows App

How can I embed a non-native application (application installed in windows 7) in a windows form using c#.
What I have tried so far:
public partial class Form1 : Form
{
Process process = new Process();
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("User32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
public Form1()
{
InitializeComponent();
// panel1.Container.Add(process);
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = false;
string path = Path.Combine(Path.GetFullPath(#"C:\Program Files (x86)\Plan-G v3.2.0"), "Plan-G3.exe");
process = Process.Start(path);
Debug.WriteLine(process.MainWindowHandle);
while (process.MainWindowHandle == IntPtr.Zero)
{
Thread.Sleep(1000); // Don't hog the CPU
process.Refresh(); // You need this since `MainWindowHandle` is cached
}
Form1.SetForegroundWindow(process.MainWindowHandle);
SetForegroundWindow(process.MainWindowHandle);
SetParent(process.MainWindowHandle, panel1.Handle);
MoveWindow(process.MainWindowHandle, 0, 0, panel1.Width - 90, panel1.Height, true);
}
}
I am able to get this to work sometimes when I start with debugging.
But when i start without debugging, the application always opens outside of the form. I don't understand why this happens.
I have also tried the [ForceForegroundWindow][1]() workaround.
Is what I am trying to accomplish possible?
Based on other forum answers, it seems it might not be...

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)

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.

Extracting an icon group from a .dll file in C#

I'm trying to extract an icon from imageres.dll. Specifically the "My Computer" or "This PC" icon. The problem is that at between Win7 and Win10, the icon number changes. However, the icon group does not (109). Is there a way to get that icon group, and then let the computer figure out which icon to use of that group, in the same way it figures out which icon to use for my app?
This is the code I'm using to get the specific icon via the index:
public class GetIcon {
public static Icon Extract(string file, int number) {
IntPtr large;
IntPtr small;
ExtractIconEx(file, number, out large, out small, 1);
try {
return Icon.FromHandle(small);
}
catch {
return null;
}
}
[DllImport("Shell32.dll", EntryPoint = "ExtractIconExW", CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern int ExtractIconEx(string sFile, int iIndex, out IntPtr piLargeVersion, out IntPtr piSmallVersion, int amountIcons);
}
Thanks.
There are a couple of ways to do this. The most reliable, and potentially most time consuming (provided you can't find an existing library), is to parse the PE File (i.e. .exe, .dll) and extract the relevant Icon group data yourself. Here's a good resource for the format: https://msdn.microsoft.com/en-us/library/ms809762.aspx
The second way, can be done easily enough with Windows functions, however there is one caveat. It will only work on PE files that are of the same bit-type as your application. So, for example, if your application is 64-bit, it will only work on 64-bit PE files.
Here's a function I just wrote - based off this: https://msdn.microsoft.com/en-us/library/windows/desktop/ms648051(v=vs.85).aspx#_win32_Sharing_Icon_Resources, that takes a file name, group number, and desired icon size, and returns a System.Drawing.Icon
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)]
static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName);
[DllImport("kernel32.dll")]
static extern IntPtr FindResource(IntPtr hModule, int lpName, int lpType);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo);
[DllImport("kernel32.dll")]
static extern IntPtr LockResource(IntPtr hResData);
[DllImport("user32.dll")]
static extern int LookupIconIdFromDirectoryEx(byte[] presbits, bool fIcon, int cxDesired, int cyDesired, uint Flags);
[DllImport("user32.dll")]
static extern IntPtr CreateIconFromResourceEx(byte[] pbIconBits, uint cbIconBits, bool fIcon, uint dwVersion, int cxDesired, int cyDesired, uint uFlags);
[DllImport("kernel32.dll", SetLastError = true)]
static extern uint SizeofResource(IntPtr hModule, IntPtr hResInfo);
const int RT_GROUP_ICON = 14;
const int RT_ICON = 0x00000003;
private System.Drawing.Icon GetIconFromGroup(string file, int groupId, int size)
{
IntPtr hExe = LoadLibrary(file);
if(hExe != IntPtr.Zero)
{
IntPtr hResource = FindResource(hExe, groupId, RT_GROUP_ICON);
IntPtr hMem = LoadResource(hExe, hResource);
IntPtr lpResourcePtr = LockResource(hMem);
uint sz = SizeofResource(hExe, hResource);
byte[] lpResource = new byte[sz];
Marshal.Copy(lpResourcePtr, lpResource, 0, (int)sz);
int nID = LookupIconIdFromDirectoryEx(lpResource, true, size, size, 0x0000);
hResource = FindResource(hExe, nID, RT_ICON);
hMem = LoadResource(hExe, hResource);
lpResourcePtr = LockResource(hMem);
sz = SizeofResource(hExe, hResource);
lpResource = new byte[sz];
Marshal.Copy(lpResourcePtr, lpResource, 0, (int)sz);
IntPtr hIcon = CreateIconFromResourceEx(lpResource, sz, true, 0x00030000, size, size, 0);
System.Drawing.Icon testIco = System.Drawing.Icon.FromHandle(hIcon);
return testIco;
}
return null;
}
The process basically works like this:
use LoadLibrary to load up the .exe or .dll file
Get the handle & data of the RT_GROUP_ICON resource
Pass the data, along with the desired size to LookupIconIdFromDirectoryEx, to get the icon index
From there, you can either use ExtractIconEx, or just repeat step 2 with the icon index, and RT_ICON instead, followed by using CreateIconFromResourceEx to get your icon handle.

Click through a form with transparent background in 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..

Categories

Resources