我们瞧一瞧ASP.NET Framework的运行机制和架构。
在开始之前,我们先跟随考古学家参观一下古老的ASP运行机制:
当你请求一个*.asp文件的时候,这个http request首先被inetinfo.exe进程所截获,这个inetinfo.exe进程就是WWW服务进程,然后她会将这个请求转交给asp.dll进程,asp.dll进程就会解释执行这个asp叶面,然后将解释后的数据流返回给客户端浏览器。
转过头来我们看看如今的ASP.NET Framework是如何处理一个http request.
当你请求一个*.aspx文件的时候,同样的这个http request会被inetinfo.exe进程截获,她判断文件的后缀之后,将这个请求转交给ASPNET_ISAPI.dll,ASPNET_ISAPI.dll会通过一个被称为Http PipeLine的管道,将请求发送给ASPNET_WP.exe进程,当这个http request进入ASPNET_WP.exe进程之后,会通过HttpRuntime来处理这个请求,处理完毕将结果返回客户端。
OK,好像并没有太大的改进嘛,不要着急,在ASP.NET Framework中我们甚至能够了解到HttpRuntime的细节。好,继续深入下去:
当Http Request进入HttpRuntime之后,会继续进入到一个被称之为HttpApplication Factory的一个Container中,她会给出一个HttpApplication来处理传递进来的请求,这个请求会依次进入如下几个Container:HttpModule->HttpHandler Factory->HttpHandler。
当系统内部的HttpHandler的ProcessResquest方法处理完毕之后,整个Http Request就完成了,客户端也就得到相应的东东了。
整理一下ASP.NET Framework处理一个Http Request的流程:
HttpRequest-->inetinfo.exe-->ASPNET_ISAPI.dll-->Http Pipeline-->ASPNET_WP.exe-->HttpRuntime-->HttpApplication Factory-->HttpApplication-->HttpModule-->HttpHandler Factory-->HttpHandler-->HttpHandler.ProcessRequest()
或许会问,我知道这个处理流程有什么用处呢?当然有用了,比如如果你想要中途截获一个Http Request并且做些自己的处理,该如何做呢?这是下一次我们探讨的东东了,下次我们详细讨论处理的细节问题。
刚刚吃完晚饭,正好在键盘上面锻炼一下手指。
接着上回继续写这个“日记”:
Chapter Two -- HttpModule是如何工作的?
我们上回说到,一个来自于客户端的Http Request被截获后经过层层转交(怎么都在踢皮球?呵呵)到达了HttpModule这个“请求监听器”。
HttpModule就类似于安插在ASPNET_WP.EXE进程中的一个窃听器,稍微有些常识的人都会很自然的想象得到窃听器是用来做什么的,而我们的HttpModule
可以说是作窃听器的绝好人选了,但是需要明确的是,HttpModule绝对不是简单的监听器,它可以做到更多的东西,比如它可以对截获的请求增加一些内容
等等。
另外需要明白的是,当一个Http Request到达HttpModule的时候,整个ASP.NET Framework系统还并没有对这个请求做任何的真正处理,但是我们可以
在这个Http Request传递到真正的请求处理中心(HttpHandler)之前附加一些我们需要的信息在这个Http Request至上,或者针对我们截获的这个Http
Request信息作一些额外的工作,或者在某些情况下干脆终止满足一些条件的Http Request,从而可以起到一个Filter过滤器的作用,而不仅仅是一个窃听器了。
通过查阅MSDN(不要去相信.NET SDK自带的那个QuickStarts Web文档,正式版本中竟然在很多地方没有更新这个文档,很多东西在正式版本是无效的),
你会发现系统HttpModule实现了一个叫做IHttpModule的接口,很自然的就应当想到,只要我们自己的类能够实现IHttpModule接口,不就可以完全替代系统的
HttpModule了吗?完全正确。
在我们开始自己的HttpModule类之前,我先来告诉你系统中的那个HttpModule是什么样子的,ASP.NET系统中默认的HttpModule有以下几个:
System.Web.Caching.OutputCacheModule
System.Web.SessionState.SessionStateModule
System.Web.Security.WindowsAuthenticationModule
System.Web.Security.FormsAuthenticationModule
System.Web.Security.PassportAuthenticationModule
System.Web.Security.UrlAuthorizationModule
System.Web.Security.FileAuthorizationModule
好了,我们来开始我们自己的HttpModule构建历程吧。
1)打开VS.NET新建一个“Class Library”项目,将它命名为MyHttpModule。
2)引用System.Web.dll文件
在代码区域敲入:
1
using System;
2
using System.Web;
3
4
namespace MyHttpModuleTest
5
{
6
/// <summary>
7
/// 说明:用来实现自定义HttpModule的类
8
/// 作者:uestc95
9
/// 联系:uestc95@263.net
10
/// </summary>
11
public class MyHttpModule:IHttpModule
12
{
13
/// <summary>
14
/// 说明:构造器方法
15
/// 作者:uestc95
16
/// 联系:uestc95@263.net
17
/// </summary>
18
public MyHttpModule()
19
{
20
21
}
22
23
/// <summary>
24
/// 说明:实现IHttpModule接口的Init方法
25
/// 作者:uestc95
26
/// 联系:uestc95@263.net
27
/// </summary>
28
/// <param name="application">HttpApplication类型的参数</param>
29
public void Init(HttpApplication application)
30
{
31
application.BeginRequest +=new EventHandler(this.Application_BeginRequest);
32
application.EndRequest +=new EventHandler(this.Application_EndRequest);
33
}
34
35
/// <summary>
36
/// 说明:自己定义的用来做点事情的私有方法
37
/// 作者:uestc95
38
/// 联系:uestc95@263.net
39
/// </summary>
40
/// <param name="obj">传递进来的对象参数</param>
41
/// <param name="e">事件参数</param>
42
private void Application_BeginRequest(Object obj,EventArgs e)
43
{
44
HttpApplication application=(HttpApplication)obj;
45
HttpContext context=application.Context;
46
HttpResponse response=context.Response;
47
HttpRequest request=context.Request;
48
49
response.Write("我来自Application_BeginRequest,:)");
50
51
}
52
53
/// <summary>
54
/// 说明:自己定义的用来做点事情的私有方法
55
/// 作者:uestc95
56
/// 联系:uestc95@263.net
57
/// </summary>
58
/// <param name="obj">传递进来的对象参数</param>
59
/// <param name="e">事件参数</param>
60
private void Application_EndRequest(Object obj,EventArgs e)
61
{
62
HttpApplication application=(HttpApplication)obj;
63
HttpContext context=application.Context;
64
HttpResponse response=context.Response;
65
HttpRequest request=context.Request;
66
67
response.Write("我来自Application_EndRequest,:)");
68
69
}
70
71
/// <summary>
72
/// 说明:实现IHttpModule接口的Dispose方法
73
/// 作者:uestc95
74
/// 联系:uestc95@263.net
75
/// </summary>
76
public void Dispose(){}
77
}
78
}
79![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
2
![](/Images/OutliningIndicators/None.gif)
3
![](/Images/OutliningIndicators/None.gif)
4
![](/Images/OutliningIndicators/None.gif)
5
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
6
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
7
![](/Images/OutliningIndicators/InBlock.gif)
8
![](/Images/OutliningIndicators/InBlock.gif)
9
![](/Images/OutliningIndicators/InBlock.gif)
10
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
11
![](/Images/OutliningIndicators/InBlock.gif)
12
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
13
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
14
![](/Images/OutliningIndicators/InBlock.gif)
15
![](/Images/OutliningIndicators/InBlock.gif)
16
![](/Images/OutliningIndicators/InBlock.gif)
17
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
18
![](/Images/OutliningIndicators/InBlock.gif)
19
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
20
![](/Images/OutliningIndicators/InBlock.gif)
21
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
22
![](/Images/OutliningIndicators/InBlock.gif)
23
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
24
![](/Images/OutliningIndicators/InBlock.gif)
25
![](/Images/OutliningIndicators/InBlock.gif)
26
![](/Images/OutliningIndicators/InBlock.gif)
27
![](/Images/OutliningIndicators/InBlock.gif)
28
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
29
![](/Images/OutliningIndicators/InBlock.gif)
30
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
31
![](/Images/OutliningIndicators/InBlock.gif)
32
![](/Images/OutliningIndicators/InBlock.gif)
33
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
34
![](/Images/OutliningIndicators/InBlock.gif)
35
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
36
![](/Images/OutliningIndicators/InBlock.gif)
37
![](/Images/OutliningIndicators/InBlock.gif)
38
![](/Images/OutliningIndicators/InBlock.gif)
39
![](/Images/OutliningIndicators/InBlock.gif)
40
![](/Images/OutliningIndicators/InBlock.gif)
41
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
42
![](/Images/OutliningIndicators/InBlock.gif)
43
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
44
![](/Images/OutliningIndicators/InBlock.gif)
45
![](/Images/OutliningIndicators/InBlock.gif)
46
![](/Images/OutliningIndicators/InBlock.gif)
47
![](/Images/OutliningIndicators/InBlock.gif)
48
![](/Images/OutliningIndicators/InBlock.gif)
49
![](/Images/OutliningIndicators/InBlock.gif)
50
![](/Images/OutliningIndicators/InBlock.gif)
51
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
52
![](/Images/OutliningIndicators/InBlock.gif)
53
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
54
![](/Images/OutliningIndicators/InBlock.gif)
55
![](/Images/OutliningIndicators/InBlock.gif)
56
![](/Images/OutliningIndicators/InBlock.gif)
57
![](/Images/OutliningIndicators/InBlock.gif)
58
![](/Images/OutliningIndicators/InBlock.gif)
59
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
60
![](/Images/OutliningIndicators/InBlock.gif)
61
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
62
![](/Images/OutliningIndicators/InBlock.gif)
63
![](/Images/OutliningIndicators/InBlock.gif)
64
![](/Images/OutliningIndicators/InBlock.gif)
65
![](/Images/OutliningIndicators/InBlock.gif)
66
![](/Images/OutliningIndicators/InBlock.gif)
67
![](/Images/OutliningIndicators/InBlock.gif)
68
![](/Images/OutliningIndicators/InBlock.gif)
69
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
70
![](/Images/OutliningIndicators/InBlock.gif)
71
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
72
![](/Images/OutliningIndicators/InBlock.gif)
73
![](/Images/OutliningIndicators/InBlock.gif)
74
![](/Images/OutliningIndicators/InBlock.gif)
75
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
76
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
77
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
78
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
79
![](/Images/OutliningIndicators/None.gif)
3)在VS.NET中编译之后,你会得到MyHttpModule.dll这个文件。
4)接下来我们的工作就是如何让ASPNET_WP.exe进程将http request交给我们自己写的这个HttpModule呢?方法就是配置web.config文件。
在web.config文件中增加如下几句话:
<httpModules>
<add name="test" type="MyHttpModuleTest.MyHttpModule,MyHttpModule"/>
</httpModules>
注意要区分大小写,因为web.config作为一个XML文件是大小写敏感的。“type=MyHttpModuleTest.MyHttpModule,MyHttpModule”告诉我们
系统将会将http request请求交给位于MyHttpModule.dll文件中的MyHttpModuleTest.MyHttpModule类去处理。而这个DLL文件系统将会自动
到\bin子目录或者系统全局程序集缓冲区(GAC)搜寻。我们可以将我们刚才得到的DLL文件放在bin子目录中,至于后者,你可以通过.NET SDK正式版
自带的Config工具做到,我们不详细说了。
好了,我们的用来截获http request请求的HttpModule就完成并且装配完成了,你可以试着在你的web项目中建立一个新的WebForm,运行看看呢?:)
最后,我们假设一个使用这个HttpModule的场合。A站点提供免费的ASP.NET虚拟空间给大家,但是A站点的管理者并不想提供免费的午餐,他想要在每一个
页面被浏览的时候自动弹出自己公司的广告(就像现在的www.X63.com一样),我总不能时刻监视所有用户的所有页面吧,并且想要在每一个页面手动添加
一段JS代码,工作量是不可想象的,也是不现实的。那末好了,只要我们的HttpModule一旦被挂接完成,这一切都将是轻而易举的事情了,只要我们在每一个
Http Request被我们捕获的时候,给他增加上一些JS就好了!
我们上面提到在Init()方法中使用了两个事件BeginRequest和EndRequest,这两个事件分别是Init()中可以处理的所有事件的最开始事件和最终事件,在他们
中间还有一些其它的事件可以被我们利用,可以查阅MSDN。
另外在我关闭EditPlus之前,需要敲下如下的话:
在HttpModule中可以正常使用Response,Request,Server,Application,但是不能操作任何与Session有关代码!
为什么呢?自己考虑一下吧,下回看看原因在哪里,另外再给出一个问题,你能发现系统默认的那几个HttpModule在哪里配置的呢?找找看。
下回我们看看HttpHandler部分以及如何同HttpModule相配合的东东。
这几天胃口还算好,虽然算不上“吃嘛嘛香”,但是也算是不错了,但愿能增上几斤才好。
怎么样,我们在Chapter Two最后提出的两个问题估计早出来了吧,:)
First:为什么在HttpModule中不能使用Session?
Second:系统默认的几个HttpModule在哪里配置的?
我们先挑软柿子捏,第二个问题的答案是:在文件machine.config中配置,比如在你的系统文件目录中的C:\WI
NNT\Microsoft.NET\Framework\v1.0.3705\CONFIG\machine.config。
虽然是个软柿子,但是还是有些东东在里面的,那就是machine.config和我们常见的web.config有什么关
系呢?在ASP.NET Framework启动处理一个Http Request的时候,她会依次加载machine.config以及你请求页面
所在目录的web.config文件,里面的配置是有<remove>标签的,什么意思不说也知道了吧。如果你在machine.c
onfig中配置了一个自己的HttpModule,你仍然可以在离你最近web.config文件中“remove”掉这个映射关系。
至于第一个问题,呵呵,如果你仔细的运行过上次的文件但是没有自己仔细深入研究一下的话,一定会觉
得在HttpModule中的确无法使用Session,:)。如果你发现上次提出的问题本身就是一个"Problem",那么恭喜你,你没有掉进我故意给出的框框中,并且很有质疑精神,:)
今天我们就来解释一下HttpModule和HttpHandler之间的关系,在今天的“日记”完成的时候,你也就会发现第一个问题的答案了。
Chapter Three -- 深入HttpModule
我们曾经提及当一个Http Request被ASP.NET Framework捕获之后会依次交给HttpModule以及HttpHandler来处理,但是不能理解为HttpModule和HttpHandler是完全独立的,实际上是,在Http Request在HttpModule传递的过程中会在某个事件内将控制权交给HttpHandler的,而真正的处理在HttpHandler中完成之后,再将控制权交还给HttpModule。也就是说HttpModule在某个请求经过她的时候会再恰当时候同HttpHandler进行通信,在何时,如何通信呢?这就是下面提到的了。
我们提到在HttpModule中最开始的事件是BeginRequest,最终的事件是EndRequest。你如果仔细看上次给出的源程序的话,应当发现在方法Init()中参数我们传递的是一个HttpApplication类型,而我们曾经提及的两个事件正是这个传递进来的HttpApplication的事件之一。
HttpApplication还有其它的事件,分别如下:
application.BeginRequest
application.EndRequest
application.PreRequestHandlerExecute
application.PostRequestHandlerExecute
application.ReleaseRequestState
application.AcquireRequestState
application.AuthenticateRequest
application.AuthorizeRequest
application.ResolveRequestCache
application.PreSendRequestHeaders
application.PreSendRequestContent
需要注意的是,在事件EndRequest之后还会继续执行application.PreSendRequestHeaders以及application.PreSendRequestContent事件,而这两个事件大家想必应当从名称上面看得出来事做什么用途的了吧。是的,一旦触发了这两个事件,就表明整个Http Request的处理已经完成了,在这两个事件中是开始向客户端传送处理完成的数据流了。看到这里,您应当有一个疑问才对:怎么没见到HttpHandler就处理完成了?不是提到过HttpHandler才是真正处理Http Request的吗?如果你有这个疑问表明你是仔细在看,也不枉我打字打得这莫累,:)。
其实一开始我就提到了,在一个http request在HttpModule传递过程中,会在某一个时刻(确切的说应当是事件)中将这个请求传递给HttpHandler的。这个事件就是ResolveRequestCache,在这个事件之后,HttpModule会建立一个HttpHandler的入口实例(做好准备了,:)),但是此时并没有将控制权交出,而是继续触发AcquireRequestState以及PreRequestHandlerExecute事件(如果你实现了的话)。看到了吗,最后一个事件的前缀是Pre,呵呵。这表明下一步就要进入HttpHandler了,的确如此,正如我们猜想的那样,在PreRequestHandlerExecute事件之后,HttpModule就会将控制权暂时交给HttpHandler,以便进行真正的http request处理工作。而在HttpHandler内部会执行ProcessRequest来处理请求。在HttpHandler处理完毕之后,会将控制权交还给HttpModule,HttpModule便会继续对处理完毕的http Request进行层层的转交动作,直到返回到客户端。
怎么样,是不是有些混乱?呵呵,苦于纯文本无法画流程图,我手头上已经画好了一个整个HttpModule的生命周期图,我只能暂且在这里用字符描绘一下前后流程了,如果你想要这个图片,可以给我发送mail,我给你mail过去。
Http Request在整个HttpModule中的生命周期图:
Http Request开始
|
HttpModule
|
HttpModule.BeginRequest()
|
HttpModule.AuthenticateRequest()
|
HttpModule.AuthorizeRequest()
|
HttpModule.ResolveRequestCache()
|
建立HttpHandler控制点
|
接着处理(HttpHandler已经建立,此后Session可用)
|
HttpModule.AcquireRequestState()
|
HttpModule.PreRequestHandlerExecute()
|
进入HttpHandler处理HttpRequest
|
HttpHandler.ProcessRequest()
|
返回到HttpModule接着处理(HttpHandler生命周期结束,Session失效)
|
HttpModule.PostRequestHandlerExecute()
|
HttpModule.ReleaseRequestState()
|
HttpModule.UpdateRequestCache()
|
HttpModule.EndRequest()
|
HttpModule.PreSendRequestHeaders()
|
HttpModule.PreSendRequestContent()
|
将处理后的数据返回客户端
|
整个Http Request处理结束
怎么样,从上面的图中应当找到上次我们提出的第一个问题的答案了吧。
为了验证上面的流程,我们可以用下面的这个自己的HttpModuel来验证一下就知道了。
注意我们下面给出的是类的内容,框架还是前次我们给出的那个,自己加上就好了:
1
public void Init(HttpApplication application)
2
{
3
application.BeginRequest += (new EventHandler(this.Application_BeginRequest));
4
application.EndRequest += (new EventHandler(this.Application_EndRequest));
5
application.PreRequestHandlerExecute +=(new EventHandler(this.Application_PreRequestHandlerExecute));
6
application.PostRequestHandlerExecute +=(new EventHandler(this.Application_PostRequestHandlerExecute));
7
application.ReleaseRequestState +=(new EventHandler(this.Application_ReleaseRequestState));
8
application.AcquireRequestState +=(new EventHandler(this.Application_AcquireRequestState));
9
application.AuthenticateRequest +=(new EventHandler(this.Application_AuthenticateRequest));
10
application.AuthorizeRequest +=(new EventHandler(this.Application_AuthorizeRequest));
11
application.ResolveRequestCache +=(new EventHandler(this.Application_ResolveRequestCache));
12
application.PreSendRequestHeaders +=(new EventHandler(this.Application_PreSendRequestHeaders));
13
application.PreSendRequestContent +=(new EventHandler(this.Application_PreSendRequestContent));
14
}
15
16
private void Application_PreRequestHandlerExecute(Object source, EventArgs e)
17
{
18
HttpApplication application = (HttpApplication)source;
19
HttpContext context = application.Context;
20
21
context.Response.Write("Application_PreRequestHandlerExecute<br>");
22
}
23
24
private void Application_BeginRequest(Object source, EventArgs e)
25
{
26
HttpApplication application = (HttpApplication)source;
27
HttpContext context = application.Context;
28
29
context.Response.Write("Application_BeginRequest<br>");
30
}
31
32
private void Application_EndRequest(Object source, EventArgs e)
33
{
34
HttpApplication application = (HttpApplication)source;
35
HttpContext context = application.Context;
36
37
context.Response.Write("Application_EndRequest<br>");
38
39
}
40
41
private void Application_PostRequestHandlerExecute(Object source,EventArgs e)
42
{
43
HttpApplication application = (HttpApplication)source;
44
HttpContext context = application.Context;
45
46
context.Response.Write("Application_PostRequestHandlerExecute<br>");
47
48
}
49
50
private void Application_ReleaseRequestState(Object source, EventArgs e)
51
{
52
HttpApplication application = (HttpApplication)source;
53
HttpContext context = application.Context;
54
55
context.Response.Write("Application_ReleaseRequestState<br>");
56
57
}
58
59
private void Application_UpdateRequestCache(Object source, EventArgs e)
60
{
61
HttpApplication application = (HttpApplication)source;
62
HttpContext context = application.Context;
63
64
context.Response.Write("Application_UpdateRequestCache<br>");
65
66
}
67
68
private void Application_AuthenticateRequest(Object source, EventArgs e)
69
{
70
HttpApplication application = (HttpApplication)source;
71
HttpContext context = application.Context;
72
73
context.Response.Write("Application_AuthenticateRequest<br>");
74
75
}
76
77
private void Application_AuthorizeRequest(Object source, EventArgs e)
78
{
79
HttpApplication application = (HttpApplication)source;
80
HttpContext context = application.Context;
81
82
context.Response.Write("Application_AuthorizeRequest<br>");
83
84
}
85
86
private void Application_ResolveRequestCache(Object source, EventArgs e)
87
{
88
HttpApplication application = (HttpApplication)source;
89
HttpContext context = application.Context;
90
91
context.Response.Write("Application_ResolveRequestCache<br>");
92
93
}
94
95
private void Application_AcquireRequestState(Object source, EventArgs e)
96
{
97
HttpApplication application = (HttpApplication)source;
98
HttpContext context = application.Context;
99
100
context.Response.Write("Application_AcquireRequestState<br>");
101
102
}
103
104
private void Application_PreSendRequestHeaders(Object source, EventArgs e)
105
{
106
HttpApplication application = (HttpApplication)source;
107
HttpContext context = application.Context;
108
109
context.Response.Write("Application_PreSendRequestHeaders<br>");
110
111
}
112
113
private void Application_PreSendRequestContent(Object source, EventArgs e)
114
{
115
HttpApplication application = (HttpApplication)source;
116
HttpContext context = application.Context;
117
118
context.Response.Write("Application_PreSendRequestContent<br>");
119
120
}
121
public void Dispose()
122
{
123
}
124![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
2
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
3
![](/Images/OutliningIndicators/InBlock.gif)
4
![](/Images/OutliningIndicators/InBlock.gif)
5
![](/Images/OutliningIndicators/InBlock.gif)
6
![](/Images/OutliningIndicators/InBlock.gif)
7
![](/Images/OutliningIndicators/InBlock.gif)
8
![](/Images/OutliningIndicators/InBlock.gif)
9
![](/Images/OutliningIndicators/InBlock.gif)
10
![](/Images/OutliningIndicators/InBlock.gif)
11
![](/Images/OutliningIndicators/InBlock.gif)
12
![](/Images/OutliningIndicators/InBlock.gif)
13
![](/Images/OutliningIndicators/InBlock.gif)
14
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
15
![](/Images/OutliningIndicators/None.gif)
16
![](/Images/OutliningIndicators/None.gif)
17
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
18
![](/Images/OutliningIndicators/InBlock.gif)
19
![](/Images/OutliningIndicators/InBlock.gif)
20
![](/Images/OutliningIndicators/InBlock.gif)
21
![](/Images/OutliningIndicators/InBlock.gif)
22
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
23
![](/Images/OutliningIndicators/None.gif)
24
![](/Images/OutliningIndicators/None.gif)
25
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
26
![](/Images/OutliningIndicators/InBlock.gif)
27
![](/Images/OutliningIndicators/InBlock.gif)
28
![](/Images/OutliningIndicators/InBlock.gif)
29
![](/Images/OutliningIndicators/InBlock.gif)
30
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
31
![](/Images/OutliningIndicators/None.gif)
32
![](/Images/OutliningIndicators/None.gif)
33
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
34
![](/Images/OutliningIndicators/InBlock.gif)
35
![](/Images/OutliningIndicators/InBlock.gif)
36
![](/Images/OutliningIndicators/InBlock.gif)
37
![](/Images/OutliningIndicators/InBlock.gif)
38
![](/Images/OutliningIndicators/InBlock.gif)
39
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
40
![](/Images/OutliningIndicators/None.gif)
41
![](/Images/OutliningIndicators/None.gif)
42
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
43
![](/Images/OutliningIndicators/InBlock.gif)
44
![](/Images/OutliningIndicators/InBlock.gif)
45
![](/Images/OutliningIndicators/InBlock.gif)
46
![](/Images/OutliningIndicators/InBlock.gif)
47
![](/Images/OutliningIndicators/InBlock.gif)
48
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
49
![](/Images/OutliningIndicators/None.gif)
50
![](/Images/OutliningIndicators/None.gif)
51
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
52
![](/Images/OutliningIndicators/InBlock.gif)
53
![](/Images/OutliningIndicators/InBlock.gif)
54
![](/Images/OutliningIndicators/InBlock.gif)
55
![](/Images/OutliningIndicators/InBlock.gif)
56
![](/Images/OutliningIndicators/InBlock.gif)
57
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
58
![](/Images/OutliningIndicators/None.gif)
59
![](/Images/OutliningIndicators/None.gif)
60
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
61
![](/Images/OutliningIndicators/InBlock.gif)
62
![](/Images/OutliningIndicators/InBlock.gif)
63
![](/Images/OutliningIndicators/InBlock.gif)
64
![](/Images/OutliningIndicators/InBlock.gif)
65
![](/Images/OutliningIndicators/InBlock.gif)
66
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
67
![](/Images/OutliningIndicators/None.gif)
68
![](/Images/OutliningIndicators/None.gif)
69
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
70
![](/Images/OutliningIndicators/InBlock.gif)
71
![](/Images/OutliningIndicators/InBlock.gif)
72
![](/Images/OutliningIndicators/InBlock.gif)
73
![](/Images/OutliningIndicators/InBlock.gif)
74
![](/Images/OutliningIndicators/InBlock.gif)
75
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
76
![](/Images/OutliningIndicators/None.gif)
77
![](/Images/OutliningIndicators/None.gif)
78
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
79
![](/Images/OutliningIndicators/InBlock.gif)
80
![](/Images/OutliningIndicators/InBlock.gif)
81
![](/Images/OutliningIndicators/InBlock.gif)
82
![](/Images/OutliningIndicators/InBlock.gif)
83
![](/Images/OutliningIndicators/InBlock.gif)
84
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
85
![](/Images/OutliningIndicators/None.gif)
86
![](/Images/OutliningIndicators/None.gif)
87
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
88
![](/Images/OutliningIndicators/InBlock.gif)
89
![](/Images/OutliningIndicators/InBlock.gif)
90
![](/Images/OutliningIndicators/InBlock.gif)
91
![](/Images/OutliningIndicators/InBlock.gif)
92
![](/Images/OutliningIndicators/InBlock.gif)
93
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
94
![](/Images/OutliningIndicators/None.gif)
95
![](/Images/OutliningIndicators/None.gif)
96
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
97
![](/Images/OutliningIndicators/InBlock.gif)
98
![](/Images/OutliningIndicators/InBlock.gif)
99
![](/Images/OutliningIndicators/InBlock.gif)
100
![](/Images/OutliningIndicators/InBlock.gif)
101
![](/Images/OutliningIndicators/InBlock.gif)
102
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
103
![](/Images/OutliningIndicators/None.gif)
104
![](/Images/OutliningIndicators/None.gif)
105
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
106
![](/Images/OutliningIndicators/InBlock.gif)
107
![](/Images/OutliningIndicators/InBlock.gif)
108
![](/Images/OutliningIndicators/InBlock.gif)
109
![](/Images/OutliningIndicators/InBlock.gif)
110
![](/Images/OutliningIndicators/InBlock.gif)
111
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
112
![](/Images/OutliningIndicators/None.gif)
113
![](/Images/OutliningIndicators/None.gif)
114
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
115
![](/Images/OutliningIndicators/InBlock.gif)
116
![](/Images/OutliningIndicators/InBlock.gif)
117
![](/Images/OutliningIndicators/InBlock.gif)
118
![](/Images/OutliningIndicators/InBlock.gif)
119
![](/Images/OutliningIndicators/InBlock.gif)
120
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
121
![](/Images/OutliningIndicators/None.gif)
122
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
123
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
124
![](/Images/OutliningIndicators/None.gif)
引用:http://www.chinaitclub.org/forums/1073/ShowPost.aspx