|
This is code that goes into, well, probably every project I ever write. [EDIT: Scratch that. Found a new way that I'd highly recommend. This one's still interesting, for a number of reasons, so I'll leave it here. But for any new projects, or even if you're revisiting an old one, take a look at my HookXP sample.] I was once kidded by the author of MsgHook that I virtually made a living explaining to people how to use that tool. Yeah, I wrote a lot of columns over the years, some listed below, that involved subclassing. But since VB5 shipped, production code omitted the OCXs, and stuck to pure inline message hooking.
Originally, I used a single BAS file for all subclassing, but found that solution to be wanting. In particular, it meant I had to modify the code module for each application. And, it prevented sinking the messages in more than a single object type. With my latest approach, I think you'll agree, we've come to be about as flexible as possible.
HookMe now includes an IHookSink interface that allows you to redirect window messages back into any object you desire. Your forms can sink their messages within themselves, or delegate message handling to companion or even global classes. Here's the entire IHookSink interface:
Public Function WindowProc( _ hWnd As Long, msg As Long, wp As Long, lp As Long) As Long ' Stub to be used with MHookMe.bas End FunctionIn your class, control, or form module, you simply Implement IHookSink, then provide code for the WindowProc method. As a very simple example, to watch the message stream for every textbox on a form, and "eat" any WM_PASTE messages that happen to not meet some arbitrary criteria (say, evaluate to positive values), you could use code like this:
Option Explicit Private Const WM_PASTE As Long = &H302 Implements IHookSink Private Sub Form_Load() Dim i As Long For i = Text1.LBound To Text1.UBound Call HookWindow(Text1(i).hWnd, Me) Next i End Sub Private Sub Form_Unload(Cancel As Integer) Dim i As Long For i = Text1.LBound To Text1.UBound Call UnhookWindow(Text1(i).hWnd) Next i End Sub Private Function IHookSink_WindowProc(hWnd As Long, msg As Long, wp As Long, lp As Long) As Long Dim buffer As String Dim nVal As Long Dim nRet As Long Select Case msg Case WM_PASTE ' Validate clipboard contents. If Clipboard.GetFormat(vbCFText) Then buffer = Clipboard.GetText On Error Resume Next nVal = CLng(buffer) On Error GoTo 0 ' Only allow >0 values to be pasted. If nVal > 0 Then nRet = InvokeWindowProc(hWnd, msg, wp, lp) Else nRet = 0 ' eat it! End If End If Case Else nRet = InvokeWindowProc(hWnd, msg, wp, lp) End Select ' Return results. IHookSink_WindowProc = nRet End FunctionNote the second parameter to the HookWindow procedure. Here, we used Me, to route window messages back into the current form. But that parameter could be any object that implements the IHookSink interface.
But Is It Safe?
It is, if you save. Seriously, running message hooks within the IDE is just downright inherently problematic if you enter Break Mode. Some people use special purpose DLLs to protect them from this situation. I prefer to just be careful. Often, I test the compiled state of an application before calling HookWindow, especially after I have fully debugged the WindowProc code. I'd advise the same precaution.
This sample, or the one from which it originally derived, was published (or at least peripherally mentioned) in the following article(s):
- VB
Can'tCan Do That!, Ask the VB Pro, VBPJ, October 1998- Prevent CD AutoRun, Q&A, VSM, May 2002
- Keep a Window Visible, Q&A, VSM, September 2002
This sample uses the following API calls:
Module Library Function CSizeGrip.cls kernel32
user32RtlMoveMemory
DrawFrameControl
GetClientRect
GetDC
GetSystemMetrics
GetWindowRect
InvalidateRect
IsWindow
PostMessage
PtInRect
ReleaseDCMHookMe.bas kernel32
user32RtlMoveMemory
CallWindowProc
GetProp
GetWindowLong
SetProp
SetWindowLongMWindowStyles.bas user32 GetWindowLong
SetWindowLong
SetWindowPosDon't see what you're looking for? Here's a complete API cross-reference.
Please, enjoy and learn from this sample. Include its code within your own projects, if you wish. But, in order to insure only the most recent code is available to all, I ask that you don't share the sample by any form of mass distribution. Download HookMe.zip, 15Kb, Last Updated: Thursday, May 12, 2005
The following resources may also be of interest:
- AniCtrl - Encapsulates an animation control within a drop-in class module.
- ClipView - Injects application into clipboard viewer chain.
- FormPair - Force a toolwindow to follow a main form as its moved around the screen.
- FullDrop - Sizes the dropdown portion of a combobox to fit number of elements.
- Grabber - Various methods to add a grabber (or "shangle") to your forms, including a new one that supports XP themes..
- HookXP - Demonstrates native Windows subclassing method with no risk of improper teardown. Very flexible!
- PrnInfo - Hook into the Windows Print Queue to find and control print jobs for any printer.
- SnapDialog - Snap dialogs to the edges of screen. Supports multiple monitors and taskbar avoidance. Uses native subclassing.
- Translucent - Create layered windows, to make forms translucent or irregularly shaped, using drop-in class.