最近在為公司實施做了一個工具,Silverlight部署早已是輕車熟路, 但對於非技術人員來說卻很是頭疼的一件事,當到現場實施碰到客戶情況也各不相同, 急需一個類似系統備份的"一鍵工具"快速實現應用程序部署和數據庫進行關聯. 網上關於這方面資源也比較混亂,其中對於IIS的編程影響因素很多,操作技巧上加以細化小結.
A:版本問題
這是你在進行編程前必須要要考慮的一個實際問題.因為IIS版本不同對應編程上基本上完全不同方式來進行的.先了解一下Iss版本在操作系統個具體要求.
ISS6.0時代主要以using System.DirectoryServices空間下的DirectoryEntry 對象作為編程訪問一個主要載體.但隨著ISS7.0發布.NET的Web程序由ISS6.0開始逐漸過渡到 7.0版本.而且在編程控制ISS上新添加的Microsoft.Web.Administration名稱空間, 提出多個用於管理 操作 和訪問ISS的對象, 使操作ISS的編程更加簡潔 高效.
B:我們要做什麼
實際用戶操作環境中主要以Windows XP/2000/2003操作系統為主, 這就導致了ISS版本主要在5.0/5.1/6.0之間,所以本次演示的代碼編程實例都是ISS7.0版本以下(不包含Iss7.0).
為了達到演示目的,我們現在需求是這樣的: 把硬盤上Silverlight應用程序成功部署到本地機器ISS上. 並支持通過局域網訪問. 需求很簡單吧就是一句話, 其實當進入實際編程時因為客戶的局域網環境是不可預知的, 這也就導致影響部署Silverlight程序到ISS上諸多未知因素. 所以要在編程中使Silverlight應用程序部署成功,我們必須利用編程除了控制ISS外還要控制其他主要影響ISS因素.
C:進入編程
既然提出需求我們大概確定一下解決思路:
在默認站點下創建一個虛擬目錄承載Silverlight 應用程序, 新建的虛擬目錄運行在獨立應用程序池中, 支持本地局域網匿名訪問其實就是對訪問權限控制, 為了直接使更改的應用生效需要多次重新啟動ISS的服務, 這就需要對ISS服務進行編程控制.
<1>ISS版本的獲取
在進入客戶環境前我們先檢測下是否存在安裝了ISS,以及ISS版本獲取提示,目前獲取ISS版本的方式主要有兩種,第一種方法是通過遍歷DirectoryEntry實體目錄 第二種方式是通過獲取注冊表的ISS修改版本值[經測試這種方式獲取版本不穩定].
1: public static void GetIssVersionByDri(string domainname)
2: {
3: try
4: {
5: if (string.IsNullOrEmpty(domainname))
6: {
7: //如果為空 則默認為本地機器
8: domainname = "LOCALHOST";
9: }
10: DirectoryEntry getEntity=new DirectoryEntry("IIS://" + domainname + "/W3SVC/INFO");
11: string Version=getEntity.Properties["MajorIISVersionNumber"].Value.ToString();
12: MessageBox.Show(Version);
13: }
14: catch (Exception se)
15: {
16: //說明一點:IIS5.0中沒有(int)entry.Properties["MajorIISVersionNumber"].Value;屬性,將拋出異常 證明版本為 5.0
17: MessageBox.Show("獲取ISS的版本是發生異常信息:"+se.Message);
18: }
19: }
通過系統注冊表獲取ISS版本的值:[測試發現不穩定]
1: public static string GetIssVersion()
2: {
3: //RegistryKey表示 Windows 注冊表中的項級節點.此類是注冊表封裝
4: string issversion = string.Empty;
5: RegistryKey getkey = Registry.LocalMachine.OpenSubKey("software\\microsoft\\inetstp");
6: if (getkey != null)
7: {
8: issversion= Convert.ToInt32(getkey.GetValue("majorversion", -1)).ToString();
9: MessageBox.Show(issversion.ToString());
10: }
11: return issversion;
12: }
<2>創建虛擬目錄
每個Internet服務可以從多個目錄中發布,通過以通用命名約定 (UNC) 名、用戶名及用於訪問權限的密碼指定目錄,可將每個目錄定位在本地驅動器或網絡上,虛擬目錄可以一個宿主程序,這可發布的操作目錄即為虛擬目錄.再來看看在ISS6.0中創建的虛擬目錄的步驟和控制的屬性.
新建虛擬目錄:
1: /// <summary>
2: /// 添加一個虛擬目錄
3: /// </summary>
4: public void CreateVirtualDir(string virtualdirname, string logicDir)
5: {
6: //如果存在重復 就直接刪除虛擬目錄
7: if (IsExitesVirtualDir(virtualdirname))
8: DeleteVirtualDir(virtualdirname);
9:
10: DirectoryEntry rootEntry;
11: rootEntry = new DirectoryEntry("IIS://localhost/W3SVC/1/root");
12:
13: DirectoryEntry newVirDir;
14: newVirDir = rootEntry.Children.Add(virtualdirname, "IIsWebVirtualDir");
15: newVirDir.Invoke("AppCreate", true);
16:
17: newVirDir.CommitChanges();
18: rootEntry.CommitChanges();
19:
20: newVirDir.Properties["AnonymousPasswordSync"][0] = true;
21: newVirDir.Properties["Path"][0] = logicDir;//+ @"virtualdirentry\virtualname\";
22:
23: //設置的端口綁定數據
24: //_newVirDir.Properties["ServerBindings"].Value =AppEntitys.WebAppInfor.HostIp+AppEntitys.WebAppInfor.HostProt+AppEntitys.WebAppInfor.AppDesc;
25:
26: //設置起始默認頁:
27: newVirDir.Properties["EnableDefaultDoc"][0] = true;
28: newVirDir.Properties["DefaultDoc"][0] = "Default.aspx";
29:
30: //_newVirDir
31: newVirDir.CommitChanges();
32: }
找到根目錄後添加新建虛擬目錄. 制定參數為Schema-指每個結點的類型:IIsVirtualDir:——虛擬目錄 IIsWebDir :——普通目錄, 添加完成後調用ADSI中的"AppCreate"方法將目錄真正創建.創建完成後通過根目錄和新目錄提交保存.
<3>目錄屬性設置
在DirectoryEntity虛擬目錄屬性可以說是非常多的,當時在編程時我為了區分屬性間區別做了一個方法去遍歷整個屬性集合PropertyCollection. 然後把常用重要的屬性跳出來進入賦值設置. 因為關於虛擬目錄的屬性在MSDN上的API中並沒有直接提到, 特別是對虛擬目錄訪問權限控制非常重要屬性等 .先說明局部的常用的屬性:
例如修改程序起始頁:
1: //設置起始默認頁:
2: newVirDir.Properties["EnableDefaultDoc"][0] = true;
3: newVirDir.Properties["DefaultDoc"][0] = "Default.aspx,Index.Html,index.asp";
4: newVirDir.CommitChanges();
DefaultDoc選項可以支持多個但注意選擇性排序.一般修改目錄屬性後都選通過CommitChanges()方法提交保存, 但有時你會發現我明明修改屬性 卻沒有保存生效. 這是因為ISS中部分屬性設置需要重新啟動ISS服務才能生效.這個時候我們需要對ISS服務進行控制.
<4>ISS服務控制
關於ISS服務控制微軟提供一個命名空間System.ServiceProcess 提供能夠快速操作本地系統服務API.對於ISS服務控制我們最常用的是重新啟動使當前設置生效. 在設置服務時我先找到服務名稱,打開計算機管理 在服務和應用程序目錄下打開服務.
我們能看到ISS服務命名是:ISSAdmin 我們通過編碼來控制:啟動ISS.
1: //獲取IIS Serivcer控制聲明 . 參數為Server在系統標識該服務的簡稱,.
2: ServiceController getservicecon = new ServiceController("IISADMIN");
3: getservicecon.Start();
重啟/暫停/停止ISS服務:
1: if (getservicecon.Status == ServiceControllerStatus.Running)
2: {
3: //停止服務
4: getservicecon.Stop();
5: //暫停服務
6: getservicecon.Pause();
7: //重啟服務
8: //Process提供對本地和遠程進程的訪問並使您能夠啟動和停止本地系統進程
9: //利用Start方法調用:啟動(或重用)此 Process 組件的 StartInfo 屬性指定的進程資源,並將其與該組件關聯
10: //如果啟動了進程資源,則為 true;如果沒有啟動新的進程資源(例如,如果重用了現有進程),則為 false
11: //通過指定文檔或應用程序文件的名稱來啟動進程資源,並將資源與新的 Process 組件關聯
12: Process.Start("iisreset");
13: }
如上就是簡單利用編程有效控制ISS服務運行狀態.
<5>應用程序池創建與控制
ISS應用程序池是將一個或多個應用程序鏈接到一個或多個工作進程集合的配置。因為應用程序池中的應用程序與其他應用程序被工作進程邊界分隔,所以某個應用程序池中的應用程序不會受到其他應用程序池中應用程序所產生的問題的影響, 當我們新建一個應用程序時 有時客戶端環境我們無法清除預知, 為了盡量減少影響ISS設置外在因素, 我們把新建虛擬目錄放到一個獨立應用程序池中.
當創建一個虛擬目錄後,也同時創建一個程序池 把虛擬目錄放到其中:
1: /// <summary>
2: /// 建立程序池後關聯相應應用程序及虛擬目錄
3: /// </summary>
4: public static void SetAppToPool(string appname)
5: {
6: //獲取目錄
7: DirectoryEntry getdir = new DirectoryEntry("IIS://localhost/W3SVC");
8: foreach (DirectoryEntry getentity in getdir.Children)
9: {
10: if (getentity.SchemaClassName.Equals("IIsWebServer"))
11: {
12: //設置應用程序程序池 先獲得應用程序 在設定應用程序程序池
13: //第一次測試根目錄
14: foreach (DirectoryEntry getchild in getentity.Children)
15: {
16: if (getchild.SchemaClassName.Equals("IIsWebVirtualDir"))
17: {
18: //找到指定的虛擬目錄.
19: foreach (DirectoryEntry getsite in getchild.Children)
20: {
21: if (getsite.Name.Equals(appname))
22: {
23: //【測試成功通過】
24: getsite.Properties["AppPoolId"].Value = appname;
25: getsite.CommitChanges();
26: }
27: }
28: }
29: }
30: }
31: }
32: }
思路如下: 首先獲得ISS的根目錄即ISS://localhost/W3SVC. 獲取成功後通過SchemaClassName獲取節點類型.ISSWebServer普通目錄 ISSWebVirturalDir虛擬目錄. 通過虛擬目錄的Name唯一名稱獲取指定虛擬目錄, 在通過屬性參數AppPoolId設置附屬的應用程序池的名稱. 然後提交保存.
其實通過應用我們可以清晰看到ISS中各個目錄之間的關聯關系.上面是修改一個已經存儲在應用程序池,如何創建:
1: public static bool CreateAppPool(string metabasePath, string appPoolName, string Username, string Password)
2: {
3: bool issucess = false;
4: try
5: {
6: if (metabasePath.EndsWith("/W3SVC/AppPools"))
7: {
8: if (MyIISHelper.AppPoolExist(appPoolName))
9: {
10: //已經存在 先刪除這個AppPool 在創建
11: //MessageBox.Show("當前以站點名稱命名的程序池已經存在!");
12: DeleteRepPool(appPoolName);
13: }
14:
15: //創建一個新程序池
16: DirectoryEntry newpool;
17: DirectoryEntry apppools = new DirectoryEntry(metabasePath);
18: newpool = apppools.Children.Add(appPoolName, "IIsApplicationPool");
19:
20: //設置屬性 訪問用戶名和密碼 一般采取默認方式
21: newpool.Properties["WAMUserName"][0] = Username;
22: newpool.Properties["WAMUserPass"][0] = Password;
23: newpool.Properties["AppPoolIdentityType"][0] = "3";
24: newpool.CommitChanges();
25: }
26: return issucess;
27: }
28: catch// (Exception ex)
29: {
30: return false;
31: }
32: }
基本上和創建一個DirectoryEntity雷同但注意指定的Schome類型是IIsApplicationPool用來標識創建的是一個應用程序池.當然創建可以刪除 刪除方式如下:
1: DirectoryEntry appPool = new DirectoryEntry("IIS://localhost/W3SVC/AppPools");
2: foreach (DirectoryEntry getdir in appPool.Children)
3: {
4: if (getdir.Name.Equals(appname))
5: {
6: getdir.DeleteTree();//刪除
7: }
8: }
關於應用程序池如果不做任何設置則在ISS中會被DefaultPool中添加, 但有時默認程序池設置環境不一定能夠滿足當前程序需求,例如最常見的關於.NET版本的控制. 其中對3.0和3.5版本應用設置需要重新注冊3.5 .NET FrameWork.部分組件這就涉及到一個.NET版本問題.
<6>.NET版本問題
在進行ISS控制中當然也是遇到各種各樣的問題, 一方面因為測試XP 2003系統環境不同, 需要控制因素過多,當然其中值得一提就是關於整個應用程序池的.NET版本問題.我們先來看看多版本下ISS中設置:
如果我們的應用程序使用3.0或3.5版本 則在應用程序池的沒有對應的.NET版本.這時應用程序運行會提示一個Http錯誤404.17 notFound:
其實ISS在處理應用程序池於.NET版本進行映射時,Net3.0\3.5沒帶處理程序aspnet_isapi.dll,所以IIS中指定網站適用框架時,這也是為什麼我們在ISS映射時看不到.Net3.0\3.5版本. 最直接的方法重新注冊.NET 3.5 由.NET 2.0版本托管: 這時我們需要執行一個Cmd命令:
從新把應用程序池.net版本切換成.net 2.0.同樣實現.net 3.0/3.5托管.
<7>使用環境以及測試條件
如上應用程序和代碼均在Windows 7./XP2/Windows Server 2003上通過. 對應的ISS版本范圍從ISS5.0/5.1到Iss 6.0/7.0. 包含ISS7.0. 篇幅有限,實際中關於ISS的操作還有很多細節問題不能面面俱到,我只是挑了其中我認為基礎或幾位重要幾個問題著重來寫, 實際中關於ISS還有其他諸多因素, 所以對於這方面控制編程 只能折中的有目的性選擇自己想做的效果. 切不可貪大求全. 因為這本身控制過程就是復雜的過程. 客戶環境也不能諸如統一. 如有疑問請在留言中回復我.