65.9K
CodeProject 正在变化。 阅读更多。
Home

如何在 Pocket PC 上卸载输入法 DLL

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.63/5 (6投票s)

2007年4月26日

CPOL

3分钟阅读

viewsIcon

47584

downloadIcon

196

展示了如何在 Pocket PC 上卸载软件输入面板 (SIP) 的输入法 (IM) DLL,这对于输入法卸载程序非常重要

引言

许多为 Pocket PC 上的软件输入面板 (SIP) 编写了自己的输入法 (IM) 的开发人员,或者为自定义输入法开发卸载程序的开发人员,都面临着卸载其 DLL 以便更新它们或将它们从设备上移除的问题。然而,似乎很难找到任何关于如何在不重启系统的情况下实现这一目标的信息。这篇简短的文章旨在解决这个问题。

背景

根据官方文档,SIP 应该在调用 IInputMethod->Deselect() 方法之后立即释放输入法 COM 对象,即在选择另一个输入法之后。我可以确认 SIP 确实会释放它。因此,似乎最简单的方法就是简单地选择另一个输入法。然而,这并不起作用。

如您所知,COM 子系统不会立即卸载未使用的 DLL。相反,它会等待用户代码调用 CoFreeUnusedLibraries()CoFreeUnusedLibrariesEx()。SIP 确实调用了第一个函数,但是,不幸的是,这不足以立即卸载所有未使用的输入法。在此函数内部,COM 子系统会为所有看起来不再使用的 DLL 调用 DllCanUnloadNow() 例程,并将所有返回 S_OK (DLL 可以卸载) 的 DLL 放入要卸载的候选列表中。当 COM 再次需要此列表中的 DLL 时(例如,应用程序请求创建一个位于此 DLL 中的新 COM 对象),该 DLL 将从此候选列表中删除。如果在再次调用 CoFreeUnusedLibraries()CoFreeUnusedLibrariesEx() 时,某个 DLL 仍然位于候选列表中,则只有在将该 DLL 放入列表后经过足够的时间,该 DLL 最终才会被卸载。

当使用 CoFreeUnusedLibrariesEx() 时,此时间间隔可以指定为第一个参数,但对于 SIP 使用的 CoFreeUnusedLibraries(),此超时是系统默认值,即 10 分钟!要求用户等待的时间太长了...

解决方案

解决此问题的一种可能方法是从 SIP 所在的同一进程中调用 CoFreeUnusedLibrariesEx(),并将超时参数设置为零或接近于零。幸运的是,这很容易实现,因为 SIP 是作为设备驱动程序实现的,并且位于所有设备驱动程序所在的位置——在 device.exe 进程中。因此,我们可以创建一个简单的驱动程序,该驱动程序调用所需的函数,然后加载它并享受删除我们的 DLL 的可能性。当然,在此之前应该取消选择输入法。还有另一个技巧。

如果我们可以从 Init() 入口点调用 CoFreeUnusedLibrariesEx(),驱动程序会更简单。这不起作用,因为 COM 子系统需要所有者进程 ID 等于当前进程 ID (device.exe),为此我们需要卸载 DLL。然而,Init() 调用中的所有者进程 ID 等于加载我们驱动程序的进程 ID(即我们的示例程序),因此 COM 尝试卸载此示例应用程序的 COM DLL。

为了克服这个问题,我们只需要创建另一个线程,并从此线程的上下文中进行调用。此线程的所有者进程 ID 等于 SIP 和输入法所在的 device.exe 的 ID。

Using the Code

该示例可以使用 eVC++ 4.0 编译。存档中有两个项目:一个简单的设备驱动程序,在加载时如上所述调用 CoFreeUnusedLibrariesEx();以及一个加载此驱动程序的简单可执行文件。

该驱动程序公开了一组最少必需的入口点 (Init/DeinitOpen/CloseIOControl),因为我们只需要将其加载到 device.exe 中,仅此而已。

注释

当需要通过“移除程序”小程序从设备上卸载应用程序时卸载输入法 DLL 时,可以通过提供所需的入口点,使用自定义安装 DLL 作为驱动程序本身,即不需要单独的 DLL。

我希望这篇文章对输入法编写者有所帮助。请随时评论或提出任何问题。

© . All rights reserved.