【转】C#综合揭秘——经过修改注册表创建Windows自定义协议

引言javascript

本文主要介绍注册表的概念与其相关根项的功能,以及浏览器如何经过链接调用自定义协议并与客户端进行数据通讯。文中讲及如何经过C#程序、手动修改、安装项目等不一样方式对注册表进行修改。其中经过安装项目对注册表进行修改的状况最为常见,在通常的应用程序中都会涉及。
当中最为实用的例子将介绍如何经过"安装项目"修改注册表创建自定义协议,在页面经过ajax方式发送路径请求,并在回调函数中调用自定义协议。
最后一节还将介绍如何调用自定义协议去保持数据的保密性。
但愿本篇文章能对各位的学习研究有所帮助,当中有所错漏的地方敬请点评。html

 

目录java

1、注册表的概念node

2、以C#程序修改注册表jquery

3、在 HKEY_CLASSES_ROOT 中添加自定义协议ajax

4、经过“安装项目”方式修改注册表shell

5、自定义协议的调用数据库

 

 

 

 

1、注册表的概念json

在谈及Windows自定义协议以前,不得不预先介绍的是注册表这个概念。注册表是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中的配置调用相关的驱动程序;

回到目录

 

2、以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的键

当重启计算机时就会看到“命令提示符”将自动启动

 

回到目录

 

3、在 HKEY_CLASSES_ROOT 中添加自定义协议

上面的章节已经简单介绍如何经过程序操做注册表,下面将介绍一下若是经过修改HKEY_CLASSES_ROOT中的项,创建自定义协议。
首先创建一个应用程序MyApplication,写入简单的Hello World测试代码

   static void Main(string[] args)
        {
            Console.WriteLine("Hello World");
            Console.ReadKey();
        }

手动在注册表中创建下面的项和键:

  • 一、在HKEY_CLASSES_ROOT下添加项MyApplication.
  • 二、修改MyApplication项下的默认值输入"URL:(可为空)"。
  • 三、在MyApplication项下再添加一个键值"URL Protocol"。(必要健,不然在IE浏览器中可能没法运行)
  • 四、在MyApplication项下新建项"shell"
  • 五、在shell项下新建项"open"
  • 六、在open项下新建项"command"
  • 七、修改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、经过“安装项目”方式修改注册表

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、自定义协议的调用

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控件,自定义协议不会受到浏览器的约束,更能简化客户端与浏览器之间的信息传信。

对 .NET 开发有兴趣的朋友欢迎加入QQ群:230564952 共同探讨 !

 

C#综合揭秘

经过修改注册表创建Windows自定义协议
Entity Framework 并发处理详解 
细说进程、应用程序域与上下文 
细说多线程(上) 
细说多线程(下)
细说事务 
深刻分析委托与事件

 

from:http://www.cnblogs.com/leslies2/p/3727762.html

相关文章
相关标签/搜索