|3||Code Libraries|| |
Set the Show In Taskbar property at Run Time
A technique for control freaks - making things happen like you want them to
this code has been superceded by the version at the new site.
VB provides a ShowInTask bar property for forms which allows you to choose whether a form is shown in the Alt-Tab sequence and the shell's task bar. However there are two limitations to this:
The technique demonstrated in this article sticks two fingers up at any attempt by VB to prevent you from appearing in the task bar. You get to set any flags you want well before VB has had the chance to create your window and therefore tell Windows of your true intentions before VB's form engine stuffs it up for you. Cool!
ShowInTaskBar from a Windows Perspective
The problem with affecting built-in behaviours in Windows is that they are not well documented and sometimes troubling to use. The logic to put a window into the task bar can be dug out from MSDN although you have to search the Knowledge Base to find the answer:
You can Take Control
VB's interface into the window creation procedure is somewhat lacking. You get a Form_Initialise event when the first instance of your form object is created, followed by a Form_Load event when the form is about to be shown on screen. Form_Initialise is of no use here because it is in effect an event fired when the object is constructed, so although the object has just come into life, and hence you can use local variables, the associated UI elements have not yet been created. Therefore you may not refer to any windows or controls at this stage. Form_Load is no use either because this fires at the moment the window has been created and is about to be shown. It would have been nice to get some more intermediate events, but I suppose this is the price to pay for having an easy to use interface.
So, given that there is no VB hook into the point when the window is created, how else might you detect this event?
The Windows Hook to the rescue
When all other things have failed, a Windows Hook is often a good thing to consider. Windows Hooks allow you to get into the message stream for your app before the messages get sent to it - and allows you to change or otherwise subvert the standard processing.
By installing a hook of type WH_CALLWNDPROC during an _Initialise event, your app can be notified of any attempts by Windows to call a Window Procedure. This allows us to capture the crucial WM_CREATE notification which is sent when the window is first created.
In Code - The Theory
The first thing to do is to install a windows hook before any windows get created and start watching for WM_CREATE calls. The _Initialise event of Forms, UserControls and so on always fires before any windows have been created, whilst all other events fire after the windows have been created, so this is a good place as any to install the hook for window creation checking (note that the _Initialise event only fires once for a form object, so if you want this to work correctly you will always need a new instance of a form). Here is how to initiate the hook:
Public Sub HookAttach()
m_hHook = SetWindowsHookEx(WH_CALLWNDPROC, AddressOf AppHook, App.hInstance, App.ThreadID)
Debug.Assert m_hHook <> 0
Now whenever any window in your application has its Window Procedure called, the hook will fire your AppHook procedure. You can then check whether the call is a WM_CREATE message and also you can isolate the particular window being created:
Private Function AppHook(ByVal idHook As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Dim CWP As CWPSTRUCT
Dim k As Long, aClass As String
If idHook >= 0 Then
CopyMemory CWP, ByVal lParam, Len(CWP)
Select Case CWP.message
aClass = Space$(128)
k = GetClassName(CWP.hwnd, ByVal aClass, 128)
aClass = Left$(aClass, k)
If IsIn(aClass, C_MDIFORMCLASS_IDE, C_MDIFORMCLASS_EXE, C_MDIFORMCLASS5_IDE, _
C_MDIFORMCLASS5_EXE, C_FORMCLASS_IDE_DC, C_FORMCLASS_EXE_DC, C_FORMCLASS_IDE, _
C_FORMCLASS_EXE, C_FORMCLASS5_IDE, C_FORMCLASS5_EXE) Then
m_lHookWndProc = SetWindowLong(CWP.hwnd, GWL_WNDPROC, AddressOf Form_WndProc)
AppHook = CallNextHookEx(m_hHook, idHook, wParam, ByVal lParam)
The constants listed above specify all the various class names that VB gives to a form for VB5 and VB6 - and there's a lot of them! (Thanks to Geoff Glaze for his assistance with this):
Private Const C_MDIFORMCLASS_IDE = "ThunderMDIForm"
Private Const C_MDIFORMCLASS_EXE = "ThunderRT6MDIForm"
Private Const C_MDIFORMCLASS5_IDE = "ThunderMDIForm"
Private Const C_MDIFORMCLASS5_EXE = "ThunderRT5MDIForm"
Private Const C_FORMCLASS_IDE_DC = "ThunderFormDC"
Private Const C_FORMCLASS_EXE_DC = "ThunderRT6FormDC"
Private Const C_FORMCLASS_IDE = "ThunderForm"
Private Const C_FORMCLASS_EXE = "ThunderRT6Form"
Private Const C_FORMCLASS5_IDE = "ThunderForm"
Private Const C_FORMCLASS5_EXE = "ThunderRT5Form"
You will see that in the above function, I set a new Window Procedure to subclass the window in response to the WM_CREATE message. Why?
Well, there is a problem with calling code in response to WH_CALLWNDPROC events. If you do anything during a WH_CALLWNDPROC hook notification which would call the window procedure of that window again, then VB crashes. This rules out a lot - for example, you can't change the style of the window, and so forth. To get around this problem, you can use the hook notification to install a temporary subclass on the window to capture exactly the same message again - and then you can make as many modifications as you wish.
So finally I respond to the WM_CREATE message in a subclass procedure. As you will see, I just change the Extended Style of the form using SetWindowLong and by modifying the Window's creation data, plus I know I no longer need the subclass so I can reinstall the original version:
Private Function Form_WndProc(ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Dim lSetStyleEX As Long
' SPM - specific wnd proc for a form. Only called once for the WM_CREATE message.
Select Case Msg
Dim tCS As CREATESTRUCT
CopyMemory tCS, ByVal lParam, Len(tCS)
lSetStyleEX = GetWindowLong(hwnd, GWL_EXSTYLE)
lSetStyleEX = lSetStyleEX Or WS_EX_APPWINDOW
lSetStyleEX = lSetStyleEX And (Not WS_EX_TOOLWINDOW)
tCS.ExStyle = lSetStyleEX
CopyMemory ByVal lParam, tCS, Len(tCS)
SetWindowLong hwnd, GWL_WNDPROC, m_lHookWndProc
SetWindowLong hwnd, GWL_EXSTYLE, tCS.ExStyle
Form_WndProc = CallWindowProc(m_lHookWndProc, hwnd, Msg, wParam, lParam)
In Code - In Practice
In the download sample the code is all wrapped up into a single module, modStyles. All you have to do to make this work is call HookAttach from the Form_Initialise event and then call HookDetach sometime later (I use the Form_Load event because I know this fires after the window has been created).
There are a whole load of other possible uses of this code. Matt Hart has demonstrated how you can use this technique to modify the style of a VB control as it is created, for example, to create an Owner Draw Combo Box and an Owner Draw Tab. Have fun!
Back to top
Back to Source Code Overview