Using CBT Hooks to Centre API Dialogs
Ensure Common Dialogs and Message Boxes appear where you want them to.
Windows sometimes places Common Dialogs and Message Boxes at the "wrong" position on the screen. Whilst there are some ways of working around this for Common Dialogs, a more general solution which applies to any Windows dialog is to use a CBT (Computer-Based Training) Windows Hook. These Hooks provide notification when any Window in your process is created, activated, focused, moved or destroyed.
Using a WH_CBT Hook
WH_CBT hooks are intended primarily for Computer-Based Training applications, but provide a significant amount of power in intercepting and modifying the way Windows works. Once you install a WH_CBT hook, you will start receiving these hook notifications for every window within the process you've installed the hook in:
As you can see, you can use the hook to detect any window that is created or destroyed as well as to substantively alter how the user interface works. In the case of centring dialogs, however, we only need to detect when the dialog is created.
Note there are also three other HCBT_ codes associated with CBT applications which aren't covered here; these are only used in conjunction with a WH_JOURNALPLAYBACK hook.
Centring Common Dialogs and Message Boxes
To centre a Common Dialog or Message Box, you need to be able to do two things:
In this sample the vbAccelerator Windows Hook library is used to detect the dialog handle as it is created, and the Subclassing and Timer Assistant to subclass the dialog and intercept the WM_INITDIALOG message.
1. Using the WH_CBT hook
The previous section described the many notifications supplied by this hook. In this case detection of the dialog being created is needed; however, the hook will also provide all sorts of other notifications, including those for every child control within the dialog and activation/sizing of any controls on the dialog or your application. Therefore you want to filter the messages you receive to get the correct one. Also, for best performance the hook should be installed for as short a time as possible.
This is achieved in the demonstration by:
The code looks like this:
Private Function IWindowsHook_HookProc( _ ByVal eType As vbalWinHook.EHTHookTypeConstants, _ ByVal nCode As Long, _ ByVal wParam As Long, _ ByVal lParam As Long, _ bConsume As Boolean) As Long If eType = WH_CBT Then If nCode = HCBT_CREATEWND Then InstallWinProc wParam End If End If End Function Private Function InstallWinProc(ByVal hwnd As Long) Dim lProcOld As Long Dim sClass As String Dim iPos As Long If IsValidLocalWindow(hwnd) Then If (InStr(ClassName(hwnd), "#32770")) Then AttachMessage Me, hwnd, WM_INITDIALOG End If End If End Function Private Function IsValidLocalWindow(ByVal hwnd As Long) As Boolean If IsWindow(hwnd) Then Dim idWnd As Long Call GetWindowThreadProcessId(hwnd, idWnd) IsValidLocalWindow = (idWnd = GetCurrentProcessId()) End If End Function Private Property Get ClassName(ByVal hwnd As Long) As String Dim sBuf As String Dim iPos As Long sBuf = String$(255, 0) GetClassName hwnd, sBuf, 255 iPos = InStr(sBuf, Chr$(0)) If iPos > 1 Then ClassName = Left$(sBuf, iPos - 1) End If End Property
Responding to the WM_INITDIALOG message
Once the window has been found and the subclass installed, its just a matter of waiting for it to finish initialising and then send the WM_INITDIALOG message. When this message is sent, the dialog has been completely set up and sized, so the code can determine the dialog size, centre it, and then remove the subclass and hook. The code in the demonstration will check for multiple monitors on your system whilst centring and attempt to place the dialog at the best position. In more recent versions of Windows File and Folder selection dialogs are resizable which means you can also specify the ideal width and height you'd like for the dialog.
Using a WH_CBT hook, you can easily intercept the creation and destruction of any Window and obtain a handle to it even if that Window is not being created directly from your code (which of course is usually the case for VB applications.) Hooks of this type are particularly useful for modifying the behaviour of Windows which possibly weren't intended to be modified, particularly the Windows API dialogs.