|
Find and Fixing a Bug in SSUBTMR

"The introduction of the AddressOf operator to VB5 allows you to subclass, although it
is not nearly as simple as it ought to be. The SSUBTMR.DLL component is a stable and consistent
way of working around difficulties in subclassing."
That's how SSUBTMR.DLL is introduced on the site. It holds true now
as it did then, except in the first release there was a bug in it just waiting to happen, and
I was the lucky person to make it happen with the new vbAccelerator Toolbar and Rebar controls
(Or maybe you were? I can't apologise enough. Send your
rant!).
I can't start without thanking Jim Parzych for pointing
out the problem. Also, I should note that although this component was based on SUBTMR.DLL
designed by Bruce McKinney, author of Hardcore Visual Basic
the bug is all my own fault (although coincidentally I found and fixed a bug in the original. What goes around...)
The problem
When multiple controls subclassed the same message on the same window, the SSUBTMR code
didn't check if it had already called the default window procedure for the
message.
As a Consequence...
Consider the case of the vbAccelerator Rebar/Toolbar control in a form, where there
is one Rebar control and one Toolbar. Both the toolbar and rebar control must subclass
the WM_NOTIFY message, and this message is sent to the parent of the control.
The rebar has to capture WM_NOTIFY to detemine if its height has changed, and the toolbar
captures it to determine whether buttons or dropdowns have been clicked. Everything
works fine so far because the Rebar and Toolbar are both implemented within the same
OCX, and therefore share the same version of the subclasser. But it starts to
go pear-shaped when you add another external common control to the form with the Rebar and Toolbar
on.
All the common controls use WM_NOTIFY messaging to the parent form. So when
you do anything to a common control which raises an event (for example, clicking on
a item in a ListView) a WM_NOTIFY is sent. Both the toolbar and
rebar intercept this message.
So consider the case when a form contains a rebar, a toolbar and a COMCTL32.OCX ListView. Note
that the
rebar/toolbar requires that the toolbar's subclass is set up first and the rebar second.
This is what happens when you cause an event in the ListView:
- Clicking a ListView causes the ListView to raise a WM_NOTIFY message. This is sent
to the Parent of the ListView, which is the form the ListView is located on (note it is
irrelevant whether the Container of the ListView is another control, because the Parent
is always going to evaluate to the form).
- The subclasser intercepts the message because the toolbar and rebar also need to
check WM_NOTIFY messages sent to the parent form. It (correctly) identifies that
the message wants to be sent firstly to the toolbar and then to the rebar.
- The subclasser calls the default window procedure (correct) and then
passes the message to the toolbar (correct). Note that the toolbar ignores this
message because it determines from the NMHDR structure associated with all WM_NOTIFY
messages that the message is not associated with its own hWnd. This is also correct.
- Then the subclasser calls the default window procedure again
(WRONG,WRONG, WRONG!!!!!) before passing the message to the rebar correctly.
Again, the rebar ignores the message as the message is sent from a different hWnd to the rebar.
So if you do anything to a Common Control (ListView, TreeView, Progress Bar, UpDown, Slider,
Tab, Pager, Animation...) the event will apparently occur twice. This gets
even worse if you add a second toolbar to the rebar - the events occur three
times... As you can have an unlimited number of toolbars the problem clearly can't be worked
around in any sensible way in code because the number of events you get is the number of rebars
(always 1) plus the number of toolbars (0 to n where n is only really restricted by screen space.
Word 8 is very nice, but are we sure we need 14+ toolbars?).
The Solution
The solution is obvious, although of course solutions always are once you know
what the problem is. I now cannot believe it was not implemented from the
start and feel someone should take me to a dark room and
proceed to beat me vigorously with a printed copy of the MSDN documentation
on subclassing. But that is enough already about my personal problems and predilictions.
It is implemented by a setting a boolean flag in the subclass procedure to
determine whether the default window procedure has been called already and then
NOT CALLING IT AGAIN...
So Why Hasn't This Been a Problem Yet?
The thing is, SSUBTMR.DLL is a production component and I have distributed it in many live
applications (I don't just distribute any old thing on this site, honest, and if you'd seen the
code in the components alluded to in some of the 'coming soon'
notes of the Source Code sections you would know exactly why!). There haven't been
any problems in over a year of operation (that's not entirely true, but anything that has
gone wrong hasn't been caused by the DLLs or controls...)
Anyway, the reason is actually that whilst SSUBTMR allowed multiple messages on the same
window to be subclassed, whenever this occurred in a real life application of my
controls, SSUBTMR.DLL always ran in the same process space as the DLL doing the subclassing.
And because in-process DLLs and OCXs share the same module, the problem never arose. And of
course, when I designed the Rebar/Toolbar control, my idea was to do away with COMCTL32.OCX
forever if you could do without it - so I never tested it with a ListView. How stupid am I?
Get the Fix
SSUBTMR is a binary compatible component so the new version will run without problems
with any controls you've downloaded before. The new version is available for download
at Subclassing without the crashes.
Back to top
Back to Source Code Overview
This document was put together whilst listening to the following CDs:
- Aluminium Tunes: Switched on Volume 3 by Sterelab (Duophonic LP, 1998)
Stereolab's third collection of rare/previously unreleased songs.
- Kill At Will by Ice Cube. (4th and Broadway EP, 1990)
I can't find it in myself to dislike this record.
- Surfer Rosa by The Pixies (4AD LP, 1984)
A band at the height of its powers.
- On by Aphex Twin (Warp 12, 1993)
I finally found this amongst a set of PC magazine cover CDs. How sad is that? The drum beats on this record are created from distorted sine-waves and hitting metal objects, thus fulfiling my number one criteria for creating good music.
- Space Invaders are Smoking Grass by I-F (Interdimensional Transmissions 12, 1997)
Complete and utter rubbish. I like it a lot.
|
  |