> Also, is there a way to get Explorer to unload DLLs? I'm thinking
something
> like calling CoFreeUnusedLibraries in the shell's context - or do I have
to
> add the AlwaysUnloadDll key/value?
I've tried adding it both as a key and a value and didn't see any
difference. I have noticed that (at least on NT) deleting a file causes
Explorer to free unused DLLs. (You don't actually have to delete the file -
you can say no to the prompt.)
Cheers,
- Jim
Dumb little things like this make me swear I wish I had the source code to
Windows 9x/NT/2000.
--
(My return address is intentionally invalid to foil spammers. Delete the
".---" to get my real address. I do this on my own time with my own money;
my responses are not to be considered official technical support or advice.
Personal requests for assistance will be ignored.)
Is that what the LoadWithoutCOM registry entries are for? For example, in
the My Documents\InProcServer32 key?
Partly, yes.
:-) Any more info forthcoming? Or is this straying too far into undocumented
territory...
That sounds reasonable. Is there ever a situation where 3rd party shell
extensions (namespaces, etc) need this?
Thanks for the information, but that doesn't solve my problem.
Background:
I have written a VB5 AX DLL that gets loaded into Explorer by a helper EXE
that calls SHLoadInProc with the CLSID of the DLL.
Right now the DLL's sole purpose is to get the text of taskbar buttons and
each button's bounding rectangle and write this out to a text file i.e. it
is not a namespace extension or anything similar and doesn't appear in the
registry apart from being under HKEY_CLASSES_ROOT\CLSID\<CLSID of DLL>
However, after I finish writing the file in my Class_Initialize, I notice
that the DLL is still around... I can't delete the file, etc.
This seems to be at variance with the documentation in shlobj.h that an
object of the specified CLSID is created and then released, and seems to
tend towards the SDK docs.
The question remains: how do I tell Explorer that the DLL has done its work
and has to be unloaded?
--
Damit Senanayake | da...@mvps.org
Please reply to newsgroups, not by e-mail.
http://members.xoom.com/damit | {http,ftp}://damit.dyndns.org (experimental)
ICQ: 6930718
Have you considered using ITaskBar?
Sorry, that should be ITaskbarList...
Matthew,
The ITaskbarList interface can't be used to get the size, position or
text of the taskbar buttons. Only to add/remove buttons and activate
them.
Mattias
__________________________________________________
Mattias Sjögren (MCP) - mattiass @ hem.passagen.se
VB+ http://hem.spray.se/mattias.sjogren/
Please send questions/replies to the newsgroups
I should duck the COM/OLE questions about which I know very little.
However, if you are sure that there is no longer a possibility for anything
to call you then you should be able to FreeLibraryAndExitThread() when you
are done, no?
Regards,
Will
: Have you considered using ITaskBar?
Actually, I did think about it. But, there are some problems:
1. It's only supported in shell32.dll 4.71 - the SHLoadInProc alternative
works fine on 95, 98, NT4 and 2000 (all I need to do is change the code for
finding the SysTabControl32 window)
2. Like Mattias said, it only lets you add, remove and activate buttons.
3. I would have to go and write the IDL, which I'm too lazy to do. :-(
Also, if you could help me with another issue:
TCM_GETITEM/TCIF_TEXT does not seem to work with taskbar buttons... should I
be using the TCITEMHEADER UDT instead?
OK, that [FreeLibraryAndExitThread()] was *not* the solution - that only
kills off Explorer :-(
True. The text comes from the caption text of all top-level application
style windows. As for the size and position, well, this information can't be
got without messing around with hooks and so on.
I guess I misunderstood. <G> Sorry. As I said, I should duck the COM
questions.
But it seems to me that if your only problem is a DLL that won't go away
then FreeLibrary() ought to be the ticket. Did Explorer go away because the
thread that exited was Explorer's or because of a side effect of unloading
the DLL. If the former, perhaps you could do the FreeLibrary() thing in a
secondary thread.
Regards,
Will
Damit,
The ITaskbarList interface is a small one, and it's pretty VB friendly
as it is, so that should be a five minute cut n' paste job :-)
If you're still too lazy to do it, you could steal mine from
http://hem.spray.se/mattias.sjogren/bin/itbl10.zip
Hmmm, questions: what does SHLoadInproc actually do?
I mean you have told explorer to load an "object" specified by CLSID, but I
can't see what explorer will possibly do with the said object (short of
simply calling Release() once its finished loading it).
I suppose a more direct way of finding out is: Does the objects
QueryInterface get called, for what interfaces (and how do you find any of
this out under VB!?).
Other than that... OLE / COM / the shell does not have obvious associations
between object instances and the dll's they are hosted in. Thus, when an
object is released OLE doesn't know that the dll module is not being used,
so it says in memory for a while.
OLE has a worker thread that periodically calls the DllCanUnloadNow()
methods exported by all "good" COM inproc dll's, and OLE will FreeLibrary
any libraries that answer "S_OK". Once again, VB has probably hidden this
call from you so I have no clue how to diagnose the situation. Nonetheless,
there can be a large interval between the objects in a inproc Dll being
freed, and OLE realising it can free the Dll.
Search MSDN for information for information on "AlwaysUnloadDll"
Creating a value thus (.reg file syntax):
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\explorer]
AlwaysUnloadDll=1
should result in explorer unloading inproc dll's very fast which might just
help you :)
If this doesn't help then you Dll is getting stuck in memory with unreleased
objects - I have no clue how to resolve that short of putting breakpoints in
all the objects AddRef and Reease methods to find out which one is not being
released by what.
Chris
--
VisualC++ & Win32 FAQ: http://www.mvps.org/vcfaq
My Win32 Page: http://www.mvps.org/user32
[FreeLibraryAndExitThread()]
: I guess I misunderstood. <G> Sorry. As I said, I should duck the COM
: questions.
Err... FreeLibraryAndExitThread has *nothing* to do with COM... well, unless
your COM object is a DLL. :-)
: But it seems to me that if your only problem is a DLL that won't go away
: then FreeLibrary() ought to be the ticket. Did Explorer go away because
the
: thread that exited was Explorer's or because of a side effect of unloading
: the DLL.
I think the former reason was it (I exited Explorer's thread :-) )... I
strongly doubt that it was a side effect.
:If the former, perhaps you could do the FreeLibrary() thing in a
: secondary thread.
Where would the secondary thread be? Would it be in the helper EXE?
--
: The ITaskbarList interface is a small one, and it's pretty VB friendly
: as it is, so that should be a five minute cut n' paste job :-)
Yeah, I can spend 5 minutes loading the IDL into VC++. Oh wait, TextPad is
there, and I added tool commands to run MIDL, CL/link, VB5 in make mode and
UUIDGen. Can you say new IDE? <g>
: If you're still too lazy to do it, you could steal mine from
:
: http://hem.spray.se/mattias.sjogren/bin/itbl10.zip
OK, I'm too lazy. :-)
>I suppose a more direct way of finding out is: Does the objects
>QueryInterface get called, for what interfaces (and how do you find any of
>this out under VB!?).
Only for IUnknown when the object is created.
: I mean you have told explorer to load an "object" specified by CLSID, but
I
: can't see what explorer will possibly do with the said object (short of
: simply calling Release() once its finished loading it).
Well, the SDK docs (January 2000) don't provide any mention of what explorer
does with the object...
"Creates an instance of the specified object class from within the context
of the shell's process. "
... while shlobj.h has a semi-cryptic comment that explorer immediately
calls Release() on the object:
>>
// -------------------------------------------------------------------------
//
// SHLoadInProc
//
// When this function is called, the shell calls CoCreateInstance
// (or equivalent) with CLSCTX_INPROC_SERVER and the specified CLSID
// from within the shell's process and release it immediately.
//
// -------------------------------------------------------------------------
<<
As for your other questions, the DLL is in fact loaded, an object of the
specified CLSID is created (implying I suppose that QI() succeeds), the DLL
does its work in Class_Initialize, and then.... it just hangs around.
: putting breakpoints in
: all the objects AddRef and Reease methods
Eeek... well, that is not something you can do easily from VB without vtable
hacks :-(
[AlwaysUnloadDll]
According to Raymond, Explorer on Win2000 now ignores this - there's a
LoadWithoutCOM value under each registered shell extension. But I am NOT a
shell extension! <sob>
And LoadWithoutCOM is totally undocumented, and may even be obsolete, now
that Windows 2000 uses COM properly.
And in any case, it doesn't apply here. :-(
The basic procedure behind SHLoadInProc is as follows: the system loads your DLL
with LoadLibraryEx, it calls your DllGetClassObject function to obtain a
ClassFactory for your class, and then asks the ClassFactory to create an instance
of your class. As has been noted, SHLoadInProc, then immediately calls Release on
that instance. It is worth noting, that it is actually the ClassFactory's
CreateInstance function that calls your object's QueryInterface to get an
IUnknown interface. This isn't something that SHLoadInProc does, and your
ClassFactory doesn't necessarily have to make such a call.
Something else worth noting, is that if you're using SHLoadInProc as a means of
injecting your DLL into Explorer's address space, you can achieve that without
even creating a ClassFactory, let alone an instance of the actual class object.
When the system calls your DllGetClassObject function, you are already in
Explorer's address space. Once you have done whatever it is you need to do, you
could just return CLASS_E_CLASSNOTAVAILABLE and let the error fall through -
Explorer doesn't care. There are actually certain advantages to this which I'll
explain later.
The problem that arises with SHLoadInProc, is the issue of when to unload the
DLL. On NT 4.0 (and assumedly Windows 9x), the DLL's handle is added to an
internal table where it is kept until a cleanup routine gets around to freeing
it. Every 10 minutes, the system will call the DLL's DllCanUnloadNow function and
if it returns S_OK it should unload it. Unfortunately this means that you have to
wait at least 10 minutes for your DLL to unload.
As was mentioned by someone else in this thread, you can set a registry key to
speed up this unloading process (10 seconds instead of 10 minutes). However, this
key effects all DLLs used by the shell so it may have an adverse effect on system
performance. Incidently, if your DLL doesn't export a DllCanUnloadNow function,
it will never get unloaded, regardless of any registry settings.
On Windows 2000 things are slightly different. The shell usually just calls OLE's
CoCreateInstance to do all the work, but it essentially follows the same
procedure. I don't know much about the OLE side of things, but I'm assuming
you're still stuck with your DLL in memory until somebody decides to call
CoFreeUnusedLibraries, which may be just as long as the 10 minute delay.
The nice thing about CoCreateInstance, though, is that if you return an error
from the DllGetClassObject function, OLE will immediately unload your DLL (which
is assumedly what you want). This trick doesn't work on NT 4, though, since the
system caches your DLL's handle long before the call to DllGetClassObject. By the
time it calls DllGetClassObject, it is just using a function pointer and has no
way of knowing the DLL handle associated with that fuction.
There is a way of forcing the shell to unload DLLs without waiting 10 minutes,
but it is kinda complicated. There is an undocumented function in shell32.dll
called SHFreeUnusedLibraries which will immediately unload any DLLs that return
S_OK from their DllCanUnloadNow function. However, this function has to be called
from within Explorer, so you are again stuck with the problem of injecting a DLL
into Explorer's address space. Since this DLL will be in use at the time of the
call to SHFreeUnusedLibraries, it won't be unloaded so you're back where you
started. I suspect there are workarounds to this though.
One other thing: somebody asked about the the taskbar and obtaining the text from
the tab control. The reason you don't get anything from TCM_GETITEM/TCIF_TEXT is
because the shell never sets anything in the pszText field. However, it does set
the lParam to the handle of the window, so you should be able to get the text by
calling GetWindowText for each handle.
I hope that helps.
Regards
James Holderness
http://www.geocities.com/SiliconValley/4942/
: My apologies for joining this discussion so late, but I don't often read
: newsgroups and I only came across this thread recently. However, I have a
certain
: amount of knowledge about the SHLoadInProc function which may answer some
of the
: questions that have been asked in this thread.
Cool, let's hear it...
[SHLoadInProc internals]
[DllGetClassObject & CLASS_E_CLASSNOTAVAILABLE]
That is definitely interesting, but I should mention that I'm using VB5 to
write the DLL, so I have *no* control over DllCanUnloadNow,
DllGetClassObject, etc. (not even AddRef and Release!) Now, if this were a
C++ DLL...
: The problem that arises with SHLoadInProc, is the issue of when to unload
the
: DLL. On NT 4.0 (and assumedly Windows 9x), the DLL's handle is added to an
: internal table where it is kept until a cleanup routine gets around to
freeing
: it. Every 10 minutes, the system will call the DLL's DllCanUnloadNow
function and
: if it returns S_OK it should unload it. Unfortunately this means that you
have to
: wait at least 10 minutes for your DLL to unload.
That may be so, but in this case I noticed that even after 1 hour, the DLL
was still loaded. I experienced this when trying to compile and got
Permission Denied errors - meaning that something is still holding on to the
DLL.
: As was mentioned by someone else in this thread, you can set a registry
key to
: speed up this unloading process (10 seconds instead of 10 minutes).
However, this
: key effects all DLLs used by the shell so it may have an adverse effect on
system
: performance. Incidently, if your DLL doesn't export a DllCanUnloadNow
function,
: it will never get unloaded, regardless of any registry settings.
It did on my PC - the VM size of W2k explorer increased up to 212MB :-(
[Win2k behaviour and NT4 "bug"]
[undocumented function: SHFreeUnusedLibraries]
Thanks for the mention of this function, but like you said, it requires
*another* DLL to call this, and that won't get unloaded, etc.
: One other thing: somebody asked about the the taskbar and obtaining the
text from
: the tab control. The reason you don't get anything from
TCM_GETITEM/TCIF_TEXT is
: because the shell never sets anything in the pszText field. However, it
does set
: the lParam to the handle of the window, so you should be able to get the
text by
: calling GetWindowText for each handle.
Hmm, interesting. You've made me a happy man. ;-)
: I hope that helps.
Sure does! This is an excellent post, BTW.
Are you perhaps using Windows 2000? I just did a couple of tests with
SHLoadInProc on W2K and it appears that they no longer bother about unloading
DLLs at all. The 10 minute unload time of Win9x and NT4 may have been annoying,
but it was better than nothing. Oh well, that's Microsoft for you - always
looking to improve their products.
If you aren't using W2K then you must be missing the DllCanUnloadNow function, or
it's not working properly (i.e. it's not returning S_OK when it should be). If
that's all handled internally by VB then I suppose there's not much you can do
about it unless there are reference counts or something that you aren't
decremented when necessary.
> [undocumented function: SHFreeUnusedLibraries]
>
> Thanks for the mention of this function, but like you said, it requires
> *another* DLL to call this, and that won't get unloaded, etc.
Yes, but as I said there are probably workarounds for this, although they may not
be so easy to implement in VB. One possibility is to inject the DLL using some
other method that doesn't have this problem with unloading DLLs (e.g. using a
message hook - at least I think that'll work). Of course, if you could do that
then you probably wouldn't need to use SHLoadInProc in the first place.
Another possibility is to try and arrange that your DLL reference count is zero
by the time SHFreeUnusedLibraries gets called. I can think of two ways of doing
this. One would be to call CreateThread using SHFreeUnusedLibraries as the thread
function, just before your Release method returns. With any luck, by the time
SHFreeUnusedLibraries starts executing, your DLL will no longer be in use. The
problem with this is that SHFreeUnusedLibraries takes no parameters, and the
thread function is supposed to take one. It's possible that this may not be an
issue though - depends on how the OS implements threads.
The other possibility is to force your object's Release method to return
"through" the SHFreeUnusedLibraries function. In assembly you could do this by
JMPing to SHFreeUnusedLibraries instead of returning. In C and VB it's probably a
little more complicated. I'm almost sure I came up with an easy way of doing
this, but I've now forgotten what it was. Perhaps somebody else in this newsgroup
will have some suggestions.
Yes, I'm running W2K Pro RTM (2195)...
: The 10 minute unload time of Win9x and NT4 may have been annoying,
: but it was better than nothing. Oh well, that's Microsoft for you - always
: looking to improve their products.
I'll have to reboot to verify this :-( , but I believed that 98SE also did
something similar. OTOH, there is a new Side-by-side DLL sharing technology
(there's an MSDN technical article on it), so maybe the lack of unloading
DLLs indicates that you're supposed to use this instead...
: If you aren't using W2K then you must be missing the DllCanUnloadNow
function, or
: it's not working properly (i.e. it's not returning S_OK when it should
be). If
: that's all handled internally by VB then I suppose there's not much you
can do
: about it unless there are reference counts or something that you aren't
: decremented when necessary.
I don't think it's missing the DllCanUnloadNow function. When I create an
instance of the DLL normally (i.e. not using SHLoadInProc) and then
terminate it (Set obj = Nothing), using Comspy I see that DllCanUnloadNow is
indeed called.
: Yes, but as I said there are probably workarounds for this, although they
may not
: be so easy to implement in VB. One possibility is to inject the DLL using
some
: other method that doesn't have this problem with unloading DLLs (e.g.
using a
: message hook - at least I think that'll work). Of course, if you could do
that
: then you probably wouldn't need to use SHLoadInProc in the first place.
A message hook (I assume you're talking about WH_GETMESSAGE, or do you mean
WH_CALLWNDPROC?) sounds interesting, and could possibly eliminate the use of
SHLoadInProc. I'll have to look into this, although since the hook has to be
system-wide that means I have to use a C DLL, which makes things a *little*
more complicated...
: Another possibility is to try and arrange that your DLL reference count is
zero
: by the time SHFreeUnusedLibraries gets called. I can think of two ways of
doing
: this. One would be to call CreateThread using SHFreeUnusedLibraries as the
thread
: function, just before your Release method returns. With any luck, by the
time
: SHFreeUnusedLibraries starts executing, your DLL will no longer be in use.
The
: problem with this is that SHFreeUnusedLibraries takes no parameters, and
the
: thread function is supposed to take one. It's possible that this may not
be an
: issue though - depends on how the OS implements threads.
Is the ordinal number for SHFreeUnusedLibraries constant across platforms?
As far as possible, I am trying to go for solutions that work on all
platforms... Also, I have no control over IUnknown::Release, but would it be
acceptable if I do that after finishing my work (i.e. at the end of
Class_Initialize()) or will that leave the VB runtime library in an invalid
state?
: The other possibility is to force your object's Release method to return
: "through" the SHFreeUnusedLibraries function. In assembly you could do
this by
: JMPing to SHFreeUnusedLibraries instead of returning. In C and VB it's
probably a
: little more complicated. I'm almost sure I came up with an easy way of
doing
: this, but I've now forgotten what it was. Perhaps somebody else in this
newsgroup
: will have some suggestions.
I don't have any experience with doing this (patching code) but do you have
any other suggestions?
I tested my code on a Win98 system (not sure if it was SE - the version number
was listed as 4.10.1998), and it unloaded happily after the 10 minute delay. It's
quite possible that later updates to the OS or Internet Explorer upgrades changed
over to the W2K method though.
> (there's an MSDN technical article on it), so maybe the lack of unloading
> DLLs indicates that you're supposed to use this instead...
I'm afraid I know very little about any of this new technology, but I'm almost
sure that the shell doesn't use it when loading DLLs. This means that any shell
extensions that the shell loads are probably going to be stuck in memory until
the system shuts down.
> A message hook (I assume you're talking about WH_GETMESSAGE, or do you mean
> WH_CALLWNDPROC?) sounds interesting, and could possibly eliminate the use of
I'm not sure that it matters which of those types you use - the general idea is
to install a hook DLL that gets called from within the target process's address
space. You then send a WM_NULL message to the process's window to force the hook
to be called. I've never actually done this before, but it seems to be a commonly
used technique so there are bound to be code samples lying around on the net.
> Is the ordinal number for SHFreeUnusedLibraries constant across platforms?
As far as I'm aware, yes. The ordinal is 123.
> platforms... Also, I have no control over IUnknown::Release, but would it be
> acceptable if I do that after finishing my work (i.e. at the end of
> Class_Initialize()) or will that leave the VB runtime library in an invalid
> state?
I don't know - what is essential is that no code of yours is being executed at
the time that SHFreeUnusedLibraries is called. Your DLL is going to be released
from memory and any code that is executing will suddenly be trying to access non
existant memory. If you create the thread using VB routines, that may also be a
problem because the thread function may attempt to return to internal VB code
which no longer exists once your DLL unloads.
> : The other possibility is to force your object's Release method to return
> : "through" the SHFreeUnusedLibraries function. In assembly you could do
>
> I don't have any experience with doing this (patching code) but do you have
> any other suggestions?
Here's an idea that might work (at least in C). First you replace the vtable
pointer to your Release method with a pointer to a function that takes no
parameters (i.e. its return will be a regular RET rather than RET 4). You then
use a local variable to determine the position of the return address on the stack
and the this pointer that is immediately above it. You replace the this pointer
with the return address, and replace the return address with the address of
SHFreeUnusedLibraries.
When you return from this function, instead of returning to the original return
address, you will now jump to the SHFreeUnusedLibraries function. When it
attempts to return, it will then use the original return address. From there on
everything should be safely in the hands of the shell. At least that's the theory
- I've never tested this. Of course in VB this is probably out of the question.
: I tested my code on a Win98 system (not sure if it was SE - the version
number
: was listed as 4.10.1998), and it unloaded happily after the 10 minute
delay. It's
: quite possible that later updates to the OS or Internet Explorer upgrades
changed
: over to the W2K method though.
That's not SE - SE is 4.10.2222. I think it's likely that SE was the first
Win9x variant to use the new behaviour.
: I'm afraid I know very little about any of this new technology, but I'm
almost
: sure that the shell doesn't use it when loading DLLs. This means that any
shell
: extensions that the shell loads are probably going to be stuck in memory
until
: the system shuts down.
Here's a link: http://msdn.microsoft.com/library/techart/sidebyside.htm
[cut]
: As far as I'm aware, yes. The ordinal is 123.
Again, it takes no parameters, correct? What would I pass for the lpParams
argument of CreateThread, in that case; is 0 (NULL) a safe bet?
: I don't know - what is essential is that no code of yours is being
executed at
: the time that SHFreeUnusedLibraries is called.
I think I can get away with doing this...
: If you create the thread using VB routines, that may also
be a
: problem because the thread function may attempt to return to internal VB
code
: which no longer exists once your DLL unloads.
No, I'm afraid you can't create a thread using VB routines. Not yet, anyway
:-)
[vtable replacement and substituting the address of SHFreeUnusedLibraries]
: Of course in VB this is probably out of the
question.
What, vtable replacement? No, certainly not impossible -- there are samples
around the 'net on how to do it.
The patching of the address might be a little more work, but I think
VirtualProtectEx and VirtualQueryEx should be able to do it.
--
Damit Senanayake | da...@mvps.org | http://members.xoom.com/damit
Please reply to newsgroups, not by e-mail. | ICQ: 6930718
From what I can make out, that side-by-side stuff has got nothing to do with DLL
unloading. It's really just another way to get poor unsuspecting users to fork
out for more memory and disk space. Perhaps it'll also help alleviate the DLL
Hell problem to a certain extent, but I doubt it.
> : As far as I'm aware, yes. The ordinal is 123.
>
> Again, it takes no parameters, correct? What would I pass for the lpParams
> argument of CreateThread, in that case; is 0 (NULL) a safe bet?
NULL is as good a bet as anything. If the parameter does make a difference then
it'll probably be something very specific to the OS version and build.
> What, vtable replacement? No, certainly not impossible -- there are samples
> around the 'net on how to do it.
In that case, I would think that the vtable replacement method is probably the
safest. You need to make sure that you only do the fancy patching stuff on the
last Release though (i.e. when you delete your object).
> The patching of the address might be a little more work, but I think
> VirtualProtectEx and VirtualQueryEx should be able to do it.
I wouldn't have thought you would need to do anything fancy like that (unless
that's for VB reasons). The addresses that you are patching are all on the stack
so you already have write access to them.
: From what I can make out, that side-by-side stuff has got nothing to do
with DLL
: unloading. It's really just another way to get poor unsuspecting users to
fork
: out for more memory and disk space. Perhaps it'll also help alleviate the
DLL
: Hell problem to a certain extent, but I doubt it.
I suppose it is another way of promoting hard drive and memory
manufacturers' interests, yes. :-) It should, in theory at least, reduce DLL
Hell because applications can use their own copies without affecting the
system shared DLL. And in Windows 2000, WFP helps this also.
You are right; that doesn't seem to have anything to do with this problem,
but I brought it up as something you might (potentially) be interested in.
: NULL is as good a bet as anything. If the parameter does make a difference
then
: it'll probably be something very specific to the OS version and build.
In which case, this solution becomes a large hack. :-)
: In that case, I would think that the vtable replacement method is probably
the
: safest. You need to make sure that you only do the fancy patching stuff on
the
: last Release though (i.e. when you delete your object).
Based on your ideas, I came up with the following (pseudo-code), I haven't
tested it yet though:
Get address of Release() (should be ObjPtr(Me) + 8 if Release() comes after
AddRef() but before QueryInterface())
Save address to variable
Replace address by address of my Release()
If the reference count is > 1, call the original Release() (using the
variable)
If the reference count = 1, then call SHFreeUnusedLibraries and then return
[cut]
Yeah. This is the easiest method to implement though, so it would be nice if it
worked.
> Based on your ideas, I came up with the following (pseudo-code), I haven't
> tested it yet though:
>
> Get address of Release() (should be ObjPtr(Me) + 8 if Release() comes after
> AddRef() but before QueryInterface())
> Save address to variable
> Replace address by address of my Release()
> If the reference count is > 1, call the original Release() (using the
> variable)
> If the reference count = 1, then call SHFreeUnusedLibraries and then return
I'm afraid it's not that easy. Remember your special Release function is going to
have a different return format because it has no "this" parameter. If you return
from such a function without doing something to the stack, it will screw up. The
whole point of the special return format, is so that you can patch the stack and
make it return "through" SHFreeUnusedLibraries. If you were to make the call
directly from within your own code it wouldn't work, because by the time
SHFreeUnusedLibraries returned your code would no longer be in memory.
Since you don't have direct access to the existing AddRef and Release methods,
you're going to have to replace both of them with functions of your own (they
must still have a "this" parameter though since they need to have regular return
formats). Most of the time they will do nothing except call back down to the
orginal methods and return whatever value was returned to them.
Once your reference count gets down to 1 though (i.e. the next call to Release
will be the final one), you should replace your current Release function with the
special Release function that has no "this" parameter". At this stage, your
AddRef function will have to watch out for the case when the reference count goes
down to 1 but then gets AddRefed up to 2 again before you get another Release
call. If that happens you'll need to put your Release method back to normal again
and continue waiting for the ref count to get back down to 1.
If your special Release function does eventually get called (the one with no
"this" parameter) you can now to do the fancy patching that'll force it to return
"through" the SHFreeUnusedLibraries function. You must first call the original VB
Release function so that it can free the memory for your object. For this you'll
need to extract the "this" pointer from the stack (remember this special function
doesn't have a "this" pointer that you can access directly). When you're finished
calling the orginal Release function, you can then replace the this pointer with
your current return address, and replace the return address with the address of
SHFreeUnusedLibraries (as I explained in one of my previous messages).
When you return from this function, your object will have been deleted and the
only code still executing will belong to the shell. SHFreeUnusedLibraries will
return directly to your orginal return address without having to pass through any
of your code, so it won't matter that your DLL has been unloaded from memory.
By the way, my apologies for taking so long to reply, but I've been away from
work all week and haven't had access to the internet.