Handling AeroSnap message in WndProc - c#

In my C# .NET 4 application, I use WndProc to process some messages mostly dealing with resizing the application to and from full screen.
Right now I am just handling SC_MAXIMIZE and WM_NCLBUTTONDBLCLK to determine if the window is being resized to or from a maximized state (I know I don't need WndProc to handle SC_MAXIMIZE, but Form_Resize didn't seem to fire for a WM_NCLBUTTONDBLCLK message when I double-click on the application's title bar.
Now I noticed that if I Aero Snap the window to the top of the screen to maximize it, neither of the above messages are posted so certain logic is not applied when the window is maximized via Aero Snap. I only want to handle the message if the window is snapped to the top of the screen rather than the right or left, or if a window is unsnappped from maximized position.
I couldn't find any of the window messages related to Aero Snap. Does anyone know of any references for those messages?

I'm guessing there aren't any special messages here; Aero is likely just using the plain Win32 APIs - ShowWindow(SW_MAXIMIZE) and similar.
The thing to uderstand with the SC_ messages are that those are requests from a menu asking the window to resize/restore/etc itself, but that is not the only mechanism for changing the windows's size. What's probably happening is that when a window gets SC_MAXIMIZE, the DefWndProc implements this by calling ShowWindow(SW_MAXIMIZE).
Your best best is to listen to the WM_SIZE message, which the window receives, regardless of what triggered the size change: system menu, API, or other means. In particular, the lParam will let you know if the window was maximized (SIZE_MAXIMIZED) or restored (SIZE_RESTORED).

Here is the code for handling WM_WINDOWPOSCHANGING message for Maximise instead of WM_SIZE message. Thanks to the 20 or more questions on SO that I had to read to find all the bits to put it together and get it working. This addresses the problems that I was having with multiple monitors using different resolutions.
//register the hook
public static void WindowInitialized(Window window)
{
IntPtr handle = (new WindowInteropHelper(window)).Handle;
var hwndSource = HwndSource.FromHwnd(handle);
if (hwndSource != null)
{
hwndSource.AddHook(WindowProc);
}
}
//the important bit
private static IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case 0x0046: //WINDOWPOSCHANGING
var winPos = (WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(WINDOWPOS));
var monitorInfo = new MONITORINFO();
IntPtr monitorContainingApplication = MonitorFromWindow(hwnd, MonitorDefaultToNearest);
GetMonitorInfo(monitorContainingApplication, monitorInfo);
RECT rcWorkArea = monitorInfo.rcWork;
//check for a framechange - but ignore initial draw. x,y is top left of current monitor so must be a maximise
if (((winPos.flags & SWP_FRAMECHANGED) == SWP_FRAMECHANGED) && (winPos.flags & SWP_NOSIZE) != SWP_NOSIZE && winPos.x == rcWorkArea.left && winPos.y == rcWorkArea.top)
{
//set max size to the size of the *current* monitor
var width = Math.Abs(rcWorkArea.right - rcWorkArea.left);
var height = Math.Abs(rcWorkArea.bottom - rcWorkArea.top);
winPos.cx = width;
winPos.cy = height;
Marshal.StructureToPtr(winPos, lParam, true);
handled = true;
}
break;
}
return (IntPtr)0;
}
//all the helpers for dealing with this COM crap
[DllImport("user32")]
internal static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);
[DllImport("user32")]
internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags);
private const int MonitorDefaultToNearest = 0x00000002;
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPOS
{
public IntPtr hwnd;
public IntPtr hwndInsertAfter;
public int x;
public int y;
public int cx;
public int cy;
public int flags;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class MONITORINFO
{
public int cbSize = Marshal.SizeOf(typeof(MONITORINFO));
public RECT rcMonitor;
public RECT rcWork;
public int dwFlags;
}
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}

Related

How do you perform mouse movements & clicks virtually [user32.dll]

How do you perform mouse movements & clicks virtually ?
What I mean by virtually is that, the main mouse isn't affected, so basically "creating" a second mouse that is controlled by setting x/y position (pref with user32.dll).
I have read a few similar questions here but the answer usually is with your main mouse, Example:
DllImport("user32")]
public static extern int SetCursorPos(int x, int y);
^ this moves your main mouse to x&y position on screen, what I want is to perform this action with a "virtual mouse" so my main mouse isn't moved, so basically I can keep using my computer while this "virtual mouse" does something else in another window for example.
then using this "virtual mouse" to perform clicks [virtual key codes]
I have tried this:
IntPtr MakeLParam(int x, int y) => (IntPtr)((y << 16) | (x & 0xFFFF));
var pointPtr = MakeLParam(900, 1000);//x y cords
IntPtr hWnd = ScreenCapture.FindWindow(null, ScreenCapture.GetWindowName()); //Finds Window
PostMessage(hWnd, WM_MOUSEMOVE | WM_LBUTTONDOWN | WM_LBUTTONUP, IntPtr.Zero, pointPtr);
[DllImport("User32.dll")]
public static extern Int32 PostMessage(
IntPtr hWnd, // handle to destination window
int Msg, // message
IntPtr wParam, // first message parameter
IntPtr lParam); // second message parameter
But nothing happens at all, and my guess is that WM_MOUSEMOVE is not working properly
public const int WM_KEYDOWN = 0x100;
public const int WM_KEYUP = 0x101;
public const int WM_COMMAND = 0x111;
public const int WM_LBUTTONDOWN = 0x201;
public const int WM_LBUTTONUP = 0x202;
public const int WM_LBUTTONDBLCLK = 0x203;
public const int WM_RBUTTONDOWN = 0x204;
public const int WM_RBUTTONUP = 0x205;
public const int WM_RBUTTONDBLCLK = 0x206;
public const int WM_MOUSEMOVE = 0x0200;
UPDATE, I think I solved it somewhat !
After reading more about PostMessage /SendInput, I found out that "application windows" can have layers of "application windows", so I used ispy++ to check it out, and yes the program had another layer where I wanted to click.
So to solve this I took the class name of the window where I wanted to click as well as the window name, here is an example code:
public static bool ClickTest()
{
IntPtr MakeLParam(int x, int y) => (IntPtr)((y << 16) | (x & 0xFFFF));//Just converts x&y to InPtr lParam
IntPtr WindowhWid = ScreenCapture.FindWindow(null, ScreenCapture.GetWindowName()); //Gets the window hWid
var ClasshWid = FindWindowEx(WindowhWid, IntPtr.Zero, "Classname", null); //Gets the Class hWid using WindowhWid AND the Class name (Need to find a way to get the classname using process list)
PostMessage(ClasshWid, WM_LBUTTONDOWN | WM_LBUTTONUP, 0, MakeLParam(938, 1011));//Finally sends it to the classhWid
return true;
}
Using these two functions
[DllImport("User32.dll")]
public static extern int FindWindowEx(
IntPtr hwndParent,
IntPtr hwndChildAfter,
string strClassName,
string strWindowName);
[DllImport("User32.dll")]
public static extern Int32 PostMessage(
int hWnd, // handle to destination window
int Msg, // message
int wParam, // first message parameter
IntPtr lParam); // second message parameter
Note that it won't click if the appilication window is MINIMIZED, although it will click if the window is behind another appilication window !
Also the X & Y is the screen X & Y and not the Application X & Y !
Now the question remains, why doesn't it work while the application window is minimized ?
The click doesn't go anywhere but to the application, so shouldn't it work while the application is minimized ?
It migiht not be what you're up to and what you look after, but AutoIT Dll offers plenty of automation ready to use and in the easiest way possible. Since you rely on a windows DLL then it's perfect.
If you're still on your own way to user32.dll then check Low level hook, that's what I used.
I highly recommend AutoIT dll though, it will save you ton of hair pulling hours guessing what's happening to the windows messages that do not respond properly

Does windows api messages still exists?

Long long time ago (in a galaxy far away), I used to program some fun tools to assist me (as having a disability) or just support my lazyness :)
Tools that do things like "wait 30 seconds and then press play on my media player" or "save a list of all song names from winamp that streaming a live m3u based radio". it was almost 20 years ago, using C# and window messages api (not wanting to relay on mouse clicks and strict window size&position). I would've found the window's handle and the "control" handle and interact with it.
The question is: Can I still do it today in the age of Windows 10?
If so, how?
I would appreciate a starting point.
Let's say I want to press play on my bs.player after x seconds, or close an error message that comes up every 10 seconds (well, its not cause' my windows is healthy ..but theoretically).
Thank you :)
Yes you can, if you talk about Windows API.
You need to declare the external WinAPI's signatures as static extern using the DllImport attribute.
For example to know if the screen saver is active or if an app runs in full screen:
private const int WM_SYSCOMMAND = 0x0112;
private const int SC_SCREENSAVE = 0xF140;
private const int SPI_GETSCREENSAVERRUNNING = 0x0072;
[DllImport("user32.dll", SetLastError = true)]
static extern bool SystemParametersInfo(int action, int param, ref int retval, int updini);
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("user32.dll")]
static private extern bool GetWindowRect(HandleRef hWnd, [In, Out] ref RECT rect);
[DllImport("user32.dll")]
static private extern IntPtr GetForegroundWindow();
static private bool IsForegroundFullScreen()
{
return IsForegroundFullScreen(null);
}
static private bool IsForegroundFullScreen(Screen screen)
{
if ( screen == null ) screen = Screen.PrimaryScreen;
RECT rect = new RECT();
GetWindowRect(new HandleRef(null, GetForegroundWindow()), ref rect);
return new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top)
.Contains(screen.Bounds);
}
private bool IsScreensaverActive()
{
int active = 1;
SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, ref active, 0);
return active != 0;
}
private bool IsForegroundFullScreenOrScreensaver()
{
return IsForegroundFullScreen() || IsScreensaverActive();
}
Calling Win32 DLLs in C#
c# dllimport with pointers

Moving window with SetWindowPos

I'm trying to move the window of the program I am building inside of unity. I'm getting it's handle via interating through all processes in Process.GetProcesses(). Then, I'm calling SetWindowPos, but nothing happens. Here's my code.
internal static void CheckHandle()
{
Process[] ps = Process.GetProcesses();
foreach (Process p in ps)
{
try
{
if (string.Equals(p.ProcessName, "TestBuild0001"))
{
_correctHandle = true;
_handle = p.Handle;
}
}
catch
{
//no catch, simply exited process
}
}
}
internal static void SetPosition()
{
if (!_correctHandle)
CheckHandle();
if (_correctHandle)
{
NewGUI.SetWarning("Window set!",5,50,900,300,50);
SetWindowPos(_handle, 0, 0, 0, 0, 0, 0x0001);
}
}
NewGUI.SetWarning just displays a label and shows up properly. _correctHandle is a simple bool and SetWindowPos is put in as
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
private static extern bool SetWindowPos(IntPtr hwnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);
I've tried moving quite a few things to get it to work but am out of ideas. Trying to get foregroundwindow brings back a entirely incorrect handle, findwindow for name brings back 0, and quite a few other things don't seem to work. Anyone know what my error could be/
p.Handle is the process' handle, not the window handle. You want p.MainWindowHandle.
It's also possible that the process you're trying to attach to has a hidden message-only main window. To figure that out, you'd need to use a tool like Spy++ to look at the structure of the windows.
If all else fails Spy++ will also give you a window class, which you can use with FindWindowEx to locate the window. (I do the same trick with Sticky Notes windows in this answer)
The GetActiveWindow idea from this thread : Any way to bring Unity3d to the foreground? got me the right window ID, but changing resolutions still caused me issues since unity was setting the window location to halfway between both monitors after my SetWindowPos is called. Here's my solution for other people needing something like this.
void FixedUpdate()
{
if (Handle == IntPtr.Zero)
{
Handle = GetActiveWindow();
uint uP;
GetWindowThreadProcessId(Handle, out uP);
System.Diagnostics.Process p = System.Diagnostics.Process.GetProcessById((int)uP);
if (string.Equals(p.ProcessName, "Unity"))
AllowReset = false;
else if (string.Equals(p.ProcessName, "TestBuild00001"))
AllowReset = true;
else
Handle = IntPtr.Zero;
}
}
void Update()
{
if (ScreenSet && AllowReset && GuiValidReset)
{
GuiValidReset = false;
ScreenSet = false;
SetPosition();
}
}
void OnGUI()
{
if (ScreenSet && AllowReset && !GuiValidReset)
GuiValidReset = true;
}
internal static void SetPosition()
{
SetWindowPos(Handle, 0, 0, 0, 0, 0, 0x0001);
}
Dll imports of
[DllImport("User32.dll")]
internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll")]
internal static extern IntPtr GetActiveWindow();
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetWindowPos(IntPtr hwnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);
Did update and ongui check to assure that a full redraw of unity would happen before positioning the window, since calling it at the same time a resolution change caused the window to reset to midscreen. ScreenSet is set to true right after my resolution is set.

Refreshing system tray icons programmatically

I've an application which has a system tray icon. While uninstalling I'm killing the process if its running. So, as am not gracefully stopping the app, the icon remains in the system tray and will remove only if we hover the mouse on it. I wrote a code that would run the cursor along the tray and get the cursor back in its initial position. This is what I have done:
[DllImport("user32.dll")]
static extern IntPtr FindWindow(string className, string windowName);
[DllImport("user32.dll")]
static extern IntPtr FindWindowEx(IntPtr parent, IntPtr child, string className, string windowName);
[DllImport("user32.dll")]
static extern bool GetWindowRect(HandleRef handle, out RECT rct);
[StructLayout(LayoutKind.Sequential)]
struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
void RefreshTray()
{
IntPtr taskbar_Handle = FindWindow("Shell_Traywnd", "");
IntPtr tray_Handle = FindWindowEx(taskbar_Handle, IntPtr.Zero, "TrayNotifyWnd", "");
RECT rct;
if (!(GetWindowRect(new HandleRef(null, tray_Handle), out rct)))
{
}
System.Drawing.Point init = Control.MousePosition;
for (int i = rct.Left; i < rct.Right-20; i++)
{
Cursor.Position = new System.Drawing.Point(i, (rct.Bottom + rct.Top) / 2);
}
Cursor.Position = init;
}
This works good in all the cases except when the option "do not show notification icons" is enabled. Is there some way I could refresh the tray in this case?
EDIT
As the comments suggested I changed my approach. Instead of killing the tray application, I established a communication between my application service (yeah, forgot to mention, I have a service too running along with the application) and tray application. While uninstalling, I stop the service, from the service stop method I would send a socket message of a particular format to the tray application and ask it to close and I would set the notify icon visibility to false. This would leave the Tray Application running in background so I am using "taskkill" to remove the application. It worked fine in Win7 and Vista, but is not working properly in Win XP. But I have not written any environment specific code. Any possible clue?
That's similar to what I use.
A simple floating Keyboard I added to a touch gallery interface. The user wanted to also have my keyboard as a standalone application on their desktop. So I did this, created a tray app for it. Now - what if its open and they launch my gallery?
They would have two keyboards.
Sure - the user could end the first - but its easier to just end it. There are no repercussions from me killing it, so I do. But the tray Icon remains, as its waiting for an event. To get around this, I refresh the Tray area.
Please note - This would only work on an English Locale Installation. To get this to work on another language, change "User Promoted Notification Area", and "Notification Area" to the translated / equivalent string.
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass,
string lpszWindow);
[DllImport("user32.dll")]
public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
public static void RefreshTrayArea()
{
IntPtr systemTrayContainerHandle = FindWindow("Shell_TrayWnd", null);
IntPtr systemTrayHandle = FindWindowEx(systemTrayContainerHandle, IntPtr.Zero, "TrayNotifyWnd", null);
IntPtr sysPagerHandle = FindWindowEx(systemTrayHandle, IntPtr.Zero, "SysPager", null);
IntPtr notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "Notification Area");
if (notificationAreaHandle == IntPtr.Zero)
{
notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32",
"User Promoted Notification Area");
IntPtr notifyIconOverflowWindowHandle = FindWindow("NotifyIconOverflowWindow", null);
IntPtr overflowNotificationAreaHandle = FindWindowEx(notifyIconOverflowWindowHandle, IntPtr.Zero,
"ToolbarWindow32", "Overflow Notification Area");
RefreshTrayArea(overflowNotificationAreaHandle);
}
RefreshTrayArea(notificationAreaHandle);
}
private static void RefreshTrayArea(IntPtr windowHandle)
{
const uint wmMousemove = 0x0200;
RECT rect;
GetClientRect(windowHandle, out rect);
for (var x = 0; x < rect.right; x += 5)
for (var y = 0; y < rect.bottom; y += 5)
SendMessage(windowHandle, wmMousemove, 0, (y << 16) + x);
}
Shouldn't be difficult to close the current instance using something like pipes, or TCP if you don't feel like doing that and aren't running .NET4.0.
As everyone is implying, the issue is that by killing your process it doesn't get a chance to unregister its tray icon instance, so it sticks around until Windows attempts to send an event to it (the next time you move the mouse over it) at which point Windows will remove it.
Depending on what installer you are using, this could be quite easy or more difficult. Most popular installer frameworks allow for plugins, and a few of them support Pipes, many more support TCP requests. Alternatively, write up a small executable that your installer can run before it begins the uninstall process, which communicates with your primary app and sends a close message.
As a final note, if you can use .NET4.0 then I'd suggest looking at the built in System.IO.Pipes namespace and the included classes.
Use this tool
http://www.codeproject.com/Articles/19620/LP-TrayIconBuster
It iterates through ToolBarButtons in TrayNotifyWnd & NotifyIconOverflowWindow and removes those with null file names.
I found this (http://maruf-dotnetdeveloper.blogspot.com/2012/08/c-refreshing-system-tray-icon.html) solution worked for me.

find window Height & Width

how to find focused window Height & Width ..
it might be any windows window like notepad,mspaint etc...
i can get the focused window by help of this code
[DllImport("user32")]
public static extern IntPtr GetForegroundWindow();
hi f3lix it's working but it's return the value depends on the location only.. if i change the location it's return some other values
Kunal it's return error msg....like object refrence not set
I think you have to use user32.dll functions via PInvoke. I'm not sure, but I would do it somewhat like this:
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", SetLastError = true)]
static extern bool GetWindowRect(IntPtr hWnd, out Rectangle lpRect);
Rectangle rect = new Rectangle();
GetWindowRect(GetForegroundWindow(), out rect);
Note: I did not try this code, because I am currently not working on Windows...
EDIT:
Rory pointed out to me (see comments) that we can't use the standard Rectangle here and we need to define our own RECT.
[StructLayout(LayoutKind.Sequential)]
public struct RECT {
public int Left;
public int Top;
public int Right;
public int Bottom;
}
Don't forget to replace Rectangle with RECT in the first piece of code.
[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
public static Size GetControlSize(IntPtr hWnd)
{
RECT pRect;
Size cSize = new Size();
// get coordinates relative to window
GetWindowRect(hWnd, out pRect);
cSize.Width = pRect.Right - pRect.Left;
cSize.Height = pRect.Bottom - pRect.Top;
return cSize;
}
What exactly is your question?
How to get the focused window?
How to get the width and height of a Window?
If question 2, use Window.ActualWidth and Window.ActualHeight
If your window is inside your aplication, having an MDI app, you might use this,
having
public static extern IntPtr GetForegroundWindow();
with you, try this
int wHeight = Control.FromHandle(GetForegroundWindow()).Height;
int wWidth = Control.FromHandle(GetForegroundWindow()).Width;
If you're building an MDI app, you could use:
parentForm.ActiveMDIChild.Size

Categories

Resources