-
5 Attachment(s)
[vb6] IDE-Safety Thunks for subclassing, hooking and more: A new breed
Attached project is something I've been thinking about for a couple years, just never found the time to put it together. See post #2 for tips, tricks, other info.
Disclaimer. This is not compatible with VBA (i.e., Office modules). Not tested with VB5.
Suggestion. If you like these thunks, recommend placing a copy of the class file into your templates folder shown below. If that is done, whenever you right click in your project explorer to add a new class, the thunks class will be an option to select. When updates are made to this project, update that file also.
C:\Program Files\Microsoft Visual Studio\VB98\Template\Classes
The suite of thunks included in the sample project are wrapped in VB COM objects. This means that the thunks have properties and methods exposed just like any VB object. Granted, the object methods are late-bound (no intellisense) vs. early-bound (faster & has intellisense). Intellisense can be achieved with use of wrappers. Early bound calls can be achieved by creating/using a TLB designed to mimic the thunks.
The documentation for each thunk and more is included with the attached Readme files. Those files are important for you to download if you want to play with the thunks outside of the sample projects. Also included are the NASM scripts used to compile the thunks and directions on how to compile them if you want to manipulate the scripts.
What is a thunk? It is simply executable code placed in process memory, unknown to VB. Typically, thunks are used to enhance VB in one way or another beyond what can be done within VB and without using an external DLL. Thunks are not scary if you have the source code and can verify what is being done. I don't expect everyone to know how to read assembly; therefore, every thunk source is heavily commented so that anyone could follow the logic and see there is nothing malicious. The fact that I've included the source code should also put your mind at ease.
The key to strong resistance to crashing when in IDE is the fact that the thunk is wrapped in a COM object. By being a declared COM object, VB will always unload it. Since it knows it is being unloaded, it can manage its own destruction, including its own clean-up and removal from process memory. Also, being "external" to VB, a thunk can ask VB whether the IDE is paused or not which can't be done from "internal" code. When paused, the thunk simply passes the message on or eats it, as applicable & defined by you. Minimally, a thunk can be IDE-safe if it can:
- determine when not to callback to the IDE; that is, when the IDE cannot respond
- ensure it can unload/disengage before anything it calls back to is unloaded & becomes dead code
Thunks included, along with a brief description:
1) Subclassing. Uses the common controls subclassing library. The thunk can subclass multiple hWnds simultaneously.
2) Hooks. Windows offers various hooks, i.e., keyboard, mouse, CBT and more. A sample project shows how subclassing and hooking are used together to create owner-drawn VB comboboxes.
3) Timer. Probably of little use, but provided anyway. The only real advantage of a thunk timer is the ability to add it to a VB class and extended interval values.
4) Custom Window Classes. For those of you that are API-nuts and like to create pure or semi-pure custom windows, the thunk helps manage the class procedure.
5) Callbacks. Both standard Windows (stdCall) and C-style (cDecl) formats are supported. A sample project shows both.
6) COM object hooks. Ability to hook/subclass a COM object, either one exposed by VB or a VTable-only interface. An example shows how to subclass the stdPicture object to hook its rendering.
7) GDI+ token/handle management. Ensures GDI+ is shut down properly and GDI+ handles are destroyed even if the IDE is terminated unexpectedly. Sample included in using GDI+ to load and display a PNG inside of a VB image control and animating a GIF.
8) API caller. This thunk ensures one or more APIs are called after the project unloads, regardless of how it is unloaded. Such a thunk can be used to ensure objects created with GDI or files opened with CreateFile APIs are destroyed/released. This thunk also enables CDecl APIs to be called on demand.
9) VTable caller. Similar to DispCallFunc API, the thunk enables calling methods directly to a VTable-only interface.
10) VTable-only Implementation. A way to have a VB class, form, etc implement a VTable-only interface.
Attachment 170793
Each thunk type is included in the sample projects. Also included is a sample to allow you to attempt to crash the IDE due to "End" statements. There may be a scenario that includes various levels of usercontrols, but haven't discovered one yet that crashes the IDE due to "End". If you discover a scenario, let me know so I can find a workaround as needed.
Code:
Update History
5 July: one minor patch, one minor enhancement. See post #51
-- Subclassing thunk did not honor return value when EatMessage returned as True
-- Hooking thunk now allows global low-level keyboard and mouse hooks
4 May: Added new thunk, see thunk 10 above. See post #43 for change details.
-- fixed logic flaw when VTable-caller tasker is passed no parameters
-- fixed logic flaw in scanning host ordinals that could fail in some scenarios
-- rewrote host ordinal scanning logic, now 14x faster, no restriction on method counts any longer
-- error reporting when creating taskers now will include reason for error
-- additional IDE-safety check made to warn you if "Break on Unhandled Errors" in use
18 Apr: Several changes, primarily for version protection, see post #38
-- Also patched COM-hook thunk
-- Thunks now Base64 strings vs array - dramatic compile size difference
12 Apr: Major changes, see post #36 below for details
5 Apr: updated documentation only. Resolved mislabeling of functions as properties/subs and vice versa
3 Apr 2020: modified thunks to accept optional TLB VTable-only interfaces. Minor tweaks.
3 Sep: re-upload. Failed to parse thunk 4-byte blocks with values of zero. Needed to include
them as it is possible their memory can be allocated with junk if not overwritten.
1 Sep: Complete revision
- uses private heap vs VirtualAlloc for thunks, overall memory reduction
- includes two new thunks: APICaller and VTableCaller thunks
- documentation completely redone, each thunk has its own readme file
- thunks can be optionally used with wrappers for early-bound, intellisense capability
22 Jun: Enhancements
- replaced COM hook thunk with another that can hook multiple objects vs. just one
- added a new thunk to help make GDI+ IDE-safe and added a sample of its usage
- by request, increased default max method count expected in any VB code page to 700
- other minor enhancements/changes made
3 Jun: Bug reported when compiled. Fixed (see post #4)
2 Jun 2019: initial release
Note: In the future, changes only to the clsThunks class and not the sample project, will have the class attached separately. Will not repost the test project unless I change something in it. If the class is a separate attachment, it should be used in place of the one in the test project.
Latest changes have these zips updated: clsThunks.zip, documentation.zip. No others were updated.
-
2 Attachment(s)
Tips, Tricks, Workarounds
Reserved section for tips & such...
WARNING WARNING WARNING...
Crash resistance applies after you properly set up your thunks. Passing bad pointers or bad memory addresses to any function almost always leads to crashes. That is on you. Improperly customizing/initializing a thunk can lead to crashes. That is on you, documentation and samples are provided to prevent that.
You can disable error trapping (while in IDE only) inadvertently. This will make the thunks seem super-safe, but when outside IDE and after compiling your project -- crash due to an error you created but didn't know existed. In specific scenarios, you may have an error inside a callback event that triggers VB to jump out of that event, jump out of the thunk and back to some point earlier in the call chain. No error will be raised by VB. This scenario can occur when all 3 of these happen together: a) you coded an error in the callback event, b) that callback event has no error trapping, c) your IDE error breaking mode is to break on unhandled errors only. This applies to all thunks known to me and by all authors.
Code:
- Break on unhandled errors is set
- in this callback event, no error trapping
- in this callback event, user-generated error occurs
MsgBox 1 / 0 ' << you want the code to stop here so you can address your error
MsgBox "Hello World" ' << line and all remainng ones in the procedure aborted by VB
To prevent this, ensure at least one of these two things are done:
1) Preferred is to always include error trapping in any callback event sent from the taskers/thunks.
2) Do not use the IDE options: Break on unhandled errors. Use one of the other two choices.
Versioning: Starting with version 4, there is a SUITE_VERSION constant in all but one method within clsThunks. That version is passed between methods. If that constant's value is different, the routine aborts. This is to ensure that you are using the same code from the same version. This is only an issue if you copy & paste some things out of the class vs. using the entire class. If versions are different, then the thunks will not communicate with each other as needed and that would not be good. So don't go & just change the value to get past the problem. Instead, get the code relative to the newest constant value. The version will typically change when pvManageThunk is modified in a significant way. When individual taskers are updated, their own versions may change, but that constant's value may not. Different versions of the same tasker can co-exist as long as their SUITE_VERSION value is the same.
Tip: If an error occurs inside a callback event, it will be highlighted when you click the Debug button in the debug message box. Skipping over that error doesn't want to work by using shortcut keys or IDE menus. For those that don't know, you can drag the yellow arrow (shown below) to a safe point above/below the error, fix the error, then drag it back. You must drag the arrow to an executable line, even to the Exit Function line if wanted. Press F5 to continue.
Attachment 176427
FYI: Included in post #1 are sample wrappers for each thunk. Wrappers enable intellisense on the late-bound thunk objects. At the top of each wrapper class are the simple instructions for using them. Wrappers can help getting more comfortable with using the thunks and can be removed later if desired.
FYI: All thunks except the generic callback, COM hooking and VTable caller/implements thunks know how many parameters and the size of the parameters that are required. With those other four, the thunk needs to be told when it is created or called because they are unknown at that point. The parameter count is straightforward, but the sizes of each are not. So, the creation routines expect you to provide the number of total DWords (4 bytes) for all parameters. All parameters will use minimum of 1 DWord each. Even if the parameter is just a Byte or Integer. This is because parameters are promoted to DWords in x86 assembly stdCall/cDecl. Basically, this means in most cases, the number of DWords required for all parameters is the same as the parameter count. However, with COM, a single parameter may require 2 or more DWords (i.e., LongLong, Currency, etc). This FYI is just meant to help you better understand what is required, relative to DWord parameter sizes. The readme files for those specific thunks detail more information.
Tip. The GDI+ thunk creates tasker objects that have just a few properties, one of them being HandleByRef. That property is provided for convenience when creating handles via GDI+ API calls. But use caution. Simply referencing that property will destroy any handle already being managed by the tasker, by design, before it returns. If you are using that property, then you will be replacing its current value; hence the destruction. So don't Debug.Print it out. Its value is meaningless to you, it is just a memory location where the Handle is stored.
Code:
' using the HandleByRef property as a convenience
GdipCreateFromHDC hDC, ByVal oTasker.HandleByRef
' If you: Debug.Print oTasker.HandleByRef then you just destroyed the handle
' without using it requires 1 more line of code...
' The Handle property only destroys existing handles when
' a new handle is assigned to the tasker. So you can Debug.Print it out
GdipCreateFromHDC hDC, hGraphics
oTasker.Handle = hGraphics
Edited: This is something I'd like to readdress. I would prefer the thunk to not release the handle simply by referencing it. Will take a bit of thinking. 7/17/2020
Tip: When wanting to hook stdPictures to draw PNGs inside of image controls, a transparent icon is useful as a placeholder inside the image control. The image control needs something assigned to it in order for VB to have it redraw as needed. We need that so we can intercept and draw our PNG instead. Attached here is a 100% transparent icon: Attachment 169417. Also, a sub-tip of sorts... make Image1.Stretch=True
-
Re: [vb6] Thunks - A new breed
very good project keeps improving it.
I have not had much time to do tests.
but I had the following problem, with the project compiled in the first two examples when it closes its window closes everything, in the ide no.
a greeting and forgiveness for the language
-
Re: [vb6] Thunks - A new breed
@yokesee. I see what you are describing. I'll have to look at my subclassing thunk, something is wrong with the suicide timer procedure when compiled. Thank you for pointing it out. I'll upload the patched thunk soon.
P.S. Same problem will be with the last example.
Follow-up. Patched & re-uploaded. Problem was that I was using edi register but failed to preserve it beforehand which is a no-no and an oversight on my part. Unfortunately, the same logic was used in every thunk (copy & paste). So every thunk was modified to address the problem. Thanx again yokesee
-
Re: [vb6] Thunks - A new breed
If us XP users thunk this would work in XP
Would we be thunking wrong ?
-
Re: [vb6] Thunks - A new breed
Quote:
Originally Posted by
Bobbles
If us XP users thunk this would work in XP
Would we be thunking wrong ?
No, you'd be thunking correct. ;)
The subclassing thunk actually checks for the common controls library exporting needed APIs by ordinal, which XP did. However, I did not test it on XP. If you do play around and it works, feedback is encouraged for others. If it has problems, would like to know that too.
-
Re: [vb6] Thunks - A new breed
Quote:
4) Custom Window Classes. For those of you that are API-nuts and like to create pure or semi-pure custom windows, the thunk helps manage the class procedure.
Baby you're singing my song :afrog:
Quote:
Tip: The pvGetAddressOf routine is hardcoded to only search up to 256 methods of any host. If you have more than 256 in your host (form, class, whatever), then you will need to up that limit. In that routine, look for the value 1024. That is equal to 256*4. Increase it to a max number of methods your host could possibly have, multiplied by 4.
What? That's crazy talk, who would ever have a class with more than 256 methods??? Or even more impossible, needing to up it above the 512 limit that was in the old one :eek:
---
Great work, can't wait to check it all out.
-
Re: [vb6] Thunks - A new breed
Quote:
Originally Posted by
fafalone
What? That's crazy talk, who would ever have a class with more than 256 methods???
You already have 400-450 methods in default IDispatch vtbl "inherited" from VB's form/usercontrol base implementation. Of these significat percent are empty/dummy slots carried from legacy (VB3?) versions of the base classes but if not taken care of these use-cases explicitly, probing only first 256 methods might turn out insufficient.
cheers,
</wqw>
-
Re: [vb6] Thunks - A new breed
The joke was that this exact problem happened, because my shell browser UC has over 512 methods just in the .ctl file :eek2:
-
Re: [vb6] Thunks - A new breed
Quote:
Originally Posted by
fafalone
The joke was that this exact problem happened, because my shell browser UC has over 512 methods just in the .ctl file :eek2:
Ouch! :-))
cheers,
</wqw>
-
Re: [vb6] Thunks - A new breed
Yeah it's a little crazy, Sub+Function+Property totals 797 right now :wave:
LaVolpe, I'd even suggest retaining the default of 512; I've seen a number of UserControls that would exceed 256 (UC's being particularly vulnerable because of the desire to self-contain them and property let/gets running up the count).
-
Re: [vb6] Thunks - A new breed
Quote:
Originally Posted by
fafalone
LaVolpe, I'd even suggest retaining the default of 512; I've seen a number of UserControls that would exceed 256 (UC's being particularly vulnerable because of the desire to self-contain them and property let/gets running up the count).
Will consider it when I update the project. The newest version (on my pc only) uses a constant so it is easier to change for any individual user, as needed. The original intent of the limit imposed by Paul Caton was because he didn't provide a starting point for parsing VTables except for classes. And as mentioned, forms can be quite large, but so are UCs & property pages. I'm not sure it is even worthwhile any longer since I've added those offsets, but I haven't played with the various Designer VTables.
BTW, the reason for the newest version is a multi-COM object thunk, otherwise, I might just be done with this project barring bugs.
-
Re: [vb6] Thunks - A new breed
Updated the project with enhancements and to correct some minor code flaws. The _ReadMe file is updated to address new thunks and any other pertinent information that changed.
Major enhancements include the replacement of the COM hook thunk with a new one that can be used to hook multiple objects on the same thunk or a slightly smaller one used for single object hooking. Also included is a new GDI+ thunk that will ensure GDI+ and and handles it is managing are shut down/released even if the IDE is terminated with an End action.
-
Re: [vb6] IDE-Safety Thunks: A new breed
Looking at posting one more update, really a revision. That will remove most compatibility with this project.
I'm leaning towards revamping how the thunks are created. Currently, they are created one thunk at a time as needed. Each thunk instance uses its own reserved memory. Let's say one thunk uses 2K memory and you wanted 5 of them: 10K total. The route I'm thinking about will use reserved memory just once for a "Factory" thunk. The factory creates/spawns worker objects as requested. Each worker object will use about 32 bytes (not a typo). So if a Factory uses 2K, you can create 10 workers that only use about 320 bytes more. The COM hook thunk will be the exception and will remain mostly as is since it doesn't fit well into my Factory theme.
Also going to include one new thunk. For time being, calling it APIcaller. The gist is that you can assign an API to it, along with parameters and have it call that API when the worker is released. This can help reduce memory leaks and possible crashing when IDE is terminated unexpectedly, i.e., "End". Let's say you are using APIs to load files and want to ensure the file handle is freed on termination. That APIcaller worker is setup like:
Code:
oWorker.SetAPIcall "kernel32.dll", "CloseHandle", m_MyFileHandle
The worker can contain a batch call, of sorts too, if more than one action needs to be taken in a specific order:
Code:
oWorker.SetAPIcall "kernel32.dll", "UnmapViewOfFile", m_MyView
oWorker.SetAPIcall "kernel32.dll", "CloseHandle", m_MyFileHandle
' another example
oWorker.SetAPIcall "gdi32.dll", "SelectObject", Array(m_Hdc, m_OrigBmp)
oWorker.SetAPIcall "gdi32.dll", "DeleteObject", m_Bitmap
oWorker.SetAPIcall "gdi32.dll", "DeleteDC", m_Hdc
The worker will be able to accept multiple parameters also...
Code:
oWorker.SetAPIcall "kernel32.dll", "VirtualFree", Array(m_MemAddr, 0, MEM_RELEASE)
The idea is that the worker is setup very similar to how an API declaration is setup -- with some restrictions. It usage would typically contain stuff that is declared at class/form level that should be cleaned up in the unload/terminate event which won't happen if IDE terminates with "End"
-
Re: [vb6] IDE-Safety Thunks: A new breed
Quote:
Originally Posted by
LaVolpe
Each worker object will use about 32 bytes (not a typo).
MST thunks CoTaskMemAlloc between 16 and 24 bytes per COM object and their respective executable thunk code + vtable is VirtualAlloc'd once per process no matter if needed by self-contained thunks or by global module impl. Code address is shared through environment variables named "_MST_GLOBAL" & App.hInstance & "_" & sKey which is a bit awkward.
cheers,
</wqw>
-
Re: [vb6] IDE-Safety Thunks: A new breed
@wqweto. My factory approach appears similar to yours. A factory shares its thunk with any workers it spawns/workers (both being Com objects). The 2 VTables (factory/worker) are also included in the thunk. The workers currently use up to 28 bytes each. The factory object uses more, however only one instance of it is ever created. I could have opted to include much of the factory object data into the top/bottom of the thunk but chose against it and kept it with the Com object instead. A few different ways to skin that cat.
I have a slightly different approach to shared data/variables. Each worker has the address of the factory's object data. The worker's object data contains only per-instance variables. When a worker needs to access the shared data, it gets it from known offsets off the factory's object. These offsets are constants within the thunk.
The only thunk I have in this project, right now, that uses a factory approach is the GDI+ thunk. But I like it a lot and will convert all, except the COM-hook thunk, to factory/worker themes also.
-
Re: [vb6] IDE-Safety Thunks: A new breed
Yes, the GDI+ thunk has a lot of potential.
I'm considering impl DISPID_VALUE get/let on my cleanup thunk too (which is a whole new IDispatch interface). Will come handy for such use-cases, provided that performance testing graphics intensive routines does not foil the effort.
I'm very glad to see your alternative impl with new ideas and more use-cases.
cheers,
</wqw>
-
Re: [vb6] IDE-Safety Thunks: A new breed
Project and thunks have been completely rewritten. In short, the thunks now use a shared private heap that can significantly reduce overall memory usage when multiple thunks and taskers are used simultaneously. In addition, I've used a "class factory" approach. Each time a tasker is created, if it is the first tasker of its category (9 categories currently), then the factory is created along with the tasker. As each new tasker is created of the same category, that tasker uses the same thunk. Every thunk has been designed this way, regardless of past comments where I said there could/would be exceptions.
Also included with this update are more detailed documentation and two additional thunks. See first post at top of thread.
Also included are optional wrappers. The wrappers can be found in post #2 above. Wrappers enable early-bound method calling, intellisense capable.
This revision is completely incompatible with the previous. Do not overwrite your previous download if you still want that.
-
1 Attachment(s)
Re: [vb6] IDE-Safety Thunks: A new breed
Hi LaVolpe, thanks for this great project.
I tried to do some improvements to your project.
the changes i made are in 4 steps
step 1: mostly cosmetic and a few overflow bug fixes in pointer math.
step 2: created a typelib and added VBVM6Lib functions and replaced all possible thunkCopyMemory to their VBVM6Lib equivalent.
step 3: moved all API declaration to typelib and fixed calling code if required.
step 4: created all VTable interfaces in typelib and replaced Object with their equivalent interface.
now the taskers are early-bound and IDispatch creation is not required,
but the thunks are trying to free them on cleanup and will crash if IDispatch pointer is zero.
can you help me on the last part and maybe check if i didn't break anything in your code ?
Attachment 176971
-
Re: [vb6] IDE-Safety Thunks: A new breed
an update
in step 1: a missed overflow fix, changed the way locating main window works and used SetSafeArrayPtr instead of VarPtrArray
in step 2: replaced a few missed CopyMemory
-
Re: [vb6] IDE-Safety Thunks: A new breed
new update
replaced SetWindowSubclass with InitCommonControls for loading COMCTL32
replaced hMod Collection with long variable
replaced SetWindowSubclass Proc checking with GetWindowSubclass
attached file include:
"No TLB" version which is improved LaVolpe version with API declaration
"With TLB" version is the more improved version and will require the TLB
and the "Test" version that shows the Tasker Object can be replaced with interface from TLB, but i wasn't able to replace the TaskerObject of callback since it passed from Thunk
-
Re: [vb6] IDE-Safety Thunks: A new breed
last update
just a few small change and replaced If Err Then with If Err.Number Then which is faster
LaVolpe since i don't know much of assembly, if it's easy can you give me a few point to remove the IDispatch and replaced it with interface in your thunks ?
-
Re: [vb6] IDE-Safety Thunks: A new breed
LaVolpe did you found a time to look at this ?
-
Re: [vb6] IDE-Safety Thunks: A new breed
At first, I'm not sure how you are able to set your TLB interface to the object returned by the thunk functions.
1. Those functions return an Object supporting IDispatch
2. Your TLB interfaces do not support IDispatch and have their own GUIDs
3. When you attempt to set a TLB interface to an existing object, a QueryInterface on that object occurs and tests whether the object supports your GUID. If not, VB will throw an error like, not supported or something similar.
When QueryInterface is called on an object returned by the thunks class, the thunk logic I used goes something like this:
1. If asking for IUnknown, then return the object's pointer
2. If asking for IDispatch, then request is sent to the IDispatch itself for processing
3. Any other scenario, like your TLB GUIDs, the thunk returns E_NOINTERFACE which should generate a VB error.
Since your TLB interfaces fall into the 3rd scenario above, the thunk should reject your request & VB should error. You are saying that is not the case and that doesn't make sense to me.
Attachment 176719
-
Re: [vb6] IDE-Safety Thunks: A new breed
Quote:
Originally Posted by
LaVolpe
At first, I'm not sure how you are able to set your TLB interface to the object returned by the thunk functions.
i don't set it, i just return the object pointer as TLB interface type, since it's an interface describing your VTable.
in the attached example you can see that.
now i can remove the IDispatch and ITypeInfo part but the thunk still expect it, and since i don't understand assembly i can't remove it from the thunk.
also the thunk has to send the object pointer in TaskerObject variable instead of IDispatch object.
-
Re: [vb6] IDE-Safety Thunks: A new breed
Well, got good news & bad news. Bad news first. Attempting to modify the ASM would snowball into significant more work. Offsets within the compiled ASM would change, requiring the data in the clsThunks methods to be rewritten.
Good news next. Allow the object to be created, including the IDispatch. However, creative use of CopyMemory can get what you want with only 3 modifications per tasker creation method.
Important: In the sample code below, I'm using my original clsThunks, not the one you modified. But it is using your TLB. You are familiar with my original class but I'm not familiar with all your modifications, so defaulting to my class is common ground.
Using the 1st project sample (subclass and hook), make these changes:
1. In frmSample1
-- no changes from your version of the form, leave oSubClasser As ITskrSubclass & oHook As ITskrWinHook
2. In clsThunks.CreateTasker_Hook, make these changes
-- change my original method to return ITskrWinHook
Code:
replace: Set CreateTasker_Hook = oIUnk
with these 2 lines:
thunkCopyMemory CreateTasker_Hook, oIUnk, 4
thunkCopyMemory oIUnk, 0&, 4
3. In clsThunks.CreateTasker_Subclass, make these changes
-- change my original method to return ITskrSubclass
Code:
replace: Set CreateTasker_Subclass = oIUnk
with these 2 lines:
thunkCopyMemory CreateTasker_Subclass, oIUnk, 4
thunkCopyMemory oIUnk, 0&, 4
Now run the first sample. It should work as you expect. If happy with it, update the other clsThunks tasker methods using the same logic. What we are doing is a bit sneaky. We are creating the IDispatch (no change) and before we leave the tasker creation, we release IDispatch. But before that, we CopyMemory the tasker method's IUnknown onto your TLB tasker. If they have exactly the same methods, same order, same parameters, all should be good -- I'll leave that to you to verify. Now, even though the IDispatch was released, ASM doesn't release code until its IUnknown count decrements to zero. That won't happen until the returned TLB tasker goes out of scope or set to nothing.
Another bonus besides being an easy tweak is that it appears we keep IDE safety/anti-crashing in tact too. You'll want to test these tweaks compiled also to ensure successful tweaks. The downside to this is that I already have an updated version of this class but have not yet posted it. When I do, your TLB will need to be tweaked a bit, including new GUIDs probably, to use the new class.
-
Re: [vb6] IDE-Safety Thunks: A new breed
Quote:
Originally Posted by
LaVolpe
Good news next. Allow the object to be created, including the IDispatch. However, creative use of CopyMemory can get what you want with only 3 modifications per tasker creation method.
Important: In the sample code below, I'm using my original clsThunks, not the one you modified. But it is using your TLB. You are familiar with my original class but I'm not familiar with all your modifications, so defaulting to my class is common ground.
Using the 1st project sample (subclass and hook), make these changes:
1. In frmSample1
-- no changes from your version of the form, leave oSubClasser As ITskrSubclass & oHook As ITskrWinHook
2. In clsThunks.CreateTasker_Hook, make these changes
-- change my original method to return ITskrWinHook
Code:
replace: Set CreateTasker_Hook = oIUnk
with these 2 lines:
thunkCopyMemory CreateTasker_Hook, oIUnk, 4
thunkCopyMemory oIUnk, 0&, 4
3. In clsThunks.CreateTasker_Subclass, make these changes
-- change my original method to return ITskrSubclass
Code:
replace: Set CreateTasker_Subclass = oIUnk
with these 2 lines:
thunkCopyMemory CreateTasker_Subclass, oIUnk, 4
thunkCopyMemory oIUnk, 0&, 4
Now run the first sample. It should work as you expect. If happy with it, update the other clsThunks tasker methods using the same logic. What we are doing is a bit sneaky. We are creating the IDispatch (no change) and before we leave the tasker creation, we release IDispatch. But before that, we CopyMemory the tasker method's IUnknown onto your TLB tasker. If they have exactly the same methods, same order, same parameters, all should be good -- I'll leave that to you to verify. Now, even though the IDispatch was released, ASM doesn't release code until its IUnknown count decrements to zero. That won't happen until the returned TLB tasker goes out of scope or set to nothing.
Another bonus besides being an easy tweak is that it appears we keep IDE safety/anti-crashing in tact too. You'll want to test these tweaks compiled also to ensure successful tweaks.
well that is basically what i did in the attached test
i replaced this:
Code:
thunkCopyMemory oIUnk, hData, 4 ' create Tasker's IUnknown
with this:
Code:
thunkCopyMemory CreateTasker_Subclass, hData, 4 ' create Tasker's IUnknown
but the problem with TaskerObject in CallbackProc still remains, the IDispatch is send not the objects VTable.
so i can't replace TaskerObject As Object with TaskerObject As ITskrSubclass for example.
Quote:
Originally Posted by
LaVolpe
Attempting to modify the ASM would snowball into significant more work. Offsets within the compiled ASM would change, requiring the data in the clsThunks methods to be rewritten.
i have very limited ASM knowledge, can you instruct me to do it ?
or can't we just NOP the IDispatch part of ASM without changing the Offsets of it ?
PS:
WinMerge is very useful, you can easily compare two files and see the changes.
-
Re: [vb6] IDE-Safety Thunks: A new breed
I'll try to answer your question at the end. But first. You said you wanted to create the TLB to make the thunks early-bound, intellisense-capable. Ok, a good exercise I suppose. But you do realize (I think you do), that I added in post #2, sample wrappers to imitate early bound thunks. Also, inside each of those wrappers explains how to modify the callback TaskerObject so it can also use intellisense. I anticipated this type of question.
Now, to your latest question. Each thunk's ASM, has a routine titled: _GetObject. That routine determines what gets sent as TaskerObject to any callback methods. As currently coded, it is either a custom object you supplied with a call to the thunk object's WrapperObject method or it is the thunk object/IDispatch itself. In any case, it is an object, i.e., IDispatch. Your TLB interfaces do not implement IDispatch. The ASM was written to be owned indirectly by an IDispatch interface that also supports ITypeInfo. The correct answer to your question is: the ASM needs to be reworked to support your VTable-only interfaces, by GUID, and remove all logic that refers to any other interfaces: IDispatch, ITypeInfo.
Howver, if you do not need to use that parameter in any callbacks (just want to kinda disable it), try adding the host's ObjPtr via WrapperObject method. I see you did not include that method in your TLB. So if you try this, you'll need to set that inside the clsThunks tasker creation methods before they exit.
I am not volunteering to help you rewrite portions of the ASMs. I gave users a way to imitate/use intellisense in their projects (post #2).
-
Re: [vb6] IDE-Safety Thunks: A new breed
i use timer thunk in office 2007 x86
did i use it wrong?
Code:
Dim c As ITskrTimer
Private Sub UserForm_Initialize()
Set c = New ITskrTimer
End Sub
Private Sub UserForm_Click()
c.CreateTasker_Timer Me, 1, 1000, True
End Sub
Private Sub UserForm_Terminate()
c.Pause
Set c = Nothing
End Sub
Private Function myTimerProc(ByVal TaskerObject As Object, _
ByVal uTickCount As Long) As Long
Debug.Print uTickCount
End Function
-
Re: [vb6] IDE-Safety Thunks: A new breed
Quote:
Originally Posted by
LaVolpe
@loquat. What is ITskerTimer? If it is from my wrappers zip file in post #2, looks ok at first glance -- something wrong? does c return as Nothing? However, I am not saying the IDE safety within the thunks are guaranteed with Office, i.e., VBA. These thunks were designed for VB. IDE safety relies on a known API associated with VB and known code structure of compiled VB code. I do not know whether those knowns apply to VBA. I'd almost want to say, do not make that assumption and opt for no IDE safety if using these thunks in VBA. Otherwise, test it well by purposely placing STOP commands or breakpoints within your VBA code to see how the thunks perform when your code is paused.
the ITskerTimer is from yours, as well as clsThunk, and c return Not Nothing
You guess right, i m using it in vba, it is clearly not working on my office vba environment.
-
Re: [vb6] IDE-Safety Thunks: A new breed
Quote:
Originally Posted by
LaVolpe
You said you wanted to create the TLB to make the thunks early-bound, intellisense-capable. Ok, a good exercise I suppose. But you do realize (I think you do), that I added in post #2, sample wrappers to imitate early bound thunks. Also, inside each of those wrappers explains how to modify the callback TaskerObject so it can also use intellisense. I anticipated this type of question.
early-bound, intellisense-capable, faster and smaller.
first thing, i really appreciate your work, it's very great and i like it very much.
lately i had to optimize my project for speed and for that, i learned some tricks and habits in coding.
i reviewed your Thunks to replace my aged Paul Caton Subclass, and saw it can use some of that improvements which mostly involved the TLB.
i tried to contribute some help so that you implement it in your next version.
the core of your Thunks is the COM VTable, but to use it in VB you had to create the IDispatch (which is slow and adds to the size of your code), and for creating the IDispatch you had to create the ITypeInfo (which adds to the size of your code), and since IDispatch is not intellisense-capable you created the Wrappers (which again is slow and adds to the size of your code).
with TLB interfaces you create a shortcut and tell VB how to access the VTable directly, and your Thunks will become early-bound, intellisense-capable, faster and smaller.
also the APIs declared in TLB are faster and more flexible than VB declare and without the API declare your Thunks will be more portable, also you probably can remove pvInitFactory.
English is not my native language, sorry if what i said seemed rude or offensive.
-
Re: [vb6] IDE-Safety Thunks: A new breed
Quote:
Originally Posted by
loquat
You guess right, i m using it in vba, it is clearly not working on my office vba environment.
I took a closer look. These thunks cannot work with VBA. One of the things the thunks do is to store information on VB's hidden owner window. VBA doesn't have that owner window. I don't plan on making these compatible with VBA. There are other solutions for using timers and thunks with VBA, so you do have options. Thanks for the interest.
-
Re: [vb6] IDE-Safety Thunks: A new breed
Quote:
Originally Posted by
HosseinMoradi
English is not my native language, sorry if what i said seemed rude or offensive.
Nothing you said was offensive. I understand what you are saying. For most cases, late-bound objects are not going to cause any significant code slow-down. About the only exception I can think of is when a late-bound object is used within a lengthy loop. In order to use this with VTable-only TLB interfaces, the ASM will require significant work. Any shortcuts are likely to cause problems.
P.S. Want speed? Don't use thunks. Use standard subclassing directly within a module. Not IDE safe, but fast.
-
Re: [vb6] IDE-Safety Thunks: A new breed
Quote:
Originally Posted by
LaVolpe
There are other solutions for using timers and thunks with VBA, so you do have options. Thanks for the interest.
yes right, such as the trick and paul caton did, and even we can make a timer control for vba by using vb6 timer control
-
Re: [vb6] IDE-Safety Thunks: A new breed
Quote:
Originally Posted by
LaVolpe
P.S. Want speed? Don't use thunks. Use standard subclassing directly within a module. Not IDE safe, but fast.
yes but it's a nightmare in developing and debuging.
Quote:
Originally Posted by
LaVolpe
In order to use this with VTable-only TLB interfaces, the ASM will require significant work. Any shortcuts are likely to cause problems.
so keep this in mind for your next major version whenever you have time.
Quote:
Originally Posted by
LaVolpe
The downside to this is that I already have an updated version of this class but have not yet posted it. When I do, your TLB will need to be tweaked a bit, including new GUIDs probably, to use the new class.
when will you post your updated version ?
-
Re: [vb6] IDE-Safety Thunks: A new breed
Project updated. Summary of updates:
Major change includes reworking thunks to be smaller overall. Every thunk has destruction code that is nearly identical, along with other code that is very similar. A new "management" thunk was created that will have common-use methods that other thunks and the class can share. Related code was then removed from all existing thunks and also the clsThunks class itself -- now resides in the new thunk.
Functions changed to properties (consistency):
Custom Class: AddrOf, State, IsFiltered, IsManaged
Subclasser: IsFiltered, IsManaged
COM Hook: IsManaged, RefCount
CreateStub removed as public method from these taskers (didn't want these executed by users):
Callback, Custom Class, Windows Hook
New tasker methods (requested enhancement):
Callback: IDEredirectAddr property Get/Let added. See documentation, class comments
-- optional wrapper updated
-- if using TLB in post #25, cannot call these new properties using that TLB
Taskers that can now optionally forward events to bas-modules (optional & use at your own risk)
-- Note: IDE safety reduced when forwarding outside of the host
Callback, Custom Class, Windows Hook, Subclass, Timer
Class reorganized a bit, but every method had significant code changes.
Sample project modified slightly, previous project will work with this updated class.
A sincere "thank you" goes out to HosseinMoradi for enhancement suggestions and working with me offline for brainstorming ideas.
-
Re: [vb6] IDE-Safety Thunks: A new breed
very good work and dedication.
Greetings
-
Re: [vb6] IDE-Safety Thunks for subclassing, hooking and more: A new breed
Updated. This update contains these significant changes:
1. SUITE_VERSION constant added to all but one method. This constant will be used to help ensure thunk creation methods and support methods are of the same version. It is only an issue for you if you are picking and choosing which thunks to extract from clsThunks vs. using the entire class. The constant is also used to allow multiple version of any tasker's thunk to run side-by-side with other versions within the same heap; thus saves resources. That scenario can happen if you are including other people's code that may be using a different version tasker thunk than you.
2. All of the thunks are now written into BASE64 encoded strings vs. hardcoded array elements. The size savings is quite impressive. When the sample project was compiled before and after using the strings, file size nearly cut in half. The BASE64 strings make the thunk methods less messy and would be easier for others to update, if they so choose.
3. The COM-hook tasker was changed to include a new method: AddCoClass. That method is nearly identical to the AddItem method. But each are used for different types of interfaces. Use AddCoClass when wanting to hook VB objects like stdPictures, i.e., CoClasses. Use AddItem when wanting to hook VTable-only interfaces or non-CoClass objects. The change was needed for the thunk to know when to hook a secondary interface or not. Assumptions were made that were not 100% accurate. The readme file has more information. This change required tweaking the sample project and the optional wrapper.
FYI: For anyone that was playing with the TLB which was posted on this thread, the COM-hook tasker is no longer compatible with it. You won't find that TLB on this thread any longer; it was removed by request.
-
Re: [vb6] IDE-Safety Thunks for subclassing, hooking and more: A new breed
So just to clarify before I go rewriting; does the class need to be separate, or can I place the code into a form/class/UC like the previous thunks for self-sub?
Also, it says incompatible with VBA, but was the self-sub stuff you and Paul Caton did also incompatible?
-
Re: [vb6] IDE-Safety Thunks for subclassing, hooking and more: A new breed
fafalone. The documentation and comments answer your question. You can pick & choose which thunks to add to any project or add the entire class. If you pick & choose, you'll need to include the declarations section and the two private support functions with whatever thunks you like. The pick & choose option is why I named the APIs in the class the way I named them -- not to step on existing code that this class code may be copied & pasted into.
It's incompatible with VBA because I use the hidden VB owner window (which VBA doesn't have) to cache properties (SetProp API) so the thunks know how to find each other, tracking reference counts, etc. You'll catch on if you didn't already, that the class does not communicate with the taskers after they're created unlike many previous projects by myself & others. The documentation helps explain that too. I think the biggest reason is that it shouldn't be compatible with 64bit since the ASM is 32bit and I'd say a fair amount of potential users are on 64bit Office.
Edited. Didn't really answer that last question completely. Paul Caton's and my previous work should be compatible with Office 32bit.