11

To reproduce my problem please do the following:

  1. Create a new Windows Form Application in C#.
  2. In the Properties window of Form1 set FormBorderStyle to None.
  3. Launch program and press Windows+Up.
  4. Now you are stuck in full screen.

In the default FormBorderStyle setting the MaximizeBox property to false will disable the Windows+Up fullscreen shortcut.

If the FormBorderStyle is set to None Microsoft decided it would be a good idea to disable all the Windows+Arrow key shortcuts except for the up arrow and then disable the disabling of the MaximizeBox property.

Is this a glitch? Any simple way to disable this shortcut function the selfsame way it is disabled on all the other FormBorderStyles?

CodeCamper
  • 6,609
  • 6
  • 44
  • 94
  • This might be not a solution but if you give `MaximumSize` property of the form, that will automatically restrict your form to go in the full screen mode. – Never Quit Jun 05 '13 at 05:30
  • I tried changing `MaximizedBounds` to the current size but that doesn't resolve the issue at all. First of all it still moves the Form and therefore is firing off this shortcut event which should never have been enabled in the first place. Additionally by playing around with `MaximumSize` it will interfere with custom resizing functions. – CodeCamper Jun 05 '13 at 05:31
  • Not sure whether it's a glitch, but you can use a workaround: http://stackoverflow.com/questions/1295999/event-when-a-window-gets-maximized-un-maximized – Fabian Tamp Jun 05 '13 at 05:44
  • I don't want to work around the fact that it has been maximized or make it look like it was not maximized I want to make it exactly the same as if `MaximizeBox` is false for any other `FormBorderStyle` I don't want the event to be fired at all because I want to implement a new interface for maximizing and minimizing such as a custom button. – CodeCamper Jun 05 '13 at 06:07
  • possible duplicate of [Preventing Winform from being maximized?](http://stackoverflow.com/questions/13381127/preventing-winform-from-being-maximized) – LarsTech Jun 05 '13 at 12:52
  • LarsTech that question is very similar and I found some of the answers pretty useful. However my goal is not to disable maximizing because I want to have my own custom way of resizing/maximizing/minimizing. My goal is to simply disable the Windows+Up function in a None FormBorderStyle similar to all the other FormBorderStyles. – CodeCamper Jun 05 '13 at 23:38

4 Answers4

7

Windows does this by calling SetWindowPos() to change the position and size of the window. A window can be notified about this by listening for the WM_WINDOWPOSCHANGING message and override the settings. Lots of things you can do, like still giving the operation a meaning by adjusting the size and position to your liking. You completely prevent it by turning on the NOSIZE and NOMOVE flags.

Paste this code into your form:

    private bool AllowWindowChange;

    private struct WINDOWPOS {
        public IntPtr hwnd, hwndInsertAfter;
        public int x, y, cx, cy;
        public int flags;
    }

    protected override void WndProc(ref Message m) {
        // Trap WM_WINDOWPOSCHANGING
        if (m.Msg == 0x46 && !AllowWindowChange) {
            var wpos = (WINDOWPOS)System.Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, typeof(WINDOWPOS));
            wpos.flags |= 0x03; // Turn on SWP_NOSIZE | SWP_NOMOVE
            System.Runtime.InteropServices.Marshal.StructureToPtr(wpos, m.LParam, false);
        }
        base.WndProc(ref m);
    }

When you want to change the window yourself, simply set the AllowWindowChange field temporarily to true.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Thank you for this answer. It is pretty neat and I like how you made it toggle-able. I will definitely make use of this for another time but for now I think I will stick with something like JeffRSon's answer which will allow me to disable the Windows+Up in a makeshift None FormBorderStyle since to me it is the least indirect solution. – CodeCamper Jun 09 '13 at 08:12
2

Overriding the ProcessCmdKey (protected method in Form) explicitly allow us to apply custom hook and can be used in your scenario. This essentially allow us to override built-in keystroke handling.

Note: Following example demonstrate the idea of how to handle different keystroke or combination of it. Now, you probably need to fine tune the following code to work inline with your scenario. Eg: Ideally changing the FormBorderStyle or Form Size when user press the LWin+Up arrow.

public partial class Form1 : Form
 {

  protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
    {

     if (keyData == (Keys.LWin | Keys.Up))//Left windows key + up arrow
       {

           FormBorderStyle = FormBorderStyle.FixedDialog;
           return true;
        }

    if (keyData == Keys.Escape) //Form will call its close method when we click Escape.
        Close();

        return base.ProcessCmdKey(ref msg, keyData);
   }
}

Updated on How to disable windows Key in your case Lwin or RWin

public partial class Form1 : Form
    {


        // Structure contain information about low-level keyboard input event
        [StructLayout(LayoutKind.Sequential)]
        private struct KBDLLHOOKSTRUCT
        {
            public Keys key;
            public int scanCode;
            public int flags;
            public int time;
            public IntPtr extra;
        }

        //System level functions to be used for hook and unhook keyboard input
        private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int id, LowLevelKeyboardProc callback, IntPtr hMod, uint dwThreadId);
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool UnhookWindowsHookEx(IntPtr hook);
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hook, int nCode, IntPtr wp, IntPtr lp);
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string name);
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern short GetAsyncKeyState(Keys key);


        //Declaring Global objects
        private IntPtr ptrHook;
        private LowLevelKeyboardProc objKeyboardProcess;

        public Form1()
        {
            ProcessModule objCurrentModule = Process.GetCurrentProcess().MainModule;
            objKeyboardProcess = new LowLevelKeyboardProc(captureKey);
            ptrHook = SetWindowsHookEx(13, objKeyboardProcess, GetModuleHandle(objCurrentModule.ModuleName), 0);


            InitializeComponent();
        }

        private IntPtr captureKey(int nCode, IntPtr wp, IntPtr lp)
        {
            if (nCode >= 0)
            {
                KBDLLHOOKSTRUCT objKeyInfo = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lp, typeof(KBDLLHOOKSTRUCT));

                if (objKeyInfo.key == Keys.RWin || objKeyInfo.key == Keys.LWin) // Disabling Windows keys
                {
                    return (IntPtr)1;
                }
            }
            return CallNextHookEx(ptrHook, nCode, wp, lp);
        }



        private void Form1_KeyPress(object sender, KeyPressEventArgs e)
        {
            MessageBox.Show(e.KeyChar.ToString());
        }


    }
S.N
  • 4,910
  • 5
  • 31
  • 51
  • `if (keyData == (Keys.LWin | Keys.Up))` is evaluating false when I press both keys and it still locks in full screen. – CodeCamper Jun 05 '13 at 05:44
  • Not sure why it is not working. But it working in my case. Could be something which i missed to mentioned. Ref:http://msdn.microsoft.com/en-GB/library/system.windows.forms.form.processcmdkey.aspx – S.N Jun 05 '13 at 05:50
  • Set your `FormBorderStyle` to None and use that same code you posted and press Windows+Up it won't register it. Tried putting a MessageBox in the code too and it won't register both keys at the same time but the escape key function works fine. – CodeCamper Jun 05 '13 at 05:58
  • Wouldn't that disable Win+Up for the entire system? You could add a check to see if the foreground app was yours and only consume the keystroke then. – Idle_Mind Jun 05 '13 at 13:49
  • @Nair You should remove the first code in your answer, keep only the second and add some code to register the Hook in a Activated event handler and unregister the hook in a Deactivate event handler. I tested it and it works very very OK. That's really what the OP wants. Give you +1 for the second code. – King King Jun 05 '13 at 16:31
  • +1 for the code, but it does way more than I asked for as it disabled entirely the Windows+Up key for all applications and I have no idea where to begin to do what King King suggested. You should remove the first part of your code about the LWin + Up because the ProcessCmdKey is not capable of detecting those keys simultaneously for some reason. – CodeCamper Jun 06 '13 at 04:25
  • Just confirmed that the Win+Up key is not passed through the ProcessCmdKey method. It can be demonstrated by putting MessageBox.Show in the ProcessCmdKey method. What method is this shortcut being passed through? None? – CodeCamper Jun 06 '13 at 04:36
2

Trap the WM_GETMINMAXINFO message which will allow you to specify the maximized size and location of your form. Technically your form will still change state to Maximized, but it will appear the same since we specify the maximized size/position to be the same as the normal state of the form:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    public struct POINTAPI
    {
        public Int32 X;
        public Int32 Y;
    }

    public struct MINMAXINFO
    {
        public POINTAPI ptReserved;
        public POINTAPI ptMaxSize;
        public POINTAPI ptMaxPosition;
        public POINTAPI ptMinTrackSize;
        public POINTAPI ptMaxTrackSize;
    }

    public const Int32 WM_GETMINMAXINFO = 0x24;

    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case WM_GETMINMAXINFO:
                MINMAXINFO mmi = (MINMAXINFO)System.Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, typeof(MINMAXINFO));
                mmi.ptMaxSize.X = this.Width;
                mmi.ptMaxSize.Y = this.Height;
                mmi.ptMaxPosition.X = this.Location.X;
                mmi.ptMaxPosition.Y = this.Location.Y;
                System.Runtime.InteropServices.Marshal.StructureToPtr(mmi, m.LParam, true);
                break;
        }
        base.WndProc(ref m);
    }

}
Idle_Mind
  • 38,363
  • 3
  • 29
  • 40
  • What I don't understand is setting the `MaximizeBox` to false for any other `FormBorderStyle` will prevent the Windows key from setting it to a Maximized State. Is there anyway I can prevent it all together from setting it to a Maximized State without messing up the Maximized State. Suppose I want a custom way of maximizing in my form. I just want the dreaded Windows+Up to stop messing with me. – CodeCamper Jun 05 '13 at 05:53
  • Good point and question...shall investigate further in the morning. – Idle_Mind Jun 05 '13 at 05:57
  • You could still allow a maximized state when you want to by changing the values passed to the MINMAXINFO structure. – Idle_Mind Jun 05 '13 at 13:50
  • The code seems to disable LWin + Up but it also disables user maximizing the form by code (such as WindowState=FormWindowState.Maximized). However I still want to give u +1. – King King Jun 05 '13 at 16:13
  • Yeah...you'd have to add additional code/variables to allow/track a maximized state and pass different values to the MINMAXINFO structure accordingly. – Idle_Mind Jun 05 '13 at 16:19
  • +1 for this code because it really does resolve the issue with the least amount of damage. I just wish I had a way to intercept the Win+Up command with the ProcessCmdKey or some other way like this. For example maybe I want Win+Up to make the form full screen across all monitors right now I have a separate button for that in my form but maybe I want that shortcut to do that as well. – CodeCamper Jun 06 '13 at 04:33
1

Check this solution - it removes Maximize/Minimize/Titlebar/Border by API calls.

public partial class Form1 : Form
{
    // import necessary API functions to get and set Windows styles for P/Invoke
    [DllImport("user32.dll")]
    internal extern static int SetWindowLong(IntPtr hwnd, int index, int value);

    [DllImport("user32.dll")]
    internal extern static int GetWindowLong(IntPtr hwnd, int index);

    // define constants like they are named in SDK in order to make source more readable
    const int GWL_STYLE = -16;
    const int GWL_EXSTYLE = -20;
    const int WS_MINIMIZEBOX = 0x00020000;
    const int WS_MAXIMIZEBOX = 0x00010000;
    const int WS_CAPTION = 0x00C00000;
    const int WS_THICKFRAME = 0x00040000;
    const int WS_EX_DLGMODALFRAME = 0x00000001;
    const int WS_EX_CLIENTEDGE = 0x00000200;
    const int WS_EX_STATICEDGE = 0x00020000;

    // this replaces MinimizeBox=false and MaximizeBox=false
    void HideMinimizeAndMaximizeButtons()
    {
        // read current style
        int style = GetWindowLong(Handle, GWL_STYLE);
        Debug.WriteLine("0x{0:X}", style);
        // update style - remove flags for MinimizeBox and MaximizeBox
        style = style & ~WS_MINIMIZEBOX & ~WS_MAXIMIZEBOX;
        Debug.WriteLine("0x{0:X}", style);
        SetWindowLong(Handle, GWL_STYLE, style);
    }

    // part of removing the whole border
    void HideTitleBar()
    {
        // read current style
        int style = GetWindowLong(Handle, GWL_STYLE);
        Debug.WriteLine("0x{0:X}", style);
        // update style - remove flag for caption
        style = style & ~WS_CAPTION;
        Debug.WriteLine("0x{0:X}", style);
        SetWindowLong(Handle, GWL_STYLE, style);
    }

    // hide the border
    void HideBorder()
    {
        // read current style
        int style = GetWindowLong(Handle, GWL_STYLE);
        Debug.WriteLine("0x{0:X}", style);
        // update style - remove flag for border (could use WS_SIZEBOX which is the very same flag (see MSDN)
        style = style & ~WS_THICKFRAME;
        Debug.WriteLine("0x{0:X}", style);
        SetWindowLong(Handle, GWL_STYLE, style);

        // read current extended style
        style = GetWindowLong(Handle, GWL_EXSTYLE);
        Debug.WriteLine("0x{0:X}", style);
        // update style by removing some additional border styles -
        // may not be necessary, when current border style is not something exotic,
        // i.e. as long as it "normal"
        style = style & ~WS_EX_DLGMODALFRAME & ~WS_EX_CLIENTEDGE & ~WS_EX_STATICEDGE;
        Debug.WriteLine("0x{0:X}", style);
        SetWindowLong(Handle, GWL_EXSTYLE, style);
    }

    public Form1()
    {
        InitializeComponent();

        // hide those unwanted properties - you can try to leave out one or another to see what it does
        HideMinimizeAndMaximizeButtons();
        HideTitleBar();
        HideBorder();
    }
}

This works as intended. Maximizing/minimizing by setting WindowState works as well.

One could analyze in sources what the framework does and what it does "wrong" (or not quite correct).

Edit: I added debug output of the style values. Please try this sequence of commands in Form1 constructor:

MaximizeBox = false;
FormBorderStyle = FormBorderStyle.Sizable;
HideMinimizeAndMaximizeButtons();
FormBorderStyle = FormBorderStyle.None;
MaximizeBox = true;
MaximizeBox = false;
HideMinimizeAndMaximizeButtons();
FormBorderStyle = FormBorderStyle.None;
HideMinimizeAndMaximizeButtons();

You'll see, that setting FormBorderStyle.None enables the WS_MAXIMIZEBOX style. This cannot be "corrected" by another MaximizeBox = false. It seems it's necessary to call API functions.

JeffRSon
  • 10,404
  • 4
  • 26
  • 51
  • With all other FormBorderStyles `MaximizeBox = false` **disables** the `Windows+Up`shortcut without altering any other properties that I know of. Also your solution still causes the entire screen to flicker when `Windows+Up` is pressed and besides what if I want to make `Windows+Up` do something else. All of these solutions are horrible work arounds to a simple problem. I want the `Windows+Up` to behave the *selfsame* way as when the `MaximizeBox` is disabled for any other `FormBorderStyle`. All these factors are important because I am developing highly customized resizing mechanisms. – CodeCamper Jun 08 '13 at 19:08
  • JeffRSon this answer causes the same side effect of disabling `this.WindowState = FormWindowState.Maximized;` as Idle_Mind's answer but causes an additional side effect of screen flicker. I really would like this question answered without a crazy work around. I wish there was a way of making the `MaximizeBox` property work as if it was another `FormBorderStyle` without changing anything else. – CodeCamper Jun 08 '13 at 19:20
  • Okay - I thought you simply don't want to be stuck in maxmimized mode when a rare situation of pressing that key combo occurs. Now I reworked the snippet - please check. – JeffRSon Jun 08 '13 at 20:42
  • Thank you JeffRSon all of the answers here everyone provided are great but yours actually accomplishes exactly what I want just not in a simple(for me) way(this was to be expected). However if you could add 3 things to your post I will immediately give you the +300 rep. – CodeCamper Jun 08 '13 at 21:49
  • 1) It appears `FormBorderStyle` = None is the same exact thing as the code you just provided placed on top of any other `FormBorderStyle` minus the horrible **glitch** of `MaximizeBox` not working. Is there any other differences between this *customized* `FormBorderStyle` and `FormBorderStyle` = None? 2) I asked for simple and to me I would never have been able to implement this with just my logical creativity. Can you please explain line by line with comments what each thing exactly does as if I had no idea about programming outside of the C# managed language. 3) Sources for the values. – CodeCamper Jun 08 '13 at 21:53
  • 1) Well, I'm not sure, I may have a look in reflector to see what FormBorderStyle.None actually does. Could do this tomorrow (too late today). However, this is the code (SetWindowLong a.s.o.) that you would probably use in native C++ where there's no FormBorderStyle property. – JeffRSon Jun 08 '13 at 22:03
  • 2) Well, it actually is simple. Although I wasn't sure if it succeeds, I tried to set those properties how I would do it in C++. You have to know, however, that you need to manipulate the Window style, which on the other hand is not magic if you search for, say, how to hide the maximize/minimize buttons. You'll find references to SetWindowLong along with examples. Then you search MSDN for SetWindowLong and read about windows styles. This is the function that sets properties of the underlying window. Styles are combined by bit OR or removed by negated flag combined with AND. – JeffRSon Jun 08 '13 at 22:08
  • It's a matter of finding all the flags. That's where the extended styles come in. It may not be necessary to remove those but it cannot hurt. 3) The values can be found in MSDN API documentation (see SetWindowLong for instance) or in Windows SDK file (winuser.h) In the past you had to look in MSDN for names of constants and look them up in SDK include files. Nowadays MSDN already contains those values which is obviously very helpful. – JeffRSon Jun 08 '13 at 22:11
  • JeffRSon do you think you could find with Reflector how the other FormBorderStyle's disable the Windows+Up with the MaximizeBox property? – CodeCamper Jun 09 '13 at 08:13
  • Well, there's apparently a bug in the framework. I haven't compared all flags (they operate with combined flag numbers like 268435456), but when I read styles (GetWindowLong) after setting FormBorderStyle to None, I see that the MaximizeBox flag (0x00010000) is set (even if Form.MaximizeBox is false). This is *not* the case with other styles! Now that means it is sufficient, after setting FormBorderStyle to None to merely call HideMinimizeAndMaximizeButtons() - you could even restrict to simply removing the WS_MAXIMIZEBOX flag only. – JeffRSon Jun 09 '13 at 10:02
  • Finally, by comparing flags I can confirm, that the *custom* border style None (:-)) sets exactly the same flags as the built-in one (apart from WS_MAXIMIZEBOX of course). – JeffRSon Jun 09 '13 at 10:08
  • I would love to see the program you used to list all the flags and compared them or the exact process so I could go through it just for curiosity and educational purposes although you pretty much made it self-explanatory. Actually the EXACT answer I am looking for is in this comment right here!!! `the MaximizeBox flag (0x00010000) is set (even if Form.MaximizeBox is false). This is not the case with other styles!` This directly directly directly answers my question thank you so much! Although you gave me enough info to make my own None FormBorderStyle on top of it. – CodeCamper Jun 09 '13 at 18:03
  • Well, the program I used is mostly the one above - somewhat interactively changing the sequence of setting FormBorderStyle, MaximizeBox, MinimizeBox and calling HideMinimizeAndMaximizeButtons and/or HideBorder to read the style and extended style. I let the debugger show the values in hex. Such one can easily see which flag is set (power of 2 for every position). The only change I would do is to put the change of the style in a seperate line in order to check whether the value changes by the assigment. Such, you get an additional line like `style = style & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX`. – JeffRSon Jun 09 '13 at 18:25
  • Anytime! I really wanted this answer and I gave you all my reputation for it because that is how frustrating it was to me to work around it by disabling the maximize feature haha. Can you add your last suggestion in a separate part of your post? – CodeCamper Jun 09 '13 at 18:38
  • Okay, added some statements about those style values. – JeffRSon Jun 09 '13 at 19:15