Introduction
Many developers who wrote their own Input Methods (IM) for the Software Input Panel (SIP) on Pocket PC, or those who developed uninstallers for custom Input Methods faced the problem of unloading their DLLs in order to update them or remove them from the device. However it seems a bit hard to find any information on how to achieve this without rebooting the system. This short article aims to solve this problem.
Background
According to the official documentation, the SIP should release the Input Method COM object just after calling of IInputMethod->Deselect()
method, i.e. just after another Input Method is selected. And I can confirm the SIP does release it. So it seems that the easiest method would be to simply select another Input Method. This does not work however.
As you know, the COM subsystem does not unload unused DLLs immediately. Instead, it waits for the user code to call CoFreeUnusedLibraries()
or CoFreeUnusedLibrariesEx()
. The SIP does call the first function, but, unfortunately, this is not enough to unload all unused input methods immediately. Inside this function, the COM subsystem calls DllCanUnloadNow()
routine for all DLLs that do not seem to be used anymore and puts all those DLLs that returned S_OK
(DLL can be unloaded) into a list of candidates for unloading. When a DLL from this list is needed to the COM again (for example, the application requests to create a new COM object that sits in this DLL), the DLL is removed from this candidate list. If some DLL is still sitting in the candidate list when either CoFreeUnusedLibraries()
, or CoFreeUnusedLibrariesEx()
is called again, this DLL finally gets unloaded if and only if enough time has passed since putting the DLL into the list.
When using CoFreeUnusedLibrariesEx()
this time interval can be specified as the first parameter, but for CoFreeUnusedLibraries()
used by the SIP this timeout is the system default value, which is 10 minutes! Quite a long time to ask the user to wait for...
Solution
One of the possible ways to workaround this problem would be to call CoFreeUnusedLibrariesEx()
from the same process where the SIP lives with zero or near to zero timeout parameter. Fortunately, this is easily to achieve, because the SIP is implemented as a device driver and resides where all device drivers reside--in the device.exe process. So we can create a simple driver, which calls the needed function(s), then load it and enjoy the possibility to remove our DLL. Sure, the input method should be deselected before this. There is also another trick.
The driver would be simpler, if we could call CoFreeUnusedLibrariesEx()
from the Init()
entry point. This does not work, because the COM subsystem needs the owner process ID to be equal to the current process ID (device.exe), for which we need DLLs to be unloaded. However the owner process ID inside the Init()
call is equal to the ID of process which loaded our driver (i.e. our example program), and therefore COM tries to unload COM DLLs for this example application.
To overcome this, we just need to create another thread and do our call from the context of this thread. Owner process ID for this thread is equal to the ID of device.exe, where the SIP and Input Methods live.
Using the Code
The example can be compiled with eVC++ 4.0. There are two projects in the archive: a simple device driver, which when loaded calls CoFreeUnusedLibrariesEx()
as described above; and a simple executable that loads this driver.
The driver exposes a minimal set of required entry points (Init/Deinit
, Open/Close
, IOControl
), because we just need it to be loaded into the device.exe, nothing more.
Notes
When it is needed to unload the Input Method DLL while an application is being uninstalled from the device via the Remove Programs applet, it is possible to use a Custom Setup DLL as a driver itself by providing needed entry points, i.e. there is no need in a separate DLL.
I hope this article will be helpful to Input Method writers. Do not hesitate to comment or ask any questions.