Resolved Prevent a form from being minimized

JuggaloBrotha

VB.NET Forum Moderator
Staff member
Joined
Jun 3, 2004
Messages
4,530
Location
Lansing, MI; USA
Programming Experience
10+
I've got this little 'Sticky Notes' type of application and the form I have is a borderless, taskbarless form and for Windows 2000 compatability I'm using VS 2008 and targeting the 2.0 framework. One of the hurdles I have is that in XP, Vista & 7 when you click the Show Desktop button all of my note's windows get minimized and without a taskbar icon, it's not easy to get them back (at least not for a normal user) & I'm looking for a way to either right after the windows minimize I just have them all show again or if I can skip the minimizing message in the wndproc altogether that'd be great.

I've put together a test app that'll show the messages in a listbox for the form, but I'm not sure how to go about skipping sending the message to the form's base class. Here's a snippet, listMessages is the listbox on the form:
VB.NET:
Private Const WmSize As Integer = 5
Private Const SizeRestored As Integer = 0
Private Const SizeMinimized As Integer = 1
Private Const SizeMaximized As Integer = 2
Private Const SizeShow As Integer = 3
Private Const SizeHide As Integer = 4

Protected Overrides Sub WndProc(ByRef m As Message)
    MyBase.WndProc(m)
    Try
	If m.Msg = WmSize Then
	    Dim wparam As Integer = m.WParam.ToInt32()
	    Select Case wparam
		Case SizeMinimized, SizeHide, SizeMaximized, SizeShow, SizeRestored
		    listMessages.Items.Add(String.Format("{0:X} {1:X} {2:X} {3:X} {4:X}", m.Msg, m.WParam.ToInt32(), m.LParam.ToInt32(), m.HWnd.ToInt32(), m.Result.ToInt32()))
	    End Select
	End If
    Catch generatedExceptionName As Exception
	listMessages.Items.Add("Err: " & generatedExceptionName.Message)
    End Try
End Sub
 
You can shadow the WindowState property and prevent the Minimized state being set.
 
Well I've learned that Windows doesn't really minimize the windows but it moves everything to the end of the z-order so the desktop is on top. I can use frm.BringToFront() to show them but 2 things with that: (1) I can't get it to automatically do that, the Borderless form doesn't receive that message that I can tell like a normal bordered form does and (2) the BringToFront gets them to show, but they don't stay shown until I click on another program in the taskbar to bring it up to the screen.
 
I was testing with the 'minimize all' (win-m), and the above suggestion prevents that form being minimized.

However, the 'show desktop' force desktop to front and in effect hiding other windows without minimizing them. What you can do about that is to set TopMost to True. Topmost window stay in front even when you click 'show desktop'.
 
I've thought about just doing the TopMost thing, but if you think about it, I doubt people would use an app where all of your little sticky notes are always on top of everything, it'd get pretty annoying pretty quickly, so I would like to allow other apps to be able to be on top of my note's windows, I just need to always keep my notes windows on top of the desktop's window is all.
 
Another option for staying on top of 'show desktop' is making the window a child of the desktop (progman), like a "pin to desktop" functionality:
SetParent(Me.Handle, FindWindow("progman", vbNullString))

To unpin set parent to GetDesktopWindow.
 
This seems to work, thanks John!
VB.NET:
    <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
    Private Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As Integer
    End Function

    <DllImport("user32.dll")> _
    Public Shared Function SetParent(ByVal child As Integer, ByVal parent As Integer) As Integer
    End Function

    Public Sub New()
        MyBase.New()
        SetParent(Me.Handle.ToInt32, FindWindow("progman", Microsoft.VisualBasic.vbNullString))
    End Sub
 
Window handles is best kept as IntPtr, then the code will work on a 64bit system also (when compiled for AnyCpu).
 
Well I tried IntPtr at first and it gave a compile error, so I changed it to Integer and it works fine on Win7 Pro x64 (I havent tested it on x86 XP Pro yet, but will shortly)
 
Well I tried IntPtr at first and it gave a compile error
You have to use IntPtr in all declarations that represent handles also, for parameters and return types that is.
and it works fine on Win7 Pro x64
Integer will work on 64bit also when the handles numeric value will fit the integer range, but memory address range on 64bit systems is Long data type, and once a handle actually uses that extended address space you'll get an overflow error when conversion to Integer is attempted.
IntPtr said:
A platform-specific type that is used to represent a pointer or a handle.

The IntPtr type is designed to be an integer whose size is platform-specific. That is, an instance of this type is expected to be 32-bits on 32-bit hardware and operating systems, and 64-bits on 64-bit hardware and operating systems.
 
So it should be:
VB.NET:
    <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
    Private Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
    End Function

    <DllImport("user32.dll")> _
    Public Shared Function SetParent(ByVal child As Integer, ByVal parent As IntPtr) As Integer
    End Function
?
 
Function SetParent(ByVal child As Integer, ByVal parent As IntPtr) As Integer
For example SetParent function is documented here: SetParent function (Windows) You can see from the declaration which types are declared as window handles (HWND), parameters and return value is also explained more in depth. So first parameter for SetParent is the handle to the child window, and return value is the window handle for previous parent (if success), in VB those should be IntPtr as well. This will also mean you don't convert ToInt32 the forms Handle, which is already type IntPtr.
You can also find many and often correct declarations at pinvoke.net: the interop wiki!
 
Ok, I got it, it actually makes sense now:
VB.NET:
    <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
    Private Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
    End Function

    <DllImport("user32.dll")> _
    Public Shared Function SetParent(ByVal child As IntPtr, ByVal parent As IntPtr) As Integer
    End Function

I don't get the chance to get into the API calls very often.
 
By the way, I found that GetShellWindow also returns the progman window handle.
 
You're right it does.
VB.NET:
    <DllImport("user32.dll")> _
    Public Shared Function GetShellWindow() As IntPtr
    End Function
 
Back
Top