[转载]MYSQL中UNIX时间戳与日期的转换

mikel阅读(1121)

[转载]MYSQL中UNIX时间戳与日期的转换_就这样每一天~寻找刺激 -_百度空间.

UNIX时间戳转换为日期用函数: FROM_UNIXTIME()

select FROM_UNIXTIME(1156219870);

日期转换为UNIX时间戳用函数: UNIX_TIMESTAMP()

Select UNIX_TIMESTAMP(’2006-11-04 12:23:00′);

例:mySQL查询当天的记录数:

$SQL=”select * from message Where DATE_FORMAT(FROM_UNIXTIME(chattime),’%Y-%m-%d’) = DATE_FORMAT(NOW(),’%Y-%m-%d’) order by id desc”;

当然大家也可以选择在PHP中进行转换

UNIX时间戳转换为日期用函数: date()

date(‘Y-m-d H:i:s’, 1156219870);

日期转换为UNIX时间戳用函数:strtotime()

strtotime(‘2010-03-24 08:15:42′);

mysql> select FROM_UNIXTIME(1156219870,’%y-%m-%d’);
+————————————–+
| FROM_UNIXTIME(1156219870,’%y-%m-%d’) |
+————————————–+
| 06-08-22                             |
+————————————–+
1 row in set (0.03 sec)

mysql> SELECT UNIX_TIMESTAMP(‘2006-11-04 12:23:00’);
+—————————————+
| UNIX_TIMESTAMP(‘2006-11-04 12:23:00’) |
+—————————————+
|                            1162614180 |
+—————————————+
1 row in set (0.00 sec)

[转载]有关Lucene的问题(7):用Lucene构建实时的索引

mikel阅读(887)

[转载]有关Lucene的问题(7):用Lucene构建实时的索引 – 觉先 – 博客园.

由于前一章所述的Lucene的事务性,使得Lucene可以增量的添加一个段,我们知道,倒排索引是有一定的格式的,而这个格式一旦写入是非常难 以改变的,那么如何能够增量建索引呢?Lucene使用段这个概念解决了这个问题,对于每个已经生成的段,其倒排索引结构不会再改变,而增量添加的文档添 加到新的段中,段之间在一定的时刻进行合并,从而形成新的倒排索引结构。

然而也正因为Lucene的事务性,使得Lucene的索引不够 实时,如果想Lucene实时,则必须新添加的文档后IndexWriter需要commit,在搜索的时候IndexReader需要重新的打开,然而 当索引在硬盘上的时候,尤其是索引非常大的时候,IndexWriter的commit操作和IndexReader的open操作都是非常慢的,根本达 不到实时性的需要。

好在Lucene提供了RAMDirectory,也即内存中的索引,能够很快的commit和open,然而又存在 如果索引很大,内存中不能够放下的问题。

所以要构建实时的索引,就需要内存中的索引RAMDirectory和硬盘上的索引 FSDirectory相互配合来解决问题。

1、初始化阶段

首先假设我们硬盘上 已经有一个索引FileSystemIndex,由于IndexReader打开此索引非常的慢,因而其是需要事先打开的,并且不会时常的重新打开。

我们在内存中有一个索引MemoryIndex,新来的文档全部索引到内存索引中,并且是索引完IndexWriter就 commit,IndexReader就重新打开,这两个操作时非常快的。

如下图,则此时新索引的文档全部能被用户看到,达到实时的目 的。

绘图8

2、合并索引阶段

然而经过一段时间,内存中的索引会比较大了,如果不合并到硬盘上,则可能造成内存不够用,则需要进行合并的过程。

当然在合并的过 程中,我们依然想让我们的搜索是实时的,这是就需要一个过渡的索引,我们称为MergingIndex。

一旦内存索引达到一定的程度,则 我们重新建立一个空的内存索引,用于合并阶段索引新的文档,然后将原来的内存索引称为合并中索引,并启动一个后台线程进行合并的操作。

在 合并的过程中,如果有查询过来,则需要三个IndexReader,一个是内存索引的IndexReader打开,这个过程是很快的,一个是合并中索引的 IndexReader打开,这个过程也是很快的,一个是已经打开的硬盘索引的IndexReader,无需重新打开。这三个IndexReader可以 覆盖所有的文档,唯一有可能重复的是,硬盘索引中已经有一些从合并中索引合并过去的文档了,然而不用担心,根据Lucene的事务性,在硬盘索引的 IndexReader没有重新打开的情况下,背后的合并操作它是看不到的,因而这三个IndexReader所看到的文档应该是既不少也不多。合并使用 IndexWriter(硬盘索引).addIndexes(IndexReader(合并中索引)),合并结束后Commit。

如下 图:

merging

3、重新 打开硬盘索引的IndexReader

当合并结束后,是应该重新打开硬盘索引的时候了,然而这是一个可能比较慢的过 程,在此过程中,我们仍然想保持实时性,因而在此过程中,合并中的索引不能丢弃,硬盘索引的IndexReader也不要动,而是为硬盘索引打开一个临时 的IndexReader,在打开的过程中,如果有搜索进来,返回的仍然是上述的三个IndexReader,仍能够不多不少的看到所有的文档,而将要打 开的临时的IndexReader将能看到合并中索引和原来的硬盘索引所有的文档,此IndexReader并不返回给客户。如下图:

reopen

4、替代 IndexReader

当临时的IndexReader被打开的时候,其看到的是合并中索引的 IndexReader和硬盘索引原来的IndexReader之和,下面要做的是:

(1) 关闭合并中索引的IndexReader

(2) 抛弃合并中索引

(3) 用临时的IndexReader替换硬盘索引原来的IndexReader

(4) 关闭硬盘索引原来的IndexReader。

上面说的这几个操作必须是原子性的,如果做了(2)但没有做(3),如果来一个搜索,则将 少看到一部分数据,如果做了(3)没有做(2)则,多看到一部分数据。

所以在进行上述四步操作的时候,需要加一个锁,如果这个时候有搜索 进来的时候,或者在完全没有做的时候得到所有的IndexReader,或者在完全做好的时候得到所有的IndexReader,这时此搜索可能被 block,但是没有关系,这四步是非常快的,丝毫不影响替代性。

如下图:

replace

经过这几个过程,又达到了第一步的状态,则进行 下一个合并的过程。

5、多个索引

有一点需要注意的是,在上述的合并过程中,新添 加的文档是始终添加到内存索引中的,如果存在如下的情况,索引速度实在太快,在合并过程没有完成的时候,内存索引又满了,或者硬盘上的索引实在太大,合并 和重新打开要花费太长的时间,使得内存索引以及满的情况下,还没有合并完成。

为了处理这种情况,我们可以拥有多个合并中的索引,多个硬盘 上的索引,如下图:

multiple

  • 新添加的文档永远是进入内存索引
  • 当内存索引到达一定的大小的时候,将其加入合并中索引链表
  • 有一个后台线程,每隔一定的时刻,将合并中索引写入 一个新的硬盘索引中取。这样可以避免由于硬盘索引过大而合并较慢的情况。硬盘索引的IndexReader也是写完并重新打开后才替换合并中索引的 IndexReader,新的硬盘索引也可保证打开的过程不会花费太长时间。
  • 这样会造成硬盘索引很多,所以,每隔一定的时刻,将硬 盘索引合并成一个大的索引。也是合并完成后方才替换IndexReader

大家可能会发现,此合并的过程和Lucene的段 的合并很相似。然而Lucene的一个函数IndexReader.reopen一直是没有实现的,也即我们不能选择哪个段是在内存中的,可以被打开,哪 些是硬盘中的,需要在后台打开然后进行替换,而IndexReader.open是会打开所有的内存中的和硬盘上的索引,因而会很慢,从而降低了实时性。

[转载]Net资源泄露(内存泄露,GDI泄露,handle 泄露等)的终极解决方案

mikel阅读(1569)

[转载]Net资源泄露(内存泄露,GDI泄露,handle 泄露等)的终极解决方案 – 云镜 – 博客园.

摘要

什么是.Net内存泄露?如何确定是发生了内存泄露?如何预防内存泄露的发生?

正文

1.dot Net内存泄露简介

刚开始使用Net的读者(甚至做了一两年商业开发的同行)可能对Net的内存泄露不是很了解,甚至会说Net不存在内存泄露,他们会问“不是有GC机制 吗?”恩,是有这么回事,它保证了通常应用时不用考虑头疼的资源释放问题,但很遗憾的是这个机制不保证你开发的程序就不存在内存泄露甚至可以说,Net内 存泄露是很常见的。这是因为: 一方面,GC机制本身的缺陷造成的;另一方面,Net中托管资源和非托管资源的处理是有差异的,托管资源的处理是由GC自动执行的,而非 托管资源 (占少部分,比如文件操作,网络连接等)必须显式地释放,否则就可能造成泄露。综合起来说的话,由于托管资源在Net中占大多数,通常不做显式的资源释放 是可以的,不会造成明显的资源泄露,而非托管资源则不然,是发生问题的主战场,是最需要注意的地方。

另外,照我看来,很多情况下,衰老测试关注的主要是有没有内存泄露的发生,而对其他泄露的重视次之。为什么这样做呢,我认为有两方面的原因,一是内存跟其 他资源是正相关的,也就是说没有内存泄露的发生,其他泄露的发生概率也较小,其根本在于所有的资源最后会反应在内存上;另一个就是很多Net应用开发,用 到的非托管资源,多提供Dispose方法的,而Dispose之后,不光内存及时释放,其他的也做了释放。因此,通常情况下我们主要关注的是内存的使用 情况,而对自定义控件开发等情况则需要关注GDI,handle等其他资源的情况。

2.dot Net内存泄露的确诊

上面说了这么多,那么到底如何判断有没有内存泄露的发生呢?

如果程序报“Out of memory”之类的错误,事实上也占据了很大部分的内存,应该说是典型的内存泄露,这种情况属于彻底的Bug,解决之道就是找到问题点,改正。但我的经 验中,这种三下两下的就明显的泄露的情况较少,除非有人在很困的情况下编码,否则大多是隐性或渐进式地泄露,这种需经过较长时间的衰老测试才能发现,或者 在特定条件下才出现,对这种情况要确定问题比较费劲,有一些工具(详见3.3)可以利用,但我总感觉效果一般,也可能是我不会使用吧,我想大型程序估计得 无可奈何的用这个,详细的参见相关手册。

需要强调的是,判断一个程序是不是出现了”memory leak”,关键不是看它占用的内存有多大,而是放在一个足够长的时期(程序进入稳定运行状态后)内,看内存是不是还是一直往上涨,因此,刚开始的涨动或 者前期的涨动不能做为泄露的充分证据。

以上呢都是些感性的说法,具体的判断是否发生了内存泄露,可以通过一些性能计数器来测定。一般来讲,我测性能时,主要关注Process里 以下几个指标,如果这些量整体来看是持续上升的,基本可以判断是有泄露情况存在的。

A.Handle Count

B.Thread Count

C.Private Bytes

D.Virtual Bytes

E.Working Set

F.另外.NET CLR Memory下的Bytes in all heeps也是我比较关注的。

通关观察,你如果发现这些参数是在一个区间内震荡的,应该是没有大的问题的,如果是一个持续上涨的状态,那你就得注意,很可能存在内存泄露。

3.内存泄露诊断工具

如何测定以上的计数器呢,我大多使用windows自带的perfmon.msc。在此稍微说说改工具的使用。

3.1perfmon.msc 的使用

在Run中输入perfmon.msc,运行,其他的自己摸索,不难。

3.2 一些重要的性能计数器

重要的 计数器1, 重要的计数器2

3.3其他检测工具

我用过的里面CLRProfilerdotTrace 还行(下载地址见链接)windeg也还行。不过坦白的说,准确定位比较费劲,最好还是按常规的该Dispose的加Dispose,也可以加 GC.Collect()。

4.如何制造出健壮的程序

4.1 Dispose()的使用

如果使用的对象提供Dispose()方法,那么当你使用完毕或在必要的地方(比如Exception)调用该方法,特别是对非托管对象,一定要加以调 用,以达到防止泄露的目的。另外很多时候程序提供对Dispose()的扩展,比如Form,在这个扩展的Dispose方法中你可以把大对象的引用什么 的在退出前释放。

对于DB连接,COM组件(比如OLE组件)等必须调用其提供的Dispose方法,没有的话最好自己写一个。

4.2 using的使用

using除了引用Dll的功用外,还可以限制对象的适用范围,当超出这个界限后对象自动释放,比如

4.3 事件的卸载

4.4 API的调用

4.5继承 IDisposable实现自己内存释放接口

Net 如何继承IDisposable接口,实现自己的Dispose()函数

4.6弱引用(WeakReference

通常情况下,一个实例如果被其他实例引用了,那么他就不会被GC回收,而弱引用的意思是,如果一个实例没有被其他实例引用(真实引用),而仅仅是被弱引 用,那么他就会被GC回收。

4.7析构函数(Finalize())

有了前面的 Dispose,一般不推荐自定义析构函数。

5.其他资源泄露

GDI leak,handle leak。

6.几个特例

1.对于使用了Bitmap对象的部分需要调用DestroyIcon来删除对象

[DllImport(“user32”, EntryPoint = “DestroyIcon”)]
private static extern void DestroyIcon(IntPtr handle);
using (System.IO.MemoryStream mstream = new System.IO.MemoryStream())
{
bmp.Save(mstream, System.Drawing.Imaging.ImageFormat.Png);
intP = new Bitmap(mstream).GetHicon(); this.notifyIcon.Icon = Icon.FromHandle(intP);
DestroyIcon(intP);
}

2. 多线程中的GC

//Force garbage collection.
GC.Collect();
//Wait for all finalizers to complete
GC.WaitForPendingFinalizers();

7.参考文献

1.发 现并防止 托管代码中出现内存泄漏

2..NET Memory Leak reader email: Are you really “leaking” .net memory

3.How to detect and avoid memory and resources leaks in .NET applications

4.其 他参考资料

8.扩展阅读

1.实 用.net内存泄露(memory leak)解决方案

2.Garbage Collection: Automatic Memory Management in the Microsoft .NET Framework By Jeffrey Richter  http://msdn.microsoft.com/en-us/magazine/bb985010.aspx

3.OutOfMemoryException and Pinning

https://blogs.msdn.com/yunjin/archive/2004/01/27/63642.aspx

后记

其实本文更像一篇编写出健壮WinForm程序的指南。

—-全文完—–

[转载]前端开发必须知道的JS(一) 原型和继承

mikel阅读(943)

[转载]前端开发必须知道的JS(一) 原型和继承 – JayChow – 博客园.

原型和闭包是Js语言的难点,此文主要讲原型及原型实现的继承,在(二)中会讲下闭包,希望对大家有所帮助。若有疑问或不正之处,欢迎提出指正 和讨论。

一. 原型与构造函数

Js所有的函数都有一个prototype属性,这个属性引用了一个对象,即原型对象,也简称原型。这个函数包括构造函数和普通函数,我们讲的 更多是构造函数的原型,但是也不能否定普通函数也有原型。譬如普通函数:

function F(){   ; } alert(F.prototype instanceof Object) //true

构造函数,也即构造对象。首先了解下通过构造函数实例化对象的过程。

function A(x){   this.x=x; } var obj=new A(1);

实例化obj对象有三步:

1. 创建obj对象:obj=new Object();

2. 将obj的内部__proto__指向构造他的函数A的prototype,同 时,obj.constructor===A.prototype.constructor(这个是永远成立的,即使A.prototype不再指向原来 的A原型,也就是说:类的实例对象的constructor属性永远指向”构造函数”的prototype.constructor), 从而使得obj.constructor.prototype指向 A.prototype(obj.constructor.prototype===A.prototype,当A.prototype改变时则不成立, 下文有遇到)。obj.constructor.prototype与的内部_proto_是两码事,实例化对象时用的是_proto_,obj是没有 prototype属性的,但是有内部的__proto__,通过__proto__来取得原型链上的原型属性和原型方法,FireFox公开了 __proto__,可以在FireFox中alert(obj.__proto__);

3. 将obj作为this去调用构造函数A,从而设置成员(即对象属性和对象方法)并初始化。

当这3步完成,这个obj对象就与构造函数A再无联系,这个时候即使构造函数A再加任何成员,都不再影响已经实例化的obj对象了。此 时,obj对象具有了x属性,同时具有了构造函数A的原型对象的所有成员,当然,此时该原型对象是没有成员的。

原型对象初始是空的,也就是没有一个成员(即原型属性和原型方法)。可以通过如下方法验证原型对象具有多少 成员。

var num=0; for(o in A.prototype) {   alert(o);//alert出原型属性名字   num++; } alert("member: " + num);//alert出原型所有成员个数。

但是,一旦定义了原型属性或原型方法,则所有通过该构造函数实例化出来的所有对象,都继承了这些原型属性和原型方法,这是通过内部的 _proto_链来实现的。

譬如

A.prototype.say=function(){alert(“Hi”)};

那所有的A的对象都具有了say方法,这个原型对象的say方法是唯一的副本给大家共享的,而不是每一个对象都有关于say方法的一个副本。

二. 原型与继承

首先,看个简单的继承实现。

1 function A(x){ 2   this.x=x; 3 }
4  function B(x,y){ 5   this.tmpObj=A; 6   this.tmpObj(x); 7   delete this.tmpObj; 8   this.y=y; 9 }

第5、6、7行:创建临时属性tmpObj引用构造函数A,然后在B内部执行,执行完后删除。当在B内部执行了this.x=x后(这里的 this是B的对象),B当然就拥有了x属性,当然B的x属性和A的x属性两者是独立,所以并不能算严格的继承。第5、6、7行有更简单的实现,就是通过 call(apply)方法:A.call(this,x);

这两种方法都有将this传递到A的执行里,this指向的是B的对象,这就是为什么不直接A(x)的原因。这种继承方式即是类继承(js没有类, 这里只是指构造函数),虽然继承了A构造对象的所有属性方法,但是不能继承A的原型对象的成员。而要实现这个目的,就是在此基础上再添加原型继承。

通过下面的例子,就能很深入地了解原型,以及原型参与实现的完美继承。(本文核心在此^_^)

1 function A(x){ 2   this.x = x; 3 } 4 A.prototype.a = "a"; 5 function B(x,y){ 6   this.y = y; 7   A.call(this,x); 8 } 9 B.prototype.b1 = function(){ 10   alert("b1"); 11 } 12 B.prototype = new A(); 13 B.prototype.b2 = function(){ 14   alert("b2"); 15 } 16 B.prototype.constructor = B; 17 var obj = new B(1,3);

这个例子讲的就是B继承A。第7行类继承:A.call(this.x);上面已讲过。实现原型继承的是第12行:B.prototype = new A();

就是说把B的原型指向了 A的1个实例对象,这个实例对象具有x属性,为undefined,还具有a属性,值为”a”。所以B原型也具有了这2个属性(或者说,B和A建立了原型 链,B是A的下级)。而因为方才的类继承,B的实例对象也具有了x属性,也就是说obj对象有2个同名的x属性,此时原型属性x要让位于实例对象属性x, 所以obj.x是1,而非undefined。第13行又定义了原型方法b2,所以B原型也具有了b2。虽然第9~11行设置了原型方法b1,但是你会发 现第12行执行后,B原型不再具有b1方法,也就是obj.b1是undefined。因为第12行使得B原型指向改变,原来具有b1的原型对象被抛弃, 自然就没有b1了。


第12行执行完后,B原 型(B.prototype)指向了A的实例对象,而A的实例对象的构造器是构造函数A,所以B.prototype.constructor就是构造对 象A了(换句话说,A构造了B的原型)。

alert(B.prototype.constructor) 出来后就是”function A(x){…}” 。同样地,obj.constructor也是A构造对象,alert(obj.constructor)出来后就是”function A(x){…}” ,也就是说B.prototype.constructor===obj.constructor(true),但是 B.prototype===obj.constructor.prototype(false),因为前者是B的原型,具有成员:x,a,b2,后者是 A的原型,具有成员:a。如何修正这个问题呢,就在第16行,将B原型的构造器重新指向了B构造函数,那么 B.prototype===obj.constructor.prototype(true),都具有成员:x,a,b2。


如果没有第16行,那是 不是obj = new B(1,3)会去调用A构造函数实例化呢?答案是否定的,你会发现obj.y=3,所以仍然是调用的B构造函数实例化的。虽然 obj.constructor===A(true),但是对于new B()的行为来说,执行了上面所说的通过构造函数创建实例对象的3个步骤,第一步,创建空对象;第二步,obj.__proto__ === B.prototype,B.prototype是具有x,a,b2成员的,obj.constructor指向了 B.prototype.constructor,即构造函数A;第三步,调用的构造函数B去设置和初始化成员,具有了属性x,y。虽然不加16行不影响 obj的属性,但如上一段说,却影响obj.constructor和obj.constructor.prototype。所以在使用了原型继承后,要 进行修正的操作。

关于第12、16行,总 言之,第12行使得B原型继承了A的原型对象的所有成员,但是也使得B的实例对象的构造器的原型指向了A原型,所以要通过第16行修正这个缺陷。


毕了。

作者:JayChow
出处:http://ljchow.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,请在文章页面明显位置给出原文链接。

[转载]打造第二代测试框架TestDriven 2.0(七)

mikel阅读(1129)

[转载]打造第二代测试框架TestDriven 2.0(七)—— 让测试驱动更加的自动吧! – 美丽人生 – 博客园.

——————–

前 言 Preface

——————–

本 文介绍了一种新颖的测试思路,并制作了原型系统展示其效果。

此技术将作为测试驱动框架2.0的一个部分(Testdriven 2.0) 。

而 测试驱动2.0的目的是:让代码之间沟通,让变化更加容易。

——————–

测 试分类 与 本文的讨论对象 Catalog 

——————–

测试包含了很多种, 每一种需要特定的技术去解决,例如:

1. Winform类的界面测试:通常使用钩子等Win32接口去捕捉用户的操作,然后模拟回放进行测试(此技术不在讨论范围)

2. Web界面测试:一般使用JS嵌入测试页面,同样模拟用户操作;或者使用IE内核等调用内部函数实现用户操作(此技术不讨论)

3. Web的Http模拟测试:在.net 2.0比较难实现,到了3.0之后的版本,微软对HttpContext这个庞然大物体做了重构,因此让测试变得稍微简单了。(此技术不讨论)

4. 类库、代码测试:这个是我需要讨论的终点,包含了各种框架、逻辑应用等。现有的技术主要是UNit / Testdriven.net / Mock等。他们也貌似很好的解决了一些问题。但是。。。

所以,本文接下来将针对第4种测试 (类库、代码测试),也是最常见的测试进行讨论。

———————

现有的问题 Problem

———————

现在的测试,很大一 部份是在回归。开发者梦想编写好自动测试代码,日后如果有变动,通过做回归,就知道是否破坏了之前的功能、是否产生了bug。朝着这个目标,诞生了很多测 试工具。可是在我看来,他们只是从一个小坑跳到了另外一个大坑。(也就是我之前说的,掉入自己挖的坑里了)

首先,测试的结 果是否准确,完全取决于测试数据是否全面准确。 那么编写测试数据本身就存在了人为的bug。

其次,测试代码和业务代码紧 密联系,一旦业务代码修改,测试代码往往是全盘否定的。这就出现一个矛盾:如果测试代码写的马虎,日后基本上不能用;如果测试代码写的精细,一旦修改起 来,之前的工作都白费了。

所以,我们真正期望的是:能够自动分析业务代码,自动编写测试代码, 而不是人去写。这个目标现在还很难实现(微软在VS2010里面已经大量引入了Code Gen等技术,可是。。)

可 是难,不等于不行,下面我将尝试迈出一小步。

———————

原理分析 Analysis

———————

要 让测试变得自动,首先需要抽象出测试过程,然后逐一攻破。经过我分析,一段测试代码主要包含了三个方面:

1. 测试数据生成。

直 接决定了测试代码是否有效;因此要求全面、准确、也业务逻辑精密绑定。 这部分工作目前是没有更好的思路。

2. 调用对应方法。

这个很简单,就是调用一个方法,传入测试数据。没有优化的必要。

3. 查看测试结果是否符合预期。

这部分以往是写这非常无聊的Assert.IsEqual等。既无聊,又浪费时间。而恰恰这部分我发现了提升的 空间。

测试结果无非就是字符串、对象等。就是C#的ValueType / class。 而如果是对象,也一定是对象的某些属性(Property) 。而这些数据都是可以被序列化、反序列化的!

因 此 这个过程完全可以被机器代替,从而让测试代码变得更加灵活,立马减少50%的工作量!现在我就展示一下目前的原型系统效果。

———————

原 型系统效果  ProtoType

———————

首先是一段测试代 码。

public void test00001()
{
object pojo = CreatePojo();

Assert.SaveOrVerify(test000 create pojo, pojo);
}

这 段代码的目的是 测试生成的pojo对象是否符合预期。 而一个Pojo对象可能是下面一个接口的实例:

代码

interface IinterfaceWithAllCollection
{
byte[] Image { get;set;}
string Name { get;set;}
double Fee { get;set;}
int Age { get;set;}
IinterfaceWithAllCollection[] pojos {
get;set;}
List
<IinterfaceWithAllCollection> pojos2 { get;set;}
ObjectWithValue objPojo {
get;set;}
ObjectWithValue[] objPojos {
get;set;}
List
<ObjectWithValue> objPojos2 { get;set;}
}

按照以往的做法,一定是要展开这个对象,获 取每一个属性,然后做Assert。现在我仅仅需要一句话就完成了测试 结果验证
Assert.SaveOrVerify(test000 create pojo, pojo);

不 知道您感受到了其中的魅力和惊喜没有?

调用了这句话, 框架首先会搜索保存在磁盘的结果数据,一般是xml文件;这些数据本质上就是预期结果的序列化值。然后逐一和这些值做对比。

那 么您一定好奇,这些期望的值从哪里来?答案就是,代码本身生成。当第一次执行的时候,是没有预期值的,那么框架会给出警告,然后将当前数据持久到磁盘。例 如:

代码

—— Test started: Assembly: Pixysoft.Framework.Configurations.dll ——

WARNING: expected value do not existed. create new one for test000 create pojo
Verifying IinterfaceWithAllCollection.Image.
TRUE: expected
=%01, actual=%01

Verifying IinterfaceWithAllCollection.Name.
TRUE: expected=hello, actual=hello

Verifying IinterfaceWithAllCollection.Fee.

。。。。。 省 略

这个时候,我调用一个配置界面,就可以查看第一次生成的 值,然后修改成为预期值:

这 样一个序列化结构就可以通过 树形结构 展示出来。 现在我们只要查看第一次运行的结果是否正确,调整ExpectedValue。 这样这段测试代码就可以永远被重复调用了。

———————-

小 结 Summary

———————-

因为是原型系统,目前还不能发出第 一个release,不过很快就可以完成了。主要是我写的代码和屎一样烂,需要进行优化才好意思拿出来。

等release 出来时候,所有代码都会采取开源的策略。不过是一种新的开源策略。

如果各位有急着需要的,咱们可以相互讨论交流下。希望大 家多提供些思路,多提供您期望的需求。谢谢!


我们的最新动态 (Bamboo@pixysoft.net)

  • 1.DynamicProxy开发完成.和微软的Realproxy比较.性能提高1.5倍… [2010-6-5]
  • 2.对象反序列化的emit框架完成.性能测试提高了15倍.[2010-6-4]
  • 3. 对象序列化操作使用最新的emit框架.动态编译.性能提高了10倍…[2010-6-3]
  • 我们每天都在努力着!

作者:美丽人生
技术支持:reborn_zhang@hotmail.com

[转载]封装jQuery表格插件jqGrid,控件化jqGrid(二):显示

mikel阅读(1159)

[转载]封装jQuery表格插件jqGrid,控件化jqGrid(二):显示 – 黑曜石 – 博客园.

本文编码数据提供类

1,创建AjaxData类,继承接口IHttpHandler,实现其方法public void ProcessRequest(HttpContext context)和参数public bool IsReusable

2,使用SQLHelper类与数据库进行交互(在MVC中可以使用entity framework提供数据,当然在MVC中,最好就不要使用服务器控件了,可以直接输出字符串)

3,在AjaxData类中,创建两个方法

public string jsonString(HttpContext context)用于处理接收到的基本数据,最后经过以下方法返回数据的json字符串

其代码如下:

 

其中使用到了SQLHelper.cs提供的SQL2005存储过程。

public string DataTableToJson(DataTable dt, int page, int total, int records, XmlDocument xmlDoc)用于DataTable转化为jqGrid格式的字符串,其代码如下:



[转载]封装jQuery表格插件jqGrid,控件化jqGrid(一):显示

mikel阅读(1139)

[转载]封装jQuery表格插件jqGrid,控件化jqGrid(一):显示 – 黑曜石 – 博客园.

先上完结后效果图(点击放大):

image

功能:排序,列宽拖拉,自动行号,添加,编辑,删除,查询等

只要在页面中引入:JQuery.js及

<AllenJqGrid:JqGrid ID=”MyJqGrid” runat=”server” TableName=”diamond” Search=”true” Add=”true” Edit=”true” Del=”true” />

这么一段,上图就将呈现在浏览器中。

注:本控件数据提供仅使用json。

写在前面的,为何要控件化

这里http://www.trirand.com/jqgrid/jqgrid.html是 官方的jqGrid Demo页。

其基本的页面JS如下:

jQuery("#list2").jqGrid({
   	url:'server.php?q=2',
	datatype: "json",
   	colNames:['Inv No','Date', 'Client', 'Amount','Tax','Total','Notes'],
   	colModel:[
   		{name:'id',index:'id', width:55},
   		{name:'invdate',index:'invdate', width:90},
   		{name:'name',index:'name asc, invdate', width:100},
   		{name:'amount',index:'amount', width:80, align:"right"},
   		{name:'tax',index:'tax', width:80, align:"right"},		
   		{name:'total',index:'total', width:80,align:"right"},		
   		{name:'note',index:'note', width:150, sortable:false}		
   	],
   	rowNum:10,
   	rowList:[10,20,30],
   	pager: '#pager2',
   	sortname: 'id',
    viewrecords: true,
    sortorder: "desc",
    caption:"JSON Example"
});
jQuery("#list2").jqGrid('navGrid','#pager2',{edit:false,add:false,del:false});

这只是基本的配置就有这么多,用过的人应该会知道jqGrid有很丰富的API,但常用的其实也就那么几个,值也差不多。

我在一个项目中的表格大部分都是使用jqGrid实现,大部分时候都是Ctrl+C,然后Ctrl+V,最后改改。

改改的时候经常就会发生默名的错误,基本上找了半天,发现就是少了个逗号或JSON出问题之类。

所以为了不复制粘贴,不重复的设置各项功能及其服务器对应的功能代码,控件化有必要。

控件制作正式开始

一,文件准备:

开个项目,ASP.NET服务器控件,这里命名为AspJqGrid,VS2010会自动生成一个cs控件文件,留下

protected override void RenderContents(HtmlTextWriter output),用于输出控件

接下来,将jqGrid需要的文件接入项目

新建文件夹Css,将ui.jqgrid.css复制进去

新建文件夹Script,将grid.locale-cn.js和JQuery.jqGrid.min.js复制进去,前一个文件用于本地化,也是 不可缺的

为使用系统资源,调整复制进来的三个文件的属性,如图:

image

另外在Properties中的AssemblyInfo.cs中声明如下:

[assembly: WebResource(“AspJqGrid.Css.ui.jqgrid.css”, “text/css”)]
[assembly: WebResource(“AspJqGrid.Scripts.grid.locale-cn.js”, “application/x-JavaScript”, PerformSubstitution = true)]
[assembly: WebResource(“AspJqGrid.Scripts.JQuery.jqGrid.min.js”, “application/x-JavaScript”, PerformSubstitution = true)]

这样,文件类的资源准备完毕。

二,编码控件主体:

1)根据jqGrid表体的各项参数,设置参数

如:参数_rowNum声明,其它的_sortname,_sortorder,_rowList等都一样

//每页显示行数默认50
        private int _rowNum = 50;
        [Description("每页显示行数")]
        public int RowNum
        {
            get { return _rowNum; }
            set { _rowNum = value; }
        }

2)在控件输出之前,需要向页面输出一些必要的文件资源

protected override void OnPreRender(EventArgs e)
        {
            this.Page.PreRenderComplete += new EventHandler(Page_PreRenderComplete);
            base.OnPreRender(e);
        }

        //向页面注册css和js
        void Page_PreRenderComplete(object sender, EventArgs e)
        {
            HtmlLink jqGridStyle = new HtmlLink();
            jqGridStyle.Attributes["type"] = "text/css";
            jqGridStyle.Attributes["rel"] = "stylesheet";
            jqGridStyle.Attributes["href"] = this.Page.ClientScript.GetWebResourceUrl(this.GetType(), "AspJqGrid.Css.ui.jqgrid.css");
            this.Page.Header.Controls.Add(jqGridStyle);

            this.Page.ClientScript.RegisterClientScriptResource(this.GetType(), "AspJqGrid.Scripts.grid.locale-cn.js");
            this.Page.ClientScript.RegisterClientScriptResource(this.GetType(), "AspJqGrid.Scripts.jquery.jqGrid.min.js");
        }

3)构造控件输出到页面的JS,使用StringBuilder,将第一步的各参数融入,以下是部分

StringBuilder sb = new StringBuilder();
            sb.Append("\n<script type=\"text/javascript\">\n");
            sb.Append("$(function(){\n");
            //-----------------------------------------------jqGrid js构1造ì开a始?
            sb.Append("$(\"#" + this.ID + "Table\").jqGrid({\n");
            sb.Append("url:\"data.ashx?tablename=" + TableName + "&action=view\",\n");
            sb.Append("datatype: 'json',\n");   //此?处鋦原-为adataType,?则ò无T法ぁ?处鋦理え?数簓据Y状痢?态?,?大洙?小?写′敏?感D
            sb.Append("rowNum: " + RowNum + ",\n");
            sb.Append("pager: '#" + this.ID + "Pager',\n");
            sb.Append("sortname: '" + Sortname + "',\n");
            sb.Append("sortorder: '" + Sortorder + "',\n");

其中页面构造最复杂的是colModel,这里使用XML文件配置colModel,为此需要新建类,用于自动生成基本的XML配置文件。

新建一个表体列属性的model类,命名为jqGridColumns

包含属性:

private string columnName;

private int columnSize;

private string dataType;

private bool isIdentity = false;

private List<jqGridColumns> jqGridColumnsList = new List<jqGridColumns>();

新建一个配置文件生成类,命名为jqGridColumnsConfig

包含两个方法:

public List<jqGridColumns> GetColumnsList(string fields, string tableName)

其核心为使用SQLDataReader的GetSchemaTable方法,获得表体的列属性

public void BuildColModelXML(string xmlDocPath,string fields,string tableName)

使用第一个方法后得到表体的列属性,生成colModel的XML配置文件,包含基本的colModel API,完成colModel配置文件的自动生成。

接下来继续主体控件输入JS编程,决断是否存在配置文件,否则使用上述提供的方法,自动生成colModel的配置文件

然后读取XML配置文件,以colModel格式输出JS。

4)最后输出控件,包含页面JS和表体框架

protected override void RenderContents(HtmlTextWriter output)
        {
            string jqGridTag = "\n<table id=\"" + this.ID + "Table\"></table>\n<div id=\"" + this.ID + "Pager\"></div>";
            output.Write(BuildJqGridHtml() + jqGridTag);
        }

至些,完成控件主体编码工作,下一篇完成为jqGrid提供数据的类

[转载]C#断点续传原理与实现

mikel阅读(948)

在了解HTTP断点续传的原理之前,让我们先来了解一下HTTP协议,HTTP协议是一种基于tcp的简单协议,分为请求和回复两种。请求协议是由客户机 (浏览器)向服务器(WEB SERVER)提交请求时发送报文的协议。回复协议是由服务器(web server),向客户机(浏览器)回复报文时的协 议。请求和回复协议都由头和体组成。头和体之间以一行空行为分隔。

以下是一个请求报文与相应的回复报文的例子:

GET /image/index_r4_c1.jpg HTTP/1.1
Accept: */*
Referer: http://192.168.3.120:8080
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705)
Host: 192.168.3.120:8080
Connection: Keep-Alive

HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Tue, 24 Jun 2003 05:39:40 GMT
Content-Type: image/jpeg
Accept-Ranges: bytes
Last-Modified: Thu, 23 May 2002 03:05:40 GMT
ETag: “bec48eb862c21:934″
Content-Length: 2827
下面我们就来说说” 断点续传”,顾名思义,断点续传就是在上一次下载时断开的位置开始继续下载。
在HTTP协议中,可以在请求报文头中加入Range段,来表 示客户机希望从何处继续下载。

比如说从第1024字节开始下载,请求报文如下:
GET /image/index_r4_c1.jpg HTTP/1.1
Accept: */*
Referer: http://192.168.3.120:8080
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705)
Host: 192.168.3.120:8080
Range:bytes=1024-
Connection: Keep-Alive

.NET 中的相关类

明白了上面的原理,那么,我们来看看.NET FRAMEWORK中为我们提供了哪些类可以来做这些事。

完 成HTTP请求 System.Net.HttpWebRequest

HttpWebRequest 类 对 WebRequest 中定义的属性和方法提供支持,也对使用户能够直接与使用 HTTP 的服务器交互的附加属性和方法提供支持。

HttpWebRequest 将 发送到 Internet 资源的公共 HTTP 标头值公开为属性,由方法或系统设置。下表包含完整列表。可以将 Headers 属性中的其他标头设 置为名称/值对。但是注意,某些公共标头被视为受限制的,它们或者直接由 API公开,或者受到系统保护,不能被更改。Range也属于被保护之列,不 过,.NET为开发者提供了更方便的操作,就是 AddRange方法,向请求添加从请求数据的开始处或结束处的特定范围的字节范围标头

完 成文件访问 System.IO.FileStream

FileStream 对象支持使用Seek方法对文件进行随机访 问, Seek 允许将读取/写入位置移动到文件中的任意位置。这是通过字节偏移参考点参数完成的。字节偏移量是相对于查找参考点而言的,该参考点可以是 基础文件的开始、当前位置或结尾,分别由SeekOrigin类的三个属性表示。

代码实现

了 解了.NET提供的相关的类,那么,我们就可以方便的实现了。

代码如下:

static void Main(string[] args)
{

    string StrFileName = "c:\\aa.zip"; //根据实际情况设置 
    string StrUrl = "http://www.xxxx.cn/xxxxx.zip"; //根据实际情况设置 

    //打开上次下载的文件或新建文件 
    long lStartPos = 0;
    System.IO.FileStream fs;
    if (System.IO.File.Exists(StrFileName))
    {
        fs = System.IO.File.OpenWrite(StrFileName);
        lStartPos = fs.Length;
        fs.Seek(lStartPos, System.IO.SeekOrigin.Current); //移动文件流中的当前指针 
    }
    else
    {
        fs = new System.IO.FileStream(StrFileName, System.IO.FileMode.Create);
        lStartPos = 0;
    }

    //打开网络连接 
    try
    {
        System.Net.HttpWebRequest request = (System.Net.HttpWebRequest) System.Net.HttpWebRequest.Create(StrUrl);
        if (lStartPos > 0)
            request.AddRange((int) lStartPos); //设置Range值 

        //向服务器请求,获得服务器回应数据流 
        System.IO.Stream ns = request.GetResponse().GetResponseStream();

        byte[] nbytes = new byte[512];
        int nReadSize = 0;
        nReadSize = ns.Read(nbytes, 0, 512);
        while (nReadSize > 0)
        {
            fs.Write(nbytes, 0, nReadSize);
            nReadSize = ns.Read(nbytes, 0, 512);
        }
        fs.Close();
        ns.Close();
        Console.WriteLine("下载完成");
    }
    catch (Exception ex)
    {
        fs.Close();
        Console.WriteLine("下载过程中出现错误:" + ex.ToString());
    }
}

[转载]C#动态生成EXCLE并进行添加内容

mikel阅读(974)

[转载]C#动态生成EXCLE并进行添加内容 – 章术 – 博客园.

Microsoft Excel是Microsoft Office的一个组件,是功能强大的电子表格处理软件,它与文本处理软件的差别在于它能够运算复杂的公式,并且有条理地显示结果。Microsoft Excel是除了Microsoft Word之外最常用的办公软件之一,本节将介绍如何使用C#创建Excel文档。
与在C#中添加Word文档的方法类似,添加Excel文档时需要为项目添加对 Microsoft Excel X Object Library的引用,其中的X对应为版本号。Excel 2007对应12.0。在Microsoft Excel X Object Library中,一个Excel文档由MSExcel.Workbook表示。

using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using MSEXCLE = Microsoft.Office.Interop.Excel;//定义MSEXCLE
using System.Reflection;
namespace 动态创建EXCLE并添加文字图表
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private MSEXCLE.Application excleapp;//定义excleapp应用程序
private MSEXCLE.Workbook excleWok;//定义exclework文档
private void button1_Click(object sender, EventArgs e)
{
object filename = “f:\\aa.xls”;
//若f:\\aa.xls中文件存在那么就删除这个文件
if (File.Exists((string)filename))
{
File.Delete((string)filename);
}
object Nothing=System.Reflection.Missing.Value;//定义Nothing
excleapp = new MSEXCLE.ApplicationClass();//对excleapp进行初始化
excleWok = excleapp.Workbooks.Add(Nothing);//对exclework进行初始化
MSEXCLE.Worksheet ws = (MSEXCLE.Worksheet)excleWok.Worksheets[1];//定义ws为工作文档中的第一个sheet
MSEXCLE.Range range1 = ws.get_Range(“A1”, “A1”);//选定(A1,A1)这个单元格
range1.Value2 = “3”;//对这个单元格进行填充内容
range1 = ws.get_Range(“A2”, “A2”);//同上
range1.Value2 = “5.7”;//同上
range1 = ws.get_Range(“A3”, “A3”);//同上
range1.Value2 = “4.8”;//同上
range1 = ws.get_Range(“A4”, “A4”);//同上
range1.Value2 = “9.2”;//同上
range1 = ws.get_Range(“A5”, “A5”);//同上
range1.Value2 = “6.4”;//同上
excleWok.Charts.Add(Nothing, Nothing, Nothing, Nothing);//添加一个图表
excleWok.ActiveChart.ChartType = MSEXCLE.XlChartType.xl3DColumnClustered  ;//设置图表的类型是三维柱状图
excleWok.ActiveChart.SetSourceData(ws.get_Range(“A1”, “A5”), MSEXCLE.XlRowCol.xlColumns  );//设置这个三维柱状图的数据源
excleWok.ActiveChart.Location(MSEXCLE.XlChartLocation.xlLocationAsObject, “sheet1”);//设置你要将这个柱状图添加到什么地方
excleWok.ActiveChart.HasTitle = true;//设置柱状图是否有标题
excleWok.ActiveChart.ChartTitle.Text = “创建图表”;//设置标题的内容为“创建图表”
excleWok.ActiveChart.HasDataTable = false;//设置柱状图是否有数据表
excleWok.SaveAs(filename, Nothing , Nothing, Nothing, Nothing, Nothing, MSEXCLE.XlSaveAsAccessMode.xlExclusive, Nothing, Nothing, Nothing, Nothing, Nothing);//保存动态生成的excle表
//释放资源
excleapp.Application.Quit();
if (excleapp != null)
excleapp = null;
}
}
}

[转载]IIS5和Tomcat6整合,实现IIS5支持ASP(.NET)+PHP+JSP

mikel阅读(1219)

[转载]IIS5和Tomcat6整合,实现IIS5支持ASP(.NET)+PHP+JSP – 水稻的梦想 – 博客园.

环 境描述:

Windows XP SP3

IIS5.Net1.12.03.03.54.0PHP5

任 务目标:

使本机IIS可以支持JSP,也就是可以同样使用80端口而不是8080端口就可以访问JSP

通 关物品:

Tomcat6

isapi_redirector2.dll

workers2.propertiesjk2.properties文件

说 明:

通关后,IIS5的主目录设为C:\Inetpub\wwwroot\ROOTTomcat6appBase则指向C:\Inetpub\wwwroot

开 始通关:

1、安装Tomcat6,安装成功后浏览器访 问http://localhost:8080,显示Tomcat欢迎页,安装成功。(本文默认本机Tomcat6安装到C:\Program Files\Apache Software Foundation\Tomcat 6.0目录下)

2、下载isapi_redirector2.dll文件并保存到C:\Program Files\Apache Software Foundation\Tomcat 6.0\conf\connector。(事实上保存到哪里都可以,配置过程中只要修改相应的指向路径即可)

3、C:\Program Files\Apache Software Foundation\Tomcat 6.0\conf下建立workers2.propertiesjk2.properties两个文件(新建文本重命名即可),内容如下:

workers2.properties

[shm]
# file的值要根据实际情况做修改
file=C:/Program Files/Apache Software Foundation/Tomcat 6.0/logs/jk2.log
size=1048576

# Example socket channel, override port and host.
[channel.socket:localhost:8009]
port=8009
host=127.0.0.1

# define the worker
[ajp13:reynir_net:8009]
channel=channel.socket:localhost:8009

# Uri mapping
[uri:/*.jsp]
[uri:/web/*]
[uri:/view/*]
worker=ajp13:localhost:8009

# define the worker
[status:status]

# Uri mapping
[uri:/jkstatus/*]
worker=status:status

jk2.properties

request.tomcatAuthentication=false

4、修改注册表,为isapi_redirector2.dll提供必须的参数:
在任意位置新建注册表文件isapi_redirect.2.0.reg,内容如 下:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Jakarta Isapi Redirector]
[HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Jakarta Isapi Redirector\2.0]
“serverRoot”=”C:\\Program Files\\Apache Software Foundation\\Tomcat 6.0”
“extensionUri”=”/jakarta/isapi_redirector2.dll”
“workersFile”=”C:\\Program Files\\Apache Software Foundation\\Tomcat 6.0\\conf\\workers2.properties”
“logLevel”=”Debug
其中,serverRootworkerFile的值要根据实际情况做相应修改,本机绝对路径中使用“\\”来分隔,extensionUri的值不要修改。
执行这个注册表文件将值导入系统注册表。

5、修改IIS设置:
打 开“
Internet 信息 服务”,在“你的计算机”“网 站”“默认网站”上点击右 键,选“属性”,点击“ISAPI筛选器”选项卡,点“添加”按钮,名称为“jakarta”,可执行文件就是“isapi_redirector2.dll”文件的完整路径。


完成之后一路“确定”退出所有对话框。
还是在“默认网站”上点击右键,新建虚拟目录,别名一定 是“jakarta”,路径是“isapi_redirector2.dll”文件所在的目录,最后要加上“执行”权限,再一路“确定”退出所有对话框。

6、统一IIS5Tomcat6的根目录:
文本编辑器打开C:\Program Files\Apache Software Foundation\Tomcat 6.0\conf\server.xml,找到<Host />节,参考如下修改:
<Host name=”localhost”  appBase=”C:\\Inetpub\\wwwroot\\”
unpackWARs=”true” autoDeploy=”true”
xmlValidation=”false” xmlNamespaceAware=”false” ></Host>
name属性为主机 名,就是俗称网址,一个站点可以绑定多个主机名,每一个主机名就是一条<Host />节;
appBase属性就是Tomcat的主目录,初始值为“webapps”,是相对路径,相对于Tomcat的安装目录,需要注意,当设置绝对路径时要用“\\”来分隔,而且Tomcat的站点根目录是在这里设置的主目录下的ROOT目录中,所以这里虽然设置为“C:\\Inetpub\\wwwroot\\”,实际上要在wwwroot下新建一个名为ROOT的文件夹。
修 改好后保存server.xml文件,重启Tomcat6,随便写一个JSP测试页保存到c:\inetpub\wwwroot\ROOT下,例 如:index.jsp,内 容为Now time is: <%=new java.util.Date()%>,然后浏览器访问http://localhost:8080/index.jsp,看到当前时间,说明Tomcat主目录设置成功。
打开“Internet 信息服务”,在“你的计算机”网站“默认网站”上点击右键,选“属性”,在“主目录”选项卡中把“本地路径”修改为“C:\Inetpub\wwwroot\ROOT”,一 路“确定”退出对话框。至此,你可以再次重启一下Tomcat6IIS5,然后进入下一步。

7、激动人心的一刻:
在确保IIS5Tomcat6服务都正常开启的情况下,浏览器访问http://localhost/index.jsp,看到了吗?再开一个网页,访问http://localhost:8080/index.jsp,怎么样,两个页面结果是不是一样的?是的,已经成功了,至此,一个即支持ASP.NET),又支持PHP,同时还支持JSPIIS已经在你的机器上运行起来了!
不放心?
ROOT目录下再新建一个index.php,内容为<?php phpinfo(); ?>,浏览器访问http://localhost/index.php,将会显示php配置信息。
ROOT目录下新建一个index.asp,内容为<%=”Hello WorldIIS仍然可以解析ASP文件!“%>,浏览器访问http://localhost/index.asp, 怎么样?

本次通过记录暂到这里。

由于配置历时太长,而且在我 写这篇文章时页面还停止响应一次,所以文章中难免会有点疏漏,欢迎交流指正。