I have this code to run an exe, catch the hWnd, and move it inside a panel of my C# app.
All is ok, but this process must be restarted every hour, and when I do this by killing it and restarting it, it takes the active focus.
If I am writing something in another app, or watching a video in fullscreen, it causing me to exit fullscreen, or me to write inside this new process instead of Word or whatever...
How I can avoid the launched exe from taking focus when restarted?
[DllImport("user32.dll", SetLastError = true)]
internal 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 cx, int cy, bool repaint);
Funzioni.HWnd = IntPtr.Zero;
PSI = new ProcessStartInfo(Funzioni.AppPath)
{
CreateNoWindow = true,
RedirectStandardInput = false,
RedirectStandardOutput = false,
RedirectStandardError = false
};
P = Process.Start(PSI);
int MaxCount = 10000;
int Count = 0;
while (Funzioni.HWnd == IntPtr.Zero || Count > MaxCount)
{
P.WaitForInputIdle();
P.Refresh();
Funzioni.HWnd = P.MainWindowHandle;
Count++;
}
if (Funzioni.HWnd == IntPtr.Zero) throw new ApplicationException("The process is taking long to start");
Funzioni.SetParent(Funzioni.HWnd, Pnl_Centrale.Handle);
Funzioni.MoveWindow(Funzioni.HWnd, Funzioni.ExePosX, Funzioni.ExePosY, Funzioni.ExeLarghezza, Funzioni.ExeAltezza - Funzioni.AltezzaToolBar, true);
The ProcessStartInfo properties you are setting only applies to console applications. For a GUI application you must set the ProcessStartInfo.WindowStyle property to Hidden.
It is possible that this will change how MainWindowHandle works because Win32 does not actually have a main window concept. You probably have to p/invoke FindWindow or EnumWindows+GetWindowThreadProcessId.
Solved by myself in this way:
Funzioni.HWnd = IntPtr.Zero;
// Start hidden the process
PSI = new ProcessStartInfo(Funzioni.AppPath)
{
CreateNoWindow = true,
RedirectStandardInput = false,
RedirectStandardOutput = false,
RedirectStandardError = false,
WindowStyle = ProcessWindowStyle.Hidden
};
P = Process.Start(PSI);
int MaxCount = 10000;
int Count = 0;
Thread.Sleep(500);
while (Funzioni.HWnd == IntPtr.Zero || Count > MaxCount)
{
P.WaitForInputIdle();
P.Refresh();
// Get the hidden main handle
Funzioni.HWnd = Funzioni.EnumerateProcessWindowHandles(P.Id).First();
Count++;
}
if (Funzioni.HWnd == IntPtr.Zero) throw new ApplicationException("The process is taking long to start");
// Set the parent exe
Funzioni.SetParent(Funzioni.HWnd, Pnl_Centrale.Handle);
// Set the window of the nested exe in no-top most and not active
Funzioni.SetWindowPos(Funzioni.HWnd, (IntPtr)SpecialWindowHandles.HWND_NOTOPMOST,
Funzioni.ExePosX, Funzioni.ExePosY, Funzioni.ExeLarghezza, Funzioni.ExeAltezza - Funzioni.AltezzaToolBar,
(uint)SetWindowPosFlags.SWP_NOACTIVATE | (uint)SetWindowPosFlags.SWP_NOOWNERZORDER);
// Move again the the window in the desired position (i don't know why the function above don't work if using "NOTOPMOST"
Funzioni.MoveWindow(Funzioni.HWnd, Funzioni.ExePosX, Funzioni.ExePosY, Funzioni.ExeLarghezza, Funzioni.ExeAltezza - Funzioni.AltezzaToolBar, true);
P.Refresh();
// Show the back the hidden process/ese/window
Funzioni.ShowWindow(Funzioni.HWnd, 1);
WinAPI:
#region WinApi
[DllImport("user32.dll", SetLastError = true)]
internal 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 cx, int cy, bool repaint);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
internal delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
internal static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn,
IntPtr lParam);
internal static IEnumerable<IntPtr> EnumerateProcessWindowHandles(int processId)
{
var handles = new List<IntPtr>();
foreach (ProcessThread thread in Process.GetProcessById(processId).Threads)
EnumThreadWindows(thread.Id,
(hWnd, lParam) => { handles.Add(hWnd); return true; }, IntPtr.Zero);
return handles;
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool InvalidateRect(IntPtr hWnd, IntPtr rect, bool bErase);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool UpdateWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, UInt32 uFlags);
#endregion
Enums:
public enum SpecialWindowHandles
{
// ReSharper disable InconsistentNaming
/// <summary>
/// Places the window at the top of the Z order.
/// </summary>
HWND_TOP = 0,
/// <summary>
/// Places the window at the bottom of the Z order. If the hWnd parameter identifies a topmost window, the window loses its topmost status and is placed at the bottom of all other windows.
/// </summary>
HWND_BOTTOM = 1,
/// <summary>
/// Places the window above all non-topmost windows. The window maintains its topmost position even when it is deactivated.
/// </summary>
HWND_TOPMOST = -1,
/// <summary>
/// Places the window above all non-topmost windows (that is, behind all topmost windows). This flag has no effect if the window is already a non-topmost window.
/// </summary>
HWND_NOTOPMOST = -2
// ReSharper restore InconsistentNaming
}
[Flags]
public enum SetWindowPosFlags : uint
{
// ReSharper disable InconsistentNaming
/// <summary>
/// If the calling thread and the thread that owns the window are attached to different input queues, the system posts the request to the thread that owns the window. This prevents the calling thread from blocking its execution while other threads process the request.
/// </summary>
SWP_ASYNCWINDOWPOS = 0x4000,
/// <summary>
/// Prevents generation of the WM_SYNCPAINT message.
/// </summary>
SWP_DEFERERASE = 0x2000,
/// <summary>
/// Draws a frame (defined in the window's class description) around the window.
/// </summary>
SWP_DRAWFRAME = 0x0020,
/// <summary>
/// Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE is sent only when the window's size is being changed.
/// </summary>
SWP_FRAMECHANGED = 0x0020,
/// <summary>
/// Hides the window.
/// </summary>
SWP_HIDEWINDOW = 0x0080,
/// <summary>
/// Does not activate the window. If this flag is not set, the window is activated and moved to the top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter parameter).
/// </summary>
SWP_NOACTIVATE = 0x0010,
/// <summary>
/// Discards the entire contents of the client area. If this flag is not specified, the valid contents of the client area are saved and copied back into the client area after the window is sized or repositioned.
/// </summary>
SWP_NOCOPYBITS = 0x0100,
/// <summary>
/// Retains the current position (ignores X and Y parameters).
/// </summary>
SWP_NOMOVE = 0x0002,
/// <summary>
/// Does not change the owner window's position in the Z order.
/// </summary>
SWP_NOOWNERZORDER = 0x0200,
/// <summary>
/// Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent window uncovered as a result of the window being moved. When this flag is set, the application must explicitly invalidate or redraw any parts of the window and parent window that need redrawing.
/// </summary>
SWP_NOREDRAW = 0x0008,
/// <summary>
/// Same as the SWP_NOOWNERZORDER flag.
/// </summary>
SWP_NOREPOSITION = 0x0200,
/// <summary>
/// Prevents the window from receiving the WM_WINDOWPOSCHANGING message.
/// </summary>
SWP_NOSENDCHANGING = 0x0400,
/// <summary>
/// Retains the current size (ignores the cx and cy parameters).
/// </summary>
SWP_NOSIZE = 0x0001,
/// <summary>
/// Retains the current Z order (ignores the hWndInsertAfter parameter).
/// </summary>
SWP_NOZORDER = 0x0004,
/// <summary>
/// Displays the window.
/// </summary>
SWP_SHOWWINDOW = 0x0040,
// ReSharper restore InconsistentNaming
}
Unfortunately, sometimes (not ever) the GUI of the nested app is not draw correctly, forcing me to restart it manually after the auto-restart every 60 mins. I have used [DllImport("user32.dll")] static extern bool UpdateWindow(IntPtr hWnd); but without luck. Any other redraw/function are not working. There is a way to force redraw before show up?
Also, the code above, make the Windows status bar to pop up while are in full screen on (for example) YouTube. Any suggestions to avoid this too?
Related
I hook a window into a Panel using this code:
SetParent(win, pnlApp.Handle);
SetWindowLong(win, GWL_STYLE, WS_VISIBLE);
MoveWindow(win, 0, 0, pnlApp.Width, pnlApp.Height, true);
where win is the Window handle, as an IntPtr. Here think about Notepad, it will hook very well into a Panel.
But when I try to release (unhook) it using this code:
SetParent(win, (IntPtr)0);
It's shown without the Window frame! I mean it will release into the Desktop again, but it has not any Window frame.
How to solve this issue?
In relation to the code in the question:
SetWindowLong() should be replaced by SetWindowLongPtr() - see the warning in the Docs. The latter calls the former in case the calling code requires it.
You need to call GetWindowLongPtr() to get the current Window Styles, then add or remove Styles as needed; store the original value: it will be used to restore the previous state.
► In the example, the WS_SYSMENU Style is removed, hence the Window will not show the System Menu and all the Buttons usually located in the Caption. It's just an example, to see how this works (it may not be effective with all Windows).
SetParent() returns the Handle of the previous Parent. Store this value, it will be needed to restore the Window to its previous owner, whatever that is. If the Window is a top-level Window, the previous Parent appears to be the Desktop, so you expect IntPtr.Zero. It might not be, you can get a Handle that is not IntPtr.Zero. Just store the returned value, see what that is, if you're interested :)
You should check the return value of both GetWindowLongPtr() and SetWindowLongPtr(): if it's 0, then there was an error. You can use Marshal.GetLastWin32Error() to get the error code. Note that SetWindowLongPtr() doesn't clear the error code, so you have to clear it yourself, calling SetLastError(0).
You can use MoveWindow() to reposition the Windows inside the new Parent's bounds; I suggest to call SetWindowPos() instead, you have more options. See the code here.
Disclaimer: keep in mind what's in Raymond Chen's blog post:
Is it legal to have a cross-process parent/child or owner/owned window relationship
In other words, it's a just because you asked thing :)
Set these Fields (or whatever fits):
windowdHwnd is the Handle of the Window to re-parent
oldParent is the Handle of the previous Parent, returned by SetParent()
oldStyles will store the previous Window's Styles, returned by GetWWindowLongPtr()
IntPtr windowdHwnd = IntPtr.Zero;
IntPtr oldParent = (IntPtr)(-1);
int oldStyles = 0;
// [...]
When you have the Handle of the Window you care about, set windowdHwnd to this value. Call this code after (it could be a method, pass the Window Handle and the Container Control that will be the new Parent - here, a Control named somePanel).
// Already parented, resets the previous styles, clean up and return
if (windowdHwnd != IntPtr.Zero && oldParent != (IntPtr)(-1)) {
NativeMethods.SetParent(windowdHwnd, oldParent);
NativeMethods.SetWindowLongPtr(windowdHwnd, NativeMethods.GWL_Flags.GWL_STYLE, new IntPtr(oldStyles));
windowdHwnd = IntPtr.Zero;
oldParent = (IntPtr)(-1);
return;
}
// Store the existing Styles, to restore when the Window is dismissed
oldStyles = NativeMethods.GetWindowLongPtr(windowdHwnd, NativeMethods.GWL_Flags.GWL_STYLE);
if (oldStyles == IntPtr.Zero) {
int error = Marshal.GetLastWin32Error(); // Show the error code or throw an exception
return;
}
// Removes the System Menu from the Window: it will also remove the Buttons from the Caption
int newStyle = oldStyles.ToInt32()^ (int)NativeMethods.WinStyles.WS_SYSMENU;
NativeMethods.SetLastError(0);
// Sets the new Styles
IntPtr result = NativeMethods.SetWindowLongPtr(windowdHwnd, NativeMethods.GWL_Flags.GWL_STYLE, (IntPtr)newStyle);
if (result == IntPtr.Zero) {
int error = Marshal.GetLastWin32Error(); // Show the error code or throw an exception
return;
}
oldParent = NativeMethods.SetParent(windowdHwnd, somePanel.Handle);
// Repositions the Window and shows it, if needed
var flags = NativeMethods.SWP_Flags.SWP_ASYNCWINDOWPOS | NativeMethods.SWP_Flags.SWP_SHOWWINDOW;
NativeMethods.SetWindowPos(windowdHwnd, IntPtr.Zero, 0, 0, somePanel.Width, somePanel.Height, flags);
You can write (this just removes):
int newStyle = oldStyles &~(int)NativeMethods.WinStyles.WS_SYSMENU;
instead of (this switches on/off):
int newStyle = oldStyles ^ (int)NativeMethods.WinStyles.WS_SYSMENU;
NativeMethods class:
using System.Runtime.InteropServices;
public class NativeMethods
{
[Flags]
public enum SWP_Flags : uint
{
/// <summary>Retains the current size (ignores the cx and cy parameters).</summary>
SWP_NOSIZE = 0x0001,
/// <summary>Retains the current position (ignores X and Y parameters).</summary>
SWP_NOMOVE = 0x0002,
/// <summary>Retains the current Z order (ignores the hWndInsertAfter parameter).</summary>
SWP_NOZORDER = 0x0004,
/// <summary>Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to
/// the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent
/// window uncovered as a result of the window being moved. When this flag is set, the application must
/// explicitly invalidate or redraw any parts of the window and parent window that need redrawing.</summary>
SWP_NOREDRAW = 0x0008,
/// <summary>Does not activate the window. If this flag is not set, the window is activated and moved to the
/// top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter
/// parameter).</summary>
SWP_NOACTIVATE = 0x0010,
/// <summary>Draws a frame (defined in the window's class description) around the window.</summary>
SWP_DRAWFRAME = 0x0020,
/// <summary>Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to
/// the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE
/// is sent only when the window's size is being changed.</summary>
SWP_FRAMECHANGED = 0x0020,
/// <summary>Displays the window.</summary>
SWP_SHOWWINDOW = 0x0040,
/// <summary>Hides the window.</summary>
SWP_HIDEWINDOW = 0x0080,
/// <summary>Discards the entire contents of the client area. If this flag is not specified, the valid
/// contents of the client area are saved and copied back into the client area after the window is sized or
/// repositioned.</summary>
SWP_NOCOPYBITS = 0x0100,
/// <summary>Does not change the owner window's position in the Z order.</summary>
SWP_NOOWNERZORDER = 0x0200,
/// <summary>Same as the SWP_NOOWNERZORDER flag.</summary>
SWP_NOREPOSITION = 0x0200,
/// <summary>Prevents the window from receiving the WM_WINDOWPOSCHANGING message.</summary>
SWP_NOSENDCHANGING = 0x0400,
/// <summary>Internal use.</summary>
SWP_NOCLIENTSIZE = 0x0800,
/// <summary>Internal use.</summary>
SWP_NOCLIENTMOVE = 0x1000,
/// <summary>Prevents generation of the WM_SYNCPAINT message.</summary>
SWP_DEFERERASE = 0x2000,
/// <summary>If the calling thread and the thread that owns the window are attached to different input queues,
/// the system posts the request to the thread that owns the window. This prevents the calling thread from
/// blocking its execution while other threads process the request.</summary>
SWP_ASYNCWINDOWPOS = 0x4000
}
[Flags]
public enum WinStyles : uint
{
WS_BORDER = 0x00800000, //The window has a thin-line border.
WS_CAPTION = 0x00C00000, //The window has a title bar (includes the WS_BORDER style).
WS_CHILD = 0x40000000, //The window is a child window. A window with this style cannot have a menu bar. This style cannot be used with the WS_POPUP style.
WS_CHILDWINDOW = 0x40000000, //Same as the WS_CHILD style.
WS_CLIPCHILDREN = 0x02000000, //Excludes the area occupied by child windows when drawing occurs within the parent window. This style is used when creating the parent window.
WS_CLIPSIBLINGS = 0x04000000, //Clips child windows relative to each other; that is, when a particular child window receives a WM_PAINT message, the WS_CLIPSIBLINGS style clips all other overlapping child windows out of the region of the child window to be updated. If WS_CLIPSIBLINGS is not specified and child windows overlap, it is possible, when drawing within the client area of a child window, to draw within the client area of a neighboring child window.
WS_DISABLED = 0x08000000, //The window is initially disabled. A disabled window cannot receive input from the user. To change this after a window has been created, use the EnableWindow function.
WS_DLGFRAME = 0x00400000, //The window has a border of a style typically used with dialog boxes. A window with this style cannot have a title bar.
WS_GROUP = 0x00020000, //The window is the first control of a group of controls. The group consists of this first control and all controls defined after it, up to the next control with the WS_GROUP style. The first control in each group usually has the WS_TABSTOP style so that the user can move from group to group. The user can subsequently change the keyboard focus from one control in the group to the next control in the group by using the direction keys.
//You can turn this style on and off to change dialog box navigation. To change this style after a window has been created, use the SetWindowLong function.
WS_HSCROLL = 0x00100000, //The window has a horizontal scroll bar.
WS_ICONIC = 0x20000000, //The window is initially minimized. Same as the WS_MINIMIZE style.
WS_MAXIMIZE = 0x01000000, //The window is initially maximized.
WS_MAXIMIZEBOX = 0x00010000, //The window has a maximize button. Cannot be combined with the WS_EX_CONTEXTHELP style. The WS_SYSMENU style must also be specified.
WS_MINIMIZE = 0x20000000, //The window is initially minimized. Same as the WS_ICONIC style.
WS_MINIMIZEBOX = 0x00020000, //The window has a minimize button. Cannot be combined with the WS_EX_CONTEXTHELP style. The WS_SYSMENU style must also be specified.
WS_OVERLAPPED = 0x00000000, //The window is an overlapped window. An overlapped window has a title bar and a border. Same as the WS_TILED style.
WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | //The window is an overlapped window. Same as the WS_TILEDWINDOW style.
WS_CAPTION |
WS_SYSMENU |
WS_THICKFRAME |
WS_MINIMIZEBOX |
WS_MAXIMIZEBOX,
WS_POPUP = 0x80000000, //The windows is a pop-up window. This style cannot be used with the WS_CHILD style.
WS_POPUPWINDOW = WS_POPUP | //The window is a pop-up window. The WS_CAPTION and WS_POPUPWINDOW styles must be combined to make the window menu visible.
WS_BORDER |
WS_SYSMENU,
WS_SIZEBOX = 0x00040000, //The window has a sizing border. Same as the WS_THICKFRAME style.
WS_SYSMENU = 0x00080000, //The window has a window menu on its title bar. The WS_CAPTION style must also be specified.
WS_TABSTOP = 0x00010000, //The window is a control that can receive the keyboard focus when the user presses the TAB key. Pressing the TAB key changes the keyboard focus to the next control with the WS_TABSTOP style.
//You can turn this style on and off to change dialog box navigation. To change this style after a window has been created, use the SetWindowLong function. For user-created windows and modeless dialogs to work with tab stops, alter the message loop to call the IsDialogMessage function.
WS_THICKFRAME = 0x00040000, //The window has a sizing border. Same as the WS_SIZEBOX style.
WS_TILED = 0x00000000, //The window is an overlapped window. An overlapped window has a title bar and a border. Same as the WS_OVERLAPPED style.
WS_TILEDWINDOW = WS_OVERLAPPED | //The window is an overlapped window. Same as the WS_OVERLAPPEDWINDOW style.
WS_CAPTION |
WS_SYSMENU |
WS_THICKFRAME |
WS_MINIMIZEBOX |
WS_MAXIMIZEBOX,
WS_VISIBLE = 0x10000000, //The window is initially visible. This style can be turned on and off by using the ShowWindow or SetWindowPos function.
WS_VSCROLL = 0x00200000, //The window has a vertical scroll bar.
}
public enum GWL_Flags : int
{
GWL_USERDATA = -21,
GWL_EXSTYLE = -20,
GWL_STYLE = -16,
GWL_ID = -12,
GWLP_HWNDPARENT = -8,
GWLP_HINSTANCE = -6,
GWL_WNDPROC = -4,
DWLP_MSGRESULT = 0x0,
DWLP_DLGPROC = 0x4,
DWLP_USER = 0x8
}
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern IntPtr GetWindowLongPtr(IntPtr hWnd, GWL_Flags nIndex);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern IntPtr SetWindowLongPtr(IntPtr hWnd, GWL_Flags nIndex, IntPtr dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, SWP_Flags uFlags);
[DllImport("kernel32.dll")]
internal static extern void SetLastError(uint dwErrCode);
}
I have an application (that always runs in minimized mode) that constantly listens to keys typed on the keyboard.
When certain key is pressed, I want the C# application to post a command on the message queue of the active window to minimize, close, maximize, etc.
I know it is possible to get the handle to the active window, but, how can I post a message on its message queue (as we can do in win32).
Thanks.
You can use interop to call the native WINAPI functions. Using the p/invoke website I have created the following solution:
var proc = Process.GetProcesses().First(p => p.ProcessName.Contains("firefox"));
PostMessageSafe(
new HandleRef(proc, proc.MainWindowHandle),
(uint) WM.WM_SYSCOMMAND,
new IntPtr((int) SysCommands.SC_MAXIMIZE),
IntPtr.Zero);
What I basically do here is find a WindowHandle I'm interested in and then invoke PostMessage with the WM_SYSCOMMAND in Msg and the appropriate syscommand in the wParam, in this case Maximize, with the value 0xF030. The lParam is set to 0.
Please be aware that if your only goal is to change the window state you better use the specialized API endpoint for that, it's called ShowWindow
Its signature looks like this:
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow);
and it is invoked like this:
ShowWindow(proc.MainWindowHandle, ShowWindowCommands.Maximize);
When either of these methods are called, a window from a process named firefox will be maximized.
Here are the helper methods and enums you need as a wrapper around PostMessage
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
// http://www.pinvoke.net/default.aspx/Enums/WindowsMessages.html
public enum WM : uint
{
WM_SYSCOMMAND = 0x0112
}
// http://www.pinvoke.net/default.aspx/Enums/SysCommands.html
enum SysCommands : int
{
SC_SIZE = 0xF000,
SC_MOVE = 0xF010,
SC_MINIMIZE = 0xF020,
SC_MAXIMIZE = 0xF030,
SC_NEXTWINDOW = 0xF040,
SC_PREVWINDOW = 0xF050,
SC_CLOSE = 0xF060,
SC_VSCROLL = 0xF070,
SC_HSCROLL = 0xF080,
SC_MOUSEMENU = 0xF090,
SC_KEYMENU = 0xF100,
SC_ARRANGE = 0xF110,
SC_RESTORE = 0xF120,
SC_TASKLIST = 0xF130,
SC_SCREENSAVE = 0xF140,
SC_HOTKEY = 0xF150,
//#if(WINVER >= 0x0400) //Win95
SC_DEFAULT = 0xF160,
SC_MONITORPOWER = 0xF170,
SC_CONTEXTHELP = 0xF180,
SC_SEPARATOR = 0xF00F,
//#endif /* WINVER >= 0x0400 */
//#if(WINVER >= 0x0600) //Vista
SCF_ISSECURE = 0x00000001,
//#endif /* WINVER >= 0x0600 */
/*
* Obsolete names
*/
SC_ICON = SC_MINIMIZE,
SC_ZOOM = SC_MAXIMIZE,
}
// http://www.pinvoke.net/default.aspx/Enums/ShowWindowCommand.html
enum ShowWindowCommands
{
/// <summary>
/// Hides the window and activates another window.
/// </summary>
Hide = 0,
/// <summary>
/// Activates and displays a window. If the window is minimized or
/// maximized, the system restores it to its original size and position.
/// An application should specify this flag when displaying the window
/// for the first time.
/// </summary>
Normal = 1,
/// <summary>
/// Activates the window and displays it as a minimized window.
/// </summary>
ShowMinimized = 2,
/// <summary>
/// Maximizes the specified window.
/// </summary>
Maximize = 3, // is this the right value?
/// <summary>
/// Activates the window and displays it as a maximized window.
/// </summary>
ShowMaximized = 3,
/// <summary>
/// Displays a window in its most recent size and position. This value
/// is similar to <see cref="Win32.ShowWindowCommand.Normal"/>, except
/// the window is not activated.
/// </summary>
ShowNoActivate = 4,
/// <summary>
/// Activates the window and displays it in its current size and position.
/// </summary>
Show = 5,
/// <summary>
/// Minimizes the specified window and activates the next top-level
/// window in the Z order.
/// </summary>
Minimize = 6,
/// <summary>
/// Displays the window as a minimized window. This value is similar to
/// <see cref="Win32.ShowWindowCommand.ShowMinimized"/>, except the
/// window is not activated.
/// </summary>
ShowMinNoActive = 7,
/// <summary>
/// Displays the window in its current size and position. This value is
/// similar to <see cref="Win32.ShowWindowCommand.Show"/>, except the
/// window is not activated.
/// </summary>
ShowNA = 8,
/// <summary>
/// Activates and displays the window. If the window is minimized or
/// maximized, the system restores it to its original size and position.
/// An application should specify this flag when restoring a minimized window.
/// </summary>
Restore = 9,
/// <summary>
/// Sets the show state based on the SW_* value specified in the
/// STARTUPINFO structure passed to the CreateProcess function by the
/// program that started the application.
/// </summary>
ShowDefault = 10,
/// <summary>
/// <b>Windows 2000/XP:</b> Minimizes a window, even if the thread
/// that owns the window is not responding. This flag should only be
/// used when minimizing windows from a different thread.
/// </summary>
ForceMinimize = 11
}
I have created a small Window without border by WinApi functions in C#. I want to move this window when right mouse button is pressed. I am trying to catch a mouse offset by anylizing WM_MOUSEMOVE event. It seems to work and i can move my window holding Right Mouse Button.
But I am loosing control of the window when i move my mouse too fast. That's because my Window is too small and if mouse leaves window very quick it doesn'n recieve WM_MOUSEMOVE messages anymore and i can't calculate a MouseOffset to move my Window.
So, How I can fix that?
You need to call SetCapture to tell Windows that your hwnd wants all the events even when the mouse isn't physically over the window. http://msdn.microsoft.com/en-us/library/windows/desktop/ms646262(v=vs.85).aspx
You can tell Windows that the user actually moused down on the title bar and it will handle the rest automatically for you.
[DllImport("user32.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool ReleaseCapture();
[DllImport("user32.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
internal const uint WM_NCLBUTTONDOWN = 0xA1;
internal const int HTCAPTION = 2; // Window captions
internal const int HTBOTTOMRIGHT = 17; // Bottom right corner
/// <summary>
/// Simulates a Windows drag on the window border or title.
/// </summary>
/// <param name="handle">The window handle to drag.</param>
/// <param name="dragType">A HT* constant to determine which part to drag.</param>
internal static void DragWindow(IntPtr handle, int dragType) {
User32.ReleaseCapture();
User32.PostMessage(handle, User32.WM_NCLBUTTONDOWN, new IntPtr(dragType), IntPtr.Zero);
}
The MSV-Studio description for Locked is "The Locked property determines if we can move or resize the control" so I set the winforms Locked property to true but the form is still movable.
What is the correct way to prevent the form from moving?
Maximize it. Thanks, JackN. ;-)
I use the following code to display a form dialog window for a corporate security application written in-house - one of the requirements was that the form could not be moved, resized or live under any other form. Anyway, see below for a start...
/// <seealso href="http://msdn.microsoft.com/en-us/library/ms633548(v=vs.85).aspx"/>
/// <seealso href="http://msdn.microsoft.com/en-us/library/ms633545(v=vs.85).aspx"/>
public class ShowMessage
{
const int SW_SHOWMAXIMIZED = 3; //for maximising (if desired)
const int SW_SHOW = 5; //for simply activating the form (not needed)
const int SW_SHOWNORMAL = 1; //displays form at original size and position (what we use here)
const UInt32 SWP_NOSIZE = 0x0001; //cannot be resized
const UInt32 SWP_NOMOVE = 0x0002; //cannot be moved
static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); //always lives at the top
const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE; //sets the flags for no resize / no move
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
/// <summary>
/// Displays the passed form using the parameters set in the base ShowMessage class
/// </summary>
/// <param name="frm">A Windows Form object</param>
/// <example><code>ShowMessage.ShowTopmost(new myForm());</code></example>
public static void ShowTopmost(Form frm)
{
ShowWindow(frm.Handle, SW_SHOWNORMAL); //shows the form
SetWindowPos(frm.Handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS); //sets the form position as topmost, centered
}
}
Then I simply call
ShowMessage.ShowTopmost(new frmMessage());
I'm not saying it's the only way or the right way, but it a way to do it.
It's generally bad form to prevent the user from moving the window. The user should be able to have the window wherever he wants. Preventing resizing is one thing, preventing moving is another. I'm not aware of any C# native way of doing this, but you can probably hook down into Win32 to prevent the window from moving.
You might be able to use the Move event of the form and set the form back to the starting position. You would have to capture and store (in memory) the starting position.
Thank you for previous answers that enabled to me complete the basic tool that shows large red cross in the mouse coordinates in order to let be more visible. The red cross is an image with transparent background in the transparent form. The problem is that you cannot click through, since its topmost and the center of form is actually positioned to mouse xy. Is there any way how to make this usable in order to have the cross still displayed on the cursor but "clickable" through?
You can use SetWindowLong to set the WS_EX_TRANSPARENT window style:
If the layered window has the WS_EX_TRANSPARENT extended window style, the shape of the layered window will be ignored and the mouse events will be passed to the other windows underneath the layered window.
CodeProject has this article detailing the technique. Though it's in VB.NET it should be easy to convert to C#.
I have used the following code in the past:
public enum GWL
{
ExStyle = -20
}
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);
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
int wl = GetWindowLong(this.Handle, GWL.ExStyle);
wl = wl | 0x80000 | 0x20;
SetWindowLong(this.Handle, GWL.ExStyle, wl);
SetLayeredWindowAttributes(this.Handle, 0, 128, LWA.Alpha);
}
but it also was copied from somewhere else. The important lines here are in the OnShown method. Though I have to admit that the line
wl = wl | 0x80000 | 0x20;
is a little cryptic, setting the WS_EX_LAYERED and WS_EX_TRANSPARENT extended styles.
You can probably also set it like
wl = wl | WS_EX.Layered | WS_EX.Transparent;
To provide a more detailed/commented version, which also uses TransparencyKey as transparency key (not black as the version above), and one might set _alpha as desired.
/// <summary>
/// 0: the window is completely transparent ... 255: the window is opaque
/// </summary>
private byte _alpha;
private enum GetWindowLong
{
/// <summary>
/// Sets a new extended window style.
/// </summary>
GWL_EXSTYLE = -20
}
private enum ExtendedWindowStyles
{
/// <summary>
/// Transparent window.
/// </summary>
WS_EX_TRANSPARENT = 0x20,
/// <summary>
/// Layered window. http://msdn.microsoft.com/en-us/library/windows/desktop/ms632599%28v=vs.85%29.aspx#layered
/// </summary>
WS_EX_LAYERED = 0x80000
}
private enum LayeredWindowAttributes
{
/// <summary>
/// Use bAlpha to determine the opacity of the layered window.
/// </summary>
LWA_COLORKEY = 0x1,
/// <summary>
/// Use crKey as the transparency color.
/// </summary>
LWA_ALPHA = 0x2
}
[DllImport("user32.dll", EntryPoint = "GetWindowLong")]
private static extern int User32_GetWindowLong(IntPtr hWnd, GetWindowLong nIndex);
[DllImport("user32.dll", EntryPoint = "SetWindowLong")]
private static extern int User32_SetWindowLong(IntPtr hWnd, GetWindowLong nIndex, int dwNewLong);
[DllImport("user32.dll", EntryPoint = "SetLayeredWindowAttributes")]
private static extern bool User32_SetLayeredWindowAttributes(IntPtr hWnd, int crKey, byte bAlpha, LayeredWindowAttributes dwFlags);
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
//Click through
int wl = User32_GetWindowLong(this.Handle, GetWindowLong.GWL_EXSTYLE);
User32_SetWindowLong(this.Handle, GetWindowLong.GWL_EXSTYLE, wl | (int)ExtendedWindowStyles.WS_EX_LAYERED | (int)ExtendedWindowStyles.WS_EX_TRANSPARENT);
//Change alpha
User32_SetLayeredWindowAttributes(this.Handle, (TransparencyKey.B << 16) + (TransparencyKey.G << 8) + TransparencyKey.R, _alpha, LayeredWindowAttributes.LWA_COLORKEY | LayeredWindowAttributes.LWA_ALPHA);
}