4
votes

How do you properly unload a DLL from all processes when the system-wide hook that loaded them gets unloaded?

From MSDN:

You can release a global hook procedure by using UnhookWindowsHookEx, but this function does not free the DLL containing the hook procedure. This is because global hook procedures are called in the process context of every application in the desktop, causing an implicit call to the LoadLibrary function for all of those processes. Because a call to the FreeLibrary function cannot be made for another process, there is then no way to free the DLL. The system eventually frees the DLL after all processes explicitly linked to the DLL have either terminated or called FreeLibrary and all processes that called the hook procedure have resumed processing outside the DLL.

So what I am looking for, is a method to detect when the hook is unhooked, and then call FreeLibrary from all the processes that were hooked. Are there any other ways to cause instant unloading of a DLL when the hook is unloaded?

2

2 Answers

5
votes

Hook dll are unloaded in their message loop. Forcing them to pass in the message loop help to unload them.

Add this after your UnhookWindowsHookEx to force all message loops to wake up :

DWORD dwResult;
SendMessageTimeout(HWND_BROADCAST, WM_NULL, 0, 0, SMTO_ABORTIFHUNG|SMTO_NOTIMEOUTIFNOTHUNG, 1000, &dwResult);

However I still have the issue from time to time. I don't know where it's coming from. I suppose a locked process could prevent the dll to unload, but I have no proof of that.

1
votes

In general you should use global windows hooking if FreeLibrary is not a required be called. If you do want to do this you can use DLL injection (see for example http://www.codeproject.com/KB/threads/winspy.aspx and http://www.codeproject.com/KB/system/hooksys.aspx) with respect of CreateRemoteThread and LoadLibrary technique. In the case you can do what you want in the remote process. You can combine both techniques.

If you want call FreeLibrary only to do an update of the DLL you can do this in another way. Every DLL which is loaded could be renamed (in cmd.exe for example) to a temporary name and you can call MoveFileEx with MOVEFILE_DELAY_UNTIL_REBOOT flag. Then you can already copy and use new version of the DLL. The old one DLL will be deleted at the next reboot of computer.