通過修改註冊表建立Windows自定義協議
引言
本文主要介紹註冊表的概念與其相關根項的功能,以及瀏覽器如何通過連接調用自定義協議並與客戶端進行數據通信。文中講及如何通過C#程序、手動修改、安裝項目等不同方式對註冊表進行修改。其中通過安裝項目對註冊表進行修改的情況最為常見,在一般的應用程序中都會涉及。
當中最為實用的例子將介紹如何通過"安裝項目"修改註冊表建立自定義協議,在頁面通過ajax方式發送路徑請求,並在回調函數中調用自定義協議。
最後一節還將介紹如何調用自定義協議去保持數據的保密性。
希望本篇文章能對各位的學習研究有所幫助,當中有所錯漏的地方敬請點評。
目錄
一、註冊表的概念
二、以C#程序修改註冊表
三、在 HKEY_CLASSES_ROOT 中添加自定義協議
四、通過“安裝項目”方式修改註冊表
五、自定義協議的調用
一、註冊表的概念
在談及Windows自定義協議之前,不得不預先介紹的是註冊表這個概念。註冊表是windows操作系統的一個核心數據庫,其作用是充當計算機上操作系統和應用程序的中央信息儲存庫,用於存放著各種系統級參數。它能直接控制著windows的啟動、硬件驅動程序的裝載以及一些windows應用程序的運行。
註冊表中保存有應用程序和資源管理器外殼的初始條件、首選項和卸載數據等,聯網計算機的整個系統的設置和各種許可,文件擴展名與應用程序的關聯,硬件部件的描述、狀態和屬性,性能記錄和其他底層的系統狀態信息,以及其他數據等。
1.1 打開註冊表
打開 "windows運行",然後輸入regedit或regedt32即可打開註冊表
1.2 註冊表結構
註冊表由鍵、子鍵和值項構成,一個鍵就是分支中的一個文件夾,而子鍵就是這個文件夾中的子文件夾,子鍵同樣是一個鍵。一個值項則是一個鍵的當前定義,由名稱、數據類型以及分配的值組成。一個鍵可以有一個或多個值,每個值的名稱各不相同,如果一個值的名稱為空,則該值為該鍵的默認值。
HKEY_CLASSES_ROOT 用於控制所有文件的擴展和所有可執行文件相關的信息,本章提到的Windows自定義協議也是在此項中註冊產生的(在後面章節將詳細講述);
HEKY_CURRENT_USER 用於管理系統當前的用戶信息,及其應用程序的相關資料,例如:當前登錄的用戶信息,包括用戶登錄用戶名和暫存的密碼、當前用戶使用的應用軟件信息等。用戶登錄時,其信息會在HEKY_USER表中拷貝到此表中,當HEKY_USER表中信息發生改動時,HEKY_CURRENT_USER表中的信息也將隨之改動;
HKEY_CURRENT_MACHINE 用於存儲控制系統和軟件的信息,當中包括網絡和硬件上所有的軟件設備信息,比如文件的位置,註冊和未註冊的狀態,版本號等等;比較常用的例如在HKEY_LOCAL_MACHINE\Microsoft\Windows\CurrentVersion\Run下註冊程序,程序就會在Windows啟動時自動運行等等。其實在HKEY_LOCAL_MACHINE\SOFTWARE\Classes裏面就包含了HKEY_CLASSES_ROOT信息,而HKEY_CLASSES_ROOT只是它的一個鍵值的映射,方便信息管理而已;
HEKY_USER 作用是把缺省用戶和目前登陸用戶的信息輸入到註冊表編輯器,但它僅被那些配置文件激活的登陸用戶使用。當任何在HKEY_CURRENT_USER裏的信息發生改變,HKEY_USERS裏面的信息也會相應改動。
HKEY_CURRENT_CONFIG 用於存儲當前系統的配置方式,例如當Windows為同一個硬件安裝有多種驅動程序時,會在HEKY_CUREENT_MACHINE中記錄多個程序信息,而在HEKY_CURRENT_CONFIG中只是存儲默認使用的驅動信息,Windows 啟動時會默認按照HEKY_CURRENT_CONFIG中的配置調用相關的驅動程序;
二、以C#程序修改註冊表
微軟建立了Registry、RegistryKey 常用類用於修改Windows 註冊表中的節點。
2.1 Registry 類
Registry 主要用作獲取 Windows 註冊表中的根項的 RegistryKey 對象,並提供訪問項/值對的 static 方法。
它有以下幾個常用的屬性可直接用於獲取HEKY_CUREENT_MACHINE、HKEY_CLASSES_ROOT等幾個基礎項
屬性 | 說明 |
---|---|
ClassesRoot | 定義文檔的類型(或類)以及與那些類型關聯的屬性。 該字段讀取 Windows 註冊表基項 HKEY_CLASSES_ROOT。 |
CurrentConfig | 包含有關非用戶特定的硬件的配置信息。 該字段讀取 Windows 註冊表基項 HKEY_CURRENT_CONFIG。 |
CurrentUser | 包含有關當前用戶首選項的信息。 該字段讀取 Windows 註冊表基項 HKEY_CURRENT_USER |
DynData | 已過時。包含動態註冊表數據。 該字段讀取 Windows 註冊表基項 HKEY_DYN_DATA。 |
LocalMachine | 包含本地計算機的配置數據。 該字段讀取 Windows 註冊表基項 HKEY_LOCAL_MACHINE。 |
PerformanceData | 包含軟件組件的性能信息。 該字段讀取 Windows 註冊表基項 HKEY_PERFORMANCE_DATA。 |
Users | 包含有關默認用戶配置的信息。 該字段讀取 Windows 註冊表基項 HKEY_USERS。 |
Registry屬性表2.1.1
Registry 也提供幾個常用方法用於獲取或設置註冊表中指定名稱的項值。
方法 | 說明 |
---|---|
GetValue (String, String, Object) | 檢索與指定的註冊表項中的指定名稱關聯的值。 如果在指定的項中未找到該名稱,則返回您提供的默認值;或者,如果指定的項不存在,則返回 null。 |
SetValue(String, String, Object) | 設置指定的註冊表項的指定名稱/值對。 如果指定的項不存在,則創建該項。 |
SetValue(String, String, Object, RegistryValueKind) | 通過使用指定的註冊表數據類型,設置該指定的註冊表項的名稱/值對。 如果指定的項不存在,則創建該項。 |
Registry方法表2.1.2
2.2 RegistryKey 類
RegistryKey類主要用於管理 Windows 註冊表中的項級節點,通過 Registry 類的屬性就可以獲取註冊表中的根節點。它包含了以下幾個常用屬性
屬性 | 說明 |
---|---|
Handle | 獲取一個 SafeRegistryHandle 對象,該對象表示當前 RegistryKey 對象封裝的註冊表項。 |
Name | 檢索項的名稱。 |
SubKeyCount | 檢索當前項的子項數目。 |
ValueCount | 檢索項中值的計數。 |
View | 獲取用於創建註冊表項的視圖。 |
RegistryKey屬性表2.2.1
RegistryKey類的方法比較多,通過CreateSubKey(String)、GetValue(String)、SetValue(String, Object)、DeleteValue(String)等常用方法,就可以實現對註冊表的查詢修改。下面簡單介紹一下RegistryKey的幾個常用方法
方法 | 說明 |
---|---|
Close() | 關閉該項,如果該項的內容已修改,則將該項刷新到磁盤。 |
CreateSubKey(String) | 創建一個新子項或打開一個現有子項以進行寫訪問。 |
CreateSubKey(String, RegistryKeyPermissionCheck) | 使用指定的權限檢查選項創建一個新子項或打開一個現有子項以進行寫訪問。 |
CreateSubKey(String, RegistryKeyPermissionCheck, RegistryOptions) | 使用指定的權限檢查和註冊表選項,創建或打開一個用於寫訪問的子項。 |
CreateSubKey(String, RegistryKeyPermissionCheck, RegistrySecurity) | 使用指定的權限檢查選項和註冊表安全性創建一個新子項或打開一個現有子項以進行寫訪問。 |
CreateSubKey(String, RegistryKeyPermissionCheck, RegistryOptions, RegistrySecurity) | 使用指定的權限檢查選項、註冊表選項和註冊表安全性,創建或打開一個用於寫訪問的子項。 |
DeleteSubKey(String) | 刪除指定的子項。 |
DeleteSubKey(String, Boolean) | 刪除指定的子項,並指定在找不到該子項時是否引發異常。 |
DeleteSubKeyTree(String) | 遞歸刪除子項和任何子級子項。 |
DeleteSubKeyTree(String, Boolean) | 以遞歸方式刪除指定的子項和任何子級子項,並指定在找不到子項時是否引發異常。 |
DeleteValue(String) | 從此項中刪除指定值。 |
DeleteValue(String, Boolean) | 從此項中刪除指定的值,並指定在找不到該值時是否引發異常。 |
Flush() | 將指定的打開註冊表項的全部特性寫到註冊表中。 |
GetSubKeyNames() | 檢索包含所有子項名稱的字符串數組。 |
GetValue(String) | 檢索與指定名稱關聯的值。 如果註冊表中不存在名稱/值對,則返回 null。 |
GetValue(String, Object) | 檢索與指定名稱關聯的值。 如果未找到名稱,則返回您提供的默認值。 |
GetValue(String, Object, RegistryValueOptions) | 檢索與指定的名稱和檢索選項關聯的值。 如果未找到名稱,則返回您提供的默認值。 |
SetValue(String, Object) | 設置指定的名稱/值對。 |
RegistryKey方法表2.2.2
2.3 應用實例
下面先通過幾個例子,簡單介紹一下如何通過 Registry、RegistryKey 類修改系統註冊表。
2.3.1 新建自定義的項
下面應用程序將會在註冊表中新建 MyApplication項,並在其子項Path的默認值中保存應用程序的StartupPath、在AppPath值中保存應用程序的UserAppDataPath
static void Main(string[] args) { //獲取Machine根項 RegistryKey machine = Registry.LocalMachine; //打開SOFTWARE項 RegistryKey software = machine.OpenSubKey("SOFTWARE", true); //新項MyApplication項 RegistryKey myApplication = software.CreateSubKey("MyApplication"); RegistryKey subkey = myApplication.CreateSubKey("Path"); //新建鍵值,當鍵值名稱為空時,將被設置為默認值 subkey.SetValue(null, Application.StartupPath); subkey.SetValue("AppPath", Application.UserAppDataPath); }
運行應用程序後,打開"windows運行",然後輸入regedit,在註冊表LocalMachine項中可以查找到新建的MyApplication項
2.3.2 開機啟動 “命令提示符”
註冊表所包含的信息很多,其中在“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run”處正是用於Windows開機啟動的程序信息。下面以Windows自帶的“命令提示符”工具為例子,通過修改註冊表實現開機啟動。(說明:"命令提示符"的路徑是在“C:\Windows\System32\cmd.exe”)
static void Main(string[] args) { //打開註冊表子項 RegistryKey key = Registry.LocalMachine .OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true); //增加開機啟動項 key.SetValue("Cmd", "C:\\Windows\\System32\\cmd.exe"); }
修改後可以看到註冊表中在“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run”中會增加了一個名為Cmd的鍵
當重啟計算機時就會看到“命令提示符”將自動啟動
三、在 HKEY_CLASSES_ROOT 中添加自定義協議
上面的章節已經簡單介紹如何通過程序操作註冊表,下面將介紹一下如果通過修改HKEY_CLASSES_ROOT中的項,建立自定義協議。
首先建立一個應用程序MyApplication,寫入簡單的Hello World測試代碼
static void Main(string[] args) { Console.WriteLine("Hello World"); Console.ReadKey(); }
手動在註冊表中建立下面的項和鍵:
- 1、在HKEY_CLASSES_ROOT下添加項MyApplication.
- 2、修改MyApplication項下的默認值輸入"URL:(可為空)"。
- 3、在MyApplication項下再添加一個鍵值"URL Protocol"。(必要健,否則在IE瀏覽器中可能無法運行)
- 4、在MyApplication項下新建項"shell"
- 5、在shell項下新建項"open"
- 6、在open項下新建項"command"
- 7、修改command項的默認鍵值為MyApplication應用程序的路徑 "D:\C# Projects\MyApplication.exe" "%1"
註意:把 command 鍵值設置為 "D:\C# Projects\MyApplication.exe" "%1",只要當中包含標示符“%1”,應用程序可以根據自定義協議的路徑獲取對應的參數,其使用方式將在下面的章節再詳細說明。
簡單測試:建立一個HTML頁面,如入以下代碼。
註意:此連接路徑正是以註冊表產首項的MyApplication名稱相同。
<html xmlns="http://www.w3.org/1999/xhtml"> <head> ...... </head> <body> <a href="MyApplication://command">Hello World</a> </body> </html>
當按下Hello World連接符時,系統就會調用自定義協議MyApplication,啟動“D:\C# Projects\MyApplication.exe”
四、通過“安裝項目”方式修改註冊表
4.1 建立應用程序
上面章節所介紹的只是自定義協議的簡單使用方式,然而在做軟件項目的時候,無論是使用C/S或者B/S方式,自定義協議都必須實現在客戶端的自動安裝才能使用,因此客戶端的註冊表設置只能通過程序進行修改。有見及此,微軟早在“安裝項目”中設置了註冊表修改功能。下面的章節將為大家介紹如何通過 Visual Studio 2010 的“安裝項目”,實現註冊表的修改。
建立一個新的MyApplication應用程序,輸入以下代碼。
[DataContract] public class Person { [DataMember] public int ID; [DataMember] public string Name; [DataMember] public int Age; } class Program { static void Main(string[] args) { if (args != null) { //獲取輸入參數 string data = args[0].Split(‘&‘)[1]; //把JSON轉換成Person對象 Person person = GetPerson(data); //數據顯示 Console.WriteLine(person.Name + "‘s age is:" + person.Age); Console.ReadKey(); } } //數據轉換 static Person GetPerson(string data) { DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Person)); MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(data)); Person person = (Person)serializer.ReadObject(stream); stream.Close(); return person; } }
4.2 添加安裝項目
然後在解決方案裏面添加一個“安裝項目”
右鍵點擊"安裝項目",選擇“視圖-文件系統”後,在應用程序文件夾中添加當前的“MyApplication”項目。
4.3 修改註冊表
右鍵點擊"安裝項目",選擇“視圖-註冊表”後,按照第三節的例子在HKEY_CLASSES_ROOT上建立多個必要項。為方法獲取此應用程序的安裝路徑,可以在MyApplication項中加入一個鍵值Path,綁定"[TARGETDIR]",便於系統隨時能通過此鍵值獲取應用程序的安裝路徑。
在“安裝項目”中有多個可用的綁定值,例如:“[TARGETDIR]”用於綁定應用程序的安裝路徑,“[Manufacturer]”用於綁定應用程序制造商名稱等等。在command的值中輸入"[TARGETDIR]MyApplication.exe""%1",系統成功安裝後,此值就會轉換成應用程序的安裝路徑。例如:MyApplication應用程序安裝在"D:\C# Projects"安件夾中,那麽註冊表的command默認值就會變成“D:\C# Projects\MyApplication.exe” “%1”。
4.4 添加安裝自定義操作
在安裝應用程序的前後,很多時候需要做一些必要的操作,例如存儲程序的Path值,為應用程序生成一個sn文件作為標識等等。這時候就可以通過建立Installer的子類,在安裝的前後的事件進行操作。
首先建立新項目InstallComponent,在項目中加入一個具備RunInstaller特性的類繼承Installer類,RunInstaller特性是作用是用於指示在程序集安裝期間是否調用該安裝程序。而Installer類是Framework 中所有自定義安裝程序的基類,它具備了以下多個方法。
方法 | 說明 |
---|---|
Commit | 在派生類中重寫時,完成安裝事務。 |
Install | 在派生類中被重寫時,執行安裝。 |
OnAfterInstall | 引發 AfterInstall 事件。 |
OnAfterRollback | 引發 AfterRollback 事件。 |
OnAfterUninstall | 引發 AfterUninstall 事件。 |
OnBeforeInstall | 引發 BeforeInstall 事件。 |
OnBeforeRollback | 引發 BeforeRollback 事件。 |
OnBeforeUninstall | 引發 BeforeUninstall 事件。 |
OnCommitted | 引發 Committed 事件。 |
OnCommitting | 引發 Committing 事件。 |
Rollback | 在派生類中重寫時,還原計算機的安裝前狀態。 |
Uninstall | 在派生類中重寫時,移除安裝。 |
Installer方法表4.4.1
只要自定義的類型繼承了Installer類並重寫 Install、Commit、Rollback 和 Uninstall 等方法,系統就可以在應用程序安裝的多個不同狀態下進行操作。下面這個常用例子當中,MyInstaller加入了AfterInstall事件的處理方法,在MyApplication應用程序安裝完成後,會根據註冊表的Path鍵值獲取應用程序的安裝路徑,並在該文件夾內加入sn文件。sn文件內存儲著一個GUID,作為該應用程序的一個標識。
[RunInstaller(true)] public partial class MyInstaller : Installer { public MyInstaller() : base() { //綁定完成安裝事件的處理方法 this.AfterInstall += new InstallEventHandler(OnAfterInstall); } /// 加入安裝完成後的處理方法 protected override void OnAfterInstall(object sender, InstallEventArgs e) { CreateSn(); } //在完成安裝後建立一個sn文件,對該應用程序進行標識 private void CreateSn() { var regKey = Registry.ClassesRoot.OpenSubKey("MyApplication", true); if (regKey != null) { //根據註冊表中的Path鍵值,獲取系統在客戶端的安裝路徑 string path = regKey.GetValue("Path").ToString(); //建立sn文件 string snPath = path + "sn"; StreamWriter writer = new StreamWriter(snPath, true, Encoding.Unicode); writer.Write(Guid.NewGuid().ToString()); writer.Close(); } } /// 重寫安裝過程方法 public override void Install(IDictionary stateSaver) { base.Install(stateSaver); } /// 重寫卸載方法 public override void Uninstall(IDictionary savedState) { base.Uninstall(savedState); } /// 重寫回滾方法 public override void Rollback(IDictionary savedState) { base.Rollback(savedState); } //重寫提交方法 public override void Commit(IDictionary savedState) { base.Commit(savedState); } }
4.5 在安裝項目中綁定自定義操作
當完成自定義操作的設定後,回到安裝項目,右鍵點擊"安裝項目",選擇“視圖-文件系統”後,在應用程序文件夾中添加自定義操作的“InstallComponent”項目。
然後右鍵點擊"安裝項目",選擇“視圖-自定義操作-添加自定義操作-應用程序文件夾”,選擇剛才導入的 “InstallComponent”項目。
把安裝、提交、回滾、卸載等操作都與InstallComponent的MyInstaller類進行綁定。
生成安裝項目後,點擊setup應用程序進行系統安裝,完成安裝後你就會發現在註冊表的HKEY_CLASSES_ROOT下將添加了MyApplication項。而且在該應用程序文件夾中會自動增加一個sn文件,裏面將保存著一個CUID碼。
五、自定義協議的調用
5.1 以連接方式調用
調用自定義協議的方式很多,其中最簡單的就是以連接方式來調用,好像下面的例子,當頁面連接地址為MyApplication://自定義協議時,系統就會自動到註冊表查找該協議,根據command默認項的綁定路徑對該程序進行調用。並把“MyApplication://command&{‘ID‘:‘1‘,‘Name‘:‘Rose‘,‘Age‘:‘26‘}" 路徑名作為static void main(string[] args) 方法中的其中一個參數輸入。
<body> <script type="text/javascript"> window.onload = function () { var link = "MyApplication://command&{‘ID‘:‘1‘,‘Name‘:‘Rose‘,‘Age‘:‘26‘}"; location.href = link; } </script> </body>
觀察第4節的內容,自定義協議的main方法會把args[0]參數按照字符‘&‘進行分組,然後把後面的"{‘ID‘:‘1‘,‘Name‘:‘Rose‘,‘Age‘:‘26‘}"JSON字符串轉換成Person對象進行顯示。
5.2 動態調用
回顧前面例子,一直都是運用最簡單的連接方式對自定義協議進行調用。然而在現實的開展過程中,這種方法並不可行,因為每次調用客戶端的自定義協議可能需要不同的參數,把參數直接通過連接路徑來傳輸具有安全性問題。
最簡單的解決方案是把自定義協議的路徑在IHttpHandler裏面生成,再通過ajax來獲取,在回調函數中再調用此協議。通過此方法,協議的路徑不會以文本的方式存在於客戶端。除非是熟悉開發人員通過測試工具進行逐步檢索,否則一般人不能通過頁面找到自定義協議信息。
下面就是一個以IHttpHandler生成自定義協議的一個例子,MyHandler會根據url路徑獲取請求對象的id值,然後進行數據查詢,並把查詢到的對象轉化為JSON格式。最後把自定義協議的路徑返回到客戶端。
[DataContract] public class Person { public Person(int id, string name, int age) { ID = id; Name = name; Age = age; } [DataMember] public int ID; [DataMember] public string Name; [DataMember] public int Age; } public class MyHandler : IHttpHandler { public bool IsReusable { get { return true; } } public void ProcessRequest(HttpContext context) { //通過QueryString獲取id string data = context.Request.QueryString["id"]; if (data != null) { int id = int.Parse(data); //根據id進行數據查找 foreach (var person in DataSource()) { if (person.ID == id) { //把Person對象轉化為JSON數據 string json = ConvertToJson(person); //輸出自定義協議路徑 context.Response.Write(GetUrl(json)); } } } } //獲取自定義協議路徑 private string GetUrl(string json) { return "MyApplication://command&" + json; } //把Person對象轉化為JSON private string ConvertToJson(Person person) { DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Person)); MemoryStream stream = new MemoryStream(); serializer.WriteObject(stream, person); byte[] bytes = stream.ToArray(); stream.Close(); return Encoding.ASCII.GetString(bytes); } //模擬數據源 private IList<Person> DataSource() { IList<Person> list = new List<Person>(); Person person1 = new Person(1, "Rose", 34); list.Add(person1); ...... return list; } }
客戶端通過ajax把id發送到MyHandler.ashx進行查詢,在回調函數中調用所獲取到的自定義協議。
如果自定義協議參數中具有保密資料的信息還可以通過加密方式進行傳遞,好像上面的一個例子,客戶端可以先把自動生成的sn發送到服務器進行記錄,並與客戶ID進行綁定。在請求自定義協議的路徑時通過IHttpHandler把相關的信息通過sn進行加密,等到數據發送到客戶端後再進行解密。
<head> <title></title> <script src="Scripts/jquery-1.8.2.min.js" type="text/javascript"></script> </head> <body> <a name="send" id="send" href="#">GetPerson</a> <script type="text/javascript"> $(function () { $(‘#send‘).click(function () { $.ajax({ type: "GET", url: "MyHandler.ashx?id=1", data: null, dataType: null, success: function (data) { location.href = data; } }); }); }); </script> </body> </html>
本章小結
自定義協議有著廣泛的應用,像QQ、迅雷、淘寶等等這些的常見的應用程序都會使用自定義協議。特別在大型的企業系統開發過程中,C/S、B/S融合開發的情況很常見,這時候自定義協議更發揮其獨特的作用。一般在系統自動更新,客戶端信息獲取等這些功能上都會使用自定義協議進行開發。相對於ActiveX控件,自定義協議不會受到瀏覽器的約束,更能簡化客戶端與瀏覽器之間的信息傳信。
轉載於http://www.cnblogs.com/leslies2/p/3727762.html
通過修改註冊表建立Windows自定義協議