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

一个杀死不必要窗口的 Windows 服务:第二部分

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.83/5 (5投票s)

2008 年 9 月 9 日

CPOL

3分钟阅读

viewsIcon

28517

downloadIcon

331

演示了如何在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执行以下步骤

  1. 使用RegistrationHelper安装程序集。
  2. 创建一个ICOMAdminCatalog对象。
  3. 获取当前COM+应用程序的所有角色。
  4. 查找角色AverageUser
  5. 将帐户分配给找到的AverageUser角色。
  6. 获取当前应用程序的所有COM+组件。
  7. 检查DesktopManager COM+组件是否存在。
  8. 授予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手动注册两个程序集:DesktopManagerDllWIndowKiller.exe,它是.NET Framework的一部分

InstallUtil.exe DesktopManagerDll.dll
InstallUtil.exe WindowKiller.exe

注意:WindowKiller EXE已实现自安装功能-请参阅我以前的文章。因此,您可以通过从命令行调用它来安装/卸载此服务,如下所示...

WindowKiller -a

...其中参数-a表示“自动”安装/卸载,具体取决于当前服务状态。要获得有关其他命令行参数的帮助,可以使用-h参数调用该服务。

历史

这是我之前文章的第二部分。

© . All rights reserved.