I have a Windows 8.1 wpf application that can change its mode: kiosk or maintenance. For the kiosk mode I have a code:
window.WindowStyle = WindowStyle.None;
window.WindowState = WindowState.Maximized;
window.Topmost = true;
It work very good, but when I change the mode from the maintenance to the kiosk mode I have a problem: if window was maximized before (window.WindowState == WindowState.Maximized) a taskbar stay visible.
I tried resolve this problem: I calculated monitor width and height using User32.dll:
public MainWindow()
{
InitializeComponent();
this.SourceInitialized += new EventHandler(win_SourceInitialized);
}
void win_SourceInitialized(object sender, EventArgs e)
{
System.IntPtr handle = (new WindowInteropHelper(this)).Handle;
HwndSource.FromHwnd(handle).AddHook(new HwndSourceHook(WindowProc));
}
private static System.IntPtr WindowProc(System.IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case 0x0024:
WmGetMinMaxInfo(hwnd, lParam);
handled = true;
break;
}
return (IntPtr)0;
}
private static void WmGetMinMaxInfo(System.IntPtr hwnd, System.IntPtr lParam)
{
MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));
// Adjust the maximized size and position to fit the work area of the correct monitor
int MONITOR_DEFAULTTONEAREST = 0x00000002;
System.IntPtr monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if (monitor != System.IntPtr.Zero)
{
MONITORINFO monitorInfo = new MONITORINFO();
GetMonitorInfo(monitor, monitorInfo);
RECT rcMonitorArea = monitorInfo.rcMonitor;
mmi.ptMaxPosition.x = rcMonitorArea.left;
mmi.ptMaxPosition.y = rcMonitorArea.top;
mmi.ptMaxSize.x = Math.Abs(rcMonitorArea.right - rcMonitorArea.left);
mmi.ptMaxSize.y = Math.Abs(rcMonitorArea.bottom - rcMonitorArea.top);
}
Marshal.StructureToPtr(mmi, lParam, true);
}
[DllImport("user32.dll")]
internal static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);
[DllImport("user32.dll")]
static extern bool GetCursorPos(ref Point lpPoint);
[DllImport("User32.dll")]
internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags);
But it don't work after maximizes window in maitenance mode whatever. I understand that WindowState is WindowState.Maximized already and event of change window size do not happen. But I don't know how I can resolve it.
Of course, I can change the size of my window before maximizing:
window.WindowStyle = WindowStyle.None;
window.WindowState = WindowState.Normal;
window.WindowState = WindowState.Maximized;
window.Topmost = true;
It work, but I see flicker of the window. It's very bad for me.
I hope somebody had this bug and he resolved it happily.
Related
I am developing a single-window app on Windows with Unity.
I allow a user to resize the window but the aspect ratio must be kept.
I want to prevent fullscreen and halfscreen, because they break the aspect ratio.
I've found the following operations make an app fullscreen or halfscreen.
fullscreen:
clicking a maximize button on the title bar
dragging the window to top of screen
pressing Alt + Enter key
pressing Windows + Up-Arrow key
halfscreen:
dragging the window to left/right of screen
pressing Windows + Left/Right -Arrow key
I want to disable all of them.
This can disable a maximize button on the title bar.
HandleRef hWnd = new HandleRef(null, GetActiveWindow());
long style = GetWindowLong(hWnd, GWL_STYLE);
style &= ~WS_MAXIMIZEBOX;
SetWindowLong(hWnd, GWL_STYLE, style);
And this can disable Windows + Up-Arrow key.
private static IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
switch (msg)
{
case WM_SYSCOMMAND:
if (wParam.ToInt32() == SC_MAXIMIZE) {
return IntPtr.Zero;
}
break;
}
return CallWindowProc(oldWndProcPtr, hWnd, msg, wParam, lParam);
}
But the other operations still work.
How can I disable the other operations?
private static IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
switch (msg)
{
case WM_SYSCOMMAND:
if (wParam.ToInt32() == SC_MAXIMIZE) {
return IntPtr.Zero;
}
break;
// BY PASS *** Alt + Enter ***
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
if (wParam == VK_RETURN)
if ((HIWORD(lParam) & KF_ALTDOWN))
return 0;
break;
}
return CallWindowProc(oldWndProcPtr, hWnd, msg, wParam, lParam);
}
The standard Win + M keyboard shortcut minimizes open windows, but it does not minimize a borderless form.
I'm thinking of capturing key events of Win + M in KeyDown event but no luck, since it does not capture both but only captures the first key.
I can't programmatically minimize it using a keyboard shortcut
Any idea on how to minimize my for form by keyboard shortcut? or can I capture Win + M to trigger minimize function?
I'm using borderless form. To reproduce the problem, create a Form and set its BorderStyle to None and run the application. When you press Win + M, all the windows minimize, but my borderless form stays normal.
As an option you can enable system menu (having minimize window style) for your borderless form, then while it doesn't show the control box, but the minimize command will work as expected.
Add the following piece of code to your Form and then Win + M will work as expected:
private const int WS_SYSMENU = 0x80000;
private const int WS_MINIMIZEBOX = 0x20000;
protected override CreateParams CreateParams
{
get
{
CreateParams p = base.CreateParams;
p.Style = WS_SYSMENU | WS_MINIMIZEBOX;
return p;
}
}
As a totally different option you can register a low level keyboard hook using SetWindowsHookEx and check if you received the Win + M, then minimize the window and let the key goes through other windows.
Add the following piece of code to your Form and then Win + M will work as expected:
private const int WH_KEYBOARD_LL = 13;
[StructLayout(LayoutKind.Sequential)]
private struct KBDLLHOOKSTRUCT
{
public Keys vkCode;
public int scanCode;
public int flags;
public int time;
public IntPtr dwExtraInfo;
}
private delegate IntPtr HookProc(int code, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(
int idHook, HookProc lpfn, IntPtr hmod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(
IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern short GetKeyState(int keyCode);
private const int KEY_PRESSED = 0x8000;
private static bool IsKeyDown(Keys key)
{
return Convert.ToBoolean(GetKeyState((int)key) & KEY_PRESSED);
}
private IntPtr ptrHook;
private HookProc hookProc;
private IntPtr CaptureKeys(int code, IntPtr wParam, IntPtr lParam)
{
if (code >= 0)
{
KBDLLHOOKSTRUCT objKeyInfo =
(KBDLLHOOKSTRUCT)Marshal.PtrToStructure(
lParam, typeof(KBDLLHOOKSTRUCT));
if (objKeyInfo.vkCode == (Keys.M) &&
(IsKeyDown(Keys.LWin) || IsKeyDown(Keys.RWin)))
{
this.WindowState = FormWindowState.Minimized;
return (IntPtr)0;
}
}
return CallNextHookEx(ptrHook, code, wParam, lParam);
}
bool HasAltModifier(int flags)
{
return (flags & 0x20) == 0x20;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
var module = Process.GetCurrentProcess().MainModule;
hookProc = new HookProc(CaptureKeys);
ptrHook = SetWindowsHookEx(
WH_KEYBOARD_LL, hookProc, GetModuleHandle(module.ModuleName), 0);
}
i'm currently trying to get wndproc handling in wpf but without any success..
i need to get the event of window created, window activated and window destroid events.
here's what i tried so far
int uMsgNotify;
public MainWindow()
{
InitializeComponent();
WinApi.SetTaskmanWindow(new WindowInteropHelper(this).Handle);
WinApi.RegisterShellHookWindow(new WindowInteropHelper(this).Handle);
uMsgNotify = WinApi.RegisterWindowMessage("SHELLHOOK");
MainForm.ShowInTaskbar = false;
MainForm.ShowActivated = true;
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
source.AddHook(WndProc);
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
IntPtr handle;
if (msg == uMsgNotify)
{
switch (wParam.ToInt32())
{
case WinApi.HSHELL_WINDOWCREATED:
handle = lParam;
string windowName = GetWindowName(handle);
IntPtr hWnd = WinApi.FindWindow(null, windowName);
add_icon(windowName, handle);// add new task's icon in taskbar
break;
case WinApi.HSHELL_WINDOWACTIVATED:
handle = lParam;
break;
case WinApi.HSHELL_WINDOWDESTROYED:
handle = lParam;
del_icon(handle); //remove icon from taskbar
break;
}
}
return IntPtr.Zero;
}
private static string GetWindowName(IntPtr hWnd)
{
// Allocate correct string length first
int length = WinApi.GetWindowTextLength(hWnd);
StringBuilder sb = new StringBuilder(length + 1);
WinApi.GetWindowText(hWnd, sb, sb.Capacity);
return sb.ToString();
}
code doesn't give any kind of runtime error whatsoever.... but it wont work..
to make more sense i'm developing an alternate shell for windows for my gaming cafe... where it needs to have a kind of taskbar..
any help?
I don't know if you still need it but : If you use ShowInTaskbar = false, it won't be able to catch any message with WndProc.
I'm trying to maximize a window which has a transparent border. When maximized, the transparent border shouldn't show. I follow the method found here and by using the below code I can get it to work half way.
void win_SourceInitialized(object sender, EventArgs e) {
System.IntPtr handle = (new WinInterop.WindowInteropHelper(this)).Handle;
WinInterop.HwndSource.FromHwnd(handle).AddHook(new WinInterop.HwndSourceHook(WindowProc));
}
private static System.IntPtr WindowProc(System.IntPtr hwnd, int msg,
System.IntPtr wParam, System.IntPtr lParam, ref bool handled) {
switch (msg) {
case 0x0024:/* WM_GETMINMAXINFO */
WmGetMinMaxInfo(hwnd, lParam);
handled = true;
break;
}
return (System.IntPtr)0;
}
private static void WmGetMinMaxInfo(System.IntPtr hwnd, System.IntPtr lParam) {
MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));
// Adjust the maximized size and position to fit the work area of the correct monitor
int MONITOR_DEFAULTTONEAREST =0x00000002;
System.IntPtr monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if (monitor != System.IntPtr.Zero) {
MONITORINFO monitorInfo = new MONITORINFO();
GetMonitorInfo(monitor, monitorInfo);
RECT rcWorkArea = monitorInfo.rcWork;
RECT rcMonitorArea = monitorInfo.rcMonitor;
mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.left - rcMonitorArea.left) - thickness;
mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.top - rcMonitorArea.top) - thickness;
mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left) + 2 * thickness;
mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top) + 2 * thickness;
}
Marshal.StructureToPtr(mmi, lParam, true);
}
The screenshot below shows how it's expanded correctly in the horizontal direction but for some reason it won't stretch in the vertical direction.
I've tried the following code with updating MINMAXINFO.ptMaxTrackSize and it works.
Related problem is also described here: Can a window be resized past the screen size/offscreen?
private static void WmGetMinMaxInfo(System.IntPtr hwnd, System.IntPtr lParam) {
MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));
// Adjust the maximized size and position to fit the work area of the correct monitor
int MONITOR_DEFAULTTONEAREST =0x00000002;
System.IntPtr monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if (monitor != System.IntPtr.Zero) {
MONITORINFO monitorInfo = new MONITORINFO();
GetMonitorInfo(monitor, monitorInfo);
RECT rcWorkArea = monitorInfo.rcWork;
RECT rcMonitorArea = monitorInfo.rcMonitor;
mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.left - rcMonitorArea.left) - thickness;
mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.top - rcMonitorArea.top) - thickness;
mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left) + 2 * thickness;
mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top) + 2 * thickness;
mmi.ptMaxTrackSize = mmi.ptMaxSize;
}
Marshal.StructureToPtr(mmi, lParam, true);
}
I am trying to create a WPF window with WindowStyle="None" (for custom buttons and no title) that cannot be resized. Setting ResizeMode to NoResize removes the aero border, which I want to keep.
I could set the min/max size properties and be done with it, except that:
The resize cursors are still visible, and
The window is displayed in response to a user action and fits to its contents. It displays an image, so the size changes.
So, I have a simple scheme that gets me 99% of the way there:
public class BorderedWindowNoResize : Window
{
[DllImport( "DwmApi.dll" )]
public static extern int DwmExtendFrameIntoClientArea(
IntPtr hwnd,
ref MARGINS pMarInset );
[DllImport( "user32.dll", CharSet = CharSet.Auto )]
public static extern IntPtr DefWindowProc(
IntPtr hWnd,
int msg,
IntPtr wParam,
IntPtr lParam );
public BorderedWindowNoResize()
{
Loaded += BorderedWindowNoResize_Loaded;
}
private void BorderedWindowNoResize_Loaded( object sender, RoutedEventArgs e )
{
IntPtr mainWindowPtr = new WindowInteropHelper( this ).Handle;
HwndSource mainWindowSrc = HwndSource.FromHwnd( mainWindowPtr );
mainWindowSrc.AddHook( WndProc );
}
private IntPtr WndProc( IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled )
{
var htLocation = DefWindowProc( hwnd, msg, wParam, lParam ).ToInt32();
if( msg == (uint)WM.NCHITTEST )
{
handled = true;
switch( htLocation )
{
case (int)HitTestResult.HTBOTTOM:
case (int)HitTestResult.HTBOTTOMLEFT:
case (int)HitTestResult.HTBOTTOMRIGHT:
case (int)HitTestResult.HTLEFT:
case (int)HitTestResult.HTRIGHT:
case (int)HitTestResult.HTTOP:
case (int)HitTestResult.HTTOPLEFT:
case (int)HitTestResult.HTTOPRIGHT:
htLocation = (int)HitTestResult.HTBORDER;
break;
}
}
return new IntPtr( htLocation );
}
}
Basically;
Override the window procedure.
Call the default window procedure.
If the message it is WM_NCHITTEST, check for the border results.
If it is a border, return the regular HTBORDER.
This works as far as allowing me to keep the aero window border and hiding the resize cursor(s), but it adds a ~5 pixel white border to the inside of my window.
In fact, even if I return the default windows procedure result at the top of WndPrc and do nothing else the border is still there. I need a different background color on my window, so this won't work for me.
Any ideas? Thanks in advance as always.
When you add your hook, you should only handle the messages you need to, and ignore the others. I believe you are handling certain messages twice, since you call DefWindowProc, but never set the handled parameter to true.
So in your case, you'd use:
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
if (msg == (uint)WM.NCHITTEST) {
handled = true;
var htLocation = DefWindowProc(hwnd, msg, wParam, lParam).ToInt32();
switch (htLocation) {
case (int)HitTestResult.HTBOTTOM:
case (int)HitTestResult.HTBOTTOMLEFT:
case (int)HitTestResult.HTBOTTOMRIGHT:
case (int)HitTestResult.HTLEFT:
case (int)HitTestResult.HTRIGHT:
case (int)HitTestResult.HTTOP:
case (int)HitTestResult.HTTOPLEFT:
case (int)HitTestResult.HTTOPRIGHT:
htLocation = (int)HitTestResult.HTBORDER;
break;
}
return new IntPtr(htLocation);
}
return IntPtr.Zero;
}
Also, I'd probably add the hook in an OnSourceInitialized override, like so:
protected override void OnSourceInitialized(EventArgs e) {
base.OnSourceInitialized(e);
IntPtr mainWindowPtr = new WindowInteropHelper(this).Handle;
HwndSource mainWindowSrc = HwndSource.FromHwnd(mainWindowPtr);
mainWindowSrc.AddHook(WndProc);
}
You can try from anywhere in a WPF App
ComponentDispatcher.ThreadFilterMessage += new ThreadMessageEventHandler(ComponentDispatcherThreadFilterMessage);
and:
// ******************************************************************
private static void ComponentDispatcherThreadFilterMessage(ref MSG msg, ref bool handled)
{
if (!handled)
{
if (msg.message == WmHotKey)
{
HotKey hotKey;
if (_dictHotKeyToCalBackProc.TryGetValue((int)msg.wParam, out hotKey))
{
if (hotKey.Action != null)
{
hotKey.Action.Invoke(hotKey);
}
handled = true;
}
}
}
}