Important alert: (current site time 7/16/2013 2:53:05 AM EDT)
 

winzip icon

Self-subclassing Controls/Forms - NO dependencies. Updated 09/10

Email
Submitted on: 6/1/2004 4:58:34 AM
By: Paul Caton 
Level: Advanced
User Rating: By 70 Users
Compatibility: VB 5.0, VB 6.0
Views: 27932
 
     VB makes it easy to compile controls into an application if you have the source. It's much more convenient for users if the only file that needs to be added to the project is just the control’s ctl file. If the Control uses subclassing, then you're out of luck, or rather... you were. This submission includes the FIRST ever Control/Form that can subclass itself without ANY dependencies – modules, classes, type-libraries, references or components. The techniques used are based on my WinSubHook2 submission and thus incorporate IDE breakpoint and stop safety; it won’t crash the IDE. My main target audience here would be control authors, however, anyone who needs form subclassing may well find this to be an ideal solution. 06/02: Can now subclass multiple hWnd’s. 06/07: Byte array substituted in place of a string for the machine code buffer. 06/18: Hidden bug fixed, see comments. 06/19: Optimised to within an inch of its life. 06/20: Road to Damascus. 06/21 New sample added, see screenshot. 06/28 Final (I hope). 06/29 Oops fixed. 09/10 fixed bug in UserControl_Terminate, marked zSubclass_Proc hidden.

 
winzip iconDownload code

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:
  1. Re-scan downloaded files using your personal virus checker before using it.
  2. NEVER, EVER run compiled files (.exe's, .ocx's, .dll's etc.)--only run source code.
  3. Scan the source code with Minnow's Project Scanner

If you don't have a virus scanner, you can get one at many places on the net including:McAfee.com

 
Terms of Agreement:   
By using this code, you agree to the following terms...   
  1. 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.
  2. 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.   
  3. You may link to this code from another website, but ONLY if it is not wrapped in a frame. 
  4. You will abide by any additional copyright restrictions which the author may have placed in the code or code's description.


Other 13 submission(s) by this author

 


Report Bad Submission
Use this form to tell us if this entry should be deleted (i.e contains no code, is a virus, etc.).
This submission should be removed because:

Your Vote

What do you think of this code (in the Advanced category)?
(The code with your highest vote will win this month's coding contest!)
Excellent  Good  Average  Below Average  Poor (See voting log ...)
 

Other User Comments
6/1/2004 5:37:13 AMPaul Caton

I've included a form sample that may be of interest to all VB coders, not just UserControl authors. Oh, and I just wanted to register another thanks to Fred.cpp for pushing me to make this happen.
(If this comment was disrespectful, please report it.)

 
6/1/2004 6:32:42 AMMike

Very clever Paul.
But what happens when you want to sub class multiple components on the same form?

(If this comment was disrespectful, please report it.)

 
6/1/2004 6:37:52 AMPaul Caton

Then you'd need a regular subclasser. This is specifically designed for subclassing the UserControl itself.
(If this comment was disrespectful, please report it.)

 
6/1/2004 6:53:51 AMPaul Caton

On second thoughts, it's do-able, give me a couple of days and look for an update.
(If this comment was disrespectful, please report it.)

 
6/1/2004 7:35:05 AMLight Templer

Paul, as usual: GREAT!
Many thx for the many comments/hints and for the "try to VB5" I will check it out with it. Regards and (of course) °°°°°
LiTe
(If this comment was disrespectful, please report it.)

 
6/1/2004 8:04:55 AMPaul Caton

When I'm rich and famous, LiTe, I'm gonna donate you a copy of VB6 :-)
(If this comment was disrespectful, please report it.)

 
6/1/2004 9:43:13 AMLight Templer

Sorry Paul for this two missunderstandings:

* I HAVE a copy of VB6, but I don't want to use it!

* You ARE already famous in VB comunity!
(Ok, maybe not rich, but wait a little bit ;))) ...)
(If this comment was disrespectful, please report it.)

 
6/1/2004 11:52:17 AMFred.cpp

Hi Paul. Is nice to see yous code here.
I've said Everything. I have your photo as my wallpaper:-)) , well, Actually is only your name, cuz I don't have a photo.
You can see I used your code in my isExplorerBar and I'll use It In all my controls. It's the most useful stuff in the year. Again So many thanks!
Kind Regards.
(If this comment was disrespectful, please report it.)

 
6/1/2004 11:54:21 AMFred.cpp

Also Thanks for give the url of my control :)
I forgot to say, you got my 5 billions of globes (but PSC only let me give you 5 :( )
(If this comment was disrespectful, please report it.)

 
6/1/2004 2:01:56 PMselftaught

Excellent as always. Perhaps it's already been thought of, but for a usercontrol you could streamline the ASM allocation by only parsing the hex string in the initproperties event, and storing it as a byte array in the property bag. You could have the hex string as a private const in the InitProperties event, and then just read it back instead of parsing it over and over.
(If this comment was disrespectful, please report it.)

 
6/1/2004 3:11:23 PMskansoft

GREAT!! I am waiting for your update for multiple subclasser
(If this comment was disrespectful, please report it.)

 
6/1/2004 3:41:17 PMAnders Nissen

Wow, that is some really hardcore coding techniques. 5 of the small ones from me for sharing this masterpiece :)
(If this comment was disrespectful, please report it.)

 
6/1/2004 6:28:39 PMPaul Caton

Good idea, selftaught, I will look into it for the multi-version. Self-taught also.
(If this comment was disrespectful, please report it.)

 
6/2/2004 6:54:55 AMmugman21

Excellent idea; trying to view all your code but the power keeps flickering (da*n tornados last night + UPS battery is bad). :-(

Hope I can make use of this, needing to subclass a listview scroll event to reposition my progressbars....

Takes for sharing, 5 globes from me.
(If this comment was disrespectful, please report it.)

 
6/2/2004 7:02:45 PMFred.cpp

Paul, I don't know what else can I ask for, you have changed the way I'll subclass controls and forms =).
congratulations.
(If this comment was disrespectful, please report it.)

 
6/3/2004 5:45:47 AMPaul Caton

Update - Now supports multiple hWnds. I will do another update in a few days when everything has been optimized to death. Thanks all for the votes and comments.
(If this comment was disrespectful, please report it.)

 
6/3/2004 7:37:01 AMPaul Caton

Update - I optimized some of the code and improved the samples. UserControl sample subclasses itself and the parent form. Form sample subclasses itself, a picture box and any number of other forms.
(If this comment was disrespectful, please report it.)

 
6/3/2004 8:12:43 AMLight Templer

Hi Paul,

it starts to be the allround
perfect solution ;))) !
After too small changes it works fine for VB5 in IDE and compiled, too (tested on Win NT4, SP6):

For bot (form/uc) do:
1 - Change in line
Private Enum TRACKMOUSEEVENT_FLAGS
to 'Public'
2 - In Private Function zAddrFunc() follow Pauls hint and comment out the line Debug.Assert zAddrFunc

Bingo!

One more time: Thx alot, Paul!
LiTe
(If this comment was disrespectful, please report it.)

 
6/3/2004 8:16:30 AMPaul Caton

Gotta ask, LiTe, why do you persist with VB5 if you have VB6? Did they break something you liked?
(If this comment was disrespectful, please report it.)

 
6/4/2004 8:54:50 AMReadError

Wonderful
(If this comment was disrespectful, please report it.)

 
6/4/2004 10:29:12 AMAlexandru Ionescu

Bringing VB even closer to C. One day we will hack this languge to pieces, you and I ;). Great work...
(If this comment was disrespectful, please report it.)

 
6/4/2004 11:02:12 AMPaul Caton

Okay, this *should* be my final update. Managed to get rid of a subroutine, but more importantly (for me) an On Error Resume. The zAddrTbl function isn't needed because with this Subclasser you have to Start it before you can AddMsg. Improved some comments etc. I'm done. Enjoy!
(If this comment was disrespectful, please report it.)

 
6/4/2004 11:55:42 AMPaul Caton

Selftaught, I decided against because I didn't want to pollute the UserControl file-level variable space. Since I went to the multi-hwnd version the subclassing adds but a SINGLE file level variable, aSubData, I like this feature-ette, so I stuck with the way I was doing it before. However, no matter how many hWnds are subclassed, the code is only "constructed" the once.
(If this comment was disrespectful, please report it.)

 
6/4/2004 4:02:38 PMChris Seelbach

Excellent. This also works if/when the U.C. has a transparent backgroung, rendering mouse move events on opaque regions only. This
(If this comment was disrespectful, please report it.)

 
6/4/2004 4:11:15 PMChris Seelbach

Sorry Paul, I got excited and fell off my chair. "This opens the doors" for further development. Thank's for sharing this. ;)
(If this comment was disrespectful, please report it.)

 
6/4/2004 9:22:12 PMselftaught

I'm sure that since you decided against it, it wasn't the best route after all. Still, I think there's got to be a way to optimize the hex decoding... hmmm... my brain hurts. That's what I get for trying to think up a way to improve on your code! ;)
(If this comment was disrespectful, please report it.)

 
6/4/2004 9:43:50 PMselftaught

OK, being the obsessive optimizer that I am, I couldn't let it go! Here's all the ideas I could come up with: 1. you could preallocate the sBuf string and use mid$() in the for..next loop instead of concatenation, 2. you could use a long array and hardcode each pair of opcodes in the initialize event; such as when initizalizing a power2 array (doesn't VB need arrays of constants?!?), Or you could have another tiny asm snippet for string hex to opcode conversion (perhaps not practical, I don't know what it would take to emulate chrb$() and Val() hex conversion in asm, but it certainly would have numerous other uses) and inject it into a function like you did in your cShift class (which I am still using and I love by the way).
(If this comment was disrespectful, please report it.)

 
6/5/2004 5:29:02 PMEmiliano Scavuzzo

Great code Paul!
You're the best.
(If this comment was disrespectful, please report it.)

 
6/5/2004 9:13:01 PM

Excellent as usual Paul.
(If this comment was disrespectful, please report it.)

 
6/7/2004 5:55:17 AMPaul Caton

Minor optimization that saves a few mS on my PC. String code buffer substituted with a byte array.
(If this comment was disrespectful, please report it.)

 
6/7/2004 9:00:29 PMselftaught

Of course! all the benefits of the static array for the code buffer but no hardcoded opcodes. It seems so obvious now that I see it. Someday I'll have gotten up to speed. Maybe. ;)
(If this comment was disrespectful, please report it.)

 
6/8/2004 1:35:43 AMDean Dusenbery

Paul, glad to see you switched to storing the asm code in byte arrays. The ChrB thing really threw me the first time I saw it! Great work!
(If this comment was disrespectful, please report it.)

 
6/9/2004 12:53:13 PMMArio Flores G

Great way to CODE!! amazing submission
Thanks for sharing ..maybe i will use this on my next submission..or maybe everybody must do!! :)
(If this comment was disrespectful, please report it.)

 
6/10/2004 8:04:42 AMPaul Caton

Well thanks for all the votes and comments, peeps. I really didn't expect to do this well. I rather thought my target audience was going to be limited to control authors alone. Anyway, I'll do another update soon, I've optimised it to death and am now in fact rolling some back.. It's really not worth the trouble of using SAFEARRAY array mapping just to save .1 of a millisecond in Subclass_Start - Cheers.
(If this comment was disrespectful, please report it.)

 
6/11/2004 3:55:25 PMFred.cpp

I'm using this once again, hehe, )I'll rewrite all my controls! this is like reinvent usercontrols, forms, etc. I'm still excited the new posibilities in a single file control. Again thanks Paul.
(If this comment was disrespectful, please report it.)

 
6/12/2004 6:22:19 AMMH

Simply, Awesome. I always wanted to have the ability as C++ does to subclass controls easily without dependencies. Now, thanks to you we all do.

Peace
MH
(If this comment was disrespectful, please report it.)

 
6/14/2004 5:58:30 PMDavid J. Fritts

Paul, I see you patch in an ObjPtr(Me)! This is really unneeded, visual basic passes a hidden "this/me" parameter to all member functions.
(If this comment was disrespectful, please report it.)

 
6/14/2004 5:59:55 PMDavid J. Fritts

well, perhaps it is needed depending on wether or not you are calling the function.
(If this comment was disrespectful, please report it.)

 
6/15/2004 3:14:19 AMPaul Caton

David, I think you've got the wrong end of the stick here. The machine code thunk has to have a way of finding the callback routine. It does this thru the vtable which it knows via the ObjPtr(Me) patch. The minor discovery in this submission is that the first Public method in a form/usercontrol is located in the vtable at fixed locations. Hence no implements and no external dependencies.
(If this comment was disrespectful, please report it.)

 
6/17/2004 8:47:58 AMNoel H

Probably the most useful piece of code on PSC.
Congratulations.
(If this comment was disrespectful, please report it.)

 
6/17/2004 5:25:30 PMDavid J. Fritts

Paul, lol... I was pointing out to you that all member functions of visual basic objects are passed a hidden this/me parameter over the stack. But, since you are the one calling the callback-- yes you have to patch in the this/me pointer
(If this comment was disrespectful, please report it.)

 
6/18/2004 4:39:03 PMPaul Caton

MAJOR FIX - I hate accidental success... when the array of subclass data is redim-ed to add a new hWnd the code buffers get moved in memory. You might think that would cause an instant crash as the relative call addresses would then be wrong. But no, the window procedure carries on calling the old code with the correct offsets.. but that code is just waiting to be reclaimed by the garbage collector, which unfortunately doesn't do it's thing unless pushed. You can see this with the old form sample if you keep clicking the "Another form" button. The fix is to repatch the existing buffers and WndProc pointer each time a new hWnd is subclassed. Only glad I saw it before any of you lot did.
(If this comment was disrespectful, please report it.)

 
6/18/2004 4:55:06 PMFred.cpp

Paul: wow, Nice update and your'e right you detected It before someone. I would love to do the same, but I get always lot of bug Reports hehe.
I would like to Vote again, maybe the more complex/poweful submission Even In PSC. This makes VB to go where Microsoft didn't even thought.
Best Regards.
(If this comment was disrespectful, please report it.)

 
6/19/2004 4:44:10 PMPaul Caton

06/19: Optimized to death. Sorry about all the endless updates folks, but just for the obsessive optimisers out there (you know who you are:) I wanted to give it everything I’ve got in that regard. I doubt if there's much more that can be wrought out of it. If you see something I’ve changed and are wondering why, don’t hesitate to ask. I added a Subclass_StopAll method that does what it says on the can.
(If this comment was disrespectful, please report it.)

 
6/20/2004 10:27:20 AMPaul Caton

LOL - Three updates in as many days; sorry. I just stumbled over the *elegant* (and umm, obvious) solution to the issue of the Redim moving the code buffers around in memory. Use allocated memory for the code buffers. No need to re-patch now, hurrah! Simplifies things significantly. I also cut-down in some other areas to allow double subclassing, ie subclassing the same window more than once.
(If this comment was disrespectful, please report it.)

 
6/21/2004 4:31:25 AMLight Templer

Perfect service, Paul ;) - thank you very much for the time you spent in.
LiTe
(If this comment was disrespectful, please report it.)

 
6/21/2004 4:30:51 PMtamogachi

please take a look at my post http://www.planet-source-code.com/vb/scripts/showcode.asp?txtCodeId=54503&lngWId=1
(If this comment was disrespectful, please report it.)

 
6/22/2004 10:11:53 AMVlad Vissoultchev

hi Paul,

do you know anything about buffer overflow protection XP SP2 is coming up with? i'm worried most of the "non-official" (non-MS) thunks will stop working unless specifically tweaked. namely, the memory pages the thunk occupies have to be flagged "for execution" -- this is a new flag they plan to introduce in SP2.

cheers,

(If this comment was disrespectful, please report it.)

 
6/22/2004 10:33:28 AMPaul Caton

Heya Vlad, nice to see you around.

Yes the NX flag as found on the A64 and Itanium... to be honest, dunno. Maybe somebody out there with an AMD64/SP2 could say. If it is a problem then I guess the solution is to use VirtualAlloc with execute permission.
(If this comment was disrespectful, please report it.)

 
6/27/2004 4:21:52 PMLuprix

Excellent idea and perfect implementation.
very useful and mainly to learn like vb work.

Best regards.
(If this comment was disrespectful, please report it.)

 
6/28/2004 7:31:16 AMPaul Caton

Update 06/28 a little safety improvement and some better comments. Hopefully done unless there's an issue with the NX flag on AMD64's running Windows XP Service Pack 2
(If this comment was disrespectful, please report it.)

 
6/29/2004 4:10:49 AMPaul Caton

Update 06/29 - Thanks to Dean for spotting an InIDE goof.
(If this comment was disrespectful, please report it.)

 
6/29/2004 5:47:19 AMZhu JinYong

In sample 1,GDI increased by step 9 once adding a new form in XP.Is it normal?
(If this comment was disrespectful, please report it.)

 
6/29/2004 8:16:41 AMPaul Caton

I don't know, Zhu, to be honest with you. What tool are you using? You could comment out the Subclass_Start/Stop/AddMsg's and see if it makes any difference. I would be surprised if it did.
(If this comment was disrespectful, please report it.)

 
6/29/2004 8:05:19 PMZhu JinYong

Sorry,it has nothing with your subclass.I have tested without subclass,but GDI keep increasing.
(If this comment was disrespectful, please report it.)

 
6/30/2004 5:48:40 AMPaul Caton

A big thanks to all those who voted and commented, thankyou. Sorry about all the updates :)
(If this comment was disrespectful, please report it.)

 
7/7/2004 11:56:59 AMFred.cpp

Thanks for the continuous updates! For me, This is the code of the year!
(If this comment was disrespectful, please report it.)

 
7/8/2004 12:51:27 AMMArio Flores G

How do we call the Old WindowProc
how do catch values (hwnd, iMsg, wParam, lParam) to the old proc

Public Sub zSubclass_Proc(ByVal bBefore As Boolean, ByRef bHandled As Boolean, ByRef lReturn As Long, ByRef lng_hWnd As Long, ByRef uMsg As Long, ByRef wParam As Long, ByRef lParam As Long) this is the new patched proc , but can we still use the old proc?


can we callit without unsubclassing
SetWindowLong F.hwnd, GWL_WNDPROC,sc_aSubData(zIdx(lng_hWnd)).nAddrOrig

is it possible i hope you undestand my question


(If this comment was disrespectful, please report it.)

 
7/8/2004 5:47:04 AMPaul Caton

Mario,

Just add the api declaration for CallWindowProc

Get the array index for the hWnd and pass whatever values you want...

With sc_aSubData(zIdx(hWnd))
Call CallWindowProc(.nAddrOrig, hWnd, uMsg, wParam, lParam)
End With

I'll add it into an update if anyone else needs it.
(If this comment was disrespectful, please report it.)

 
7/20/2004 8:54:10 AMAlT

To quote Belushi in Animal House....Holy Sh-t! It is code like this that makes me ask myself why I bother.
(If this comment was disrespectful, please report it.)

 
7/20/2004 4:46:37 PMPaul Caton

Thanks AIT, it's comments like that, that
make me bother :)
(If this comment was disrespectful, please report it.)

 
7/23/2004 11:45:01 AMAlT

Would it be possible to use this technique inside a dll with a window created using CreateWindowEx? Since I have already managed to create memory exceptions both in the IDE and compiled when substituting an api window for the form hWnd in the examples, I am thinking some modification may be necessary? ;-)
(If this comment was disrespectful, please report it.)

 
7/23/2004 12:17:48 PMPaul Caton

AIT,
I could probably patch it up to handle a CreateWindowEx window, let me take a look. Question: One of the predefined classes, eg "STATIC" or would you need to register the window class as well?

In the meantime take a look here...
Particularly the Shadow/Fade ocx and the cWindow class, that may be all you need.
http://www.planet-source-code.com/vb/scripts/showcode.asp?txtCodeId=51403&lngWI d=1

regards,
Paul.
(If this comment was disrespectful, please report it.)

 
7/23/2004 1:51:19 PMAlT

Hi Paul,

Thanks for the quick reply. 'STATIC' is the type of window I will create in this instance. I am simply using a hidden window as a callback for some other API's I am calling. I am getting the memory exception when the window receives the MM_MIXM_CONTROL_CHANGE message from a mixer that I've opened. I also tried using a non-visible VBThunderMain as the callback window but got the page fault there too. However, I have to say that this technique works great in a usercontrol and is pretty slick. Although it will take some time for me to figure it all out, it is a big time saver and code reducer for some of the things I am doing right now. Hmmm, I suppose I could try siting the usercontrol on the hidden window launched from within the dll or is this just a completely different animal using this technique in a dll as opposed to a usercontrol or form in a regular project?
(If this comment was disrespectful, please report it.)

 
7/23/2004 1:57:13 PMAlT

Oh yeah, thanks again. This is really great stuff. I wish a lot of people here would compare their code to some of yours, Vlad's, Alexander's, etc., before begging for votes with rotating text code....
(If this comment was disrespectful, please report it.)

 
9/4/2004 9:06:44 AMRde

Beware using InIDE (as is) in a dll - will not return True when compiled dll being used in IDE project.
(If this comment was disrespectful, please report it.)

 
9/4/2004 9:18:52 AMRde

Very slick code Paul.

I get the feeling AIT is talking about me when I consider my submissions next to ones like this :)

Added bonus in this submission is the working uc mouse over/out solution that is second-to-none.

5 of course for this Paul, oh, and keep showing me up :)
(If this comment was disrespectful, please report it.)

 
9/4/2004 11:48:36 AMPaul Caton

Re InIDE, yes this is true but the code is essentially designed for UserControls that are compiled into the exe rather than external ocx's and dll's
(If this comment was disrespectful, please report it.)

 
10/26/2004 9:52:11 AMAlT

Hi Paul, I seem to have found a way to crash the IDE when subclssing the WM_ACTIVATE message. If I run the project and hit the IDE's minimize button, it locks up the IDE. So, I don't do that anymore. ;-) Just thought I'd see if you can reproduce it. Running VB6 SP3 on Win2K. Still love the code...especially the enums...
(If this comment was disrespectful, please report it.)

 
10/29/2004 5:10:00 AMChris Cochran

At masterpiece Paul. I have a vision of you toiling obsessively over this submission, and I am sure I speak for all when I say it is sincerely appreciated.

Congrats on a great piece of work.

C
(If this comment was disrespectful, please report it.)

 
6/23/2005 9:37:47 PMDonn C. Romasanta

Hands down always! No argument! You're the best! Even though I don't have the need for this now, but still I voted because very name PAUL CATON makes me excited about programming VB. We're not worthy!!!{act of worship} 5 globes from me even though your already the winner...
(If this comment was disrespectful, please report it.)

 
10/8/2005 3:17:34 PMOption Explicit

Paul, this submission is invaluable to people like me, who like to write self contained controls. My "MorphListBox" and all my other controls would be severely diminished without this classic piece of work. Many, many thanks - Matt U.
(If this comment was disrespectful, please report it.)

 
10/8/2005 6:34:54 PMPaul Caton

Thanks OE, look for a new release comming, ummm soonish!
(If this comment was disrespectful, please report it.)

 
2/13/2006 9:08:32 PMLaVolpe

P.Caton, couple questions if you don't mind: 1) Still supporting this version? 2) What are consequences if subclass_stop/stopall not called? 3) if any consequences have you considered auto-unsubclassing when wm_destroy is received? 4) when zIdx is called, check for -1 to prevent errors with invalid hWnd param? Only place checked is subclass_Start 5) if your tlb version is what you are concentrating on now, do you have a non-tlb version -- psc makes uploading tlb (req'd with that version) difficult if not impossible.
(If this comment was disrespectful, please report it.)

 
7/1/2006 11:48:21 AMSoorya

Paul,
You are the right person for my question. Do u have any simple solution to system wide hook or any other trick, to make all the windows to open in Secondary Monitor instead of usual Primary (X=800). Pls. help me.
(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.)
 

To post feedback, first please login.