Update: See my new submission here...
If you do want the original zip then email me at Paul_Caton@hotmail.com
cSuperClass.cls is i believe the fastest, safest compiled in window subclasser around. Speed: The WndProc is executed entirely in run-time dynamically generated machine code. The class only calls back on messages that you choose. Safety: So far I've not been able to crash the IDE by pressing the end button or with the End statement. Flexible: The programmer can choose between filtered mode (fastest) and all messages mode. In filtered mode the user decides which windows messages they're interested in and can individually specify whether the message is to callback after default processing or before. Before mode additionally allows the programmer to specify whether or not default processing is to be performed subsequently.
No module: AFAIK this is the only subclasser ever to eschew the use of a module. So how do I get the address of the WndProc routine? Simple, the dynamically generated machine code lives in a byte array; you can get its address with the undocumented VarPtr function. The real magic in cSuperClass.cls is getting from the WndProc to the callback interface routine using ObjPtr against the owning Form/UserControl, see the assembler .asm model file included in the zip. Speaking of which... it may well be the case that my assembler is sub-optimal. Any experts out there willing to take a look? I thought I had a nifty/dirty stack trick working for a while but it didn't pan out. Should work with VB5 if VarPtr & ObjPtr were in that release? Sample project included. Regards.
Note: Due to the size or complexity of this submission, the author has submitted it as a .zip file to shorten your download time. Afterdownloading it, you will need a program like Winzip to decompress it.Virus note:All files are scanned once-a-day by Planet Source Code for viruses, but new viruses come
out every day, so no prevention program can catch 100% of them. For your own safety, please:
Re-scan downloaded files using your personal virus checker before using it.
NEVER, EVER run compiled files (.exe's, .ocx's, .dll's etc.)--only run source code.
If you don't have a virus scanner, you can get one at many places on the net
Terms of Agreement:
By using this code, you agree to the following terms...
You may use
this code in your own programs (and may compile it into a program and distribute it in compiled format for languages that allow it) freely and with no charge.
You MAY NOT redistribute this code (for example to a web site) without written permission from the original author. Failure to do so is a violation of copyright laws.
You may link to this code from another website, but ONLY if it is not wrapped in a frame.
You will abide by any additional copyright restrictions which the author may have placed in the code or code's description.
I'm more interested in widespread distribution than globes, though obviously votes will help in that regard. So feel free to post the zip to the four winds, claim it as your own, whatever... So long as the innovative techniques become more widely known and exploited then we all should benefit. (If this comment was disrespectful, please report it.)
Thanks :) - Not sure what you're asking, but i'll take a stab at... What are the two asm files doing in the zip? First I wrote the asm source of how, approx, the Filtered mode and All Message mode WndProc's would work; then I assembled and linked those sources into exe's. That done I loaded the exe's up into a hex editor so i could see the bytes for each asm instruction and then copied them into Const strings in hex notation. Look at Const WNDPROC_ALL and compare with the equivalent asm file, that one is the easiest to figure because it's short and linear, no jumps, just two Call's, the calls to WndProcPrev and iSubClass_After. So, what you see in that string is a hex representation of the op-code bytes that the CPU will execute, save for two 8 char sequences that are patched at run-time with the with the actual memory addresses. Hope this helps if not ask again. (If this comment was disrespectful, please report it.)
This is beyond anything that I would attempt, but you may be interested in some code on www.vbdotcom.com where the LoadLibray api is used to pick up the entry address of an api declared in VB. This can then be inserted into the asm binary after the point where the api parameters are pushed on the stack. This potentially can avoid the need for the WindProc.inc Masm32 library. I haven't worked all this out yet and apologise if this is all known by you. ***** (If this comment was disrespectful, please report it.)
Thanks Robert for the feedback and globes. My original thought was that I'd use LoadLibrary/GetProcAddress to get the memory address of the CallWindowProc API routine so as to call the previous WndProc but then in a Damascean flash of light I thought $!&% I've got it's address anyway, I might as well call it directly without the overhead Duh! I must say though that if anyone's interested in calling arbitary assembler routines in a byte buffer CallWindowProc is the way to go... It takes an address as the first parameter and allows you to pass four data arguments to your code. Whoo-hoo. If anyone's interested I can submit an example. (If this comment was disrespectful, please report it.)
Thanks Andrea, much appreciated. Tell you what folks... I'm seriously suprised, 11 x 5 globes in less than 24 hours! I would never have guessed that a submission this esoteric would attract that kind of support. If I'd known, I would've submitted at the start of a month. Still, should this make it up the table into prize land (unlikely at this late stage) I promise that i'll forward the prize to the contributor of the best feedback. (If this comment was disrespectful, please report it.)
Excellent, innovative, glad you uploaded this 'class'class here. A crash proof subclassing class should get more than 5/5. Keep up the good work and thanks for sharing. (If this comment was disrespectful, please report it.)
Gerco, until six weeks ago I hadn't touched assembler since the eighties on an Atari ST (Motorola 68000k) - At that time I took a quick look at the X86 nightmare architecture and promptly decided to learn C. It's a lot easier now with the flat memory model and the extended registers. There's plenty of free assemblers out there, give it a go, suprise yourself, I know I did.
Thanks go out to you and the other recent voters. Now up to #4, can't quite believe it, just 36 hours! (If this comment was disrespectful, please report it.)
Excellent Paul.... Thanks for uploading. It is very rare to find something done with VB+Asm on PSC or the web that is this unique (and hardcore). The two that come to mind are Robert Rayment's CallWindowProc stuff and CompileController by John Chamberlain (Was in VBPJ a few years back). Thanks again.... (If this comment was disrespectful, please report it.)
Thanks Michael, yes it's been up on Francesco's site for a few weeks. I just wanted to see if any issues popped up from it's exposure there before laying it on the masses that trawl thru here. It's still worth downloading from PSC though as i've cleaned up the patching slightly, nothing functional has changed though. (If this comment was disrespectful, please report it.)
Say gasha you've spoilt my 100% voting record. WHAAAAAAAA.... If you've got any complaints/criticisms/suggestions I'd much rather hear them than not. (If this comment was disrespectful, please report it.)
Very uncredible wonderful job how can u perform a so complicated task, in add u use MASM32 asm code GREAT ONE QUESTION IT IS POSIBLE TO MAKE ASMLIB with MASM32 to make Graphics routines for a Software3d engine with VB+MASM32 Or a lib for fast image processing with callBack I want VB PSC people to think about the innovation of that code Definitly GOOD JOB Polaris (If this comment was disrespectful, please report it.)
I developed an ActiveX Control that I posted on PSC that uses subclassing. One of the worse problems that I encountered while developing this control was that I always crashed Visual Basic if I used the End statement before unloading the control.
If your submission works the way you claimed, it will resolve my problem. I will try to implement it into my control. However, I am not sure if I am skilled enough to do so!! :)
I will give you my best vote, anyway! :)
(If this comment was disrespectful, please report it.)
Polaris: thanks. If I understand you correctly, sure it's possible but i wouldn't want to attempt it myself.
I thought I saw an entry near the top of the all time list that was a graphics dll where the author was offering both VB and C++ source code if that helps. (If this comment was disrespectful, please report it.)
Elias, I always avoid the end statement like the plague. There's almost always a better approach.
The code itself is a little out-there but the interface is straight-forward. Follow the code in frmMain, you don't need any kind of understanding about what's happening in cSuperClass.cls to be able to use it effectively.
Most times out of ten you'll probably be using after default processing mode.
If you want to let me know publicly or privately what windows messages it is that you're after I can probably point you in the right direction.
The subclasser has been up on vb2themax for a few weeks now and with 900+ downloads from here since saturday i'm getting reasonably comfortable re its resilience.
That's not to say it's impossible to crash. If someone did a CopyMemory for example across the op-code buffer or something mad in response to the wrong windows message then all bets are off!
Thanks for the vote -Paul (If this comment was disrespectful, please report it.)
Elias, a further thought... some people get a little intimidated by the Implements statement. The MSDN help isn't much either (help that is). But really there's nothing much to think about. As soon as you add the Implements iSuperClass statement you'll see an entry in the left combo at the top of your code window for iSuperClass. Select that and it'll build you the iSuperClass_After implementation interface, then select the before version in the right hand combo. You must implement both, though most times the *before* interface is empty, contains no code.
Why use it rather than an event? Implemented interfaces are somewhere between 6 and 10 times faster! As in effect they are pretty much (in concept) like a method call. Whereas an event... well that's something else entirely. BUT! same result. (If this comment was disrespectful, please report it.)
Reply: It'll cost you your vote, if you haven't already ;-)
What the implements statement means here is that you guarantee to implement ALL the methods in the iSuperClass.cls
Go to the drop-down combo box at the top left of your code Window Select iSuperClass. It will take you to the iSuperClass_After routine. Now in the right hand combo box select the before event It will create the routine. Don't worry if there's no code to go in there, but it must exist.
This'll teach me to leave my Email address in the source code. A lifetime of consumer support.
Good luck, next time ask me at PSC then others can benefit.
-Paul (If this comment was disrespectful, please report it.)
paul: do a general purpose class that implements iSuperClass, contains an instance of cSuperClass and exposes its methods but raises events instead. should be ~30 lines :-))
btw, do you know what superclassing usually refers to?
btw, do you know what term "thunk[ing]" is used for? :-))
final note, you know that "REP SCASW" is quit fast. i mean you are using code (couple of instruction) as data and vice versa. on AddMessage better fill a lookup table and "scas" it then use ECX as an index etc. etc.
</wqw> p.s. i like it -- a lot! (If this comment was disrespectful, please report it.)
Oh no, it's Vlad the impaler, now i'm in trouble...
Cool idea, re general pupose class, would certainly help with the Implements challenged. As an update do you think?
But what do I call it ? ;-)
Ahem, a SuperClass *usually* refers to an aggregate class that errrrr agregates other classes into a single entity... Ok, I'll state the obvious - Just as you suggested above.
Thunk - originally I think to refer to a translation layer between 16 and 32bit, but I think more generally as a seemless translation from somefink to anotherfink. Like wot i fink ms will do with the dll's we know and love. Make em thunks into .NET
btw. Why do you ask? I'm just an 'umble self taught code-slinger (tugs at forelock)
Continued below... (If this comment was disrespectful, please report it.)
When I said I picked up a copy of MASM32 six weeks ago (upstream) I wasn't joking. REP I recognise as part of a repeat instruction but my asm stuff is on another computer. Give me a day to look it up and i'll get back to you if i'm still in the dark.
Maybe I could discuss my dirty stack trick with you.
You may've noticed further up the page that stated that should I win a prize that I'd donate it to the best feedback as I was more interested in wide distribution than globes. Well you're clearly way ahead in that department. So you could get two!
You like it a lot but no cigar! - OK, me first. (If this comment was disrespectful, please report it.)
Yesterday, I replaced my previous subclassing codes with yours. All of them works fine! I also like the Implements method. When I used your code with a usercontrol, it seems that your codes give more redability and easier control.
Thanks! Great Code! (If this comment was disrespectful, please report it.)
"Maybe I could discuss my dirty stack trick with you." -- i like it too, at least saves instructions. the problem with it is that *usually* in the before method i would prefer the arguments to be ByRef as to be able to modify current message and w/lparams (!) but this can easily be achieved by consuming the message a subsequent call to CallWindowProc(orig_wnd_procm, etc)
well, people are obviously afraid of "Implementing" anything, that's why i thought of late-bounding but alas, the slicky stack trick would not work. so we need a kind of proxy, kind of the events proxy i spoke before.
my experience: yes it seems immune to "End" in the IDE but breakpointing in the before/after [implemented] methods still crashes poor VB :-)) maybe you should put a reentrancy check in the asm and spare the debugger another function entry while in break mode, instead procede normal processing
just my $.02
</wqw> (If this comment was disrespectful, please report it.)
S.Y. Glad to hear... probably because it is faster than a regular subclasser. Plus Implements can be up to 10 times quicker than an event.
And finally, IIRC new mouse messages, for example, will eat older mouse messages that haven't yet been pulled out of the message queue and dispatched to the WndProc - For what is the point in an application being told that mouse at x,y when the data is already out of date. IE the faster they are processed the more you'll receive. This is why I chose to focus on mouse messages in the example.
If anyone knows better i'm sure we'll be informed. (If this comment was disrespectful, please report it.)
Vlad, took a look at REP SCASW, i'd need 2 tables (before/after), or possibly 1 if you analyze the users requests and construct accordingly. Might be worth it if the user was adding a significant number of msgs, but that wont be the majority situation. Also considered a 228byte 2bit/entry lookup covering msgs up to WM_PENLAST or a straight byte/entry lookup - with fallback processing for msgs outside the range. So many options, so little time. Wrote asm for re-entry, but first I need to be sure that the VB runtime WndProc isn't calling DispatchMessage for its own devious purposes, otherwise msgs could be masked from the user in after mode. Perhaps you could tell me if this is a silly^ notion? Wrote the cSuperClass event proxy, will update this weekend.
^At this point i used the word st*pid, input rejected, inapropriate language, had to re-type from scratch. BAH! (If this comment was disrespectful, please report it.)
Mark, thanks, that request would be pretty easy to implement. Either a little jump to the next test or just overwrite with nop's (no operation) but if you don't mind i'll leave it till version 2 which I hope to get out within the next month or so (very, very, busy) I want to leave it til then because the job will be even easier at that time as I'll be deploying the SCASW op to scan a table rather than the series of 'if then's' that I'm using at the moment.
I guess my thought at the time for leaving that functionality out was that users would just ignore the message in the implemented interfaces. User wants, user gets (if it's reasonable)
regards -Paul (If this comment was disrespectful, please report it.)
Wow, i don't understand any assembler but your code motivated me to search for the old asm-books in my fathers house :-) Excellent code and i have to be afraid that you could be placed higher in the all time hall of fame than me *gg* (place 8 remotecontrolcode). Anyways... i HAVE to vote with ***** and you got my HIGH respect ... greetings from germany -Daniel- (If this comment was disrespectful, please report it.)
Hi, I just wanted to inform you that I used your great SuperClass for handeling a winsock connection (client and server) I published it here: http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=39939&lngWI d=1
(If this comment was disrespectful, please report it.)
11/21/2002 12:34:50 AM:
i love this thing, i'd like to know when you plan on releasing version 2? =D cant wait! (If this comment was disrespectful, please report it.)
Hey, was digging around for a subclasser and I remembered this one again. I have a problem this time though. I tried it on a Win 98 machine and I got an overflow error in the PatchOffset of cSuperClass:
Advanced? No way, I think you have redefined eliteVB. Rock'n'Roll!
(If this comment was disrespectful, please report it.)
3/31/2005 8:19:44 AM:
Crashes when the following code is used
Private Sub iSubclass_Proc(ByVal bBefore As Boolean, ByRef bHandled As Boolean, ByRef lReturn As Long, ByRef hWnd As Long, ByRef uMsg As WinSubHook2.eMsg, ByRef wParam As Long, ByRef lParam As Long) If bBefore Then lReturn = sc.CallOrigWndProc(uMsg, wParam, lParam) bHandled = True End If End Sub
..and you unload the subclassed form from a menu
I think the asm might need to (cSubclass.Class_Terminate is getting called inside the iSubclass_Proc) (If this comment was disrespectful, please report it.)
6/5/2005 9:11:20 PM:
I viewed your code and incorporated it into a user control on my form - MCI with events - in a matter of a half hour or so. I really appreciate your hard work - 5 globes from Uncle Dan (If this comment was disrespectful, please report it.)
Very nice implimentation. I had to redo the subclassing for my scintilla wrapper due to some issues with speed and yours seems to have worked beautifully. I really appreciate this great work. I know it's been out for a long time but 5 from me :) (If this comment was disrespectful, please report it.)
Hello there, i've downloaded ur subclassing code. it works well when its in IDE but the compiled version generates a Memory cannot be written error. The line which causes this is at cSuperClass::SubClass()
Call SetWindowLong(hWnd, GWL_WNDPROC, pCode)
Can you think of a reason why this happens? Im just starting with subclassing and dont know much yet, but i find ur work very good! Thanks so much! (If this comment was disrespectful, please report it.)
Add Your Feedback
Your feedback will be posted below and an email sent to
the author. Please remember that the author was kind enough to
share this with you, so any criticisms must be stated politely, or they
will be deleted. (For feedback not related to this particular code, please
click here instead.)