自定义Object与XML互换(序列化)

by Administrator 2012.6.5 14:49
反射真是个好东西,值得去深入学习.今天利用它实现对象的序列化与反序列化.由于是基于可互换的原则,在没有找到反序列化之前,抛弃了部分通用的做法.如果只是简单的将对象序列化成为XML,本是很方便的,但在考虑需要反序列化后,不得不考虑很多的问题.比如,序列化值类型(Type.IsValueType为true)的类型时,基本只需要简单的ToString()就可以.但如果想返序列化的话,有些类型就得去考虑,比如,日期的字符串格式.我是通过下面的方式,在确定的区域和日期格式中实现日期与字符串之间做转换:  static DateTimeFormatInfo dtfi = new CultureInfo("zh-CN", false).DateTimeFormat;  public override void ToXml(object val, StringBuilder builder)  {      builder.AppendFormat("{0}", ((DateTime)val).ToString(dtfi));  }    public override Object ToObject(Type type, XmlNode node)  {      String val = node.InnerText;      return DateTime.Parse(val, dtfi);  }   在实现功能时,集合无疑是最麻烦的,特别有了泛型之后,规则就更复杂了.加上考虑接口类型的话,就难上加难.像在确定集合中元素的类型,也不一样.如:数组元素类型: Type.GetElementType()泛型的T类型:Type.GetGenericArguments()而数组的元素是一个Type,但泛型的T类型却是Type[].特别在确定System.Collections.CollectionBase的元素类型时,没有找到可从Type中反射的方法,转换使用下面的方式确定:  System.Collections.IEnumerator tor = ((IEnumerable)val).GetEnumerator();  if (tor.MoveNext())  {      Type t = tor.Current.GetType();  }   这个其实是确定集合元素类型的通用方法,但它在反序列化时却不能使用.因为这样做只能在集合不为空时才能使用,而反序列化时,指定的集合却有可能是空的.所以这个取出来的类型,需要将它的所属程序集与类型名称保存下来,比如在节点中以itype的属性把它保存下来.这样做又增加了程序的复杂度.同时,在反序列化接口类型的属性时,也有可能遇到相似的问题,比如如何确定接口的实现类型?这个利用反射是做不到的.那么又只好把实现类型的相关信息在序列化时保存了,那不保存到ctype属性值中去吧.有了一大堆并不完全兼容的转换方法后,如何将代码的复杂度尽量降低呢?在这个序列化与反序列中,我们需要确定哪些转换方法适用于哪些类型,那么就让他主动告诉调用者吧.定义一个负责转换的基类:Switcher,它的作用是定义规则,实现最通用的做法,具体的事务由更详细的实现类来确定如何进行.下面是Switcher的全部代码:  using System;  using System.Collections.Generic;  using System.Text;  using System.Reflection;  using System.Xml;    namespace Guaik.Serialization.Xml  {      /// <summary>      /// 转换器      /// </summary>      public class Switcher      {          /// <summary>          /// 得到类型的属性列表          /// </summary>          public virtual PropertyInfo[] GetProperties(Type t)          {              return t.GetProperties();          }            /// <summary>          /// 检查是否可使用此转换器          /// </summary>          public virtual bool IsSwitcher(Type t)          {              return true;          }            /// <summary>          /// 转换为MXL          /// </summary>          public virtual void ToXml(Int32 level, String name, Type type, Object val, StringBuilder builder)          {              String sp = " ".PadLeft(level * 2);              builder.AppendFormat("{0}<{1}>{2}</{1}>", sp, name, val).AppendLine();          }            public virtual void ToXml(Object val, StringBuilder builder)          {              builder.AppendFormat("{0}", val);          }            /// <summary>          /// 转换为对象          /// </summary>          public virtual Object ToObject(Type type, XmlNode node)          {              return Convert.ChangeType(node.InnerText, type);          }      }  }    可以看到,其实主要是实现了三个方法:IsSwitcher(Type t)  这个用来告诉使用者,你这个转换器是否适用于Type类型?ToXml(.....)  这个用来将对象转换为XMLToObject(Type type, XmlNode node)  负责将node中的序列化的内容转换为type类型返回GetProperties(Type t)  现在只是简单的调用Type类型的方法返回,而以后可以定义相关的规则,可以在序列化时实现隐藏属性,重命名等规则在Switcher中实现的方法都是相对通用的,实现类中,只需要重写相关的方法则可.比如枚举是不可以像上面那样,使用Convert.ChangeType来转换的.因此需要定义一个用于处理枚举的Switcher,如下:  using System;  using System.Collections.Generic;  using System.Text;  using System.Reflection;  using System.Xml;    namespace Guaik.Serialization.Xml  {      /// <summary>      /// 枚举      /// </summary>      public class EnumSwitcher : Switcher      {          public override bool IsSwitcher(Type t)          {              return t.IsEnum;          }            public override Object ToObject(Type type, XmlNode node)          {              String val = node.InnerText;              return Enum.Parse(type, val);          }      }  }    实现的内容也非常简单.由于看到实现一个不兼容的类型还是相对方便的. 而在实现将数据序列化与反序列化中,也可以保持代码的简洁,下面是实现转换的类:  using System;  using System.Collections.Generic;  using System.Text;  using System.Reflection;  using System.Xml;    namespace Guaik.Serialization.Xml  {      /// <summary>      /// XML持久化      /// </summary>      public class XmlSerialization      {          private static Switcher[] Switcher = new Switcher[] {               new EnumSwitcher(),              new StringSwitcher(),              new DateTimeSwitcher(),              new ValueTypeSwitcher(),              //new GenericSwitcher(),              new CollectionSwitcher()          };            private static PropertyInfo FindProperty(Type type, String name)          {              PropertyInfo[] infos = GetProperties(type);              foreach (PropertyInfo info in infos)              {                  if (info.Name == name) return info;              }              return null;          }            private static PropertyInfo[] GetProperties(Type t)          {              return t.GetProperties();          }            private static Switcher GetSwitcher(Type t)          {              foreach (Switcher sw in Switcher)              {                  if (sw.IsSwitcher(t))                  {                      return sw;                  }              }              return null;          }           #region Xml2Object          public static Object Xml2Object(Type type, String path)          {              XmlDocument doc = new XmlDocument();              doc.Load(path);              return Xml2Object(type, doc);          }            public static Object Xml2Object(Type type, XmlDocument doc)          {              return Xml2Object(type, doc.SelectSingleNode(type.Name));          }            public static Object Xml2Object(Type type, XmlNode root)          {              Switcher swr = null;                swr = GetSwitcher(type);              if (swr != null)              {                  return swr.ToObject(type, root);              }              else              {                  Object val = null;                  Switcher switcher = null;                  PropertyInfo info = null;                  Object obj = Activator.CreateInstance(type, true);                  foreach (XmlNode node in root.ChildNodes)                  {                      info = FindProperty(type, node.Name);                      if (info != null)                      {                          switcher = GetSwitcher(info.PropertyType);                          val = switcher.ToObject(info.PropertyType, node);                          if (val != null)                          {                              info.GetSetMethod().Invoke(obj, new object[] { val });                          }                      }                  }                  return obj;              }          }          #endregion           #region Object2Xml          public static string Object2Xml(Object obj)          {              Type t = obj.GetType();              StringBuilder builder = new StringBuilder();              builder.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\" ?> ");                builder.AppendFormat("<{0}>", t.Name).AppendLine();                Object2Xml(1, obj, builder);                builder.AppendFormat("</{0}>", t.Name).AppendLine();                return builder.ToString();          }            public static bool Object2Xml(Int32 level, Object obj, StringBuilder builder)          {              Switcher swr = null;              Type t = obj.GetType();                swr = GetSwitcher(t);              if (swr != null)              {                  swr.ToXml(obj, builder);                  return true;              }              else              {                  object val = null;                  MethodInfo mi = null;                  PropertyInfo[] infos = GetProperties(t);                  foreach (PropertyInfo info in infos)                  {                      mi = info.GetGetMethod();                      if (mi != null)                      {                          val = mi.Invoke(obj, null);                          if (val != null)                          {                              swr = GetSwitcher(val.GetType());                              if (swr != null)                              {                                  swr.ToXml(level + 1, info.Name, info.PropertyType, val, builder);                              }                          }                      }                  }                  return false;              }          }          #endregion      }  }   在这个类中,有三个重要的方法:Object2Xml(...) 将对象转换为XMLXml2Object(...)  将XML转换为对象,提供了方法重载,方便使用者GetSwitcher(..)  确定哪个转换器可用于当前的转换GetSwitcher(..)方法中,使用了一个配置表Switcher ,在这里存储可用于转换的转换器集合.这个集合的次序确定了转换器的调用.在调用方面,也非常简单,下面是测试代码:  //类型定义      public class User      {          public string Name { get; set; }          public bool Sex { get; set; }          public int Age { get; set; }          public UserType Type { get; set; }          public DateTime CreateAt { get; set; }          public String[] Logs{get;set;}          public IList<String> List { get; set; }          public UserCollection Users { get; set; }      }        public enum UserType      {          Login,          Logout      }        /// <summary>      /// 数据库集合      /// </summary>      public class UserCollection : CollectionBase      {          public User this[int index]          {              get              {                  return ((User)List[index]);              }              set              {                  List[index] = value;              }          }            public User this[string name]          {              get              {                  foreach (User d in this)                  {                      if (d.Name.ToLower() == name.ToLower())                      {                          return (User)d;                      }                  }                  return null;              }          }            public int Add(User value)          {              return (List.Add(value));          }            public int IndexOf(User value)          {              return (List.IndexOf(value));          }            public void Insert(int index, User value)          {              List.Insert(index, value);          }            public void Remove(User value)          {              List.Remove(value);          }            public bool Contains(User value)          {              // If value is not of type User, this will return false.              return (List.Contains(value));          }            protected override void OnInsert(int index, Object value)          {              // Insert additional code to be run only when inserting values.          }            protected override void OnRemove(int index, Object value)          {              // Insert additional code to be run only when removing values.          }            protected override void OnSet(int index, Object oldValue, Object newValue)          {              // Insert additional code to be run only when setting values.          }            protected override void OnValidate(Object value)          {              if (!(value is User))                  throw new ArgumentException("value must be of type User.", "value");          }      }        //序列化          User user = new User();              user.Name = "邓铭武";              user.Sex = true;              user.Age = 20;              user.Type = UserType.Logout;              user.CreateAt = DateTime.Now;              user.Logs = new string[] {                  "登陆",                  "注销"              };              user.List = new List<String>();              user.List.Add("Name1");              user.List.Add("Name2");              user.List.Add("Name3");                user.Users = new UserCollection();                User su = new User();              su.Name = "邓铭武1";              user.Users.Add(su);              su = new User();              su.Name = "邓铭武2";              user.Users.Add(su);              su = new User();              su.Name = "邓铭武3";              user.Users.Add(su);                string xml = XmlSerialization.Object2Xml(user);              richTextBox1.Text = xml;      //反序列化          User user = new User();              XmlDocument doc = new XmlDocument();              doc.LoadXml(richTextBox1.Text);                Object obj = (User)XmlSerialization.Xml2Object(typeof(User), doc);    从上面可以看到,序列化与反序列化调用时,只需要调用一句代码就可以实现,过程非常方便.目前它可以支持C#类型系统中的所有基本类型,IList接口,IList<T>接口和CollectionBase集合,数组等.如果想实现其它类型的转换操作,可以通过实现Switcher重新配置XmlSerialization.Switcher表就可以达到目的.

Tags:

Dotnet

C# 中获取CPU序列号/网卡mac地址等

by Administrator 2012.6.5 14:46
这是我网上找的代码,但我自己对它进行了一些优化.1.cpu序列号2.mac序列号3.硬盘id在给软件加序列号时这三个应该是最有用的,可以实现序列号和机器绑定,代码如下:using System; using System.Management;     /// Computer Information     public class Computer     {         public string CpuID; //1.cpu序列号        public string MacAddress; //2.mac序列号        public string DiskID; //3.硬盘id        public string IpAddress; //4.ip地址        public string LoginUserName; //5.登录用户名        public string ComputerName; //6.计算机名        public string SystemType; //7.系统类型        public string TotalPhysicalMemory; //8.内存量 单位:M        public  Computer()         {             CpuID = GetCpuID();             MacAddress = GetMacAddress();             DiskID = GetDiskID();             IpAddress = GetIPAddress();             LoginUserName = GetUserName();             SystemType = GetSystemType();             TotalPhysicalMemory = GetTotalPhysicalMemory();             ComputerName = GetComputerName();         }     //1.获取CPU序列号代码         string GetCpuID()         {             try             {                  string cpuInfo = "";//cpu序列号                 ManagementClass mc = new ManagementClass("Win32_Processor");                 ManagementObjectCollection moc = mc.GetInstances();                 foreach (ManagementObject mo in moc)                 {                     cpuInfo = mo.Properties["ProcessorId"].Value.ToString();                 }                 moc = null;                 mc = null;                 return cpuInfo;             }             catch             {                 return "unknow";             }             finally             {             }         }    //2.获取网卡硬件地址         string GetMacAddress()         {             try             {                  string mac = "";                 ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration");                 ManagementObjectCollection moc = mc.GetInstances();                 foreach (ManagementObject mo in moc)                 {                     if ((bool)mo["IPEnabled"] == true)                     {                         mac = mo["MacAddress"].ToString();                         break;                     }                 }                 moc = null;                 mc = null;                 return mac;             }             catch             {                 return "unknow";             }             finally             {             }         }     //3.获取硬盘ID         string GetDiskID()         {             try             {                  String HDid = "";                 ManagementClass mc = new ManagementClass("Win32_DiskDrive");                 ManagementObjectCollection moc = mc.GetInstances();                 foreach (ManagementObject mo in moc)                 {                     HDid = (string)mo.Properties["Model"].Value;                 }                 moc = null;                 mc = null;                 return HDid;             }             catch             {                 return "unknow";             }             finally             {             }         }         //4.获取IP地址         string GetIPAddress()         {             try             {                 string st = "";                 ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration");                 ManagementObjectCollection moc = mc.GetInstances();                 foreach (ManagementObject mo in moc)                 {                     if ((bool)mo["IPEnabled"] == true)                     {                         //st=mo["IpAddress"].ToString();                         System.Array ar;                         ar = (System.Array)(mo.Properties["IpAddress"].Value);                         st = ar.GetValue(0).ToString();                         break;                     }                 }                 moc = null;                 mc = null;                 return st;             }             catch             {                 return "unknow";             }             finally             {             }         }             /// 5.操作系统的登录用户名          string GetUserName()         {             try             {                 string un= "";                st = Environment.UserName;                             return un;             }             catch             {                 return "unknow";             }             finally             {             }         }           //6.获取计算机名          string GetComputerName()         {             try             {                 return System.Environment.MachineName;            }             catch             {                 return "unknow";             }             finally             {             }         }    ///7 PC类型         string GetSystemType()         {             try             {                 string st = "";                 ManagementClass mc = new ManagementClass("Win32_ComputerSystem");                 ManagementObjectCollection moc = mc.GetInstances();                 foreach (ManagementObject mo in moc)                 {                     st = mo["SystemType"].ToString();                 }                 moc = null;                 mc = null;                 return st;             }             catch             {                 return "unknow";             }             finally             {             }         }                    catch             {                 return "unknow";             }             finally             {             }         }     /// 8.物理内存         string GetTotalPhysicalMemory()         {             try             {                 string st = "";                 ManagementClass mc = new ManagementClass("Win32_ComputerSystem");                 ManagementObjectCollection moc = mc.GetInstances();                 foreach (ManagementObject mo in moc)                 {                     st = mo["TotalPhysicalMemory"].ToString();                 }                 moc = null;                 mc = null;                 return st;             }     }

Tags:

Dotnet

通过使用客户端证书调用 Web 服务以便在 ASP.NET Web 应用程序中进行身份验证

by Administrator 2012.6.5 14:43
通常,Web 服务必须对调用该 Web 服务的应用程序执行身份验证。Web 服务必须对调用应用程序执行身份验证,然后才能执行授权。其中一种身份验证技术就是要求调用该 Web 服务的应用程序提供客户端证书。如果一个 ASP.NET Web 应用程序试图调用使用证书身份验证的 Web 服务,您可能会收到“access denied”(访问被拒绝)错误信息。如果控制台应用程序或 Microsoft Windows 窗体应用程序调用相同的 Web 服务,则不会收到错误信息。之所以会发生这种行为,是因为计算机维护两种不同的证书存储: • 本地机器存储:ASP.NET Web 应用程序在此类存储中进行查找以定位客户端证书。 • 本地用户存储:交互式用户应用程序在此类存储中进行查找以定位客户端证书。 通常,如果交互式用户应用程序安装客户端证书,会将客户端证书安装在本地用户存储中。因此,客户端证书可用于交互式用户应用程序。但是,客户端证书不用于 ASP.NET Web 应用程序。要使 ASP.NET Web 应用程序能够使用客户端证书,您必须在本地机器存储中安装客户端证书。如果您在本地机器存储中安装客户端证书,则该客户端证书仅对管理员组中的用户帐户和安装该客户端证书的用户可用。因此,您必须向用于运行 ASP.NET Web 应用程序的用户帐户授权,使其能够访问客户端证书。注意:必须安装 Microsoft .NET Framework 1.1 Service Pack 1 (SP1) 才能在本地机器存储中使用客户端证书。此外,在 ASP.NET Web 应用程序调用 Web 服务时,该应用程序必须从证书存储中导出客户端证书,然后将其添加到 Web 服务调用中。安装客户端证书并向用户帐户授予访问权限要安装客户端证书,并向用于运行 ASP.NET Web 应用程序的用户帐户授权,使其能够访问客户端证书,请执行下列步骤。 步骤 1:在本地机器存储中安装客户端证书如果您在 PKCS#12 (.pfx) 文件中有客户端证书,则可以使用 Microsoft Windows HTTP 服务证书配置工具 (WinHttpCertCfg.exe) 来安装客户端证书并向其他用户帐户(如网络服务帐户)授权,使其能够访问客户端证书。为此,请按照下列步骤操作: 1. 下载然后安装 Microsoft Windows HTTP 服务证书配置工具。要获得此工具,请访问下面的 Microsoft 网站:http://www.microsoft.com/downloads/details.aspx?familyid=c42e27ac-3409-40e9-8667-c748e422833f (http://www.microsoft.com/downloads/details.aspx?familyid=c42e27ac-3409-40e9-8667-c748e422833f) 2. 在命令提示符处运行以下命令:WinHttpCertCfg.exe –i PfxFile -c LOCAL_MACHINEMY -p Password注意:PfxFile 是 .pfx 文件的名称。Password 是 .pfx 文件的密码。如果该文件不要求密码,则省略参数 -p。通常,WinHttpCertCfg.exe 文件位于以下文件夹中:C:Program FilesWindows Resource KitsTools 如果您无权访问 .pfx 文件,并已经在运行 Microsoft Windows Server 2003 或 Microsoft Windows 2000 Server 的计算机上安装了 Microsoft 证书服务,则可以提出请求,并直接在本地机器存储中安装客户端证书。为此,请按照下列步骤操作: 1. 使用具有管理员凭据的用户帐户登录到客户机。 2. 在 Microsoft Internet Explorer 中,访问证书颁发机构 (CA) 网站。例如,如果 CA 服务器名为 CAServer,请访问下面的网站:http://CAServer/certsrv 3. 在“选择一个任务”下,单击“申请一个证书”然后单击“高级证书申请”。 4. 单击“创建并向此 CA 提交一个申请”。 5. 在“高级证书申请”窗体中,输入姓名和电子邮件地址。 6. 在“需要的证书类型”部分,单击“客户端身份验证证书”。 7. 在“密钥选项”部分,单击以选中“将证书保存在本地计算机存储中”复选框,然后单击“提交”。请注意客户端证书的申请 ID。 8. 在“潜在的脚本冲突”对话框中,单击“是”。 9. 在 CA 颁发客户端证书后,单击“主页”,然后单击“查看挂起的证书申请的状态”。 10. 在“请选择您要查看的证书申请”下,单击您在步骤 7 中提交的证书申请。 11. 单击“安装此证书”,然后在“潜在的脚本冲突”对话框中单击“是”。 步骤 2:配置对客户端证书的访问权限在此步骤中,您必须向 ASP.NET 帐户授予权限,使其能够访问存储在本地机器存储中的客户端证书。在 Windows Server 2003 中,网络服务帐户是运行 Web 应用程序的默认帐户。因此,您必须向网络服务帐户授权使其能够访问该证书。如果您配置了一个自定义帐户以运行 ASP.NET,则必须向该自定义帐户授予访问权限。注意:在 Microsoft Internet Information Server (IIS) 5.0 中,ASP.NET 在 ASPNET 帐户而非网络服务帐户下运行。因此,在运行 IIS 5.0 的计算机上,您必须向 ASPNET 帐户授予权限。要向特定的用户帐户授予访问权限,请在命令提示符处运行以下命令:WinHttpCertCfg.exe -g -c LOCAL_MACHINEMY -s "IssuedToName" -a "AccountName"注意:AccountName 是本地计算机帐户名或域帐户名。IssuedToName 是客户端证书颁发对象的公司名或域名。该命令包含一个不区分大小写的搜索字符串。该搜索字符串查找第一个主题名称中包含该字符串的枚举证书。下面的命令行命令是一个如何在 Microsoft Internet 信息服务 (IIS) 6.0 中向网络服务帐户授权,使其能够访问客户端证书的示例:WinHttpCertCfg.exe -g -c LOCAL_MACHINEMY -s "IssuedToName" -a "NT AUTHORITYNetworkService"下面的命令行命令是一个如何在 IIS 5.0 中向 ASPNET 帐户授权,使其能够访问客户端证书的示例:WinHttpCertCfg.exe -g -c LOCAL_MACHINEMY -s "IssuedToName" -a "ASPNET"注意:如果您使用 Windows HTTP 服务证书配置工具,则可以将导入客户端证书的过程与配置对客户端证书的访问权限的过程合并为一个步骤。例如,下面的命令行命令将执行这两个过程:Winhttpcertcfg.exe -i PFXFile -c LOCAL_MACHINEMy -a “AccountName”步骤 3:将客户端证书从本地用户存储复制到本地机器存储如果一个交互式应用程序(如 Windows 窗体应用程序)或命令行应用程序能够访问客户端证书,则该客户端证书已经存储在本地用户存储中。但是,如果一个服务应用程序(如 ASP.NET Web 应用程序)不能访问同一个客户端证书,则该客户端证书可能未存储在本地机器存储中。本步骤将介绍如何使用证书导出向导将本地用户存储中的客户端证书复制到本地机器存储中。注意:如果客户端证书已经在本地机器存储中,或者如果您能够按照步骤 1 所述直接在本地机器存储中安装客户端证书,请转到步骤 4。但是,如果您使用了步骤 3,则之后必须返回步骤 2 以授予对客户端证书的访问权限。要将客户端证书复制到本地机器存储中,请执行下列步骤: 1. 单击“开始”,单击“运行”,键入 mmc,然后单击“确定”。 2. 在“文件”菜单上,单击“添加/删除管理单元”,然后单击“添加”。 3. 在“添加独立管理单元”对话框中,依次单击“证书”、“添加”、“计算机帐户”、“下一步”,然后单击“完成”。 4. 在“添加独立管理单元”对话框中,依次单击“证书”、“添加”、“我的用户帐户”,然后单击“完成”。 5. 单击“关闭”,然后单击“确定”。 6. 要从本地用户存储中导出客户端证书,请执行下列步骤:a. 依次展开“证书 - 当前用户”、“个人”,然后单击“证书”。 b. 右键单击此客户端证书,依次单击“所有任务”、“导出”,然后单击“下一步”。 c. 如果“是,导出私钥”选项不可用,则 ASP.NET Web 应用程序不能使用该客户端证书。您必须获取其他客户端证书。为此,请按照步骤 1 和步骤 2 中的说明进行操作。否则,单击“是,导出私钥”,然后单击两次“下一步”。 d. 在“密码”框和“确认密码”框中,键入密码,然后单击“下一步”。 e. 在“文件名”框中,键入文件名。单击“下一步”,然后单击“完成”。 f. 在“证书导出向导”对话框中,单击“确定”。 7. 要将客户端证书导入到本地机器存储中,请执行下列步骤:a. 展开“证书(本地计算机)”,然后展开“个人”。 b. 右键单击“证书”,依次单击“所有任务”、“导入”,然后单击“下一步”。 c. 在“文件名”框中,键入您在步骤 6e 中指定的文件名,然后单击“下一步”。 d. 在“密码”框中,键入您在步骤 6d 中指定的密码,然后单击两次“下一步”。 e. 单击“完成”,然后单击“确定”。 步骤 4:安装 CA 根证书如果客户端证书已经由外部 CA(如 VeriSign)签名,或者如果您已经安装了 CA 根证书,则可以省略步骤 4。 默认情况下,Windows 已将许多外部 CA 根证书预安装在受信任的根证书存储中。验证是否安装了根证书要验证是否安装了 CA 根证书,请执行下列步骤: 1. 单击“开始”,单击“运行”,键入 mmc,然后单击“确定”。 2. 在“文件”菜单上,单击“添加/删除管理单元”,然后单击“添加”。 3. 在“添加独立管理单元”对话框中,依次单击“证书”、“添加”、“计算机帐户”、“下一步”,然后单击“完成”。 4. 单击“关闭”,然后单击“确定”。 5. 依次展开“证书(本地计算机)”、“受信任的根证书颁发机构”,然后单击“证书”。 6. 在右窗格中,请确认列出了要使用的 CA 根证书。 安装根证书如果没有列出要使用的 CA 根证书,则必须安装此根证书。如果要使用的 CA 根证书颁发在证书文件(如 .cer 文件、.der 文件、或 .pfx 文件)中,请执行下列步骤: 1. 展开“证书(本地计算机)”,右键单击“受信任的根证书颁发机构”,单击“所有任务”,然后单击“导入”。 2. 在“证书导入向导”对话框中,单击“下一步”,然后在“文件名”框中,键入证书文件的名称,然后单击两次“下一步”。 3. 单击“完成”,然后单击“确定”。 申请根证书如果要使用的 CA 包括在 Microsoft 证书服务安装数据库中,那么您可以申请根证书。为此,请按照下列步骤操作: 1. 在 Internet Explorer 中,访问 CA 网站。例如,如果 CA 服务器名为 CAServer,请访问下面的网站:http://CAServer/certsrv 2. 单击“下载一个 CA 证书,证书链或 CRL”,然后单击“下载 CA 证书”。 3. 在“文件下载”对话框中,单击“保存”。 4. 在“另存为”对话框中,输入要保存证书文件的位置,然后单击“保存”。 5. 保存根证书文件后,使用“安装根证书”部分中的步骤在受信任的根证书颁发机构存储中安装证书文件。 回到顶端 调用 Web 服务当您在本地机器存储或本地用户存储中安装客户端证书后,您就可以从 ASP.NET Web 应用程序中访问该客户端证书以调用 Web 服务。对于 Windows 窗体应用程序或 ASP.NET Web 应用程序,访问客户端证书的步骤是相同的。如果使用的是 .NET Framework 1.1,则必须先将密钥导出到一个 DER 编码文件中。必须导出密钥的原因是 System.Security.Cryptography.X509Certificates.X509Certificate 类中不包含可以直接访问证书存储中的证书详细信息的方法。因此,应用程序必须从 DER 编码文件中读取证书的详细信息。注意:Web Services Enhancements 2.0 for Microsoft .NET (WSE) 为应用程序提供了一种从证书存储中直接检索证书详细信息的方法。下面的 C# 示例代码显示如何通过传递客户端证书进行身份验证来调用 Web 服务。using System.Security.Cryptography.X509Certificates;...public void CallWebService(){  // TODO: Replace <C:WSClientCert.cer> with the path of your certificate file.  string certPath = @"<C:WSClientCert.cer>";  // Create an instance of the Web service proxy.  WebSvc.math mathservice = new WebSvc.math();  // TODO: Replace <https://wsserver/securemath/math.asmx> with a valid URL.  mathservice.Url = @"<https://wsserver/securemath/math.asmx>";  // Create an X509Certificate object from the information  // in the certificate export file, and then add the certificate to the  // ClientCertificates collection of the Web service proxy.  mathservice.ClientCertificates.Add(    X509Certificate.CreateFromCertFile(certPath));  long lngResult = 0;  try  {    lngResult = mathservice.Add(Int32.Parse(operand1.Text),      Int32.Parse(operand2.Text));    string result = lngResult.ToString();  }  catch(Exception ex)  {    if(ex is WebException)    {    WebException we = ex as WebException;    WebResponse webResponse = we.Response;    throw new Exception("Exception calling method. " + ex.Message);    }  }}回到顶端 Web Services Enhancements 2.0 for Microsoft .NETWeb Services Enhancements 2.0 for Microsoft .NET (WSE) 是一个 Microsoft .NET 类库,它使用最新的 Web 服务协议来创建 Web 服务。这些协议包括:• WS-Security • WS-SecureConversation • WS-Trust • WS-Policy • WS-SecurityPolicy • WS-Addressing • WS-Attachments 注意:WSE 不作为 .NET Framework 的一部分包括在内。要获得 WSE,请访问下面的 Microsoft 网站:http://www.microsoft.com/downloads/details.aspx?FamilyId=FC5F06C5-821F-41D3-A4FE-6C7B56423841 (http://www.microsoft.com/downloads/details.aspx?FamilyId=FC5F06C5-821F-41D3-A4FE-6C7B56423841)您不必使用这些协议中的任何一种来访问要求客户端证书身份验证的 Web 服务。但是,您可能要使用 Microsoft.Web.Services2.Security.X509 类。Microsoft.Web.Services2.Security.X509 类包含可以直接访问证书存储中的客户端证书的方法。如果您使用这些方法,就不必将证书导出到文件中。下面的 C# 示例代码显示如何在本地机器存储中查找第一个名为 SecureMathClient 的证书。然后,该示例代码使用此证书调用 math Web 服务的 Add 方法。math Web 服务要求客户端证书。...// TODO: Replace <SecureMathClient> with the name of the client certificate.string certName = "<SecureMathClient>";// WSE 2.0 methodX509CertificateStore store =  X509CertificateStore.LocalMachineStore(X509CertificateStore.MyStore);store.OpenRead();// Look for the first certificate that is named SecureMathClient.// Look in the local machine store.X509CertificateCollection col=  (X509CertificateCollection)store.FindCertificateBySubjectString(certName);X509Certificate cert =null;try{// This sample obtains the first matching certificate from the collection.  cert = col[0];}catch(Exception ex){  throw new Exception("Certificate not Found!");}// Create an instance of the Web service proxy.math mathservice = new math();// TODO: Replace <https://wsserver/securemath/math.asmx> with a valid URL.mathservice.Url = @"<https://wsserver/securemath/math.asmx>";mathservice.ClientCertificates.Add(cert);long lngResult = 0;try{  lngResult = mathservice.Add(Int32.Parse(operand1.Text),    Int32.Parse(operand2.Text));  result.Text = lngResult.ToString();}catch(Exception ex){  if(ex is WebException)  {    WebException we = ex as WebException;    WebResponse webResponse = we.Response;    throw new Exception("Exception calling method. " + ex.Message);  }}参考有关 System.Security.Cryptography.X509Certificates.X509Certificate 类的更多信息,请访问下面的 Microsoft Developer Network (MSDN) 网站:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemsecuritycryptographyx509certificatesx509certificateclasstopic.asp (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemsecuritycryptographyx509certificatesx509certificateclasstopic.asp)有关如何使用 ASP.NET Web 应用程序调用安全性增强的网站的更多信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章: 817854 (http://support.microsoft.com/kb/817854/) FIX:ASP.NET Web 应用程序不能向安全的 Web 站点传送客户端证书 

Tags:

Dotnet

关于AppDomain和线程

by Administrator 2012.6.5 10:20
进程是系统资源的拥有者,是Windows资源分本的单位;线程是Windows任务调度的最小单位,进程是线程的容器,线程必须在进程中运行,每个进程创建时都会有一个主线程被创建。一个进程内运行多个应用程序就是应用程序域,一个应用程序中出现的错误不会影响其他应用程序。因为类型安全的代码不会导致内存错误,所以使用应用程序域可以确保在一个域中运行的代码不会影响进程中的其他应用程序。 能够在不停止整个进程的情况下停止单个应用程序。使用应用程序域使您可以卸载在单个应用程序中运行的代码,那么怎么让不同的线程在不同的应用程序域上运行呢?1、.Net新增了一个隔离层,称为应用程序域或AppDomain,应用程序域不像进程那样是一个物理独立的部分,而是进程内部的一个逻辑独立部分,在一个进程中可以存在多个应用程序域。应用程序域与线程也有直接的关系,他们可以保持一个或多个线程,就像进程一样,不同之处在于,应用程序域可以在进程内部创建,但不创建新线程。应用程序域和进程以及线程的关系如下图: 2. 设置AppDomain数据 代码public class MyAppDomain    {        public AppDomain domain;        public int ThreadID;        public void SetDomainData(string vName, string vValue)        {            domain.SetData(vName, vValue);             ThreadID = AppDomain.GetCurrentThreadId();        }        public string GetDomainData(string name)        {            return (string)domain.GetData(name);        }        static void Main()        {            string dataName = "MyData";            string dataValue = "Some Data to be stored";            Console.WriteLine("Retrieving current domain");            MyAppDomain obj = new MyAppDomain();            obj.domain = AppDomain.CurrentDomain;            Console.WriteLine("Setting domain data");            obj.SetDomainData(dataName, dataValue);            Console.WriteLine("Getting domain data");                 Console.WriteLine("The Data found for key" + dataName + "is" +                     obj.GetDomainData(dataName) + " running on thread id:" +obj.ThreadID);        }    }   3、在指定的应用程序域中执行代码代码class Program    {        static void Main(string[] args)        {            AppDomain DomainA;            DomainA = AppDomain.CreateDomain("DomainA");            string StringA = "DomainA Value";            DomainA.SetData("DomainKey", StringA);            CommonCallBack();            CrossAppDomainDelegate delegateA = new CrossAppDomainDelegate(CommonCallBack);            DomainA.DoCallBack(delegateA);        }        public static void CommonCallBack()        {            AppDomain Domain;            Domain = AppDomain.CurrentDomain;            Console.WriteLine("The value'" + Domain.GetData("DomainKey")                 + "' was found in " + Domain.FriendlyName                 + " running on thread id :"                 + AppDomain.GetCurrentThreadId().ToString());        }    } 

Tags:

Dotnet

DotNet 区分大小回车

by Administrator 2010.8.23 09:03
C#里面普通的KeyDown KeyUP 区分不了大小回车,如果 要区分需要处理Windows消息: ......方法一:public class KeyFilter : IMessageFilter{    #region IMessageFilter Members    const int WM_KEYDOWN = 0x0100;    const int VK_RETURN = 13;    private Form parent;    public KeyFilter(Form parent)    {        this.parent = parent;    }    public bool PreFilterMessage(ref Message m)    {        switch (m.Msg)        {            case WM_KEYDOWN:                if (VK_RETURN != ((int)m.WParam & 0xff)) return false;                if (!(parent.ActiveControl is TextBox)) return false;                if (((int)m.LParam & 0x1000000) != 0)                {                    //MessageBox.Show("小键盘回车");                    SendKeys.Send("{TAB}");                    return true; // 过滤                }                else                {                    //MessageBox.Show("大键盘回车");                }                break;        }        return false;    }    #endregion}private void Form1_Load(object sender, EventArgs e){    Application.AddMessageFilter(new KeyFilter(this));}private void textBox1_KeyDown(object sender, KeyEventArgs e)方法二:   #region 键盘钩子        #region DllImport        //设置钩子        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]        public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]        //抽掉钩子        public static extern bool UnhookWindowsHookEx(int idHook);        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]        //调用下一个钩子        public static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);        [DllImport("kernel32")]        public static extern int GetCurrentThreadId();        #endregion        #region 自定义事件        public void Hook_Start()        {            // 安装键盘钩子            if (hHook == 0)            {                KeyBoardHookProcedure = new HookProc(KeyBoardHookProc);                hHook = SetWindowsHookEx(WH_KEYBOARD_LL,                            KeyBoardHookProcedure,                            IntPtr.Zero, GetCurrentThreadId());                //如果设置钩子失败.                if (hHook == 0)                {                    Hook_Clear();                    throw new Exception("设置Hook失败!");                }            }        }        //取消钩子事件        public void Hook_Clear()        {            bool retKeyboard = true;            if (hHook != 0)            {                retKeyboard = UnhookWindowsHookEx(hHook);                hHook = 0;            }            //如果去掉钩子失败.            if (!retKeyboard) throw new Exception("UnhookWindowsHookEx failed.");        }        //这里可以添加自己想要的信息处理        public int KeyBoardHookProc(int nCode, IntPtr wParam, IntPtr lParam)        {            if ((nCode >= 0) && ((int)wParam == 13)&&((int)lParam & 0x1000000) != 0)            {                //小回车             }             else            {                //非小回车            }            return CallNextHookEx(hHook, nCode, wParam, lParam);            return CallNextHookEx(hHook, nCode, wParam, lParam);        }        #endregion        #endregion

Tags:

Dotnet

在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式

by Administrator 2010.8.8 05:27
多线程出现:在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式。请确保您的 Main 函数带有 STAThreadAttribute 标记。       本文 可以解决New Thread()的多线程,至于 BackupGroundWorder 还没有解决的办法 。   在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式。请确保您的 Main 函数带有 STAThreadAttribute 标记。 只有将调试器附加到该进程才会引发此异常。     程序之前运行正常,我只是在一个事件中定义了做了如下操作,程序抛出此异常  Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> private void OnExportPlan(object sender, EventArgs e) { try { if (null != ExportPlan) { Cursor = Cursors.WaitCursor; saveFileDialog1.Filter = "(Excel2003).xls|*.xls|(Excel2007).xlsx|*.xlsx"; if (DialogResult.OK == saveFileDialog1.ShowDialog()) { mExportFilePath = saveFileDialog1.FileName; Application.DoEvents(); ExportPlan(this, null); } } } finally { Cursor = Cursors.Default; } }  查看MSDN,有如下说明:   单元是进程内部具有相同线程访问要求的对象的逻辑容器。同一单元中的所有对象都可以接收从该单元中的任何线程发出的调用。.NET Framework 不使用单元,托管对象自己负责以线程安全的方式使用所有共享资源。  由于 COM 类使用单元,因此公共语言运行库需要在 COM interop 的情况下调用 COM 对象时创建并初始化一个单元。托管线程可以创建并进入只允许有一个线程的单线程单元 (STA) 或者包含一个或多个线程的多线程单元 (MTA)。通过将线程的 ApartmentState 属性设置为 ApartmentState 枚举值之一,可以控制所创建的单元的类型。由于给定线程只能初始化 COM 单元一次,因此在第一次调用非托管代码之后就不能更改单元类型。  成员定义:     STA:Thread 将创建并进入一个单线程单元。     MTA:Thread 将创建并进入一个多线程单元  Unknown:尚未设置 ApartmentState 属性。解决办法两种:1) 在Main入口添加STAThread的Attribute    Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[STAThread] static void Main()  2) Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> Thread app = new Thread(new ParameterizedThreadStart(ShowWindow));app.ApartmentState = ApartmentState.STA; //也可以 Thread newThread = new Thread(new ThreadStart(ThreadMethod)); newThread.SetApartmentState(ApartmentState.MTA);

Tags:

Dotnet

Reflector反编译winform心得

by Administrator 2010.7.10 03:09
     Reflactor就不介绍了,.net下的免费反编译工具。Reflactor在某些情况下是很必须的,当遇到bug的时候,可以通过Reflactor看其内部的实现机制,从而找到真正的bug所在。或者可以通过reflactor查看私有生成,然后在用反射去调用。    上面说的都是正当用途,有的时候也可以用来反编译别人的软件。混淆了的软件,我就不管了。这里要讲的是没有混淆和加密的软件的反编译。reflector的右键菜单中的export就能做到反编译,如下图。    反编译的文件默认保存在My Documents\Reflector\Disassembler下,但是这个反编译有一些问题,对于winform的资源文件无法反编译。这就需要我们另寻他法。    经过我的不懈努力找到了一个插件File dessassemble,他能实现这样的功能。    反编译出来后,用visual sutio打开后就能编辑了,但是有时候会有一些问题。下面总结一下。1、编译没有问题,运行出现“类型初始值设定项引发异常”    这主要是在类中使用了两个静态变量,而其中一个静态变量的初始化依赖令一个静态变量,而被依赖的静态变量放在依赖变量的前面,比如下面的代码就会出现这样的问题。CodeCode highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->1        private static string SettingFileName = Path.Combine(SettingFolderPath, @"GoogleMusicDesktop\config.xml");2        public static string SettingFolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "aaaSoft");    问题很好解决,掉个个就解决了。2、出现无法找到资源文件,如图片等    这种情况一般是和资源文件的命名如果,请把资源文件命名保持和类一直,重新编译一下就会解决问题,注意需要重新生成,否则就不一定好使了。3、打开form的时候出现错误    这个问题,我通过搜索解决了,园子里有一篇文章,我转过来。转载文章Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 用Reflector和FileDisassembler配合反编译.net Windows程序URL: http://www.cnblogs.com/verygis/archive/2008/12/02/1346072.html用FileDisassembler反编译Windows Application出现了问题,resx资源文件和cs文件不匹配,项目编译根本通不过,把resx文件修改放入cs文件夹后,虽然运行没有问题,但是不能直接在窗体设计里面"可视地"修改控件,一来麻烦,二来不直观,那么就应该修改下源代码,使其符合VS2008的源文件布局.下面总结一下修改的步骤:1.由于FileDisassembler会给每个命名空间生成一个目录,保存这个空间的源代码,而对于资源resx文件就是直接加在项目目录下,所有要把它放回源码目录下.例如对于项目文件夹里面的TryAssemb.Form1.resx,首先改为Form1.resx然后移动到TryAssemb目录里面.2.然后对窗体打开"视图设计器",发现会出现下面的错误:修改方法就是对所有System.Windows.Forms.命名空间里面的控件需要全命名空间的声明,例如里面 base.AutoScaleMode = AutoScaleMode.Font;就要改成base.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;而TextBox textBox1; 也要改成private System.Windows.Forms.TextBox textBox1;这样VS2008就能识别到这个控件是要绘制在Form上面的.PS.开始是的时候还以为要像VS一样把Form1拆开成Form1.cs和Form1.Designer.cs.每次是都辛辛苦苦拆开,最后才发现原来根本不需要,^_^.相信大多数.Net程序员都有使用Reflactor的经历。无论出于什么目的,当用Reflactor反编译托管程序后,还想对其代码加以修改,那么本文所列举的可能是一份有用的参考。用Reflactor的FileGenerator插件反编译代码后可以得到包括项目文件的源代码,但代码中存在各种问题,一般无法一次编译通过,以下将详谈这些问题:枚举问题为了代码可读,可能需要花点时间查阅metadata把int值修改回枚举值,尤其是想利用窗体设计器的,VS2008可能还不理解int值。属性问题比如一个叫Names的属性被反编译后,可能还原为的set_Names(names),get_Names()方法,逐个替换可能很慢,可采用正则表达式整体替换。对于set_Xxx(xxx)方法,可替换set_{[a-z]*}\(为\1 = (对于get_Xxx()方法,可替换get_{[a-z]*}\(\)为\1然后,再修复个别被误换的方法。委托和回调函数问题一般会被还原为add_Xxx(MethodsName)方法,需要改为 += MethodsName资源问题需要使用.Net Framework SDK 下的 resgen.exe 工具,反编译嵌入资源文件*.resources为*.resx文件,语法为:ResGen.exe *.resources *替换为System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ClassName));窗体设计器才能正常识别。本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/ma_ta/archive/2008/12/11/3497532.aspx    这文章很好,转了好几次了。

Tags:

Dotnet

使用RichTextBox的一点心得

by Administrator 2010.6.21 02:31
    最近开发一个控件,主要是对RichTextBox进行操作,实现对文本的编辑,包括加粗,下划线,改变字号,改变颜色等。最主要的是在光标位置插入带背景颜色的标签。         我的实现方式如下:        最近开发一个控件,主要是对RichTextBox进行操作,实现对文本的编辑,包括加粗,下划线,改变字号,改变颜色等。最主要的是在光标位置插入带背景颜色的标签。         我的实现方式如下:1. 新建一个控件类,让他继承RichTextBox类。2. 文本的编辑主要使用RichTextBox的Select、SelectionColor、SelectionFont来实现。其中文本颜色是调用ColorDialog组建来实现。3.带背景颜色的标签。插入这个标签,开始我还真没有想到好的解决方法,最后想起了RichTextBox是支持Rtf规范的。利用Rtf规范将文本格式化成一个string,然后将这个string付给RichTextBox的SelectedRtf属性,之后就算是好了。Rtf有个关键之处:当你改变了一段文本后,在这段文本后面接着输入文字,后续的文字和前面的文字的格式是一样的。但是在我们的大多数应用中我们要求后面的文字是默认的格式,也就是黑字白底。在没有办法的时候我在我的标签后面加了一个用Rtf格式化的一个默认的空格。我的代码如下:  private string textColor = @"\red0\green0\blue0;";//文字颜色-->黑        private string BgWhite = @"\red255\green255\blue255;";//背景颜色-->白        private string BgGray = @"\red180\green180\blue180;";//背景颜色-->灰                //插入文字        private string getTagRtf(string text, string textColor, string textBgColor)        {            StringBuilder rtfText = new StringBuilder();            rtfText.Append(@"{\rtf1\ansi\ansicpg936\deff0\deflang1033\deflangfe2052");//rtf头            rtfText.Append(@"{\fonttbl{\f0\fnil\fcharset134\''cb\''ce\''cc\''e5;}}");//字体            rtfText.Append(@"{\colortbl;");//定义颜色            rtfText.Append(textColor);//文字颜色            rtfText.Append(textBgColor);//文字背景颜色            rtfText.Append(@"}");            rtfText.Append(@"{\*\generator Msftedit 5.41.21.2500;}\viewkind4\uc1\pard\highlight2\b\lang2052\f0\fs18");//文本体            rtfText.Append(text);//要显示的文本。            rtfText.Append(@"}");//rtf结尾//\par            string rtfcode = rtfText.ToString();            return rtfcode;        }        //插入空格        private string getTextRtf(string textColor, string textBgColor)        {            StringBuilder rtfText = new StringBuilder();            rtfText.Append(@"{\rtf1\ansi\ansicpg1252\deff0\deflang1033\deflangfe2052");//rtf头            rtfText.Append(@"{\fonttbl{\f0\fnil\fcharset134\''cb\''ce\''cc\''e5;}}");//字体            rtfText.Append(@"{\colortbl;");//颜色            rtfText.Append(textColor);            rtfText.Append(textBgColor);            rtfText.Append(@"}");            rtfText.Append(@"\viewkind4\uc1\pard\highlight2\lang1033\fs18\~");//最后的\~在Rtf规范中表示一个空格            //rtfText.Append(string.Empty);//要显示的文本。            rtfText.Append(@"\pard}");//rtf结尾//\par            string rtfcode = rtfText.ToString();            return rtfcode;        } 关于Rtf格式规范,大家可以在google上搜索一下,有中文版的提供下载,也有很多人做了简单的解释,写下我的开发过程是给其他人做个参考,别再走弯路。

Tags:

Dotnet

C#简单实现高亮语法编辑器(一)

by Administrator 2010.6.19 05:09
        ——TextBox ,RichTextBox的局限性 一、RichTextBox基本设置二、实现语法高亮三、关键字提示四、实现行号就简单快速得开发文本编辑器TextBox 最为简单,大家用得也多,缺点是无法实现复杂的操作。RichTextBox虽然是则功能比它强大很多。图 1.1  输入框控件关系这里要实现以下功能的编辑器:1、实现语法高亮;2、关键字提示;3、行号。显然TextBox 无法完成我们的任务,虽然都派生自TextBoxBase,但就控制力而言RichTextBox比它优秀很多。这里选用RichTextBox尝试开发。注:以下只讨论简单开发,不考虑复杂的关键字查找机制。一、RichTextBox基本设置这里先建立一个工程,建立窗体Form1。可以简单添加RichTextBox控件,可以在Form1_Load中建立。代码如下: 1             this.WindowState = System.Windows.Forms.FormWindowState.Maximized; 2  3             RichTextBox rich = new RichTextBox(); 4             rich.Multiline = true; 5             rich.Height = this.Height - 100; 6             rich.Width = this.Width - 100; 7             rich.Left = 40; 8             rich.Top = 40; 9             rich.WordWrap = true;10             rich.Text = "12345678";11             rich.ScrollBars = RichTextBoxScrollBars.ForcedVertical;12             this.Controls.Add(rich);这样就建立了简单的RichTextBox,宽度和高度都设置了。没有做Form1窗体缩放的处理。二、实现语法高亮在RichTextBox里实现语法高亮还是非常简单的。可以使用1             rich.Select(0,1);2             rich.SelectionFont = new Font("宋体", 12, (FontStyle.Regular));3             rich.SelectionColor = Color.Blue;意思是,先选择第一个字母,按上面的设置,选择到了数字‘1’,然后设置这个字的字体大小,再设置字的颜色。如果对关键字进行处理(这里只处理光标向后流动的情况)首先添加输入事件1        rich.KeyDown += new KeyEventHandler(rich_KeyDown);   //这一行添加到Form1_Load中2 3         void rich_KeyDown(object sender, KeyEventArgs e)4         {5             //throw new Exception("The method or operation is not implemented.");6         }建立关键字 1         public static List<string> AllClass() 2         { 3             List<string> list = new List<string>(); 4             list.Add("function"); 5             list.Add("return"); 6             list.Add("class"); 7             list.Add("new"); 8             list.Add("extends"); 9             list.Add("var");10             return list;11         }当KeyDown事件发生时,向前查找 1         //返回搜索字符 2         public static string GetLastWord(string str,int i) 3         { 4             string x = str; 5             Regex reg= new Regex(@"\s+[a-z]+\s*",RegexOptions.RightToLeft); 6             x = reg.Match(x).Value; 7  8             Regex reg2 = new Regex(@"\s"); 9             x = reg2.Replace(x, "");10             return s;11         } 1         void rich_KeyDown(object sender, KeyEventArgs e) 2         { 3             RichTextBox rich = (RichTextBox)sender; 4             //throw new Exception("The method or operation is not implemented."); 5             string s = GetLastWord(rich.Text, rich.SelectionStart); 6  7             if (AllClass().IndexOf(s) > -1) 8             { 9                 MySelect(rich, rich.SelectionStart, s, Color.CadetBlue, true);10             }11         } 1         //设定颜色 2         public static void MySelect(System.Windows.Forms.RichTextBox tb, int i, string s, Color c,bool font) 3         { 4             tb.Select(i - s.Length, s.Length); 5             tb.SelectionColor = c;               //是否改变字体 6             if(font) 7                 tb.SelectionFont = new Font("宋体", 12, (FontStyle.Bold)); 8             else 9                 tb.SelectionFont = new Font("宋体", 12, (FontStyle.Regular));                 //以下是把光标放到原来位置,并把光标后输入的文字重置10             tb.Select(i,0);11             tb.SelectionFont = new Font("宋体", 12, (FontStyle.Regular));12             tb.SelectionColor = Color.Black;13         }这样就完成了高亮工作。三、关键字提示实现关键字提示也是在KeyDown中实现,在提示字种搜索GetLastWord返回的文字,如果前半部分匹配。那么就建立ListBox控件。 1       void tb_KeyDown(object sender, KeyEventArgs e) 2         { 3             RichTextBox tb = (RichTextBox)sender; 4             if (//条件搜索到匹配字符) 5             { 6                 //搜索ListBox是否已经被创建 7                 Control[] c = tb.Controls.Find("mylb", false); 8                 if (c.Length > 0) 9                     ((ListBox)c[0]).Dispose();  //如果被创建则释放10 11                 ListBox lb = new ListBox();12                 lb.Name = "mylb";13                 lb.Items.Add("asdasdasd");14                 lb.Items.Add("asdasdasd");15                 lb.Items.Add("asdasdasd");16                 lb.Items.Add("asdasdasd");17                 lb.Items.Add("asdasdasd");18                 lb.Items.Add("asdasdasd");19                 lb.Items.Add("asdasdasd");20                 lb.Show();21                 lb.TabIndex = 100;22                 lb.Location = tb.GetPositionFromCharIndex(tb.SelectionStart);23                 lb.Left += 10;24                 tb.Controls.Add(lb);25             }26         }当然,另外一面,如果创建ListBox,而又在RichTextBox 点击了鼠标也去释放。1         void rich_MouseClick(object sender, MouseEventArgs e)2         {3             RichTextBox tb = (RichTextBox)sender;4             Control[] c = tb.Controls.Find("mylb", false);5             if (c.Length > 0)6                 ((ListBox)c[0]).Dispose();7         }当然还得在Form1_Load里注册事件1 rich.MouseClick += new MouseEventHandler(rich_MouseClick);然后设置ListBox 被选择后用被选择的关键字替换前文搜索到的字符。下面我们来看看实现行号。四、实现行号这个是RichTextBox 唯一令我遗憾的地方,居然无法实现行号问题。为什么呢?我首先想到的是自己画。用rich.CreateGraphics()来画。但是,由于画的时候发生在窗体被创建时,所以画不成功,而被RichTextBox 本身的绘制给覆盖了。然后我选择了在里面添加Label控件 1            Label l = new Label(); 2             l.Name = "l"; 3             l.Top = 0; 4             l.TextAlign = ContentAlignment.TopRight; 5             l.Width = 40; 6             l.Text = "1"; 7             l.Font = new Font("宋体", 12, (FontStyle.Regular)); 8             l.Height = this.Height; 9             l.BackColor = Color.Gray;10             l.BorderStyle = BorderStyle.None;11             rich.Controls.Add(l);12 13             rich.SelectionIndent = 40; rich.SelectionIndent = 40;是把光标对齐到左边距40的位置,防止光标被Label覆盖。实现编号还不是太难。麻烦出在如何让Lable能跟随RichTextBox 的滚动条滚动。不说实现的 细节,我就假设,如果滚动条向上滚,那么Lable的Top属性增加,反之则减少。但是,RichTextBox 居然无法对ScollBar进行监测。根本每办法知道滚动条滚动了多少位置,甚至都没办法知道滚动条滚动的方向。尝试去除滚动条,然后之间添加新的滚动条 1             VScrollBar vs = new VScrollBar(); 2             //vs.Dock = DockStyle.Right; 3             vs.Name = "vs"; 4             vs.Maximum = 0; 5             vs.Minimum = 0; 6             vs.MaximumSize = new Size(0,0); 7             vs.Top = 0; 8             vs.Left = rich.Parent.Width - 100 -22; 9             vs.Height = rich.Parent.Height - 100 -1;10             vs.Value = 0;11             vs.Scroll += new ScrollEventHandler(vs_Scroll);12             13             rich.Controls.Add(vs);但是非常难于实现同步滚动,位置很难控制。这个就是目前遇到的RichTextBox 的最大局限性了,非常遗憾,无法开发出这个功能。

Tags:

Dotnet

RichTextBox无闪烁着色

by Administrator 2010.6.17 04:17
       很多时候,我们希望对用户输入的文字进行即时的分析,并对部分文字的字体颜色进行修改,让那些文字更加醒目。如果说要对部分的文字进行字体颜色的修改,我们很可能就会想到使用RichTextBox这个控件,因为这个控件至少能够让部分文字的字体颜色变得和其他部分不一样。然而实际上对于现实静态的东西,RichTextBox也许是适合的,但是对于想这种的“动态着色”则不见得有效率。最为头痛的一个问题就是,通过SelectionStart、SelectionLength的设置,然后再修改SelectionFont和SelectionColor,必然有一个短暂的时间会有文字被选中,结果就是你要加亮的文字会在一瞬间闪现蓝色背景的白色文字。这个现象并不一定每次都能够看到,但是如果在不停的进行输入的话,就一定会看到这个情况了。        很多时候,我们希望对用户输入的文字进行即时的分析,并对部分文字的字体颜色进行修改,让那些文字更加醒目。如果说要对部分的文字进行字体颜色的修改,我们很可能就会想到使用RichTextBox这个控件,因为这个控件至少能够让部分文字的字体颜色变得和其他部分不一样。然而实际上对于现实静态的东西,RichTextBox也许是适合的,但是对于想这种的“动态着色”则不见得有效率。最为头痛的一个问题就是,通过SelectionStart、SelectionLength的设置,然后再修改SelectionFont和SelectionColor,必然有一个短暂的时间会有文字被选中,结果就是你要加亮的文字会在一瞬间闪现蓝色背景的白色文字。这个现象并不一定每次都能够看到,但是如果在不停的进行输入的话,就一定会看到这个情况了。这种不停的闪烁的情景比较让人感到讨厌,怎么解决呢?在没有试验之前,我想到了三种办法:1、在进行着色的时候,让焦点从当前的RichTextBox上面转移到别的地方,同时HideSelection设置成Ture;2、用一个后台RichTextBox进行着色,然后把着色的结果复制到前台;3、在进行着色的时候暂时屏蔽RichTextBox的画面更新,着色完毕之后再允许画面更新。首先说第一个,这个方法看起来很好,但事实上首先效率有问题,其次很多时候并不能够成功的转移焦点。尤其是牵扯到一个后面将会提到的快速输入引起问题的时候,为了让行为正确,设计将会变得非常复杂和麻烦,所以我放弃了这个方案。而第二个则是我在NfaGen1里面所使用的方案,因为这个方案甚至连RichTextBox都不需要改造就能够实现,开发起来最为简单易行。代码类似于:private void rtxEdit_TextChanged(){    if (busy)    {        busy = false;        return;    }    busy = true;    int oriStart, oriLength;    rtxBuffer.Text = rtxEdit.Text;    oriStart = rtxEdit.SelectionStart;    oriLength = rtxEdit.SelectionLength;    AnalyzeBuffer();    rtxEdit.Rtf = rtxBuffer.Rtf;    rtxEdit.SelectionStart = oriStart;    rtxEdit.SelectionLength = oriLength;}上面的rtxEdit是用户输入区,rtxBuffer则是一个不可见的后台处理区。由于处理的时候在另外一个RichTextBox里面,要闪也是在那个看不见的后台空间内部“闪”,所以基本上解决了“选中”-“闪烁”的问题。但是这个方案也不见得很明智,因为需要不断的更新前台输入区的内容,需要通过busy变量来避免更新前台输入的时候引发TextChanged事件,进而引起一个死循环。而这个方案实际上并不能够完全杜绝“闪烁”问题,因为当输入的内容超过文本框的大小范围,就会引起“更新”-“闪烁”问题。因为当你从后台拷贝Rtf到前台的时候,会使得SelectionStart变成0,也就是回到了最前面的位置,而很多时候原本光标的位置里最前面的位置相差有一个RichTextBox的大小,因此画面就会短暂停留在和原来显示内容不相同的位置上,然后通过后面恢复SelectionStart来恢复光标位置以及原来显示内容的位置。这个时候就会闪现整个文本最开始的内容的画面,如果有滚动条,你还会看到滚动条的位置也会不断的来回跳跃。但是对于内容非常有限的,能够保证输入内容不会超过文本框大小的情况下,用这个方法的确是比较合适的。实际上第三种方法才是最好的,因为不需要将文字来回的搬动,就不应该出现任何的闪烁,同时效率也应该相对较高。可这么好的方法,为什么在NfaGen1里面使用呢?因为我遇到了困难,不过幸运的是现在我已经有解决方法了。困难就是:首先override OnPaint似乎不起作用,其次截获WM_PAINT不传递给基类的WndProc函数来阻止重画会引起系统不断发送WM_PAINT消息,非常地占用CPU。而且事实上被截获的消息最后还是要发出去,也就是说本来你想阻止不让画的东西似乎还是要画出来的,或者至少背负了一些不必要的消息处理花费。解决问题的办法就是绕过去!我猜测这个WM_PAINT的重发并不是操作系统引起的,因为在MSDN里面说,如果这个窗口处理这个消息,返回0就OK了。如果WM_PAINT不断重发,那么很可能是因为.NET Framework的底层代码发出了这个WM_PAINT来达到重画画面的目的,但是他发现并没有执行相应的操作,因此就不断的重发。当然,这个也是我的无理猜测而已,因为至少这部分代码在Managed部分找不到。既然有这个猜测,我就进行了相应的测试。不过大家想想,如果允许RichTextBox接受WM_PAINT消息固然可能不让消息重发,但是也不可能达到暂停重画的目的。在我分析过RichTextBox、TextBoxBase、Control等部分的WndProc函数之后,发现WM_PAINT的消息处理是通过非托管的代码来完成的,并且很可能是根据托管对象的类型来确定具体的行为方式,因此我就尝试对.NET进行“欺骗”。首先我建立一个派生自Control的类,然后在这个类里面添加一个public函数,调用base.WndProc(ref m),然后所有WM_PAINT消息就转发到这个类的对象上面,结果竟然成功了!我猜测也许hWnd确实是RichTextBox的Handle,所以系统认为那个WM_PAINT消息已经被处理了。但是同时由于这个时候的处理对象并不是一个RichTextBox,因此就不会用RichTextBox的画面更新代码来进行更新,而是什么也不做,因此达到了暂时屏蔽画面显示的效果。public class SuperBox : RichTextBox{    private class paintHelper : Control    {        public void DefaultWndProc(ref Message m)        {            this.DefWndProc(ref m);        }    }    private const int WM_PAINT = 0x000F;    private int lockPaint;    private bool needPaint;    private paintHelper pHelp = new paintHelper();    public void BeginUpdate()    {        lockPaint++;    }    public void EndUpdate()    {        lockPaint--;        if (lockPaint <= 0)        {            lockPaint = 0;            if (needPaint)            {                this.Refresh();                needPaint = false;            }        }    }    protected override void WndProc(ref Message m)    {        switch (m.Msg)        {            case WM_PAINT:                if (lockPaint <= 0)                {                    base.WndProc(ref m);                }                else                {                    needPaint = true;                    pHelp.DefaultWndProc(ref m);                }                return;        }        base.WndProc (ref m);    }}  if ($ != jQuery) { $ = jQuery.noConflict(); }  

Tags:

Dotnet

关于博主

      什么都懂一点儿,热衷技术,热爱Programming,每有难题,常夜以继日,每有解决方案,手舞足蹈而欣喜若狂;好读书,不求甚解;每有会意,便欣然忘食;常纵情于山水之间,忘乎于尘世之外,唯程序难释怀尔。
       QQ:51557288

归档

最近的评论

声明

本博所有网友评论不代表本博立场,版权归其作者所有。

© Copyright 2011