<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_P_Chou Go deep and Keep learning</title><subtitle type="text"/><id>http://feed.cnblogs.com/blog/u/67322/rss</id><updated>2012-05-25T07:43:40Z</updated><author><name>P_Chou</name><uri>http://www.cnblogs.com/P_Chou/</uri></author><generator>feed.cnblogs.com</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/P_Chou/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/67322/rss"/><entry><id>http://www.cnblogs.com/P_Chou/archive/2012/05/25/2518097.html</id><title type="text">.NET程序员的C情结（二）</title><summary type="text">C多文件编译、作用域和存储周期 所谓的编译，分为两个步骤：编译和链接编译有两个过程：a)预编译：处理#...的语句。#define的宏替换、#if条件编译、#include只是简单的把对应的文件内容复制到#include语句的位置b)单元源代码编译：随后编译器对每个cpp文件（在预编译阶段已经将#include的文件复制完成）单独编译成模块(.obj/.o等)，在这个过程中除了语法检查外，还要在本cpp文件中检查调用函数或引用变量是否声明过。最后生成的模块开头会有一个符号表，其中包括了本模块定义的函数或变量在本模块中的偏移量；以及本模块引用的外部变量或函数（称为unsolved symbol.</summary><published>2012-05-25T07:31:00Z</published><updated>2012-05-25T07:31:00Z</updated><author><name>P_Chou</name><uri>http://www.cnblogs.com/P_Chou/</uri></author><link rel="alternate" href="http://www.cnblogs.com/P_Chou/archive/2012/05/25/2518097.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/P_Chou/archive/2012/05/25/2518097.html"/><content type="html">&lt;p&gt;&lt;strong&gt;&lt;span style="color: #ff00ff;"&gt;&lt;span style="font-size: 15px;"&gt;&lt;span style="font-size: 16px;"&gt;C多文件编译、作用域和存储周期&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;所谓的编译，分为两个步骤：编译和链接&lt;br /&gt;&lt;span style="text-decoration: underline;"&gt;编译有两个过程：&lt;/span&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;a)预编译：处理#...的语句。#define的宏替换、#if条件编译、#include只是简单的把对应的文件内容复制到#include语句的位置&lt;/li&gt;&lt;li&gt;b)单元源代码编译：随后编译器对每个cpp文件（在预编译阶段已经将#include的文件复制完成）单独编译成模块(.obj/.o等)，在这个过程中除了语法检查外，还要在本cpp文件中检查调用函数或引用变量是否声明过。最后生成的模块开头会有一个符号表，其中包括了本模块定义的函数或变量在本模块中的偏移量；以及本模块引用的外部变量或函数（称为unsolved symbol）。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;链接就是将多个模块文件链接成最后的目标程序。链接过程中需要检查每个模块中的函数声明或变量声明的实际位置。比如模块A声明了一个全局变量global其实是在模块B中定义的，那么链&lt;br /&gt;接器需要把模块A对global的引用地址替换成模块B定义的global的地址。&lt;span style="text-decoration: underline;"&gt;链接过程又分为符号解析和重定位：&lt;/span&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;a)符号解析：解决各个模块符号表中的unsolved symbol。&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;b)重定位：模块被拼接起来之后，模块符号表中的符号地址将不再正确（因为模块拼接起来后，本来的0偏移，变成了有偏移量）。重定位的任务就是将这些模块的导出符号和导入符号的引用地址最终填写完整。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;在链接阶段出现最多的错误主要有：引用未找到、重复定义。&lt;br /&gt;引用未找到：就是在符号解析阶段没有找到unsolved symbol。针对上面global的例子，这个错误时因为链接器无法在其他任何模块中找到global这个变量的定义。&lt;br /&gt;重复定义：对于全局级别的变量，无论程序由多少个模块组成，对同一个变量可以由多个声明但只能有一个定义。重复定义的情况通常是因为不同模块定义了相同的变量。&lt;/p&gt;&lt;p&gt;另外需要补充的是：在编译模块的过程中，C++编译器为了支持重载，会对变量名或函数名进行&amp;ldquo;重命名&amp;rdquo;，比如会把fun这个名字变得面目全非，可能是fun@aBc_int_int#%$也可能是别的。extern "C" void fun(int a, int b); 则告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++的。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;&lt;br /&gt;变量按照存储周期（生命周期）可以分为：应用程序级和代码块级。在函数外定义的变量都是应用程序级的，因为他们是在编译阶段被作为代码的一部分写入到符号表中的，在模块中有固定的偏移量；代码块级变量是在函数中定义的，运行时动态在栈上创建和销毁的。&lt;br /&gt;变量按照作用域分为：外部变量、自动变量、静态变量。外部变量是程序每个模块在任何时候都能访问；自动变量与代码块级变量一样；静态变量是在本模块的任何时候都能访问的，特殊的情况是在函数里面定义的静态变量，这种变量的生命周期是整个应用程序级，但是只有定义它的函数可以访问。&lt;br /&gt;补充：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;有些人喜欢把全局变量的声明和定义放在一起，这样做，如果这个头文件被include的超过1次，在链接阶段会出现重复定义的错误。&lt;/p&gt;&lt;p&gt;extern char g_str[] = "123456";&lt;/p&gt;&lt;/li&gt;&lt;li&gt;用static修饰的全局变量，链接器对其是&amp;ldquo;不可见&amp;rdquo;的。&lt;/li&gt;&lt;li&gt;单独const修饰变量与static一样，具有模块级别的作用域；但static不能与extern共用，const可以与extern公用使得变量变成全模块可见。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;一些原则：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;头文件不要放定义，只放声明，定义放在cpp文件中&lt;/li&gt;&lt;li&gt;&lt;p&gt;static变量最好放在.cpp中，防止变量被其他模块重复定义，浪费空间&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;参考资源&lt;/p&gt;&lt;p&gt;&lt;a title="http://www.diybl.com/course/3_program/c++/cppsl/20071119/86983.html" href="http://www.diybl.com/course/3_program/c++/cppsl/20071119/86983.html" target="_blank"&gt;http://www.diybl.com/course/3_program/c++/cppsl/20071119/86983.html&lt;/a&gt;&lt;br /&gt;&lt;a title="http://hi.baidu.com/zengzhaonong/blog/item/30a47460eb8b0048eaf8f822.html" href="http://hi.baidu.com/zengzhaonong/blog/item/30a47460eb8b0048eaf8f822.html" target="_blank"&gt;http://hi.baidu.com/zengzhaonong/blog/item/30a47460eb8b0048eaf8f822.html&lt;/a&gt;&lt;br /&gt;&lt;a title="http://www.cppblog.com/woaidongmao/archive/2008/11/07/66254.aspx" href="http://www.cppblog.com/woaidongmao/archive/2008/11/07/66254.aspx" target="_blank"&gt;http://www.cppblog.com/woaidongmao/archive/2008/11/07/66254.aspx&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/P_Chou/aggbug/2518097.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/P_Chou/archive/2012/05/25/2518097.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/P_Chou/archive/2012/05/24/2517255.html</id><title type="text">.NET程序员的C情结</title><summary type="text">即将两年的.NET经验，一年的BMC经验，作为一个电子专业的人来说，心中仍然保留着对C和C++的情结。最近项目空闲之余在看Windows Programming和Windows via C/C++，并且用C++为我们开发的类库制作安装程序，虽然只是简单的Windows C程序，但是那份成就感油然而生。本文记录开发这个小程序过程中的心得：1、基于对话框的windows程序虽然说标准的Windows程序总是由用户注册的窗口类开始的，并且程序员需要自己用while来接收应用程序消息队列里的消息，并处理。但是如果想要快速开发小程序的话，可以像下面这样基于对话框的来开始程序。对话框自动建立消息循环，接收</summary><published>2012-05-24T15:05:00Z</published><updated>2012-05-24T15:05:00Z</updated><author><name>P_Chou</name><uri>http://www.cnblogs.com/P_Chou/</uri></author><link rel="alternate" href="http://www.cnblogs.com/P_Chou/archive/2012/05/24/2517255.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/P_Chou/archive/2012/05/24/2517255.html"/><content type="html">&lt;p&gt;即将两年的.NET经验，一年的BMC经验，作为一个电子专业的人来说，心中仍然保留着对C和C++的情结。&lt;/p&gt;&lt;p&gt;最近项目空闲之余在看Windows Programming和Windows via C/C++，并且用C++为我们开发的类库制作安装程序，虽然只是简单的Windows C程序，但是那份成就感油然而生。本文记录开发这个小程序过程中的心得：&lt;/p&gt;&lt;p&gt;1、基于对话框的windows程序&lt;/p&gt;&lt;p&gt;虽然说标准的Windows程序总是由用户注册的窗口类开始的，并且程序员需要自己用while来接收应用程序消息队列里的消息，并处理。但是如果想要快速开发小程序的话，可以像下面这样基于对话框的来开始程序。对话框自动建立消息循环，接收消息，我们要做的只是定义一个对话框处理函数。&lt;/p&gt;int WINAPI _tWinMain(&lt;br/&gt;HINSTANCE hInstance,&lt;br/&gt;HINSTANCE,&lt;br/&gt;PTSTR szCmdLine,&lt;br/&gt;int iCmdShow)&lt;br/&gt;{&lt;br/&gt;DialogBox(hInstance...,DlgProc);&lt;br/&gt;return 0;&lt;br/&gt;}&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;2、检查内存泄漏&lt;/p&gt;&lt;p&gt;虽说是个小程序，但是在最后执行的时候，程序返回后，windows弹出应用程序出现错误。我猜想是内存泄漏了。实在惭愧，这么一个小程序都能搞出内存泄漏。后来在网上查到一个检查内存泄漏的方法：&lt;a href="http://www.cppblog.com/Lyt/archive/2009/03/22/77517.html"&gt;http://www.cppblog.com/Lyt/archive/2009/03/22/77517.html&lt;/a&gt;&lt;/p&gt;#define _CRTDBG_MAP_ALLOC&lt;br/&gt;&lt;br/&gt;#include &amp;lt;Windows.h&amp;gt;&lt;br/&gt;#include &amp;lt;tchar.h&amp;gt;&lt;br/&gt;#include &amp;lt;stdio.h&amp;gt;&lt;br/&gt;#include &amp;lt;stdlib.h&amp;gt;&lt;br/&gt;#include &amp;lt;crtdbg.h&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;int WINAPI _tWinMain(&lt;br/&gt;HINSTANCE hInstance,&lt;br/&gt;HINSTANCE,&lt;br/&gt;PTSTR szCmdLine,&lt;br/&gt;int iCmdShow)&lt;br/&gt;{&lt;br/&gt;hInstance = hInstance;&lt;br/&gt;...&lt;br/&gt;_CrtDumpMemoryLeaks();&lt;br/&gt;return 0;&lt;br/&gt;}&lt;p&gt;这里的_CrtDumpMemoryLeaks()会在vs的Output窗口中给出内存泄漏的报告。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;3、根据对话框句柄无法获取对话框的资源ID&lt;/p&gt;&lt;p&gt;虽说GetWindowLong (hwndChild, GWL_ID) ;是通过句柄得到窗口资源ID，但是当句柄是对话框句柄是是无效的。另外相关函数如下：&lt;/p&gt;id = GetDlgCtrlID (hwndChild);&lt;br/&gt;&lt;br/&gt;hwndChild = GetDlgItem(hwndParent, id); &lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;4、GetWindowRect(hDlg,&amp;amp;rect);返回窗口的大小，返回的rect中的right和bottom竟然是窗口的width和height，真不知道为什么取名字要那么歧义。。GetSystemMetrics(SM_CXFULLSCREEN)和GetSystemMetrics(SM_CYFULLSCREEN)获得显示屏的像素信息。&lt;/p&gt;&lt;p&gt;MoveWindow(hDlg,inewLeft,inewTop,width,height,FALSE);可以设置窗口的位置。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;5、设置checkbox：&lt;/p&gt;SendMessage(hwnd, BM_SETCHECK, BST_CHECKED, 0);&lt;br/&gt;SendMessage(hwnd, BM_SETCHECK, BST_UNCHECKED, 0);&lt;br/&gt;SendMessage(hwnd, BM_GETCHECK, 0, 0));&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;6、FindFirstFile的目标文件可以有通配符，但是必须是全路径。还可用来判断路径是否存在（FILE_ATTRIBUTE_DIRECTORY）&lt;/p&gt;WIN32_FIND_DATA FindFileData;&lt;br/&gt;HANDLE hFind = FindFirstFile(tmp,&amp;amp;FindFileData);&lt;br/&gt;if(INVALID_HANDLE_VALUE == hFind){&lt;br/&gt;&lt;br/&gt;}&lt;br/&gt;else{&lt;br/&gt;FindClose(hFind);&lt;br/&gt;}&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;7、字符串处理&lt;/p&gt;&lt;p&gt;_tcscpy、_tcscat、_tcslen、_tcscmp、_tcsstr&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;8、注册表操作，如果要设置环境变量，必须直接在注册表中设置&lt;/p&gt;RegOpenKeyEx(HKEY_LOCAL_MACHINE&lt;br/&gt;,TEXT("SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment\\")&lt;br/&gt;,0&lt;br/&gt;,KEY_ALL_ACCESS&lt;br/&gt;,&amp;amp;hEnvKEY)&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;//读取键值的时候可以先读一次，获取需要的缓冲区长度和数据类型&lt;br/&gt;RegQueryValueEx(hEnvKEY,TEXT("Path"),NULL,&amp;amp;dwType,NULL,&amp;amp;cbData)&lt;br/&gt;RegQueryValueEx(hEnvKEY,TEXT("Path"),NULL,&amp;amp;dwType,lpbOrignalEnvPath,&amp;amp;cbData);&lt;br/&gt;&lt;br/&gt;//返回的LPBYTE其实已经是字符数组了，直接强转就可以了&lt;br/&gt;TCHAR* szOrignalEnvPath = (TCHAR*)lpbOrignalEnvPath;&lt;br/&gt;&lt;br/&gt;#ifdef _UNICODE,UNICODE&lt;br/&gt;LSTATUS status = RegSetValueEx(hEnvKEY,TEXT("Path"),NULL,dwType,(LPBYTE)szTargetEnvPath,_tcslen(szTargetEnvPath)*2+2);&lt;br/&gt;#else&lt;br/&gt;LSTATUS status = RegSetValueEx(hEnvKEY,TEXT("Path"),NULL,dwType,(LPBYTE)szTargetEnvPath,_tcslen(szTargetEnvPath)+1);&lt;br/&gt;&lt;br/&gt;//设置完后可以通过下面方法，通知系统进程，环境变量变化了。这也提示我们在编写应用程序的时候有时需要处理WM_SETTINGCHANGE的意义&lt;br/&gt;SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)TEXT("Environment"));&lt;p&gt;9、获取系统变量和环境变量&lt;/p&gt;//同样可以通过调用两次来获取缓冲区长度&lt;br/&gt;dwSize = GetEnvironmentVariable(TEXT("HOMEDRIVE"),NULL,0);&lt;br/&gt;szHomeDrive = new TCHAR[dwSize+1];&lt;br/&gt;GetEnvironmentVariable(TEXT("HOMEDRIVE"),szHomeDrive,dwSize+1);&lt;p&gt;10、C多文件编译、作用域和存储周期&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;a href="http://www.cnblogs.com/P_Chou/archive/2012/05/25/2518097.html"&gt;http://www.cnblogs.com/P_Chou/archive/2012/05/25/2518097.html&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/P_Chou/aggbug/2517255.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/P_Chou/archive/2012/05/24/2517255.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/P_Chou/archive/2012/05/20/2510621.html</id><title type="text">BMC AR 配置AREA LDAP</title><summary type="text">AR在外部认证方面使用的是插件的方式，AR在安装时自带安装插件服务，并且会安装一些内置的插件：比如AREA、ARDBC等。AR主服务与插件的交互主要通过插件服务完成。同时，AR的插件服务拥有完整的API，可以由第三方通过接口API来开发自定义的插件。在最近的一次部署AREA LDAP过程中，发现配置始终无法生效。经过艰苦卓绝的排查工作，最终找到了原因。首先看下AREA如何配置：Server Information-&gt;EA配置是很简单，但问题是不起作用，以下是艰难的排查经历：1. 在AR Server上安装Network Monitor检查是否有LDAP的请求包，发现没有；如果在这一步发现</summary><published>2012-05-20T08:22:00Z</published><updated>2012-05-20T08:22:00Z</updated><author><name>P_Chou</name><uri>http://www.cnblogs.com/P_Chou/</uri></author><link rel="alternate" href="http://www.cnblogs.com/P_Chou/archive/2012/05/20/2510621.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/P_Chou/archive/2012/05/20/2510621.html"/><content type="html">&lt;p&gt;AR在外部认证方面使用的是插件的方式，AR在安装时自带安装插件服务，并且会安装一些内置的插件：比如AREA、ARDBC等。AR主服务与插件的交互主要通过插件服务完成。同时，AR的插件服务拥有完整的API，可以由第三方通过接口API来开发自定义的插件。&lt;/p&gt;&lt;p&gt;在最近的一次部署AREA LDAP过程中，发现配置始终无法生效。经过艰苦卓绝的排查工作，最终找到了原因。&lt;/p&gt;&lt;p&gt;首先看下AREA如何配置：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201205/201205201620223966.jpg"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="clip_image002" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201205/201205201620253079.jpg" alt="clip_image002" width="338" height="236" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Server Information-&amp;gt;EA&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201205/201205201620268063.jpg"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="clip_image004" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201205/201205201620264682.jpg" alt="clip_image004" width="355" height="156" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;配置是很简单，但问题是不起作用，以下是艰难的排查经历：&lt;/p&gt;&lt;p&gt;1. 在AR Server上安装Network Monitor检查是否有LDAP的请求包，发现没有；如果在这一步发现有请求包的话，估计是Bind User的配置有问题，不是用户名不具备查询权限，就是密码配错了，过期了什么的。&lt;/p&gt;&lt;p&gt;2. 没有LDAP数据包，然后我就检查了一下ar.cfg文件中配置的LDAP信息是否跟界面上设置的一致，反复检查发现也是没问题的。曾经有一次，发现有一条什么LDAP-Hub的配置，果断删除后就可以了。查文档，说是AREA LDAP支持配多个LDAP配置，是以Hub的方式的，猜测是因为，之前配过多个AREA LDAP，但配置文件没有更新干净的缘故。&lt;/p&gt;&lt;p&gt;3. 初步检查ar.cfg没有什么问题。于是查文档，打开ar-plugin的log功能，重启ar，发现area这个plugin压根没有加载。于是查看ar.cfg中关于plug-in的配置，发现竟然没有配areaplugin.dll！果断手动加上一条记录，重启ar即可。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/P_Chou/aggbug/2510621.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/P_Chou/archive/2012/05/20/2510621.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/P_Chou/archive/2012/03/25/2416357.html</id><title type="text">BMC AR Mid-Tier使用其他版本的Tomcat</title><summary type="text">AR 7.6.04版本的Mid-Tier默认会安装Tomcat6.0，并以此作为web server使用，但是在项目实施中发现6.0版本并不稳定，表现为偶尔卡死，Atrium Explorer的图标经常显示不全，Tomcat内存占用始终1G左右。后来尝试使用Tomcat5.5版本，内存占用下降到350M左右，Atrium Explorer的图标也基本稳定显示了，下面简单介绍一下如何更换Tomcat...</summary><published>2012-03-25T01:40:00Z</published><updated>2012-03-25T01:40:00Z</updated><author><name>P_Chou</name><uri>http://www.cnblogs.com/P_Chou/</uri></author><link rel="alternate" href="http://www.cnblogs.com/P_Chou/archive/2012/03/25/2416357.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/P_Chou/archive/2012/03/25/2416357.html"/><content type="html">&lt;p&gt;AR 7.6.04版本的Mid-Tier默认会安装Tomcat6.0，并以此作为web server使用，但是在项目实施中发现6.0版本并不稳定，表现为偶尔卡死，Atrium Explorer的图标经常显示不全，Tomcat内存占用始终1G左右。后来尝试使用Tomcat5.5版本，内存占用下降到350M左右，Atrium Explorer的图标也基本稳定显示了，下面简单介绍一下如何更换Tomcat版本，以及随后如何手动配置：  &lt;p&gt;1. 如果已经使用Tomcat6.0作为web server，可以先启用BMC AR的卸载程序并且选择卸载Mid-Tier。（注意：备份更改过的页面或式样等）  &lt;p&gt;2. 在卸载Mid-Tier的过程中选择同时卸载Tomcat，OK，世界清静了  &lt;p&gt;3. 下载对应版本的Tomcat，以5.5版本为例：&lt;a href="http://apache.etoak.com/tomcat/tomcat-5/v5.5.35/bin/"&gt;http://apache.etoak.com/tomcat/tomcat-5/v5.5.35/bin/&lt;/a&gt;  &lt;p&gt;4. 我们使用的是windows环境，双击即可安装Tomcat，JRE在之前已经安装完毕。安装完毕之后无需配置  &lt;p&gt;5. 安装Mid-Tier，在安装过程中注意选择已有的Tomcat版本，并且配置指向的AR Server。  &lt;p&gt;6. 手动配置Mid-Tier。下面两个地方要手动配置一下，配置好之后，刷新一下Mid-Tier，重启Tomcat  &lt;p&gt;a)认证服务器要配置：  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201203/201203250939513429.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image002" border="0" alt="clip_image002" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201203/201203250939513953.jpg" width="341" height="76"&gt;&lt;/a&gt;  &lt;p&gt;b)AR 管理员密码要重新配一下，用Demo的密码即可  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201203/201203250939514476.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image004" border="0" alt="clip_image004" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201203/201203250939515000.jpg" width="338" height="114"&gt;&lt;/a&gt;  &lt;p&gt;7. 测试访问一下，如果登录页404的话，检查下~\Tomcat 5.5\conf\Catalina\localhost\arsys.xml文件是否存在。这个文件是web server的url routing配置  &lt;p&gt;8. 有时，tomcat会对jsp页面预编译，事实上，理论上说即使预编译，只要jsp文件改了，编译应该重新进行。但是，在一次部署过程中，我发现tomcat始终不对jsp重新编译（估计它是以时间先后判断文件的新旧的）。可以手动“刷新”一下。在C:\Program Files\Apache Software Foundation\Tomcat 5.5\work\Catalina\localhost\arsys\org\apache\jsp\shared下删除jar和class即可。  &lt;p&gt;9.如果在安装mid-tier过程中忘了选择已有Tomcat，那么需要在tomcat中加入一个ar.xml配置文件，以便把arsys路径映射到mid-tier的路径下  &lt;img src="http://www.cnblogs.com/P_Chou/aggbug/2416357.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/P_Chou/archive/2012/03/25/2416357.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/P_Chou/archive/2011/08/30/customize-razor-template-2.html</id><title type="text">利用Razor在ASP.NET MVC中的实现，自定义视图引擎框架（2）</title><summary type="text">ASP.NET MVC3开始使用Razor作为其视图引擎，取代了原来ASP.NET Web Form引擎。笔者最近研究了一下MVC3对Razor的实现，从中找到一个切入点，能够让我们自定义基于Razor语法的视图解析引擎。在项目里面可以用于诸如邮件模板定制等方面。目前，只是一个demo版本，还在进一步完善中。CodePlex : http://codeof.codeplex.com/SourceControl/list/changesets 其中的RazorEx目前支持的功能：1.支持Razor语法（基本的@语法）的模板文件解析2.支持Layout / Renderbody语法3.支持类似as</summary><published>2011-08-30T07:10:00Z</published><updated>2011-08-30T07:10:00Z</updated><author><name>P_Chou</name><uri>http://www.cnblogs.com/P_Chou/</uri></author><link rel="alternate" href="http://www.cnblogs.com/P_Chou/archive/2011/08/30/customize-razor-template-2.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/P_Chou/archive/2011/08/30/customize-razor-template-2.html"/><content type="html">&lt;p&gt;ASP.NET MVC3开始使用Razor作为其视图引擎，取代了原来ASP.NET Web Form引擎。笔者最近研究了一下MVC3对Razor的实现，从中找到一个切入点，能够让我们自定义基于Razor语法的视图解析引擎。在项目里面可以用于诸如邮件模板定制等方面。目前，只是一个demo版本，还在进一步完善中。CodePlex : &lt;a href="http://codeof.codeplex.com/SourceControl/list/changesets"&gt;http://codeof.codeplex.com/SourceControl/list/changesets&lt;/a&gt; 其中的RazorEx&lt;/p&gt;&lt;p&gt;目前支持的功能：&lt;/p&gt;&lt;p&gt;&lt;em&gt;1.支持Razor语法（基本的@语法）的模板文件解析&lt;br /&gt;2.支持Layout / Renderbody语法&lt;br /&gt;3.支持类似asp.net 动态编译机制，在程序运行期间，如果模板文件变了，无需重新编译&lt;br /&gt;4.支持名字空间引用配置&lt;br /&gt;5.支持复杂的程序集引用关系&lt;/em&gt;&lt;/p&gt;&lt;p&gt;在&lt;a href="http://www.cnblogs.com/P_Chou/archive/2011/08/23/customize-razor-template-1.html" target="_blank"&gt;利用Razor在ASP.NET MVC中的实现，自定义视图引擎框架（1）&lt;/a&gt;中，介绍了如何利用微软实现的&lt;strong&gt;System.Web.Razor&lt;/strong&gt;来解析基于Razor语法的模板，最后得到一个编译单元或者源码。本文介绍如何在代码中对编译单元或者源码进行动态编译，并执行。&lt;/p&gt;&lt;p&gt;应该没有比动态编译更灵活的了，它允许我们动态的创建程序代码并编译执行。尽管它灵活，但是实现复杂，并且效率不高，不到万不得已不要考虑。而在这个case中却不得不用这种方式，因为模板是用户创建的，我们永远不可能预知：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201108/201108301506128126.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201108/201108301506133699.png" width="426" height="87" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;在.net中，&lt;strong&gt;System.CodeDom.Compiler.CodeDomProvider&lt;/strong&gt;提供了将一个或多个源程序或编译单元编译成程序集的方法。在.net4.0中&lt;strong&gt;Microsoft.CSharp.CSharpCodeProvider&lt;/strong&gt;继承了上面这个类，并给予了实现。有了&lt;strong&gt;CSharpCodeProvider&lt;/strong&gt;，编译一个动态的程序集十分容易：&lt;/p&gt;CSharpCodeProvider provider = new CSharpCodeProvider();&lt;br/&gt;            CompilerParameters c_options = new CompilerParameters();&lt;br/&gt;            c_options.IncludeDebugInformation = false;&lt;br/&gt;            c_options.GenerateExecutable = false;&lt;br/&gt;            c_options.GenerateInMemory = true;&lt;br/&gt;            c_options.ReferencedAssemblies.Add( "System.dll" );&lt;br/&gt;            CompilerResults results = provider.CompileAssemblyFromSource(c_options, code);&lt;p&gt;上面的代码段，实例化了一个&lt;strong&gt;CSharpCodeProvider&lt;/strong&gt;以及一个&lt;strong&gt;CompilerParameters&lt;/strong&gt;。前者用于编译，后者用于指定编译时的一些选项，如上面的代码的设置。最后调用&lt;strong&gt;CompileAssemblyFromSource&lt;/strong&gt;，传入编译选项和源代码文本即可。另外&lt;strong&gt;CSharpCodeProvider&lt;/strong&gt;的&lt;strong&gt;CompileAssemblyFromDom&lt;/strong&gt;重载，可以接受编译选项对象和&lt;/p&gt;&lt;p&gt;&lt;strong&gt;CodeCompileUnit&lt;/strong&gt;对象作为参数。在上一篇中，我们知道&lt;strong&gt;RazorTemplateEngine.GenerateCode&lt;/strong&gt;方法返回的刚好是包含了&lt;strong&gt;CodeCompileUnit&lt;/strong&gt;的对象，所以我们将使用&lt;strong&gt;CompileAssemblyFromDom方法&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;在返回值&lt;strong&gt;CompilerResults.CompiledAssembly&lt;/strong&gt;中，我们可以访问到编译结果的Assembly对象，再结合反射即可执行编译代码。另外，在编译过程中，如果编译失败将抛出异常。&lt;/p&gt;&lt;p&gt;在引擎的开发过程中，除了上一篇和上述需要知道的基本内容外，分别有以下问题需要解决：&lt;/p&gt;&lt;p&gt;&lt;span style="text-decoration: underline;"&gt;1、动态编译时需要知道引用哪些dll，否则将无法编译成功。&lt;/span&gt;比如在模板里面我引用了一个复杂对象，这个对象定义显然不在引擎的程序集中，可能是用户自己的程序集，或是用户程序集引用的程序集。这就带来了一个问题，在编译时我们如何知道要引用哪些程序集？我用了一个比较笨的方案：从GetCallingAssembly开始，把相互依赖的程序集遍历一遍，并且全部在编译时引用。代码中的&lt;strong&gt;AssemblyReferenceResolver&lt;/strong&gt;就实现了这个功能；&lt;/p&gt;&lt;p&gt;&lt;span style="text-decoration: underline;"&gt;2、如上面的问题，编译的时候，源码需要有正确的命名空间的引用，否则即使引用的程序集，还是不能编译成功。&lt;/span&gt;为此，模仿mvc的实现，设计了一个configuration，添加下面这样的配置文件即可：&lt;/p&gt;&amp;lt;configuration&amp;gt;&lt;br/&gt;  &amp;lt;configSections&amp;gt;&lt;br/&gt;    &amp;lt;section name="RazorTemplateEngineImportNamespace" &lt;br/&gt;             type="RazorTemplateEngine.ImportNamespaceResolver,RazorTemplateEngine"/&amp;gt;&lt;br/&gt;  &amp;lt;/configSections&amp;gt;&lt;br/&gt;  &amp;lt;RazorTemplateEngineImportNamespace&amp;gt;&lt;br/&gt;    &amp;lt;add namespace="ModelTest"/&amp;gt;&lt;br/&gt;    &amp;lt;add namespace="System.Collections.Generic"/&amp;gt;&lt;br/&gt;  &amp;lt;/RazorTemplateEngineImportNamespace&amp;gt;&lt;br/&gt;&amp;lt;/configuration&amp;gt;&lt;p&gt;&lt;span style="text-decoration: underline;"&gt;3、一个模板对应一个类，也就对应一个程序集，如果反复解析模板会反复编译，这样会很大程度上影响效率。&lt;/span&gt;解决方案是使用缓存：将编译过的dll和模板文件存成字典，如果已经编译过了并且模板文件的最后更新时间不晚于dll的创建时间，则直接返回之前编译的程序集；否则就进行编译。代码中的&lt;strong&gt;DynamicAssemblyCache&lt;/strong&gt;类就实现这个功能；&lt;/p&gt;&lt;p&gt;&lt;span style="text-decoration: underline;"&gt;4、如何实现模板嵌套。&lt;/span&gt;在基类__TemplatePage中加入下面属性和方法：&lt;/p&gt;        private string _layout;&lt;br/&gt;&lt;br/&gt;        public virtual string Layout {&lt;br/&gt;            get { return _layout; }&lt;br/&gt;            set {&lt;br/&gt;                _layout = value;&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        public string ChildBody { get; set; }&lt;br/&gt;&lt;br/&gt;        public virtual string RenderBody()&lt;br/&gt;        {&lt;br/&gt;            if(ChildBody != null)&lt;br/&gt;                return ChildBody;&lt;br/&gt;            return string.Empty;&lt;br/&gt;        }&lt;p&gt;这样类似Layout=&amp;rdquo;&amp;rdquo;&amp;nbsp; @RenderBody的语法就可以通过编译。配合递归的Execute即可实现。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;项目现已实现基本的功能，我打算过一段试用期过后Release一个版本。源码在上面的CodePlex上，代码不多，还有待重构，有兴趣的同仁可以和我讨论，希望能实现一个健壮的引擎。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/P_Chou/aggbug/2159611.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/P_Chou/archive/2011/08/30/customize-razor-template-2.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/P_Chou/archive/2011/08/23/customize-razor-template-1.html</id><title type="text">利用Razor在ASP.NET MVC中的实现，自定义视图引擎框架（1）</title><summary type="text">ASP.NET MVC3开始使用Razor作为其视图引擎，取代了原来ASP.NET Web Form引擎。笔者最近研究了一下MVC3对Razor的实现，从中找到一个切入点，能够让我们自定义基于Razor语法的视图解析引擎。在项目里面可以用于诸如邮件模板定制等方面。目前，只是一个demo版本，还在进一步完善中。CodePlex :http://codeof.codeplex.com/SourceControl/list/changesets其中的RazorEx先来看看效果：假设有一个模板文件Action1.cshtml如下：@{ string str = "Hello world!&amp;q</summary><published>2011-08-23T10:06:00Z</published><updated>2011-08-23T10:06:00Z</updated><author><name>P_Chou</name><uri>http://www.cnblogs.com/P_Chou/</uri></author><link rel="alternate" href="http://www.cnblogs.com/P_Chou/archive/2011/08/23/customize-razor-template-1.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/P_Chou/archive/2011/08/23/customize-razor-template-1.html"/><content type="html">&lt;p&gt;ASP.NET MVC3开始使用Razor作为其视图引擎，取代了原来ASP.NET Web Form引擎。笔者最近研究了一下MVC3对Razor的实现，从中找到一个切入点，能够让我们自定义基于Razor语法的视图解析引擎。在项目里面可以用于诸如邮件模板定制等方面。目前，只是一个demo版本，还在进一步完善中。CodePlex :&amp;nbsp;&lt;a href="http://codeof.codeplex.com/SourceControl/list/changesets"&gt;http://codeof.codeplex.com/SourceControl/list/changesets&lt;/a&gt;&amp;nbsp;其中的RazorEx&lt;/p&gt;&lt;p&gt;先来看看效果：&lt;/p&gt;&lt;p&gt;假设有一个模板文件Action1.cshtml如下：&lt;/p&gt;@{&lt;br/&gt;string str = "Hello world!";&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;&amp;lt;html&amp;gt;&lt;br/&gt;&amp;lt;head&amp;gt;@TemplateData["Title"]&amp;lt;/head&amp;gt;&lt;br/&gt;&amp;lt;body&amp;gt;&lt;br/&gt;&amp;lt;h1&amp;gt;@str&amp;lt;/h1&amp;gt;&lt;br/&gt;    &amp;lt;table&amp;gt;&lt;br/&gt;    @foreach (var s in TemplateData["Students"] as IEnumerable&amp;lt;RazorLab.Student&amp;gt;)&lt;br/&gt;    {&lt;br/&gt;        &amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;@s.ID&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;@s.Name&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br/&gt;    }&lt;br/&gt;    &amp;lt;/table&amp;gt;&lt;br/&gt;&amp;lt;/body&amp;gt;&lt;br/&gt;&amp;lt;/html&amp;gt;&lt;p&gt;编写C#代码如下：&lt;/p&gt;    public class TestController : TemplateController&lt;br/&gt;    {&lt;br/&gt;        public ActionResult Action1()&lt;br/&gt;        {&lt;br/&gt;            TemplateData["Title"] = "Hello";&lt;br/&gt;            TemplateData["Students"] = new List&amp;lt;Student&amp;gt; { &lt;br/&gt;                new Student{ID = 0 ,Name = "Parker Zhou"},&lt;br/&gt;                new Student{ID = 1 ,Name = "Sue Kuang"}&lt;br/&gt;            };&lt;br/&gt;            return Template(@"D:\Project\C#\MyMvc\RazorLab\Template\Test\Action1.cshtml");&lt;br/&gt;        }&lt;br/&gt;    }&lt;p&gt;最终得到的Html如下：&lt;/p&gt;&amp;lt;html&amp;gt;&lt;br/&gt;&amp;lt;head&amp;gt;Hello&amp;lt;/head&amp;gt;&lt;br/&gt;&amp;lt;body&amp;gt;&lt;br/&gt;&amp;lt;h1&amp;gt;Hello world!&amp;lt;/h1&amp;gt;&lt;br/&gt;    &amp;lt;table&amp;gt;&lt;br/&gt;        &amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Parker Zhou&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br/&gt;        &amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Sue Kuang&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br/&gt;    &amp;lt;/table&amp;gt;&lt;br/&gt;&amp;lt;/body&amp;gt;&lt;br/&gt;&amp;lt;/html&amp;gt;&lt;p&gt;我设计了一个类似MVC的模式，使用户可以通过Controller向View中传递数据，利用Razor解析模板，并填入数据。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;原理其实很简单，类似ASP.NET的做法，把模板读入后&lt;strong&gt;解析成类&lt;/strong&gt;，再和静态的基类一起&lt;strong&gt;动态编译&lt;/strong&gt;成dll，&lt;strong&gt;反射&lt;/strong&gt;其中的代码，最后输出Html。在这个过程中，反射自然不用多说，关键是如何解析和动态编译，这篇我将介绍如何利用微软的源码来完成解析。由于我自己代码还没有完善，还在单元测试阶段，所以先不发上来献丑了。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span size="5" style="font-size: large;"&gt;System.Web.Razor&lt;/span&gt;&lt;/p&gt;&lt;p&gt;在MVC3的源码中，在这里要关注的是System.Web.Razor这个dll。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201108/201108231756573076.jpg"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="UEW]S]EKMI)SSCFDVHD@V8B" border="0" alt="UEW]S]EKMI)SSCFDVHD@V8B" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201108/201108231756588059.jpg" width="274" height="397" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;它用C#的方式实现了Razor的解析并能生成对应的&lt;strong&gt;编译单元&lt;/strong&gt;。所谓编译单元是.NET中的一个类&lt;a href="http://msdn.microsoft.com/zh-cn/library/system.codedom.codecompileunit(v=vs.80).aspx" target="_blank"&gt;CodeCompileUnit&lt;/a&gt;,这个类以CodeDom的方式保存了源码结构，可以被用于产生代码，或者动态编译。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span size="5" style="font-size: large;"&gt;System.Web.Razor.RazorTemplateEngine&lt;/span&gt;&lt;/p&gt;&lt;p&gt;在这个Project下最重要的类是System.Web.Razor.RazorTemplateEngine，这也是我们能够直接利用的类。其中&lt;strong&gt;GenerateCode&lt;/strong&gt;方法能将读入的模板解析成&lt;strong&gt;编译单元&lt;/strong&gt;，它有多个重载。下面是Action1.cshtml经过解析后生成的类。其中类名，基类名，名字空间，引用的名字空间等是可以自定义的：&lt;/p&gt;namespace @__TemplatePage.Namespace&lt;br/&gt;{&lt;br/&gt;    using RazorTemplateEngine;&lt;br/&gt;    using System.Collections.Generic;&lt;br/&gt;    public class @__TemplateInherit : @__TemplatePage&lt;br/&gt;    {&lt;br/&gt;#line hidden&lt;br/&gt;        public @__TemplateInherit()&lt;br/&gt;        {&lt;br/&gt;        }&lt;br/&gt;        public override void Execute()&lt;br/&gt;        {&lt;br/&gt;            string str = "Hello world!";&lt;br/&gt;            WriteLiteral("\r\n&amp;lt;html&amp;gt;\r\n&amp;lt;head&amp;gt;");&lt;br/&gt;            Write(TemplateData["Title"]);&lt;br/&gt;            WriteLiteral("&amp;lt;/head&amp;gt;\r\n&amp;lt;body&amp;gt;\r\n\t&amp;lt;h1&amp;gt;");&lt;br/&gt;            Write(str);&lt;br/&gt;            WriteLiteral("&amp;lt;/h1&amp;gt;\r\n    &amp;lt;table&amp;gt;\r\n");&lt;br/&gt;            foreach (var s in TemplateData["Students"] as IEnumerable&amp;lt;RazorLab.Student&amp;gt;)&lt;br/&gt;            {&lt;br/&gt;                WriteLiteral("        &amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;");&lt;br/&gt;                Write(s.ID);&lt;br/&gt;                WriteLiteral("&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;");&lt;br/&gt;                Write(s.Name);&lt;br/&gt;                WriteLiteral("&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;\r\n");&lt;br/&gt;            }&lt;br/&gt;            WriteLiteral("    &amp;lt;/table&amp;gt;\r\n&amp;lt;/body&amp;gt;\r\n&amp;lt;/html&amp;gt;");&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;生成的C#代码实际上十分容易理解。上述C#代码可以通过&lt;a href="http://msdn.microsoft.com/zh-cn/library/microsoft.csharp.csharpcodeprovider(v=vs.80).aspx" target="_blank"&gt;CSharpCodeProvider&lt;/a&gt;从&lt;a href="http://msdn.microsoft.com/zh-cn/library/system.codedom.codecompileunit(v=vs.80).aspx" target="_blank"&gt;CodeCompileUnit&lt;/a&gt;得到。（顺便提一下，&lt;a href="http://msdn.microsoft.com/zh-cn/library/microsoft.csharp.csharpcodeprovider(v=vs.80).aspx" target="_blank"&gt;CSharpCodeProvider&lt;/a&gt;只能从&lt;a href="http://msdn.microsoft.com/zh-cn/library/system.codedom.codecompileunit(v=vs.80).aspx" target="_blank"&gt;CodeCompileUnit&lt;/a&gt;得到Code，但反过来没有实现！我查了不少资料都没有，有兴趣要结合NRefactory实现一下）可以想象，我们要做的就是实现一个它的基类@__TemplatePage ，实现其中的TemplateData，WriteLiteral，Write，Execute等，使得在之后的编译中顺利编译成功。下面是我对基类的实现：&lt;/p&gt;using System;&lt;br/&gt;using System.Collections.Generic;&lt;br/&gt;using System.Text;&lt;br/&gt;&lt;br/&gt;namespace RazorTemplateEngine&lt;br/&gt;{&lt;br/&gt;    /// &amp;lt;summary&amp;gt;&lt;br/&gt;    /// This is the base class which the dynamic generated class will inherit from,&lt;br/&gt;    /// and the TemplatePageRazorHost define the class name, see TemplatePageRazorHost.DefaultBaseClass&lt;br/&gt;    /// for more infomation&lt;br/&gt;    /// &amp;lt;/summary&amp;gt;&lt;br/&gt;    public class __TemplatePage&lt;br/&gt;    {&lt;br/&gt;        /// &amp;lt;summary&amp;gt;&lt;br/&gt;        /// Store the parse result&lt;br/&gt;        /// &amp;lt;/summary&amp;gt;&lt;br/&gt;        private StringBuilder resultBuilder = new StringBuilder();&lt;br/&gt;        /// &amp;lt;summary&amp;gt;&lt;br/&gt;        /// Store the data passed from controller&lt;br/&gt;        /// &amp;lt;/summary&amp;gt;&lt;br/&gt;        private Dictionary&amp;lt;string, object&amp;gt; templateData = new Dictionary&amp;lt;string, object&amp;gt;();&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;        public StringBuilder ParseResult&lt;br/&gt;        {&lt;br/&gt;            get { return resultBuilder; }&lt;br/&gt;        }&lt;br/&gt;        public Dictionary&amp;lt;string, object&amp;gt; TemplateData&lt;br/&gt;        {&lt;br/&gt;            get { return templateData; }&lt;br/&gt;            set { templateData = value; }&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        /// &amp;lt;summary&amp;gt;&lt;br/&gt;        /// override  by the dymanic generated class, the method name is defined in &lt;br/&gt;        /// GeneratedClassContext.DefaultExecuteMethodName in System.Web.Razor&lt;br/&gt;        /// &amp;lt;/summary&amp;gt;&lt;br/&gt;        public virtual void Execute() { }&lt;br/&gt;        /// &amp;lt;summary&amp;gt;&lt;br/&gt;        /// implement method in the dymanic generated class , the method name is defined in &lt;br/&gt;        /// GeneratedClassContext.DefaultWriteLiteralMethodName in System.Web.Razor&lt;br/&gt;        /// &amp;lt;/summary&amp;gt;&lt;br/&gt;        /// &amp;lt;param name="literal"&amp;gt;&amp;lt;/param&amp;gt;&lt;br/&gt;        public virtual void WriteLiteral(string literal)&lt;br/&gt;        {&lt;br/&gt;            resultBuilder.Append(literal);&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        /// &amp;lt;summary&amp;gt;&lt;br/&gt;        /// implement method in the dymanic generated class , the method name is defined in &lt;br/&gt;        /// GeneratedClassContext.DefaultWriteMethodName in System.Web.Razor&lt;br/&gt;        /// &amp;lt;/summary&amp;gt;&lt;br/&gt;        /// &amp;lt;param name="obj"&amp;gt;&amp;lt;/param&amp;gt;&lt;br/&gt;        public virtual void Write(object obj)&lt;br/&gt;        {&lt;br/&gt;            resultBuilder.Append(obj.ToString());&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span size="5" style="font-size: large;"&gt;System.Web.Razor.RazorEngineHost&lt;/span&gt;&lt;/p&gt;&lt;p&gt;对于&lt;strong&gt;RazorTemplateEngine&lt;/strong&gt;，生成类名，基类名，名字空间，引用的名字空间等都是有默认值，但我们可以改变这种默认设置，通过&lt;strong&gt;RazorEngineHost&lt;/strong&gt;这个类，这个类中的许多属性都是virtual的，可以通过继承的方式override，这些属性可以改变&lt;strong&gt;RazorTemplateEngine&lt;/strong&gt;的行为。因此，我们要做的就是实现一个继承自&lt;strong&gt;RazorEngineHost&lt;/strong&gt;的类，重写其中必要的属性，以实现上述的自定义行为。最后&lt;strong&gt;RazorEngineHost&lt;/strong&gt;的&lt;strong&gt;PostProcessGeneratedCode&lt;/strong&gt;方法将在RazorTemplateEngine.GenerateCode方法返回结果之后，提供一个再次修改CodeDom的机会，比如加一些额外的名字空间引用。&lt;/p&gt;&lt;p&gt;有了上面的理解，我们要做到其实只剩下下面的示例代码了：&lt;/p&gt;&lt;p&gt;实现RazorEngineHost的一个继承：&lt;/p&gt;public class TestRazorEnginHost : RazorEngineHost&lt;br/&gt;    {&lt;br/&gt;        public TestRazorEnginHost() : base(new CSharpRazorCodeLanguage())&lt;br/&gt;        {&lt;br/&gt;        }&lt;br/&gt;        public override string DefaultBaseClass&lt;br/&gt;        {&lt;br/&gt;            get&lt;br/&gt;            {&lt;br/&gt;                return "PageBase";&lt;br/&gt;            }&lt;br/&gt;            set&lt;br/&gt;            {&lt;br/&gt;                base.DefaultBaseClass = value;&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        public override string DefaultClassName&lt;br/&gt;        {&lt;br/&gt;            get&lt;br/&gt;            {&lt;br/&gt;                return "PageInherit";&lt;br/&gt;            }&lt;br/&gt;            set&lt;br/&gt;            {&lt;br/&gt;                base.DefaultClassName = value;&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        public override void PostProcessGeneratedCode(System.CodeDom.CodeCompileUnit codeCompileUnit, System.CodeDom.CodeNamespace generatedNamespace, System.CodeDom.CodeTypeDeclaration generatedClass, System.CodeDom.CodeMemberMethod executeMethod)&lt;br/&gt;        {&lt;br/&gt;            base.PostProcessGeneratedCode(codeCompileUnit, generatedNamespace, generatedClass, executeMethod);&lt;br/&gt;            generatedNamespace.Imports.Add(new CodeNamespaceImport("RazorLab"));&lt;br/&gt;        }&lt;br/&gt;    }&lt;p&gt;下面的测试代码用于把一个基于Razor语法的模板C:\Test.cshtml变成C#代码：&lt;/p&gt;TestRazorEnginHost host = new TestRazorEnginHost();&lt;br/&gt;            System.Web.Razor.RazorTemplateEngine rte = new System.Web.Razor.RazorTemplateEngine(host); &lt;br/&gt;&lt;br/&gt;            FileStream fs = new FileStream(@"C:\Test.cshtml",FileMode.Open);&lt;br/&gt;            StreamReader sr = new StreamReader(fs);&lt;br/&gt;            var codeDomWrap = rte.GenerateCode(sr);&lt;br/&gt;            CSharpCodeProvider provider = new CSharpCodeProvider();&lt;br/&gt;&lt;br/&gt;            CodeGeneratorOptions options = new CodeGeneratorOptions();&lt;br/&gt;            options.BlankLinesBetweenMembers = false;&lt;br/&gt;            options.IndentString = "\t";&lt;br/&gt;&lt;br/&gt;            StringWriter sw = new StringWriter();&lt;br/&gt;            string code = string.Empty;&lt;br/&gt;            try&lt;br/&gt;            {&lt;br/&gt;                provider.GenerateCodeFromCompileUnit(codeDomWrap.GeneratedCode, sw, options);&lt;br/&gt;                sw.Flush();&lt;br/&gt;                code = sw.GetStringBuilder().ToString();&lt;br/&gt;                Debug.WriteLine(code);&lt;br/&gt;            }&lt;br/&gt;            catch { }&lt;br/&gt;            finally&lt;br/&gt;            {&lt;br/&gt;                sw.Close();&lt;br/&gt;            }&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;目前，对于模板嵌套，强类型绑定等MVC框架特有支持的功能还没有时间仔细研究。相信如果这个思路投入生产的话，这样的需求应该是会有的。过几天，我把代码放到&lt;a href="http://codeof.codeplex.com/SourceControl/changeset/view/61720" target="_blank"&gt;CodePlex&lt;/a&gt;上去，有兴趣的同仁可以联系我，毕竟一个人的力量是有限的。下篇，我将介绍如何动态编译，并把数据填入模板中。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/P_Chou/aggbug/2151099.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/P_Chou/archive/2011/08/23/customize-razor-template-1.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/P_Chou/archive/2011/05/23/2054875.html</id><title type="text">ASP.NET MVC在IIS6下部署的小技巧</title><summary type="text">通常在IIS6下部署ASP.NET MVC应用程序的时候，都是直接设置把所有请求都交由ASP.NET的ISAPI处理。MVC是基于ASP.NET的，框架默认对于任何请求都会优先检查物理路径是否存在物理文件，如果存在的话就不通过MVC的路由机制，否则才走路由。因此，如果在不考虑控制静态资源权限的情况下，可以设置静态资源不通过ASP.NET的ISAPI，而直接由IIS处理，通过这种方式提升一些性能。本文围绕这个主题，详解部署过程。0.在设计MVC网站的时候，把静态资源统一放在一个文件夹下，建议目录结构使用小写字母1.把web应用程序发布到某个路径下（略）（顺便提一句，MVC2功能上比MVC3弱一些</summary><published>2011-05-23T15:32:00Z</published><updated>2011-05-23T15:32:00Z</updated><author><name>P_Chou</name><uri>http://www.cnblogs.com/P_Chou/</uri></author><link rel="alternate" href="http://www.cnblogs.com/P_Chou/archive/2011/05/23/2054875.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/P_Chou/archive/2011/05/23/2054875.html"/><content type="html">&lt;p&gt;通常在IIS6下部署ASP.NET MVC应用程序的时候，都是直接设置把所有请求都交由ASP.NET的ISAPI处理。MVC是基于ASP.NET的，&lt;strong&gt;框架默认对于任何请求都会优先检查物理路径是否存在物理文件&lt;/strong&gt;，如果存在的话就不通过MVC的路由机制，否则才走路由。因此，如果在不考虑控制静态资源权限的情况下，可以设置静态资源不通过ASP.NET的ISAPI，而直接由IIS处理，通过这种方式提升一些性能。本文围绕这个主题，详解部署过程。&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;0.在设计MVC网站的时候，把静态资源统一放在一个文件夹下，建议目录结构使用小写字母&lt;/p&gt;&lt;p&gt;1.把web应用程序发布到某个路径下（略）（顺便提一句，MVC2功能上比MVC3弱一些，用到的组件很少，可以直接私有部署System.Web.Mvc；但是MVC3因为功能增加，而且引入了Razor引擎，依赖的组件增多，建议在部署的时候直接安装MVC3）&lt;/p&gt;&lt;p&gt;2.创建一个web站点（略）&lt;/p&gt;&lt;p&gt;3.右击创建的web站点，选择属性，切换到Home Directory页&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/201105232330068727.png"&gt;&lt;img height="400" width="412" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/201105232330072499.png" alt="clip_image001" border="0" title="clip_image001" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;4.点击Configuration，在Wildcard application maps中，点击Insert，添加C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll，&lt;strong&gt;取消Verify that file existsg的勾选&lt;/strong&gt;。（如果使用MVC2的话，可以到2.0下去找这个dll。这里去掉对静态文件的验证是关键，这使得asp.net处理所有的请求）&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/201105232330094842.jpg"&gt;&lt;img height="402" width="416" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/201105232330109170.jpg" alt="clip_image001" border="0" title="clip_image001" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;5.点OK&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/201105232330109137.png"&gt;&lt;img height="454" width="412" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/201105232330126430.png" alt="clip_image001[6]" border="0" title="clip_image001[6]" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;6.允许ASP.NET 4.0扩展&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/201105232330136855.jpg"&gt;&lt;img height="339" width="654" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/201105232330154738.jpg" alt="clip_image002" border="0" title="clip_image002" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;7.content目录是静态资源放置的目录，&lt;strong&gt;设置content目录下请求直接由IIS处理以提高性能&lt;/strong&gt;。右击content，属性&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/201105232330167637.jpg"&gt;&lt;img height="377" width="405" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/201105232330185553.jpg" alt="clip_image001[6]" border="0" title="clip_image001[6]" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;8.点击create，再点击configuration，移除Wildcard application maps下的内容。点击OK。这里利用了IIS的一个bug，把目录设置成虚拟目录，删除Wildcard application maps的设置，这样这个虚拟目录的请求就直接由IIS处理了。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/201105232330181409.png"&gt;&lt;img height="443" width="403" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/20110523233020654.png" alt="clip_image001[8]" border="0" title="clip_image001[8]" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;9. 再点击Remove，把虚拟目录恢复成普通目录。这样只有这个目录下的文件会受到IIS的&amp;ldquo;眷顾&amp;rdquo;  &lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/201105232330201734.png"&gt;&lt;img height="385" width="411" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/201105232330227948.png" alt="clip_image001[10]" border="0" title="clip_image001[10]" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" /&gt;&lt;/a&gt;  &lt;/p&gt;&lt;p&gt;10.设置AppPool权限。点击站点使用的AppPool的属性，切换到Identity，选择Local System。这是偷懒的设置方法，以避免出现应用程序本身对系统的访问权限不够  &lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/201105232330225440.png"&gt;&lt;img height="376" width="399" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/201105232330238024.png" alt="clip_image001[14]" border="0" title="clip_image001[14]" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" /&gt;&lt;/a&gt;  &lt;/p&gt;&lt;p&gt;11.设置匿名访问权限。右击站点，选择权限。点击Add&amp;hellip;，在弹出的对话框中输入IUSR_XXX（XXX为机器名）。  &lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/201105232330233846.png"&gt;&lt;img height="266" width="494" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/20110523233025301.png" alt="clip_image001[16]" border="0" title="clip_image001[16]" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" /&gt;&lt;/a&gt;  &lt;/p&gt;&lt;p&gt;点击OK，便可添加一个Internet Guest Account，保持默认的只读权限即可。  &lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/201105232330262743.png"&gt;&lt;img height="484" width="402" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/201105232330283318.png" alt="clip_image001[18]" border="0" title="clip_image001[18]" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" /&gt;&lt;/a&gt;  &lt;/p&gt;&lt;p&gt;12.最后注意，如果是首次安装.NET 4.0，不要忘了注册&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/201105232330294398.png"&gt;&lt;img height="185" width="623" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/201105232330291267.png" alt="clip_image001[12]" border="0" title="clip_image001[12]" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;在&lt;a href="http://www.cnblogs.com/P_Chou/archive/2010/11/02/details-asp-net-mvc-02.html"&gt;深入理解ASP.NET MVC（2）&lt;/a&gt;中，讨论过MVC框架的路由机制，其中提到&amp;ldquo;可以通过设置RouteCollection的&lt;strong&gt;RouteExistingFiles&lt;/strong&gt;为&lt;strong&gt;true&lt;/strong&gt;，使得路由&lt;strong&gt;不匹配&lt;/strong&gt;静态文件（注意true是不匹配，这个命名和奇怪）。&amp;rdquo;因此，我们可以这样设置，以使得MVC不对静态文件进行检查，这样又可以提高一点性能。最终请求会像下图那样被serve&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/201105232330298759.png"&gt;&lt;img height="291" width="357" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201105/201105232330302596.png" alt="image" border="0" title="image" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/P_Chou/aggbug/2054875.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/P_Chou/archive/2011/05/23/2054875.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/P_Chou/archive/2011/01/23/details-asp-net-mvc-10.html</id><title type="text">深入理解ASP.NET MVC（10）</title><summary type="text">系列目录前言Model是MVC强大的机制之一，它是MVC框架中客户端和服务端数据交互的核心机制。深入的理解Model有助于我们自己在MVC的基础上扩展，也有助于我们创造出更具复用意义的软件模块。主要包含以下议题： Templated view helpers：根据Model生成Html控件元素 Model Binding：自动映射和解析用户提交的数据 Integrating validation：集成客户端认证我们知道ASP.NET web应用程序的数据交互其实就是客户端表单数据和.NET对象（Model）之间的转化。下图说明了这个问题： 在MVC中，众多HTML Helper负责将Mo</summary><published>2011-01-23T15:24:00Z</published><updated>2011-01-23T15:24:00Z</updated><author><name>P_Chou</name><uri>http://www.cnblogs.com/P_Chou/</uri></author><link rel="alternate" href="http://www.cnblogs.com/P_Chou/archive/2011/01/23/details-asp-net-mvc-10.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/P_Chou/archive/2011/01/23/details-asp-net-mvc-10.html"/><content type="html">&lt;p&gt;&lt;span style="font-size: 18pt;"&gt;&lt;a href="http://www.cnblogs.com/P_Chou/archive/2010/11/01/details-asp-net-mvc-content.html"&gt;系列目录&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;h2 class="parker_h2"&gt;&lt;strong&gt;前言&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Model是MVC强大的机制之一，它是MVC框架中客户端和服务端数据交互的核心机制。深入的理解Model有助于我们自己在MVC的基础上扩展，也有助于我们创造出更具复用意义的软件模块。主要包含以下议题：  &lt;/p&gt;&lt;ul&gt;&lt;li&gt;Templated view helpers：根据Model生成Html控件元素  &lt;/li&gt;&lt;li&gt;Model Binding：自动映射和解析用户提交的数据  &lt;/li&gt;&lt;li&gt;Integrating validation：集成客户端认证&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;我们知道ASP.NET web应用程序的数据交互其实就是客户端表单数据和.NET对象（Model）之间的转化。下图说明了这个问题：  &lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101232321349073.jpg"&gt;&lt;img height="257" width="612" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101232321355825.jpg" alt="clip_image002" border="0" title="clip_image002" style="display: inline; border-width: 0px;" /&gt;&lt;/a&gt;  &lt;/p&gt;&lt;p&gt;在MVC中，众多HTML Helper负责将Model转化成Html标记，Model binding将用户提交的数据转化成Model。  &lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 class="parker_h2"&gt;&lt;strong&gt;Templated View Helpers&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;MVC2新增的Templated View Helpers指的是类似Html.TextBoxFor()之类的扩展方法。用这样的方法来构造链接表单之类的的Html元素的话，是十分方便和智能的。这些方法会根据Model或Model属性的类型自动决定转换成什么样的Html元素，并自动使得Model Binding得以支持。  &lt;/p&gt;&lt;p&gt;比如如果你有个属性叫Approved，是个bool类型，那么Html.EditorFor(x =&amp;gt; x.Approved)将会转化成一个check box。比如当调用Html.EditorFor()时，&lt;b&gt;MVC&lt;/b&gt;&lt;b&gt;需要选择一个合适的模板呈现&lt;/b&gt;，因此模板可以理解成对某种数据结构的预定义的Html的呈现方式。先来看看MVC内建有哪些模板，下面的代码是从TemplateHelpers中摘录的：&lt;/p&gt;static readonly Dictionary&amp;lt;string, Func&amp;lt;HtmlHelper, string&amp;gt;&amp;gt; defaultDisplayActions =&lt;br/&gt;            new Dictionary&amp;lt;string, Func&amp;lt;HtmlHelper, string&amp;gt;&amp;gt;(StringComparer.OrdinalIgnoreCase) {&lt;br/&gt;                { "EmailAddress",       DefaultDisplayTemplates.EmailAddressTemplate },&lt;br/&gt;                { "HiddenInput",        DefaultDisplayTemplates.HiddenInputTemplate },&lt;br/&gt;                { "Html",               DefaultDisplayTemplates.HtmlTemplate },&lt;br/&gt;                { "Text",               DefaultDisplayTemplates.StringTemplate },&lt;br/&gt;                { "Url",                DefaultDisplayTemplates.UrlTemplate },&lt;br/&gt;                { "Collection",         DefaultDisplayTemplates.CollectionTemplate },&lt;br/&gt;                { typeof(bool).Name,    DefaultDisplayTemplates.BooleanTemplate },&lt;br/&gt;                { typeof(decimal).Name, DefaultDisplayTemplates.DecimalTemplate },&lt;br/&gt;                { typeof(string).Name,  DefaultDisplayTemplates.StringTemplate },&lt;br/&gt;                { typeof(object).Name,  DefaultDisplayTemplates.ObjectTemplate },&lt;br/&gt;            };&lt;br/&gt;&lt;br/&gt;        static readonly Dictionary&amp;lt;string, Func&amp;lt;HtmlHelper, string&amp;gt;&amp;gt; defaultEditorActions =&lt;br/&gt;            new Dictionary&amp;lt;string, Func&amp;lt;HtmlHelper, string&amp;gt;&amp;gt;(StringComparer.OrdinalIgnoreCase) {&lt;br/&gt;                { "HiddenInput",        DefaultEditorTemplates.HiddenInputTemplate },&lt;br/&gt;                { "MultilineText",      DefaultEditorTemplates.MultilineTextTemplate },&lt;br/&gt;                { "Password",           DefaultEditorTemplates.PasswordTemplate },&lt;br/&gt;                { "Text",               DefaultEditorTemplates.StringTemplate },&lt;br/&gt;                { "Collection",         DefaultEditorTemplates.CollectionTemplate },&lt;br/&gt;                { typeof(bool).Name,    DefaultEditorTemplates.BooleanTemplate },&lt;br/&gt;                { typeof(decimal).Name, DefaultEditorTemplates.DecimalTemplate },&lt;br/&gt;                { typeof(string).Name,  DefaultEditorTemplates.StringTemplate },&lt;br/&gt;                { typeof(object).Name,  DefaultEditorTemplates.ObjectTemplate },&lt;br/&gt;            };&lt;br/&gt;&lt;p&gt;从中可以看到内建的模板有哪些。总的来说，Html元素可以分为两类，显示类和编辑类。分别地，主要有两大类的Helper，DisplayXXX、LabelXXX和EditorXXX。MVC框架包含有DefaultDisplayTemplates和DefaultEditorTemplates分别负责完成render的工作。而TemplateHelpers类负责调配选择使用那种模板。MVC内建的模板主要是简单类型，对于复杂类型MVC支持用户自己定义模板，自定义一个模板实际上就是创建ascx，然后放在合适的位置，由View引擎自行查找。框架会审查&lt;strong&gt;/Views/Shared/DisplayTemplates/&lt;/strong&gt;和&lt;strong&gt;/Views/Shared/EditorTemplates/&lt;/strong&gt;目录下的ascx文件，并将其视为自定义模板加载，所以我们可以设计复杂的ascx模板，将他视为用户控件，然后简单的使用TemplateHelper的各种方法来加载，这也不失为一种&lt;strong&gt;代替PartialView或ChildAction的方式&lt;/strong&gt;。这里的ascx文件名要尽量对应类型名，因此，我们可以创建一个DateTime.ascx，并像下面这样编辑代码来&amp;ldquo;重载&amp;rdquo;MVC内建对DateTime数据类型的模板。&lt;/p&gt;&amp;lt;%@ Control Language="C#" Inherits="ViewUserControl&amp;lt;DateTime?&amp;gt;" %&amp;gt; &lt;br/&gt;&amp;lt;%: Html.TextBox("",                                        /* Name suffix     */ &lt;br/&gt;                 ViewData.TemplateInfo.FormattedModelValue, /* Initial value   */ &lt;br/&gt;                 new { @class = "date-picker" }             /* HTML attributes */ &lt;br/&gt;    ) %&amp;gt;&lt;br/&gt;&lt;p&gt;注意到，这里用到&lt;strong&gt;ViewData.TemplateInfo.FormattedModelValue&lt;/strong&gt;，而不是Model.ToString()，这样可以充分利用ModelMetadata的特性，关于ModelMetaData将在以后更多的涉及。这里也可以这样写：&lt;/p&gt;&amp;lt;%@ Control Language="C#" Inherits="ViewTemplateUserControl&amp;lt;DateTime?&amp;gt;" %&amp;gt; &lt;br/&gt;&amp;lt;%: Html.TextBox("",                             /* Name suffix     */ &lt;br/&gt;                 FormattedModelValue,            /* Initial value   */ &lt;br/&gt;                 new { @class = "date-picker" }  /* HTML attributes */ &lt;br/&gt;) %&amp;gt;&lt;br/&gt;&lt;p&gt;注意到这里继承的是ViewTemplateUserControl而不是上面的ViewUserControl。 &lt;/p&gt;&lt;p&gt;既然MVC以模板的方式呈现UI，那么是什么影响MVC的选择呢？以下因素按优先级影响到MVC框架对模板的选择：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;在EditorFor方法中显示指定的模板名称，Html.EditorFor(x =&amp;gt; x.SomeProperty , &amp;ldquo;My Template&amp;rdquo;)。 &lt;/li&gt;&lt;li&gt;对应Model的元数据描述，比如在属性上添加特性[UIHint(&amp;ldquo;My Template&amp;rdquo;)] &lt;/li&gt;&lt;li&gt;Model的元数据描述的数据类型，比如[DataType(DataType.EmailAddress)] &lt;/li&gt;&lt;li&gt;对应属性的真实.NET 类型 &lt;/li&gt;&lt;li&gt;对于可以被转化成string的简单类型，使用String模板 &lt;/li&gt;&lt;li&gt;Model的父类属性也会被转化 &lt;/li&gt;&lt;li&gt;如果属性实现了IEnumable，将选择Collection模板 &lt;/li&gt;&lt;li&gt;最后使用Object模板&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 class="parker_h2"&gt;&lt;strong&gt;ModelMetadata&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;TemplateHelpers的确是完成render工作的最主要类，但事实上TemplateHelpers也仅仅负责render，它需要一个叫&lt;strong&gt;ModelMetadata&lt;/strong&gt;的东西来为它提供数据，而ModelMetadata本身就像它的类名，意思是&lt;strong&gt;&amp;ldquo;模型元数据&amp;rdquo;&lt;/strong&gt;，相当于一个描述数据的对象，这个对象需要&lt;strong&gt;ModelMetadataProvider&lt;/strong&gt;来提供真正提供数据，下图可以帮助理解： &lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101232321369828.jpg"&gt;&lt;img height="305" width="611" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101232321367385.jpg" alt="clip_image002[5]" border="0" title="clip_image002[5]" style="display: inline; border-width: 0px;" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;可以看到MVC内建了&lt;strong&gt;DataAnnotationModelMetadataProvider&lt;/strong&gt;来充当ModelMetadataProvider，它内建支持.NET中的Data Annotation特性，比如&lt;strong&gt;DisplayColum&lt;/strong&gt;、&lt;strong&gt;DisplayFormat&lt;/strong&gt;、&lt;strong&gt;Required&lt;/strong&gt;等，不仅如此DataAnnotationModelMetadataProvider还支持MVC特有的特性描述如：UIHint等。 &lt;/p&gt;&lt;p&gt;可以像下面这样指定一个ModelMetadataProvider：ConventionsMetadataProvider。&lt;/p&gt;protected void Application_Start() &lt;br/&gt;{ &lt;br/&gt;    AreaRegistration.RegisterAllAreas(); &lt;br/&gt;    RegisterRoutes(RouteTable.Routes); &lt;br/&gt;    ModelMetadataProviders.Current = new ConventionsMetadataProvider(); &lt;br/&gt;}&lt;br/&gt;&lt;p&gt;ModelMetadataProvider本身是个抽象类，可以从下面任意的类中继承，推荐从DataAnnotationModelMetadataProvider继承，这样我们自定义的ModelMetadataProvider就能保留原有的支持了。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101232321374976.png"&gt;&lt;img height="491" width="381" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/20110123232139191.png" alt="image" border="0" title="image" style="display: inline; border-width: 0px;" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;再来大概看看ModelMetadata有哪些属性和方法，它们中的部分将被TemplateHelpers考察并影响到Html元素的呈现。其中的&lt;strong&gt;FromLambdaExpression()&lt;/strong&gt;方法用于从Lambda表达式中得到ModelMetadata，这也是内建的TemplateHelpers要调用的方法。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101232321415962.png"&gt;&lt;img height="713" width="585" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101232321454406.png" alt="image" border="0" title="image" style="display: inline; border-width: 0px;" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;当我们需要用Attribute描述Model类的时候，也许会碰到这样的情况，Model本身是诸如ORM等工具生成的，不能直接修改这样的类。于是MVC提供了&lt;strong&gt;[MetadataType]&lt;/strong&gt;属性来解决这种情形。通常自动生成的Model类是部分类：&lt;/p&gt;public partial class Person &lt;br/&gt;{ &lt;br/&gt;    public int PersonId { get; set; } &lt;br/&gt;    public string FirstName { get; set; } &lt;br/&gt;    public string LastName { get; set; } &lt;br/&gt;    public DateTime BirthDate { get; set; } &lt;br/&gt;    public Address HomeAddress { get; set; } &lt;br/&gt;    public bool IsApproved { get; set; } &lt;br/&gt;}&lt;br/&gt;&lt;p&gt;定义另一个对应的类，并用MetadataType特性标识，在其中定义一个内部类，标注上需要的特性描述即可。&lt;/p&gt;[MetadataType(typeof(PersonMetadata))] &lt;br/&gt;public partial class Person &lt;br/&gt;{ &lt;br/&gt;    // This class is only used as a source of metadata &lt;br/&gt;    private class PersonMetadata &lt;br/&gt;    { &lt;br/&gt;        [HiddenInput(DisplayValue = false)] public int PersonId { get; set; } &lt;br/&gt;        [DisplayName("First name")] public string FirstName { get; set; } &lt;br/&gt;        [DisplayName("Last name")] public string LastName { get; set; } &lt;br/&gt; &lt;br/&gt;        // Also add any other properties for which you want to supply metadata &lt;br/&gt;    } &lt;br/&gt;}&lt;br/&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 class="parker_h2"&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;以上仅仅从框架的角度阐述了关于Template和ModelMatedata的实现。更多细节还需要在实践中多多留意。另外这部分内容还涉及到下一篇要谈到的&amp;ldquo;模型绑定&amp;rdquo;，多做些相应的扩展比较有利于理解这部分内容。&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; line-height: 18px; font-size: 13px;"&gt;劳动果实，转载请注明出处：&lt;/span&gt;&lt;a href="http://www.cnblogs.com/P_Chou/archive/2011/01/23/details-asp-net-mvc-10.html"&gt;http://www.cnblogs.com/P_Chou/archive/2011/01/23/details-asp-net-mvc-10.html&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/P_Chou/aggbug/1942726.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/P_Chou/archive/2011/01/23/details-asp-net-mvc-10.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/P_Chou/archive/2011/01/13/oms-develop-deploy.html</id><title type="text">OMS开发随笔之开发和部署要点</title><summary type="text">OMS开发随笔之概述上一篇中，介绍了OMS的特点和基本的开发概述，这篇详细谈谈具体在实践中遇到的问题及解决方案。本文之涉及到普通短信的开发，不包括彩信。OMS的开发要点总的来说开发oms没有太大的困难，毕竟只有四个接口，参数也不是很复杂。强烈建议在服务端的代码中加入日志功能以便调试。在实际操作上遇到的问题如下：GetServiceInfo的返回值说明对于GetServiceInfo的返回值中某些属性的意义，做一下说明，详见官方文档：这些值是由服务端设定的，对于serviceInfo整个内容可以用一个固定的xml文件存储，服务端程序只要简单的读入并返回给客户端即可。上面提到的四个属性值，会影响O</summary><published>2011-01-13T13:47:00Z</published><updated>2011-01-13T13:47:00Z</updated><author><name>P_Chou</name><uri>http://www.cnblogs.com/P_Chou/</uri></author><link rel="alternate" href="http://www.cnblogs.com/P_Chou/archive/2011/01/13/oms-develop-deploy.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/P_Chou/archive/2011/01/13/oms-develop-deploy.html"/><content type="html">&lt;p&gt;&lt;strong&gt;&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;a href="http://www.cnblogs.com/P_Chou/archive/2011/01/10/oms-develop-overview.html" target="_blank"&gt;OMS开发随笔之概述&lt;/a&gt;&lt;/p&gt;&lt;p&gt;上一篇中，介绍了OMS的特点和基本的开发概述，这篇详细谈谈具体在实践中遇到的问题及解决方案。本文之涉及到普通短信的开发，不包括彩信。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 class="parker_h2"&gt;&lt;strong&gt;OMS的开发要点&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;总的来说开发oms没有太大的困难，毕竟只有四个接口，参数也不是很复杂。&lt;strong&gt;强烈建议在服务端的代码中加入日志功能以便调试&lt;/strong&gt;。在实际操作上遇到的问题如下：&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;GetServiceInfo的返回值说明&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;对于GetServiceInfo的返回值中某些属性的意义，做一下说明，详见官方文档：&lt;/p&gt;  &amp;lt;supportedService&amp;gt;&lt;br/&gt;    &amp;lt;!--maxRecipientsPerMessage:最多多少个收件人--&amp;gt;&lt;br/&gt;    &amp;lt;!--maxMessagesPerSend : represents the number of separate SMS messages allowed in one xmsData. --&amp;gt;&lt;br/&gt;    &amp;lt;!--maxSbcsPerMessage : 每条短信的字数 US ASCII characters--&amp;gt;&lt;br/&gt;    &amp;lt;!--maxDbcsPerMessage : 每条短信的字数 双字节文字--&amp;gt;&lt;br/&gt;    &amp;lt;SMS_SENDER&lt;br/&gt;      maxRecipientsPerMessage="50" maxMessagesPerSend="20"&lt;br/&gt;      maxSbcsPerMessage="128" maxDbcsPerMessage="70" /&amp;gt;&lt;br/&gt;  &amp;lt;/supportedService&amp;gt;&lt;p&gt;这些值是由服务端设定的，对于serviceInfo整个内容可以用一个固定的xml文件存储，服务端程序只要简单的读入并返回给客户端即可。上面提到的四个属性值，会影响Outlook客户端的行为，比如短信预览时分页显示等。&lt;/p&gt;&lt;p&gt;这里特别注意：&amp;lt;serviceUri&amp;gt;字段的值一定要保证和用户在客户端输入的Uri完全一致，否则Outlook即使调用GetServiceInfo成功后发现服务端返回的这个Uri和用户输入的不一致也会报错，并将这里的Uri替换掉用户输入框中的Uri。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;序列化和反序列化对象处理传入和传出&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;官方文档定义的四个接口无论是参数还是返回值都是C#的string类型(C++的话我想应该用std::wstring)。然而，这里的参数都是xml格式的字符串，服务端需要解析。如果使用C#开发ws的话官方给出了xsd的schema，只要将其拷贝下来保存成.xsd，然后用xsd.exe生成对应的class（xsd.exe xsdfile.xsd /c），就可以使用XMLSerailer方便的序列化和反序列化。可以参考下面代码作序列化和反序列化：&lt;/p&gt;private static string XmlSerializer&amp;lt;T&amp;gt;(T serialObject) where T : class&lt;br/&gt;{&lt;br/&gt;    XmlSerializer ser = new XmlSerializer(typeof(T));&lt;br/&gt;    System.IO.MemoryStream mem = new MemoryStream();&lt;br/&gt;    XmlTextWriter writer = new XmlTextWriter(mem, Encoding.Unicode);&lt;br/&gt;    ser.Serialize(writer, serialObject);&lt;br/&gt;    writer.Close();&lt;br/&gt;&lt;br/&gt;    return Encoding.Unicode.GetString(mem.ToArray());&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;private static T XmlDeserialize&amp;lt;T&amp;gt;(string str) where T : class&lt;br/&gt;{&lt;br/&gt;    XmlSerializer mySerializer = new XmlSerializer(typeof(T));&lt;br/&gt;    StreamReader mem2 = new StreamReader(&lt;br/&gt;            new MemoryStream(System.Text.Encoding.Unicode.GetBytes(str)),&lt;br/&gt;            System.Text.Encoding.Unicode);&lt;br/&gt;&lt;br/&gt;    return (T)mySerializer.Deserialize(mem2);&lt;br/&gt;}&lt;p&gt;&lt;strong&gt;&lt;strong&gt;&amp;nbsp;&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;对于官方给的XML schema&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;DeliverXms的参数xmsData格式复杂，返回值string的格式也有些麻烦，然而，当我在代码中构造一个XmsResponse，并序列化成xml返回给Outlook时，Outlook确说发送失败，仔细比对发现，序列化生成的xml文档多出了两个类似名字空间的东西，无奈只能在代码里面手动构造XML文档返回。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;结论：对于输入参数xmsUser和xmsData可以使用schema和由此生成的类，并结合反序列化成对象，来读取outlook发送过来的信息。对于输出参数，还是老老实实手动构造xml吧。&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 class="parker_h2"&gt;&lt;strong&gt;OMS的部署要点&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;上面多次提到OMS开发不困难，但是部署很麻烦，尤其是对笔者这样之前没有任何web部署经验的人来说更为痛苦。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;看了无数遍的错误提示&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;在ws开发完之后，便需要部署并测试，点击Test Account Settings当然是需要过的第一关，但是在测试过程中，下面这个错误我已经记不清弹出过多少次了，每当看到这个错误的时候总是很失落，因为这个错误根本不能为我们提供什么有用的信息，强烈建议Outlook可以写入一些相应的日志，方便开发人员查看ws部署到底错误在哪里。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132142468698.png"&gt;&lt;img style="display: inline; border-width: 0px;" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132142515846.png" width="636" height="117" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;&lt;em&gt;错误1&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;http还是https&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;如果不仔细看文档，不会注意到开发的ws需要使用https协议，需要提供SSL加密通信支持。如果在设置的时候输入的URL是http开头的，outlook不会尝试连接服务端，并会给出如下错误提示：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132142514309.png"&gt;&lt;img style="display: inline; border-width: 0px;" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132142533078.png" width="632" height="124" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;&lt;em&gt;错误2&lt;/em&gt;&lt;/p&gt;&lt;p&gt;固然，作为一个在网络环境中交换敏感信息的软件，理所当然需要考虑到安全的问题，但是我们觉得也不该&amp;ldquo;一棒子打死&amp;rdquo;，如果这个ws是部署在某企业的内网环境中，不用考虑传输安全的话，根本没有必要使用https，因为这样会提高部署和乃至开发的难度。我们觉得也许这里启用一个选项会是个不错的主意。&lt;/p&gt;&lt;p&gt;基于这个原因，我们在部署ws的时候需要绑定https，证书和SSL。&amp;nbsp; &lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;证书？证书！&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;在这之前我对CA和证书没有什么概念，只知道在访问有些https的时候浏览器总是提示威胁啊什么的。在做这个应用的时候算是理解了些东西（关于CA和证书参见&lt;a href="http://www.cnblogs.com/P_Chou/archive/2010/12/27/https-ssl-certification.html" target="_blank"&gt;浅谈https\ssl\数字证书&lt;/a&gt;）。Outlook必须服务端提供一个受信任的证书，&lt;strong&gt;可以用IIS7管理工具生成一个自认证的证书&lt;/strong&gt;。这个证书是以机器名作为颁发者的，在IE的证书认证系统中，一个证书被检验三个方面的东西：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;证书是否是受信任的 &lt;/li&gt;&lt;li&gt;证书是否过期 &lt;/li&gt;&lt;li&gt;证书的颁发者是否和访问的主机具有相同的名字 &lt;/li&gt;&lt;/ul&gt;&lt;p&gt;这三个条件只要有一个不符合，IE8就会弹出威胁提示。在浏览网页的时候可以点&amp;ldquo;继续访问&amp;rdquo;，地址栏随机变成红色。但在Outlook中，这三个条件必须都符合才能受信任。&lt;/p&gt;&lt;p&gt;对于第一个条件，只要把证书导入到客户端IE浏览器的Trusted Root Certification Authorities下就会可以了。当然其中已经包含了一些权威的认证机构的证书。如果有能力去这些机构申请一个证书的话，你的证书将成为他们证书的子证书，无需将证书导入客户端，同样可以受信任。&lt;/p&gt;&lt;p&gt;对于第二个条件，过期了我想只能重新弄一个。&lt;/p&gt;&lt;p&gt;对于第三个条件，由于IIS7管理工具生成的自认证证书是以机器名作为颁发者的，所以在outlook客户端的设置中，必须以机器名作为主域名，不能使用IP地址。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;在IIS7+部署https站点&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;部署一个oms站点跟部署一个普通的https站点没有什么区别，建议测试的时候先使用浏览器，当浏览器可以完全信任的访问这个站点的时候，再测试Outlook客户端。同时在服务端方法的调用过程中通过写log的方式可以方便的跟踪方法的调用。如果GetServiceInfo没有被调用，那么一定是https站点设置问题。下面以windows server 2008 r2作为例子简要说明：&lt;/p&gt;&lt;p&gt;Window server 2008 r2使用Roles来管理服务器服务组件，首先需要激活IIS：添加一个Role，选择Web Server(IIS)&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132142553767.jpg"&gt;&lt;img style="display: inline; border-width: 0px;" title="clip_image002" border="0" alt="clip_image002" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132142569231.jpg" width="479" height="354" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;在选择IIS附属组件的时候，记得勾选ASP.NET支持。然后一路下去即可。&lt;/p&gt;&lt;p&gt;发布你的ASP.NET web service，如果基于.net 4.0，还需要安装.net 4.0，并使用aspnet_regiis &amp;ndash;i 命令设置IIS。&lt;/p&gt;&lt;ul&gt;&lt;li&gt;在IIS管理工具中新建一个站点 &lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132142587477.png"&gt;&lt;img style="display: inline; border-width: 0px;" title="clip_image003" border="0" alt="clip_image003" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143032117.png" width="377" height="371" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;物理路径指向发布的站点的路径。先使用http绑定，为了不和默认站点重复，使用8082端口。新建成功后如下图：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143059840.jpg"&gt;&lt;img style="display: inline; border-width: 0px;" title="clip_image005" border="0" alt="clip_image005" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143076516.jpg" width="625" height="444" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;对于匿名用户访问站点的权限设置挺麻烦的，偷懒的做法：双击IIS中的Authentication，编辑Anonymous Autherntication，改成Application pool identity。&lt;/p&gt;&lt;p&gt;然后，来到Application Pools，选择站点使用的应用程序池，点击右侧的高级设置，修改Identity属性，选择LocalSystem。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143074041.png"&gt;&lt;img style="display: inline; border-width: 0px;" title="clip_image006" border="0" alt="clip_image006" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143095319.png" width="405" height="204" /&gt;&lt;/a&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143091207.png"&gt;&lt;img style="display: inline; border-width: 0px;" title="clip_image007" border="0" alt="clip_image007" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143116912.png" width="352" height="204" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;现在用服务器上的浏览器访问站点的.asmx文件应该能看到web服务方法列表页面。适当设置防火墙，使外部可以通过浏览器访问站点。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;上面提到&lt;strong&gt;oms必须绑定成https的站点&lt;/strong&gt;，所以我们的站点必须绑定https，关于更多https的基础知识，可以参考&lt;a href="http://www.cnblogs.com/P_Chou/archive/2010/12/27/https-ssl-certification.html" target="_blank"&gt;浅谈https\ssl\数字证书&lt;/a&gt;。&lt;/p&gt;&lt;ul&gt;&lt;li&gt;创建服务器自签证书 &lt;/li&gt;&lt;/ul&gt;&lt;p&gt;单击IIS管理器右侧的本机图标，双击右侧的Server Certificatin，进入到如下页面&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143126488.jpg"&gt;&lt;img style="display: inline; border-width: 0px;" title="clip_image009" border="0" alt="clip_image009" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143131604.jpg" width="532" height="378" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;点击右侧的Create Self-Signed Certificate&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143158704.png"&gt;&lt;img style="display: inline; border-width: 0px;" title="clip_image010" border="0" alt="clip_image010" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143161377.png" width="370" height="283" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;填入任何字符，比如&amp;ldquo;oms&amp;rdquo;。Ok。这样就会产生一个自签的证书，如下图。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143173461.jpg"&gt;&lt;img style="display: inline; border-width: 0px;" title="clip_image012" border="0" alt="clip_image012" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143199690.jpg" width="522" height="371" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;编辑站点的绑定，选择https方式，端口保留443。选择刚刚生成的证书：oms。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143193659.png"&gt;&lt;img style="display: inline; border-width: 0px;" title="clip_image013" border="0" alt="clip_image013" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143231199.png" width="464" height="253" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;双击右侧的SSL Settings，勾选Required SSL，忽略客户端证书，Apply。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143244711.jpg"&gt;&lt;img style="display: inline; border-width: 0px;" title="clip_image015" border="0" alt="clip_image015" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143255923.jpg" width="669" height="476" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;这样站点就绑定的https，http方式将无法继续访问。用浏览器通过https访问.asmx。如下界面表示设置https成功，继续访问即可看到web服务方法列表&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/2011011321432725.jpg"&gt;&lt;img style="display: inline; border-width: 0px;" title="clip_image017" border="0" alt="clip_image017" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143289568.jpg" width="401" height="289" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;导出服务端证书，安装在客户端 &lt;/li&gt;&lt;/ul&gt;&lt;p&gt;客户端安装服务端自签证书，再次进入站点绑定对话框，选择https那个编辑：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143287092.png"&gt;&lt;img style="display: inline; border-width: 0px;" title="clip_image013[1]" border="0" alt="clip_image013[1]" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143299700.png" width="426" height="233" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;点击View，查看证书，切换到details&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143311011.png"&gt;&lt;img style="display: inline; border-width: 0px;" title="clip_image018" border="0" alt="clip_image018" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/20110113214333619.png" width="243" height="301" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;点击Copy to File，进入证书导出向导，在如下页面选择do not export the private key。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143334240.png"&gt;&lt;img style="display: inline; border-width: 0px;" title="clip_image019" border="0" alt="clip_image019" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/20110113214335817.png" width="370" height="333" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;按照向导导出证书，并将证书拷贝到客户端，在客户端把证书安装在&amp;ldquo;受信任的根证书颁发者&amp;rdquo;中。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143372194.png"&gt;&lt;img style="display: inline; border-width: 0px;" title="clip_image020" border="0" alt="clip_image020" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143392883.png" width="244" height="223" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;在客户端浏览器测试https站点，如果能畅通无阻的访问，那么已经成功了一半了。&lt;/p&gt;&lt;p&gt;在Outlook客户端配置中输入https站点地址，&lt;strong&gt;注意这里不能输入ip地址，必须是机器名，然后点&amp;ldquo;测试&amp;rdquo;。&lt;/strong&gt;如果服务端代码没有任何问题的话，就能立刻成功，但通常都比较困难，但是如果你写了log的话，&lt;strong&gt;只要能证明GetServiceInfo被Outlook调用了，那么就说明站点的部署成功了，&lt;/strong&gt;随后就是参照上文提到的各种注意事项，小心调试了。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143393298.jpg"&gt;&lt;img style="display: inline; border-width: 0px;" title="clip_image022" border="0" alt="clip_image022" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101132143414052.jpg" width="431" height="315" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;在windows server 2003上用IIS6部署基本思想和步骤差不多，就是要注意匿名访问权限的设置。在绑定自签证书的时候有所不同。需要安装服务器证书机构服务，推荐直接绑定服务器根证书，然后把根证书发放给客户端。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 class="parker_h2"&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;通过OMS的开发，其实不仅仅收获了Outlook的一种扩展服务的开发方法，更多的是熟悉了IIS，熟悉了Web站点的部署。实际在操作中遇到很多问题，比如容易忽略的防护墙设置，匿名访问权限等。有时间要好好学习一下网络方面的知识了。&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; line-height: 18px; font-size: 13px;"&gt;劳动果实，转载请注明出处：&lt;/span&gt;&lt;a href="http://www.cnblogs.com/P_Chou/archive/2011/01/13/oms-develop-deploy.html"&gt;http://www.cnblogs.com/P_Chou/archive/2011/01/13/oms-develop-deploy.html&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/P_Chou/aggbug/1934956.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/P_Chou/archive/2011/01/13/oms-develop-deploy.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/P_Chou/archive/2011/01/10/oms-develop-overview.html</id><title type="text">OMS开发随笔之概述</title><summary type="text">OMS(Outlook Mobile Service)是为Outlook Mobile插件提供短信收发功能的Web Service。事实上，Outlook仅仅提供一个集成在Outlook内的插件客户端，这个客户端通过配置，指向一个短信供应商的web服务，并自动通过协定的接口调用web服务，由web服务负责接受Outlook发来的SOAP请求，实现真正的短信发送。目前该项功能在中国区微软认证的服务提供商如下可以在如下网站上查询到http://messaging.office.microsoft.com/HostingProviders.aspx?src=O14&amp;lc=2052。从理论上讲，任何人</summary><published>2011-01-10T15:06:00Z</published><updated>2011-01-10T15:06:00Z</updated><author><name>P_Chou</name><uri>http://www.cnblogs.com/P_Chou/</uri></author><link rel="alternate" href="http://www.cnblogs.com/P_Chou/archive/2011/01/10/oms-develop-overview.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/P_Chou/archive/2011/01/10/oms-develop-overview.html"/><content type="html">&lt;p&gt;&lt;strong&gt;OMS(Outlook Mobile Service)&lt;/strong&gt;是为Outlook Mobile插件提供短信收发功能的Web Service。事实上，Outlook仅仅提供一个集成在Outlook内的插件客户端，这个客户端通过配置，指向一个短信供应商的web服务，并自动通过协定的接口调用web服务，由web服务负责接受Outlook发来的SOAP请求，实现真正的短信发送。目前该项功能在中国区微软认证的服务提供商如下可以在如下网站上查询到&lt;a href="http://messaging.office.microsoft.com/HostingProviders.aspx?src=O14&amp;amp;lc=2052"&gt;http://messaging.office.microsoft.com/HostingProviders.aspx?src=O14&amp;amp;lc=2052&lt;/a&gt;。从理论上讲，任何人都可以开发这样的ws，只要接口一致，无需微软认证。笔者在近期调研了此项功能，由于水平限制，在实现过程中遇到了很多困难，走了不少弯路，也学到了不少知识，好在最后基本实现了接口，也实现了部署。本文仅仅作为总结。&lt;/p&gt;&lt;p&gt;&lt;span style="text-decoration: underline;"&gt;&lt;span style="font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; line-height: normal; font-size: 16px; font-weight: 800;"&gt;&lt;a style="color: navy; text-decoration: none;" href="http://www.cnblogs.com/P_Chou/archive/2011/01/13/oms-develop-deploy.html" id="ctl02_TitleUrl"&gt;OMS开发随笔之开发和部署要点&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;h2 class="parker_h2"&gt;&lt;strong&gt;Outlook客户端配置&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;目前&lt;strong&gt;Outlook2007以上版本&lt;/strong&gt;才支持这个功能。希望使用此项功能的Outlook用户需要简单配置Outlook，这里以Outlook2010作为示例。&lt;/p&gt;&lt;p&gt;1.打开账户设置，选择添加新帐户：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101102257283767.png"&gt;&lt;img style="display: inline; border-width: 0px;" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101102257298619.png" width="451" height="315" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;2.选择Text Messaging(SMS)，Next（注：在outlook2007中选择Other）：&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101102257335681.png"&gt;&lt;img style="display: inline; border-width: 0px;" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101102257354054.png" width="446" height="328" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;在Service Provider URL中输入ws服务商提供的ws地址，输入用户名和密码，这里的用户名密码是指在该服务提供商处注册的用户，跟Outlook账户无关。填写完毕之后点击&lt;strong&gt;Test Account Settings&lt;/strong&gt;，如果没有错误的话OK即可。&lt;/p&gt;&lt;p&gt;设置完毕之后，即可通过邮件发短信。点击New Items，选择Text Message(SMS)：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101102257364512.png"&gt;&lt;img style="display: inline; border-width: 0px;" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101102257386756.png" width="278" height="295" /&gt;&lt;/a&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;在弹出的新建短信页中输入即可，界面跟新建邮件界面十分相似，在To中可以直接输入手机号，多个号码可以用;隔开，也可以从联系人导入，在正文中输入短信内容，左侧还能看到预览（每屏显示的文字数量是服务商设置的）。输入完毕，点击发送即可。Outlook将自动调用接口，发送短信，如果发送失败，Outlook将以邮件的形式返回错误原因；如果发送成功短信邮件将出现在&amp;ldquo;已发送&amp;rdquo;中。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101102257417538.png"&gt;&lt;img style="display: inline; border-width: 0px;" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/P_Chou/201101/201101102257453587.png" width="458" height="456" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 class="parker_h2"&gt;&lt;strong&gt;OMS的开发概述&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;以上内容是用户需要做的。对于服务提供商，需要开发一个web service，实现outlook定义的接口。微软官方的开发文档可以在如下地址找到，一共3个guideline：&lt;/p&gt;&lt;p&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/bb277361(v=office.12).aspx"&gt;http://msdn.microsoft.com/en-us/library/bb277361(v=office.12).aspx&lt;/a&gt;&lt;/p&gt;&lt;p&gt;上述文档的确详细说明了如何开发一个这样的ws，但是在实际部署和测试中出现了不少棘手的问题。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;接口定义概述&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;以下四个是outlook定义的必须实现的接口：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;string GetServiceInfo()&lt;/strong&gt;&amp;nbsp;&amp;nbsp; Outlook会在用户测试点击Test Account Settings时先调用此方法，此方法需要返回服务端的设置，比如提供商名称，提供的功能（短信、彩信），短信息格式限制等。 &lt;/li&gt;&lt;li&gt;&lt;strong&gt;string GetUserInfo(string xmsUser)&lt;/strong&gt;&amp;nbsp;&amp;nbsp; 在调用GetServiceInfo成功后，Outlook会将用户在配置中输入的用户名和密码通过此方法请求服务端，服务端负责验证使用是否合法，并返回xml结果，如果合法，结果中可以包括用户注册时的手机号和邮箱。服务提供商保存的这两个信息可以用于日后发送回复短信时使用。 &lt;/li&gt;&lt;li&gt;&lt;strong&gt;string DeliverXms(string xmsData)&lt;/strong&gt;&amp;nbsp;&amp;nbsp; Outlook在用户点击Send后，调用此方法，xmsData格式比较复杂，包含该用户的用户名和密码，短信的收件人列表，内容，如果是彩信的话，还有额外的数据。服务端得到此调用后应该先认证用户，然后发送短信，并将结果xml返回。 &lt;/li&gt;&lt;li&gt;&lt;strong&gt;string SendXms(string xmsData)&lt;/strong&gt;&amp;nbsp;&amp;nbsp; 微软官方的说法是，此方法在Outlook2007SP1之后的版本是取代DeliverXms被调用的，接口的定义跟DeliverXms一模一样。事实上我在使用Outlook2010测试的时候，发现当在提供了DeliverXms方法的情况下，仍然调用的是DeliverXms。 &lt;/li&gt;&lt;/ul&gt;&lt;p&gt;实现以上四个接口并不困难，微软的文档有详细的协议说明。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;技术选型的问题&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;由于系统需要，刚开始我们考虑用WCF开发一个ws，并通过windows service寄宿的方式工作。当得知https是必须的时候，还研读了&lt;a href="http://www.cnblogs.com/frank_xl/archive/2009/08/13/1543848.html" target="_blank"&gt;老徐的文章&lt;/a&gt;，但是到最后只实现了浏览器访问ws能够完全信任，Outlook还是弹出错误（详见部署篇）。经调试，发现Outlook根本就没有调用ws的任何方法。无奈之下，只能回到ASP.NET Web Service，毕竟官方的例子和一些成功的例子都是这样做的。&lt;/p&gt;&lt;p&gt;我们没有尝试将WCF的ws寄宿在IIS中测试，原因是一方面本人水平有限，对于WCF只是知道个皮毛；另一方面时间有限，不允许做太多的尝试。最终我们是基于ASP.NET和IIS7.5部署这个ws。详细的部署方法会在下一篇涉及到。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;劳动果实，转载请注明出处：&lt;a href="http://www.cnblogs.com/P_Chou/archive/2011/01/10/oms-develop-overview.html"&gt;http://www.cnblogs.com/P_Chou/archive/2011/01/10/oms-develop-overview.html&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/P_Chou/aggbug/1932415.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/P_Chou/archive/2011/01/10/oms-develop-overview.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
