.NET

There are 8 entries for the tag .NET

BitConverter.IsLittleEndian在x86的机器上返回false

MitchellChu 2016-03-09 .NET技术 编程语言

在DEBUG写的.NET程序时,发现一个有趣的现象,就是本应该返回true的BitConverter.IsLittleEndian,返回的却是false!但在使用BitConverter之后,再次查看其值,却发现变成了正常的true状态。难道在X86的系统上存在两种状态? DEBUG的代码如下: ... // 前面没有使用BitConverter // DEBUG断点设置到这里的时候,可以看到是false的值 // BitConverter.IsLittleEndian // 如果使用BitConverter之后,得到的值却是true byte[] bdata = BitConverter.GetBytes(num); // 此时DEBUG中看到的是true. 但这明显不科学,首先BitConverter.IsLittleEndian在X86下应该是false,其次,不应该在一个系统内,同时出现true和false这两种状态。 抱着好奇的心态,翻开了源代码看了一下,看到如下代码: public static readonly bool IsLittleEndian = true; 赫然写着true啊!哪里来的false? 经过一番搜索,发现有这么一句: 通过调试器读取成员并不会触发执行成员的初始化代码。1 也就是说:通过调试器读取内存中的成员仅仅是读取到该对象的默认初始值而已。有这么一句话,解释起来就豁然开朗了。 为了验证这个说法的真实性,我们可以自己定义一个类来进行验证下: public static class DebuggerInitTester { public static bool BoolData = true; } 在DEBUG中我们可以看到,我们可以在没有调用前尝试获取到BoolData值,发现是true,并非是默认的false!这是什么情况,再仔细看了一遍代码,发现BitConverter里面有静态构造函数: static BitConverter() { // Note: this type is marked as 'beforefieldinit'. BitConverter.IsLittleEndian = true; } 在BitConverter中使用了静态的构造函数,而我们的并没有,看来问题在这里了,我们也改造下代码: static DebuggerInitTester() { DebuggerInitTester.BoolData = true; } 再次测试,确实,这个时候默认是false了。看起来问题就在这个静态构造函数了,原来不是一直听说static的是在加载前初始化了的么?看来并不是这个样子的,还是Too Young Too Simple! 看来是beforefieldinit引起的问题了(可参见Mitchell的另一篇关于BeforeFieldInit的文章) 如果没有BeforeFieldInit标记,则CLR会在第一次访问静态成员的时候调用静态构造函数对静态类中的字段进行初始化。比如,DebuggerInitTester这个类,我们定义了静态构造函数,因此将初始化BoolData的时机锁定了。 一切看起来是这样了,可再仔细看,又蒙了——压根就没有静态构造函数!BitConverter那个构造函数是自动生成的,是有BeforeFieldInit标记的。 .class public abstract auto ansi sealed beforefieldinit System.BitConverter extends System.Object { .custom instance void __DynamicallyInvokableAttribute::.ctor() = ( 01 00 00 00 ) } 根据BeforeFieldInit规则,也就是说BitConverter中IsLittleEndian的初始化会在访问前的任意时候完成,自然也可能会和有自定义静态构造函数那样在第一次访问的时候初始化,也许CLR对于BitConverter是这样处理。  如果能解释初始化时间点后,调试器读取成员不触发成员初始化代码倒是非常可以理解的,因为这样调试器的调试才安全可靠,不至于导致意外的发生(如:读取导致数据变化带来后继逻辑的)。   困惑 BitConverter由于使用了BeforeFieldInit标记,将初始化时机交给了CLR,也就是说这将导致IsLittleEndian初始化时间点不确定,很有可能就是在第一次访问的时候进行初始化的。 令人疑惑的是,为什么自定义的DebuggerInitTester类中的BoolData会那么快就完成了初始化呢?另外测试了好几次(其他类)均是非常早的完成初始化。 这里面到底是什么原因决定CLR来选择BeforeFieldInit标记过的类的初始化时机?    参考 Why does BitConverter.LittleEndian return false on my x86 machine? IsLittleEndian field...

C#的类型构造器和beforefieldinit标志

MitchellChu 2016-03-09 .NET技术 编程语言

首先让我们来看两个类型的定义: // 代码来自C# in Depth // 为了后继的讨论方便,对类名进行了更改(Test改为Test1和Test2) // —— Mitchell Chu class Test1 { static object o = new object(); } class Test2 { static object o; static Test2() { o = new object(); } } 这两个类,经常会被误认为是一样的,但实际情况是如何呢?让我们编译后,反编译后来看看他们产生的代码: // IL代码 // Test1和Test2在定义的时候出现了不同. // —— Mitchell Chu .class private auto ansi beforefieldinit ClassLibrary1.Test1 extends [mscorlib]System.Object { // Fields .field private static object o // Methods .method public hidebysig specialname rtspecialname instance...

FLV文件完整性校验方法

MitchellChu 2016-02-28 其他技术

书接上回,我们讨论了FLV文件的结构,有了这些基础之后,我们就能够对FLV文件进行一些操作了,比如本文要讨论的——校验FLV文件的完整性。 完整的FLV文件应该按照FLV文件的结构提供完整的数据信息,如:FLV Header,FLV Body中的Previous Tag Size,Tag。那我们下面由简入繁的步骤来对FLV文件校验一番。 最简单的校验方法,仅校验FLV Header信息,如(.NET代码): public static bool IsValidFlvHeader(byte[] bytes) { if (bytes.Length != 9) return false; byte[] header0 = new byte[] { 0x46, 0x4C, 0x56 }; byte flvVer = bytes[3]; byte flvType = bytes[4]; byte[] header_offset = new byte[] { 0x00, 0x00, 0x00, 0x09 }; if (!header0.SequenceEqual(bytes.Take(3).ToArray())) return false; if (flvVer != 0x01) return false; if (flvType != 0x01 && flvType != 0x04 ...

获取本机系统内已经安装的浏览器列表

MitchellChu 2015-01-15 .NET技术 其他技术

最近突然碰到个比较棘手的问题,客户需要罗列出机器上所有可用的浏览器列表,至于作用么,就是让用户可以选择自己喜欢的浏览器来浏览指定信息。对于混杂的浏览器市场,这个需求确实够喝上一壶的了。 带着泪流满面的表情进入了无穷无尽的方案寻找中,不过到现在还是没有一个完满的答案。本文就记录下已经得到的一些信息吧。 对于一些标准的浏览器(有国内的么?自然是没有,谢谢),他们都会将自己的信息保存到StartMenuInternet这个注册表项下面,里面提供了丰富多彩的内容,基本上你想要的,他都能告诉你。需要注意的是,在64位系统中,会有两个位置可以找到。 HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Clients\StartMenuInternet HKEY_LOCAL_MACHINE\SOFTWARE\Clients\StartMenuInternet 上面这两个路径下,就包含了机器上所有的浏览器信息(再次排除国内浏览器)。如果是在32位系统中,只有后一个路径可以使用。 知道位置之后,在程序中要获取浏览器列表信息就比较简单了,参见以下C#(.NET)代码: public static void BrowsersData() { RegistryKey browsersKey; browsersKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\WOW6432Node\Clients\StartMenuInternet"); if (browsersKey == null) browsersKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Clients\StartMenuInternet"); using (browsersKey) ...

异常处理:必须使用适当的属性或方法修改此标头

MitchellChu 2013-09-03 .NET技术

在 .NET (v4.0)中,使用HttpWebRequest请求Web页面,当向HttpWebRequest的Headers的标头集合中添加Referer,Host这类HTTP标头(Header)时,会收到:System.ArgumentException: 必须使用适当的属性或方法修改此标头。这样的报错! 这个问题的引起在官方的说法是: 通常通过 WebRequest.Headers 或 WebResponse.Headers 访问 WebHeaderCollection 类。 某些公共标头被视为受限制的,它们或者直接由 API(如 Content-Type)公开,或者受到系统保护,不能被更改。 受限制的标头是: Accept Connection Content-Length Content-Type Date Expect Host If-Modified-Since Range Referer Transfer-Encoding User-Agent Proxy-Connection 要解决这个问题可以用以下这些方法: 方法一: 通过HttpWebRequest/HttpWebResponse对象的相应属性来设置这些表头 HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://blog.useasp.net/"); request.Host = "http://blog.useasp.net/"; request.Referer = "http://blog.useasp.net/tags/.net"; /* 你可以在这里设置其他限制的标头. 注意: Range HTTP标头是通过AddRange来添加 If-Modified-Since HTTP标头通过IfModifiedSince 属性设置 Accept由 Accept 属性设置。 Connection由 Connection 属性和 KeepAlive 属性设置。 Content-Length由 ContentLength 属性设置。 Content-Type由 ContentType 属性设置。 Expect由 Expect 属性设置。 Date由 Date属性设置,默认为系统的当前时间。 Host由系统设置为当前主机信息。 Referer由 Referer 属性设置。 Transfer-Encoding由 TransferEncoding 属性设置(SendChunked 属性必须为 true)。 User-Agent由 UserAgent 属性设置。 */ using((HttpWebResponse)response = (HttpWebResponse)request.GetResponse()) { // do something here too. by Mitchell Chu } 要将所有的这些受限HTTP标头在写程序的时候记住,还是有点难度的,我们更希望能够自动判定那些HTTP标头是可以直接添加的,哪些是需要特殊处理的。在WebHeaderCollection中有个方法IsRestricted正好是解决这个问题的。因此上面的代码可以改为将所有需要的标头添加到WebHeaderCollection,在为HttpWebRequest添加标头的时候,再使用IsRestricted来确定特殊处理与否。 foreach (string key in headers.AllKeys) { if...

流之间的拷贝方法

MitchellChu 2013-08-29 .NET技术

方法一:在 .NET 4.5以上,可以使用CopyToAsync方法来完成拷贝。该方法使用请参考:CopyToAsync方法 方法二:在.NET 4.0,可以直接使用Stream.CopyTo(stream)的方法。 // SourceStream 为要拷贝的流 // DestinationStream 为要拷贝流的目标 SourceStream.CopyTo(DestinationStream);  方法三:在.NET 3.5及以前的版本中,要在Stream和Stream之间拷贝,需要自己手动完成。 public static void CopyStream(Stream source, Stream destination) { byte[] buffer = new byte[4096]; //你也可以设置的更大一些 int read; while ((read = source.Read(buffer, 0, buffer.Length)) > 0) { destination.Write (buffer, 0, read); } }

HttpWebRequest请求中使用安全证书的方法

MitchellChu 2013-08-27 .NET技术

在 .NET 中,我们会使用HttpWebRequest或HttpWebResponse来发送一个客户端数字证书,具体的方法: 方法一:使用X509Certificate证书类读取数字证书文件 (*.cer),之后将证书附加到请求(SSL/TLS)当中去: public void GetResponseString(string uri) { try { X509Certificate Cert = X509Certificate.CreateFromCertFile("C:\\证书.cer"); //证书存放的绝对路径 ServicePointManager.CertificatePolicy = new CertPolicy(); //处理来自证书服务器的错误信息 HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(uri);//要访问的像https://要访问的地址/mitchellchu/test.aspx,要用SSL访问的地址 Request.ClientCertificates.Add(Cert); Request.UserAgent = "Mitchell Chu robot test"; // 使用的客户端,如果服务端没有要求可以随便填写 Request.Method = "GET"; // 请求的方式:POST/GET using(HttpWebResponse Response = (HttpWebResponse)Request.GetResponse()) //获取Response { using(StreamReader sr = new StreamReader(Response.GetResponseStream(), Encoding.Default)) { int count; char [] ReadBuf = new char[1024]; do { count = sr.Read(ReadBuf, 0, 1024); if (0 != count) { Console.WriteLine(new string(ReadBuf)); } }while(count > 0); } } } catch(Exception e) { Console.WriteLine(e.Message); } } class CertPolicy: ICertificatePolicy { public bool CheckValidationResult(ServicePoint srvPoint , X509Certificate certificate, WebRequest request, int certificateProblem) { // 你可以在这里加上证书检验的方法,错误值可以在WinError.h中获得 // 这里只是简单的返回true,任何证书都可以正常的使用。 return true; } }  优点:简单方便 缺点:需要证书文件,如果没有,需要先导出证书文件方可使用。   方法二: 使用 CryptoAPI 调用,之后获取证书集合,找到需要使用的证书,以X509Certificate对象形式返回,并附加在请求(HttpWebRequest 或HttpWebResponse)中发送证书。 /* 该案例来源于微软官方网站 @MitchellChu */ using System; using System.Net; using System.IO; using System.Text; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Runtime.InteropServices; namespace SelectClientCert { class MyCerts{ private static int CERT_STORE_PROV_SYSTEM = 10; private static int CERT_SYSTEM_STORE_CURRENT_USER = (1 << 16); ///private static int CERT_SYSTEM_STORE_LOCAL_MACHINE = (2...

.NET获取文件的MIME类型(Content Type)

MitchellChu 2013-08-23 .NET技术

获取文件的MIME类型有很多方法,闲话少絮,直接进入主题,下面是使用C#获取文件的MIME类型(Content Type)的各种方法。 第一种:这种获取MIME类型(Content Type)的方法需要在.NET 4.5之后才能够支持,但是非常简单。 MIME TYPE: // 需要先行引入System.Web // 之后可以直接使用MimeMapping类的静态方法获取文件的MIME类型 string file = @"mimetype.txt"; string contentType = MimeMapping.GetMimeMapping(file); Console.WriteLine("{0}'s MIME TYPE:{1}", file, contentType); // Console output: /* mimetype.txt's Mime Type: text/plain */ 优点:方便快捷 缺点:只能在.NET 4.5之后使用 如果没有的MIME TYPE将会返回: application/octet-stream   第二种:直接在系统中通过文件的后缀获取文件的Content Type   // 自定义一个方法 // 通过系统中的Content Type来获取MIME TYPE public static string GetMimeType(string fileName) { string mimeType = "application/unknown"; string ext = Path.GetExtension(fileName).ToLower(); RegistryKey regKey = Registry.ClassesRoot.OpenSubKey(ext); if (regKey != null && regKey.GetValue("Content Type") != null){ mimeType = regKey.GetValue("Content Type").ToString(); } return mimeType; }  优点: 1. 直接快速 2. 没有.NET的版本限制; 缺点: 1. 需要有访问注册表的权限; 2. 获取的Content Type范围受系统环境约束。 这个方法在没有找到对应的MIME Type时,返回的是application/unknow,当然,你也可以自己定义其他任何返回值。   第三种: 自己动手写代码,将MimeType写成Mapping类,需要的时候直接使用。 // 通过自己定义一个静态类 // 将所有的Content Type都扔进去吧 // 调用的时候直接调用静态方法即可。 public static class MimeMapping { ...

关于博主

  一枚成分复杂的网络IT分子,属于互联网行业分类中的杂牌军。