我们可以很方便地使用Windows提供的IIS管理器来管理IIS服务器,但有的时候,我们需要通过程序来对IIS实现自动的管理和控制。这篇文章将会介绍使用C#管理控制IIS网站的方法,并在给出一份可用的源码及其编译出来的DLL。


需要注明的是,本文中给出的方法及源码,都是针对IIS6.0及以上版本。Windows XP中的IIS版本为IIS5.1,如果使用本文提供的方法,可能会出现一些问题。


版权声明:


IISManager 由 luckzj 开发,任何人在不将其用于商业用途的前提下,都可以免费学习和使用IISManage。如果在学习使用此程序的过程中,遇到任何问题,都可以给我发email,或者直接访问本站 http://soft-bin.com 与我联系。


下载IISManager


目前IISManager只支持针对Website的操作,但在后续,我将完善IISManager,使之可以对IIS进行全面的控制。


我们的介绍将从Active Directory Service开始


Active Directory Service


我们使用C#控制IIS,需要使用Active Directory Service,关于ADS的概念我这里我不作过多讨论,网上有很多相关资料。我只介绍如何使用它。


首先我们需要添加对 System.DirectoryServices 的引用。之后需要在cs文件中引入 System.DirectoryServices 名字空间。


我们主要使用到的类是DirectoryEntry。我们可以将一个 DirectoryEntry 看作一个节点,每个节点都有其类型,被成为SchemaClass。这个节点会有其属性集合 Properties,方法以及其子节点集合 Children。 我们可以使用 DirectoryEntry 的 Properties访问节点的属性,使用Invoke 方法来调用这个节点上的方法。


DirectoryEntry 类的构造函数,可以接收一个参数,这个参数为ADS节点的名称。我们将IIS节点的名称作为参数,构造一个DirectoryEntry 的实例,就可以对IIS节点进行访问了。


使用DirectoryEntry打开IIS网站


我们可以使用以下方式构造一个DirectroyEntry的实例以使用IIS的目录服务:


DirectoryEntry Services = new DirectoryEntry("IIS://localhost/W3SVC");

“IIS://localhost/W3SVC” 即是IIS的目录服务的名称。在创建了这个实例后,我们就可以使用IIS目录服务。



上面的代码只是打开了IIS目录服务的根节点,各个网站就在这个根节点上,我们需要寻找到我们想要的网站。


private DirectroyEntry websiteEntry = null;

internal const string IIsWebServer = "IIsWebServer";

 

protected IISWebsite(DirectoryEntry Server)

{

    websiteEntry = Server;

}

 

public static IISWebsite OpenWebsite(string name)

{

    // get directory service

    DirectoryEntry Services = new DirectoryEntry("IIS://localhost/W3SVC");

    IEnumerator ie = Services.Children.GetEnumerator();

    DirectoryEntry Server = null;

 

    // find iis website

    while (ie.MoveNext())

    {

        Server = (DirectoryEntry)ie.Current;

        if (Server.SchemaClassName == IIsWebServer)

        {

            // "ServerComment" means name

            if (Server.Properties["ServerComment"][0].ToString() == name)

            {

                return new IISWebsite(Server);

                break;

             }

        }

    }

 

    return null;

}

我创建了类 IISWebsite 表示网站,用以控制IIS网站,使用其静态方法 IISWebsite.OpenWebsite()可以返回一个此类的实例。


DirectoryEntry.SchemaClassName 即代表节点的类型,我们需要查找类型为 IIsWebServer 的节点。 IIsWebServer 类型即IIS网站类型。


我们要在众多的IIS网站中查找名称为name的网站,而网站的名称保存在 DirectroyEntry.Properties["ServerComment"]中。DirectoryEntry的每一个属性又是一个集合,我们可以使用foreach语句来遍历这个属性集合,但对于网站的名称,则没有必要这么做,我们知道DirectoryEntry.Properties["ServerComment"][0]就是网站的名称。


创建网站


创建网站即是向IIS目录服务的根节点添加一个新的节点。我先列出创建新网站的源码,其后对源码进行详细讲解。


/// <summary>

/// create a new website

/// </summary>

/// <param name="name">website name</param>

/// <param name="port">website port</param>

/// <param name="rootPath">root path</param>

/// <returns></returns>

public static IISWebsite CreateWebsite(string name, int port, string rootPath, string appPool)

{

    // validate root path

    if (System.IO.Directory.Exists(rootPath) == false)

    {

        throw new DirNotFoundException(rootPath);

    }

 

    // get directory service

    DirectoryEntry Services = new DirectoryEntry("IIS://localhost/W3SVC");

 

    // get server name (index)

    int index = 0;

    foreach (DirectoryEntry server in Services.Children)

    {

        if (server.SchemaClassName == "IIsWebServer")

        {

            if (server.Properties["ServerComment"][0].ToString() == name)

            {

                throw new Exception("website:" + name + " already exsit.");

            }

 

            if (Convert.ToInt32(server.Name) > index)

            {

                index = Convert.ToInt32(server.Name);

            }

        }

    }

    index++; // new index created

 

    // create website

    DirectoryEntry Server = Services.Children.Add(index.ToString(), IIsWebServer);

    Server.Properties["ServerComment"].Clear();

    Server.Properties["ServerComment"].Add(name);

    Server.Properties["Serverbindings"].Clear();

    Server.Properties["Serverbindings"].Add(":" + port + ":");

 

    // create ROOT for website

    DirectoryEntry root = Server.Children.Add("ROOT", IISWebVirturalDir.IIsVirtualDir);

    root.Properties["path"].Clear();

    root.Properties["path"].Add(rootPath);

 

    // create application

    if (string.IsNullOrEmpty(appPool))

    {

        root.Invoke("appCreate", 0);

    }

    else

    {

        // use application pool

        root.Invoke("appCreate3", 0, appPool, true);

    }

 

    root.Properties["AppFriendlyName"].Clear();

    root.Properties["AppIsolated"].Clear();

    root.Properties["AccessFlags"].Clear();

    root.Properties["FrontPageWeb"].Clear();

    root.Properties["AppFriendlyName"].Add(root.Name);

    root.Properties["AppIsolated"].Add(2);

    root.Properties["AccessFlags"].Add(513);

    root.Properties["FrontPageWeb"].Add(1);

 

    // commit changes

    root.CommitChanges();

    Server.CommitChanges();

 

    // return the newly created website

    IISWebsite website = new IISWebsite(Server);

    return website;

}

目录服务中的节点有自己的名称,但这个名称并不是网站的名字,网站的名字是 Properties["ServerComment"][0]。目录服务的节点的名字是一个整型的数字,我们在创建一个新的节点时,需要查找到当前名字最大的节点,将其名字加1,成为新的节点的名字。在程序中,我使用index变量来获取已经存在的最大名称,然后将其加1,变成我的新的名称。然后调用以下语句添加新的节点。


DirectoryEntry Server = Services.Children.Add(index.ToString(), IIsWebServer);

在新的节点添加完成后,我需要对网站的属性进行设置,其中包括:


网站的名称: ServerComment

网站的端口: ServerBindings,这里需要注意的是,端口的表示为 :端口: ,即端口两端需要分别有一个冒号。


到目前为止,我们只是创建了网站,我们还需要为这个网站创建一个主目录:


// create ROOT for website

DirectoryEntry root = Server.Children.Add("ROOT", IISWebVirturalDir.IIsVirtualDir);

root.Properties["path"].Clear();

root.Properties["path"].Add(rootPath);

 

// create application

if (string.IsNullOrEmpty(appPool))

{

    root.Invoke("appCreate", 0);

}

else

{

    // use application pool

    root.Invoke("appCreate3", 0, appPool, true);

 }

 

root.Properties["AppFriendlyName"].Clear();

root.Properties["AppIsolated"].Clear();

root.Properties["AccessFlags"].Clear();

root.Properties["FrontPageWeb"].Clear();

root.Properties["AppFriendlyName"].Add(root.Name);

root.Properties["AppIsolated"].Add(2);

root.Properties["AccessFlags"].Add(513);

root.Properties["FrontPageWeb"].Add(1);

网站必须有一个根目录,这个根目录实际上也是网站的一个子节点,名称为”ROOT”。我创建了类IISWebVirtualDir用以管理虚拟目录,网站的根目录实际上也是一个虚拟目录。我将在后面介绍虚拟目录。


网站虚拟目录


网站需要包含虚拟目录,虚拟目录是网站节点的子节点。每一个网站都包含一个根虚拟目录,其节点名称叫做ROOT,这个根虚拟目录即是IIS网站的主路径。 其他的虚拟目录则是这个根虚拟目录的子节点。我使用IISWebVirturalDir来表示虚拟路径。在IISWebsite中,我使用 Root 来表示网站的根虚拟目录:


/// <summary>

/// Root path

/// </summary>

public IISWebVirturalDir Root

{

    get

    {

        foreach (DirectoryEntry entry in websiteEntry.Children)

        {

            if (entry.SchemaClassName == IISWebVirturalDir.IIsVirtualDir)

            {

                return new IISWebVirturalDir(entry);

            }

        }

 

        throw new WebsiteWithoutRootException(this.Name);

    }

}

有了这个根节点,我们就可以使用IISWebVirtualDir.OpenSubVirtualDir来打开任意一个子虚拟目录。


/// <summary>

/// Open a sub virtual directory

/// </summary>

/// <param name="name">Name of directory to be opened. Case insensitive.</param>

/// <returns>A IISWebVirtualDir instance if open successfully done.Otherwise null.</returns>

public IISWebVirturalDir OpenSubVirtualDir(string name)

{

    DirectoryEntry entry = this.FindSubEntry(name);

 

    if (entry == null)

    {

        return null;

    }

 

    return new IISWebVirturalDir(entry);

}

我们也可以创建一个子虚拟目录,并在创建的过程中设置子虚拟目录的属性:


/// <summary>

/// Create a sub virtual directory

/// </summary>

/// <param name="name">Name of the sub virtual directory to be created.</param>

/// <param name="path">Path of the sub virtual directory.</param>

/// <param name="appPool">

/// Application pool. Application pool with this name would be created if not exist.

/// Use string.Empty or null to this parameter if you don't want to use a application pool.

/// </param>

/// <returns>A IISWebVirtualDir if created. Otherwise  null.</returns>

public IISWebVirturalDir CreateSubVirtualDir(string name, string path, string appPool)

{

    // already exist

    if (this.ExistVirtualDir(name))

    {

        throw new VirtualDirAlreadyExistException(this._entry, path);

    }

 

    // validate path

    if (System.IO.Directory.Exists(path) == false)

    {

        throw new DirNotFoundException(path);

    }

 

    DirectoryEntry entry = this._entry.Children.Add(name, IIsVirtualDir);

    entry.Properties["path"].Clear();

    entry.Properties["path"].Add(path);

 

    // create application

    if (string.IsNullOrEmpty(appPool))

    {

        entry.Invoke("appCreate", 0);

    }

    else

    {

        // use application pool

        entry.Invoke("appCreate3", 0, appPool, true);

    }

 

    entry.Properties["AppFriendlyName"].Clear();

    entry.Properties["AppIsolated"].Clear();

    entry.Properties["AccessFlags"].Clear();

    entry.Properties["FrontPageWeb"].Clear();

    entry.Properties["AppFriendlyName"].Add(this._entry.Name);

    entry.Properties["AppIsolated"].Add(2);

    entry.Properties["AccessFlags"].Add(513);

    entry.Properties["FrontPageWeb"].Add(1);

 

    entry.CommitChanges();

    return new IISWebVirturalDir(entry);

}

应用程序池


应用程序池也是用目录服务来操作。只是我们需要打开的根节点,变成了 “IIS://localhost/W3SVC/AppPools/”


与上面程序一样的原理,我们可以打开,创建应用程序池:


/// <summary>

/// Open a application pool and return an IISAppPool instance

/// </summary>

/// <param name="name">application pool name</param>

/// <returns>IISAppPool object</returns>

public static IISAppPool OpenAppPool(string name)

{

    string connectStr = "IIS://localhost/W3SVC/AppPools/";

    connectStr += name;

 

    if (IISAppPool.Exsit(name) == false)

    {

        return null;

    }

 

    DirectoryEntry entry = new DirectoryEntry(connectStr);

    return new IISAppPool(entry);

}

 

/// <summary>

/// create app pool

/// </summary>

/// <param name="name">the app pool to be created</param>

/// <returns>IISAppPool created if success, else null</returns>

public static IISAppPool CreateAppPool(string name)

{

    DirectoryEntry Service = new DirectoryEntry("IIS://localhost/W3SVC/AppPools");

    foreach (DirectoryEntry entry in Service.Children)

    {

        if (entry.Name.Trim().ToLower() == name.Trim().ToLower())

        {

            return IISAppPool.OpenAppPool(name.Trim());

        }

    }

 

    // create new app pool

    DirectoryEntry appPool = Service.Children.Add(name, "IIsApplicationPool");

    appPool.CommitChanges();

    Service.CommitChanges();

 

    return new IISAppPool(appPool);

}

应用程序池上的操作比较简单,我们可以启动或者停止应用程序池:


/// <summary>

/// Start application pool.

/// </summary>

public void Start()

{

    this._entry.Invoke("Start");

}

 

/// <summary>

/// Stop application pool.

/// </summary>

public void Stop()

{

    this._entry.Invoke("Stop");

}

目录节点的属性


我们可以根据目录服务节点的SchemaClass访问microsoft.com来获取节点的属性值。但如果我们想枚举属性值的话,也是可以做到的,以下为示例


代码:


private void ListProperty(DirectoryEntry server)

{

     foreach (DirectoryEntry e in server.Children)

     {

         ListProperty(e);

     }

 

     StringBuilder sb = new StringBuilder();

     sb.AppendLine("Property for " + server.SchemaClassName);

     sb.AppendLine("Name = " + server.Name);

     sb.AppendLine("Path = " + server.Path);

     sb.AppendLine("UserName = " + server.Username);

            sb.AppendLine("====================================================================");

      IEnumerator ie = server.Properties.PropertyNames.GetEnumerator();

      while (ie.MoveNext())

      {

          try

          {

               string name = (string)ie.Current;

               string val = "";

                foreach (object obj in server.Properties[name])

                {

                     val += obj.ToString() + ",";

                }

 

                sb.AppendLine(name + " = " + val.ToString());

           }

           catch (Exception)

           {

           }

       }

System.IO.StreamWriter sw = new System.IO.StreamWriter("PropertyList_" + server.SchemaClassName + "_" + server.Name + ".txt");

        sw.Write(sb.ToString());

        sw.Close();

     }

}

参考文献


IIS ADSI Provider

http://msdn.microsoft.com/en-us/library/ms524997(v=VS.90).aspx