一个杀死不必要窗口的 Windows 服务:第二部分
演示了如何在Vista上的用户桌面上杀死不需要的窗口,使用COM+允许Windows服务与用户应用程序交互。
引言
这是我之前文章的第二部分。该项目的主要目标是创建一个C# Windows服务,该服务将监视所有出现的窗口并杀死不需要的窗口。在第一部分发布后,我发现(感谢附加到该文章的论坛)由于Vista中增强的安全性,它无法在Vista上运行。这意味着Windows服务在隐藏会话#0中运行,并且它们无法与用户会话(#1,#2,...)交互。所有PInvoke方法(例如SendMessage
)都不能与在用户桌面上运行的窗口/进程一起使用。在CodeProject上搜索之后,我发现Stefan Repas撰写的文章非常棒,该文章展示了如何允许用.NET编写的Windows服务使用进程外COM服务器与桌面应用程序通信。为了将.NET COM可见程序集作为进程外COM服务器运行,他使用COMAdmin.dll将DLL注册为进程外COM服务器。
背景
在Visual Studio中使用.NET创建的任何COM可见程序集(或.dll)都由dllhost.exe托管,这被称为“COM代理”。默认情况下,它在Vista上的会话#0中运行,该会话在任务管理器中将其用户名作为SYSTEM
。Windows服务可以与此COM服务器通信,但是此服务器与服务在同一会话#0中运行,并且看不到用户窗口/应用程序。要强制COM DLL在用户会话中运行的dllhost.exe中托管,我们必须使用COMAdmin.dll在用户帐户下注册它。我创建了一个类DesktopManager
,它具有一个基本的接口,可以与桌面窗口/应用程序一起使用。当然,这个简单的类可以很容易地扩展到您需要的任何东西。此类在DesktopManagerDll程序集中实现为COM+对象,该程序集使用COMAdmin.dll注册为进程外COM服务器,并在用户帐户下运行。WindowFinder
类中的所有方法(请参阅我之前的文章)都已移至DesktopManager
类。
桌面管理器
此COM对象公开以下IDesktopManager
接口
[ComVisible(true)]
[Description("Exposed DesktopManager interface")]
[Guid("258E4449-6ACC-40d8-8C37-4F47476D30C2")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IDesktopManager
{
[DispId(1)]
int FindTopWindow(string wndClass, string wndTitle);
[DispId(2)]
int FindChildWindow(int hwndParent, string wndClass, string wndTitle);
[DispId(3)]
bool CloseWindow(int hwnd, string wndClass);
[DispId(4)]
void ExitProcess(string processToExit);
[DispId(5)]
void MessageBoxShow(string text, string caption, string icon);
[DispId(6)]
string GetMainWindowTitle(string process);
}
此接口在DesktopManager
类中实现
[ComVisible(true)]
[Description("ServicedComponenet class to control IDesktopManager Interface")]
[Guid("56919971-22CB-4de7-9393-D55C72A20A1C")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("DesktopManagerDll.DesktopManager")]
public class DesktopManager : ServicedComponent, IDesktopManager
{
...
}
请注意,此类是从ServicedComponent
类派生的,需要将DesktopManager
公开为COM+对象。此对象实现了所有IDesktopManager
接口方法。例如,要实现FindChildWindow
方法,此类使用了与我先前文章中的WindowFinder
类中使用的相同技术。
为了使此对象成为进程外COM+服务器,有一个类DesktopManagerInstaller
执行以下步骤
- 使用RegistrationHelper安装程序集。
- 创建一个
ICOMAdminCatalog
对象。 - 获取当前COM+应用程序的所有角色。
- 查找角色
AverageUser
。 - 将帐户分配给找到的
AverageUser
角色。 - 获取当前应用程序的所有COM+组件。
- 检查
DesktopManager
COM+组件是否存在。 - 授予
AverageUser
角色中的用户访问权限
使用DesktopManager COM+对象
首先,您必须创建一个DesktopManager
对象
m_desktopManager = new DesktopManagerDll.DesktopManager();
// this will instantiate the DesktopManager
其次,在WindowsFinder
类中使用m_desktopManager
m_windowFinder.KillPopupWindow(m_titleToKill, classToKill);
其中
public bool KillPopupWindow(string titleToKill, PopupWindowType popupType)
{
...
m_hwndFound = FindTopWindow(classToKill, titleToKill);
if (m_hwndFound != IntPtr.Zero)
{
return m_iDesktopManager.CloseWindow(m_hwndFound.ToInt32(), classToKill);
}
}
第三,完成后释放m_desktopManager
Marshal.ReleaseComObject(m_desktopManager);
最后,关闭COM+应用程序
ICOMAdminCatalog cac = (ICOMAdminCatalog)Activator.CreateInstance(
Type.GetTypeFromProgID("COMAdmin.COMAdminCatalog"));
cac.ShutdownApplication("{75FA7F8B-7F94-4023-B4B8-7CCF6F988713}");
安装
要安装此服务,可以使用WindowKillerSetup项目生成的MS Installer输出,该项目是WindowKiller解决方案的一部分,或者使用InstallUtil手动注册两个程序集:DesktopManagerDll和WIndowKiller.exe,它是.NET Framework的一部分
InstallUtil.exe DesktopManagerDll.dll
InstallUtil.exe WindowKiller.exe
注意:WindowKiller EXE已实现自安装功能-请参阅我以前的文章。因此,您可以通过从命令行调用它来安装/卸载此服务,如下所示...
WindowKiller -a
...其中参数-a表示“自动”安装/卸载,具体取决于当前服务状态。要获得有关其他命令行参数的帮助,可以使用-h参数调用该服务。
历史
这是我之前文章的第二部分。