了解 UI 扩展创作

作者:Gurpreet Singh

介绍

IIS 7.0 及以上版本的管理工具是一个可插入模块的可扩展平台。 开发人员可以编写自己的 UI 功能,intemgr 将把他们视为一等公民。

本演练演示如何从头开始创建 UI 模块。 它展示了 IIS 团队在创建模块时遵循的最佳做法。

该任务包括创建两个程序集。 这一过程可概括为两个步骤。

  • 创建 Client 程序集
  • 创建 Server 程序集

新版 inetmgr 采用类似网络服务的架构设计,将用户界面逻辑与操作服务器设置的代码干净利落地分离开来。 这主要通过 ModuleService 和 ModuleServiceProxy 两个类来实现。 ModuleService 派生类通过 ModuleProvider ServiceType 属性与其 ModuleProvider 关联

创建空解决方案

单击“开始”,然后单击“程序”并运行 Microsoft Visual Studio 2005 或 Microsoft Visual C# 2005 学习版。在“文件”菜单中,选择“新建项目”选项,转到“其他项目类型”和“Visual Studio 解决方案”。 从右侧选择空白解决方案,并将解决方案命名为 ExtensibilityDemo。

Screenshot of New Project dialog box with Visual Studio Solutions as project type and Blank Solution as the template selected.

创建 Client 程序集

创建 Client 程序集的任务包括

  • 为 Client 程序集设置项目
  • 创建 ModuleServiceProxy
  • 创建 ModuleListPage 并使用 ModuleServiceProxy。
  • 创建 Module 类。

为 Client 程序集设置项目

在解决方案资源管理器中,右键单击解决方案,选择“添加和新建项目”,启动该窗口。 从右窗格中选择 Visual C# 和类库,并将项目命名为 Client。

Screenshot of Add New Project window with Class Library selected and Client entered as project name in the Name field.

使用解决方案资源管理器中的上下文菜单“删除”选项,删除默认添加的文件 Class1.cs(因为我们不会使用该文件)。

使用“添加引用...” 在“项目”菜单中,使用“浏览”选项卡添加对 Microsoft.Web.Management.dll 的引用,并在 \Windows\system32\inetsrv 目录中搜索它。 该目录包含用于在 inetmgr 中创建模块的扩展性类。 由于我们将引用 System.Windows.Forms 中的某些类,因此还包含对 System.Windows.Forms 的引用。

最佳做法:切勿在 Client 项目中引用 Microsoft.Web.Administration.dll。

在 InetMgr 中使用库的要求之一是需要在 GAC 中注册。 为此,我们需要确保 DLL 具有强名称。 Visual Studio 提供了一种简单的方法来创建新名称。 若要对程序集进行签名,请转到“项目属性”和“签名”选项卡,单击“对程序集进行签名”。 在对话框中,将密钥命名为 ExtensibilityDemoKey,并取消选中“使用密码保护密钥文件”

Screenshot of Signing Tab and the dialog box with Extensibility Demo Key entered as the key file name.

Screenshot of Create Strong Name Key dialog box with Extensibility Demo Key displayed in Key file name field.

按“确定”对程序集进行签名。 此外,由于我们希望程序集位于 GAC 中,因此需要添加一些生成后事件,以便每次编译时它都会自动添加到 GAC 中。 当我们添加新功能时,这会使调试和更改变得非常简单。 为此,请选择“生成事件”选项卡,并添加以下“生成后事件”命令行

gacutil.exe /if "$(TargetPath)"

Screenshot of Build Events tab showing Post build event command line populated with code.

最后,关闭项目属性,选择“文件”菜单中的“全部保存”选项。现在,使用“生成”菜单下的“生成解决方案”编译项目。 这将自动生成 dll 并将其添加到 GAC。

注意:如果用于设置 vsvars32.bat 的命令不适用于错误代码 9009,请替换在生成后事件中添加的命令,方法是将完整路径添加到 gacutil.exe,例如:

"C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" /if "$(TargetPath)

创建 ModuleServiceProxy

ModuleServiceProxy 是一个类,用于简化在 ModuleService 类中调用方法的过程。 该类非常重要,因为它抽象了管理本地服务器或使用网络管理服务 (wmsvc) 管理远程机器时的逻辑。

在此示例中,我们将编写“appSettings”的读取内容。 在“项目”菜单中选择“添加新项”选项。 选择“代码”并键入 DemoModuleServiceProxy 作为文件名。

Screenshot of Add New Item dash Client dialog box showing Demo Module Service Proxy typed as the file name.

更改代码,使其如下所示。

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Web.Management.Client;
using Microsoft.Web.Management.Server;

namespace Client {
    internal class DemoModuleServiceProxy : ModuleServiceProxy {
        public ArrayList GetAppSettings() {
            return (ArrayList )Invoke("GetAppSettings");
        }
    }
}

正如我们所见,编写代理很简单,只需编写一个派生自 ModuleServiceProxy 的类,然后在服务器端调用 ModuleService 中的方法即可。

创建 ModuleListPage

在“项目”菜单中选择“添加新项”选项。 选择“代码”并键入 DemoModuleListPage.cs 作为文件名。

更改代码,使其如下所示。

using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using Microsoft.Web.Management.Client.Win32;
using Microsoft.Web.Management.Server;

namespace Client {
    internal class DemoModuleListPage : ModuleListPage {
        private ColumnHeader _keyColumnHeader;
        private ColumnHeader _valueColumnHeader;
        private DemoModuleServiceProxy _serviceProxy;

        private DemoModuleServiceProxy ServiceProxy {
            get {
                if (_serviceProxy == null) {
                    _serviceProxy = (DemoModuleServiceProxy)CreateProxy(typeof(DemoModuleServiceProxy));
                }
                return _serviceProxy;
            }
        }

        protected override void InitializeListPage() {
            _keyColumnHeader = new ColumnHeader();
            _keyColumnHeader.Text = "Key";
            _keyColumnHeader.Width = 200;

            _valueColumnHeader = new ColumnHeader();
            _valueColumnHeader.Text = "Value";
            _valueColumnHeader.Width = 200;

            ListView.Columns.Add(_keyColumnHeader);
            ListView.Columns.Add(_valueColumnHeader);
        }

        protected override void OnActivated(bool initialActivation) {
            base.OnActivated(initialActivation);
            if (initialActivation) {
                GetAppSettings();
            }
        }

        private void GetAppSettings() {
            StartAsyncTask("Getting Settings", new DoWorkEventHandler(GetSettings),
                                              new RunWorkerCompletedEventHandler(GetSettingsCompleted));
        }

        private void GetSettings(object sender, DoWorkEventArgs e) {
            e.Result = ServiceProxy.GetAppSettings();
        }

        private void GetSettingsCompleted(object sender, RunWorkerCompletedEventArgs e) {
            ListView.BeginUpdate();
            try {
                ArrayList settingsList = (ArrayList)e.Result;
                foreach (PropertyBag bag in settingsList) {
                    ListViewItem item = new ListViewItem();
                    item.Text = (string)bag[0];
                    item.SubItems.Add((string)bag[1]);

                    ListView.Items.Add(item);
                }
            }
            finally {
                ListView.EndUpdate();
            }
        }
    }
}

在此演示中,我们将扩展 ModuleListPage 类。 它重写 InitializeListPage 方法以初始化列并将其添加到 listview。 它还会重写 OnActivated 方法。 访问此页面时会调用此方法

创建 Module 类

这是所有现有对象的客户端入口点。 它有三个主要方法,分别称为 Initialize、GetService 和 Dispose。 页面注册、为页面分配类别等所有操作都是通过这种方法完成的。

在“项目”菜单中选择“添加新项”选项。 选择“代码”并键入 DemoModule.cs 作为文件名。

更改代码,使其如下所示。

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Web.Management.Client;

namespace Client {
    internal class DemoModule : Module {
        protected override void Initialize(IServiceProvider serviceProvider, Microsoft.Web.Management.Server.ModuleInfo moduleInfo) {
            base.Initialize(serviceProvider, moduleInfo);

            IControlPanel controlPanel = (IControlPanel)GetService(typeof(IControlPanel));
            controlPanel.RegisterPage(new ModulePageInfo(this, typeof(DemoModuleListPage), "DemoPage"));
        }
    }
}

DemoModule 重写 Initialize 方法。 它首先调用基类上的 Initialize。 然后,它通过 GetService 访问控制面板服务。 然后,它通过 ControlPanel 的 RegisterPage 自行注册。

创建 Server 程序集

Server 程序集包括编写两个主类

  • ModuleProvider
  • ModuleService

服务器还应引用 Windows system32/inetsrv 目录中的两个主程序集,即 Microsoft.Web.Management.dll 和 Microsoft.Web.Management.Administration.dll。 第一个程序集包含用于为 intemgr 创建 UI 的所有框架类,第二个程序集包含用于读取配置和写入配置的类。

在解决方案资源管理器中,右键单击解决方案,选择“添加和新建项目”,启动该窗口。 从右窗格中选择 Visual C# 和类库,并将项目命名为 Server。

Screenshot of Add New Project dialog box with Server typed in the Name field as the project name.

使用解决方案资源管理器中的上下文菜单“删除”选项,删除默认添加的文件 Class1.cs(因为我们不会使用该文件)。

使用“添加引用...” 在“项目”菜单中,使用“浏览”选项卡添加对 Microsoft.Web.Management.dll 和 Microsoft.Web.Management.Administration.dll 的引用,并在 \Windows\system32\inetsrv 目录中搜索它。 该目录包含用于在 inetmgr 中创建模块的扩展性类。

在 InetMgr 中使用库的要求之一是需要在 GAC 中注册。 为此,我们需要确保 DLL 具有强名称。 Visual Studio 提供了一种简单的方法来创建新名称。 若要对程序集进行签名,请转到“项目属性”和“签名”选项卡,单击“对程序集进行签名”。在下拉菜单中,单击“浏览”。 在对话框中,转到 Client 文件夹,选择 ExtensibilityDemoKey 并点击“打开”。 保存项目属性。

Screenshot of Signing tab with the dialog box displaying the Extensibility Demo Key selected in the Client folder.

按“确定”对程序集进行签名。 此外,由于我们希望程序集位于 GAC 中,因此需要添加一些生成后事件,以便每次编译时它都会自动添加到 GAC 中。 当我们添加新功能时,这会使调试和更改变得非常简单。 为此,请选择“生成事件”选项卡,并添加以下“生成后事件”命令行

gacutil.exe /if "$(TargetPath)"

Screenshot of dialog box with Post Build event command line populated with argument.

最后,关闭项目属性,选择“文件”菜单中的“全部保存”选项。现在,使用“生成”菜单下的“生成解决方案”编译项目。 这将自动生成 dll 并将其添加到 GAC。

注意:如果用于设置 vsvars32.bat 的命令不适用于错误代码 9009,请替换在生成后事件中添加的命令,方法是将完整路径添加到 gacutil.exe,例如:

"C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" /if "$(TargetPath)"

创建 Module 服务

在此任务中,我们将创建新的 ModuleService。 该类使用 Microsoft.Web.Administration.dll 来读取或写入基础配置文件。

从“项目”菜单中选择“添加新项”选项。 在“添加新项”对话框中,选择“代码”模板并键入 DemoModuleService.cs 作为文件的名称

更改代码,使其如下所示:

using System.Collections;
using Microsoft.Web.Administration;
using Microsoft.Web.Management.Server;

namespace Server {
    internal class DemoModuleService : ModuleService {

        [ModuleServiceMethod(PassThrough = true)]
        public ArrayList GetAppSettings() {

            ArrayList settingsList = new ArrayList();

            ConfigurationSection appSettingsSection =
                ManagementUnit.Configuration.GetSection(@"appSettings");
            ConfigurationElementCollection settingsCollection = appSettingsSection.GetCollection();

            foreach (ConfigurationElement element in settingsCollection) {
                PropertyBag bag = new PropertyBag();
                bag[0] = element.GetAttributeValue("key");
                bag[1] = element.GetAttributeValue("value");
                settingsList.Add(bag);
            }

            return settingsList;
        }
    }
}

创建 AssemblyRef 类

在此任务中,你将创建 AssemblyRef 类。 此类将由 moduleprovider 引用,以实例化模块类。

从“项目”菜单中选择“添加新项”选项。 在“添加新项”对话框中,选择“代码”模板并键入 DemoModuleProvider.cs 作为文件的名称

using System.Reflection;

internal static class AssemblyRef {

    private static string client;

    internal static string Client {
        get {
            if (client == null) {
                AssemblyName assemblyName = typeof(AssemblyRef).Assembly.GetName();
                string assemblyFullName = assemblyName.FullName;
                client = assemblyFullName.Replace(assemblyName.Name, "Client");
            }

            return client;
        }
    }
}

创建模块提供程序

在此任务中,你将创建模块提供程序;这是 Inetmgr 中模块注册的主要入口点。 Administration.config 中已列明这些模块提供程序。

从“项目”菜单中选择“添加新项”选项。 在“添加新项”对话框中,选择“代码”模板并键入 DemoModuleProvider.cs 作为文件的名称

Screenshot of Add New Item dialog box with Demo Module Provider dot c s typed as the name of the file.

更改代码,使其如下所示:

using System;
using Microsoft.Web.Management.Server;

namespace Server {
    internal class DemoModuleProvider : ModuleProvider {
        public override ModuleDefinition GetModuleDefinition(IManagementContext context) {
            return new ModuleDefinition(Name, "Client.DemoModule," + AssemblyRef.Client);
        }

        public override Type ServiceType {
            get {
                return typeof(DemoModuleService);
            }
        }

        public override bool SupportsScope(ManagementScope scope) {
            return true;
        }
    }
}

在 Administration.Config 中添加条目

在此部分,我们将在 moduleProviders 列表中添加 administration.config 条目。 程序集的全名可以在 \Windows\Assembly 文件夹中找到。 搜索程序集 Client,右键单击并选择“属性”。

Screenshot of Server Properties dialog box displaying full name of the assembly.

通过该窗口可以确定程序集的全名。 例如,在本例中程序集的全名是 Server, Version=1.0.0.0, Culture=neutral, PublicKeyToken=e788138af6131538。应在 moduleProviders 中将添加到 administration.config 的条目是

<moduleProviders>
<..
<add name="DemoModule" type="Server.DemoModuleProvider,Server, Version=1.0.0.0, Culture=neutral, PublicKeyToken=e788138af6131538"/>

../ moduleProviders>

注意

添加此条目后,该模块仅注册用于服务器连接。

生成解决方案并运行 inetmgr,你将看到以下屏幕截图

Screenshot of Internet Information Services I I S console with Actions menu displayed.

Screenshot of Internet Information Services I I S Demo Page with Actions menu for Help and Online Help displayed.

总结

你已了解如何使用 inetmgr 的扩展性模型并创建自己的自定义页面。 在今后的文章中,我还将介绍如何添加 taskItems 和写入配置文件。