今天接到Product Manager的通知,Exchange 2007环境下的Native Module不再需要开发(详情可见上篇),但最近几天一直在做Prototype,那就做一下小结吧,总结一下最近几天的收获。
一. 准备工作:
1. 开发前安装Windows Platform SDK,主要是使用其中的#include <httpserv.h>(用到的很多接口都可以在其中看到)
2. WireShark,用来进行抓包,可以验证自己是否正确拿到http request和response的信息
二. Visual Studio 2005编码:
1. Native Module主要使用3个基本组件:
A. HttpModule类 - HttpModule是当前模块的积累。在HttpModule类中我们将实现请求通知方法,这个方法是由IIS在相关的请求处理事件中调用的。(其中主要是定义我们的处理时间方法,例如onBeginRequest)
B. HttpModule类工厂 - 针对每个被处理的请求,HttpModule类工厂可以创建或删除用于处理请求的模块
C. RegisterModule类函数 - 一个Native Module只会实现一个此函数,用于导出函数,使IIS能够加载模块(我遇到一个问题,至今还没解决,就是此函数中我发现只能注册一个事件,如果多个会导致IIS ative sync pool stop掉,DLL用不起来)
2. 其他具体编码可参见我的示例,具体开发流程可参见《IIS 7开发与管理完全参考手册》
三. 安装此Native Module
经过我的测试发现,我的Native Module能够工作,主要是做了以下几项工作:
1. 编译后的DLL放置在C:WindowsSystem32inetsrv下
2. 修改applicationHost.xml配置文件,C:WindowsSystem32inetsrvconfig,在其中的<globalModules>中添加我们的模块
3. IIS 7中,[DomainUser]下,Module中,右键选择Configure Native Module,然后选择Register,填入我们模块的信息。
目前我还不确定2,3是否是重复了,但是我发现这两者都做的情况,我的Native Module是工作的,而3的Register不是修改2的配置文件,具体有待验证。
经过以上三个步骤,我的Native Module可以工作了!目前可以拿到Http的Request Header的信息。希望这次小结,能对有开发此类需求的同学一点参考,由于此功能被PM去掉了,所以很遗憾这块我不能继续做下去,只能是小结啦。:-)
参考资料:
1. 《Professional IIS7》,Wrox出版社出版(Programmer to Programer的理念),非常详细的讲解IIS7,其中12章详细介绍了Http两类Module的开发。其中文版是《IIS 7开发与管理完全参考手册》
2. MSDN
http://msdn.microsoft.com/en-us/library/ms690856(v=vs.90).aspx
Prototype代码附上,功能:取Http request的header信息
#define _WINSOCKAPI_ #include <windows.h> #include <sal.h> #include <httpserv.h> #include "writeLog.h" // Create the module class. class CTestNativeModule : public CHttpModule { //TODO // Implement Notification Method/s REQUEST_NOTIFICATION_STATUS OnBeginRequest( IN IHttpContext * pHttpContext, IN IHttpEventProvider * pProvider ) { WriteLog("--> CTestNativeModule, OnBeginRequest()"); // We won’t be using this, so confirm that to avoid compiler warnings UNREFERENCED_PARAMETER( pProvider ); IHttpRequest* pHttpRequest = pHttpContext->GetRequest(); //dump request header DumpRequestHeader(pHttpContext, pHttpRequest); WriteLog("<-- CTestNativeModule, OnBeginRequest()"); return RQ_NOTIFICATION_CONTINUE; } void DumpRequestHeader(IHttpContext * pHttpContext, IHttpRequest* pHttpRequest) { WriteLog("--> CTestNativeModule, DumpRequestHeader()"); // Buffer size for returned variable values. DWORD cbValue = 512; PCSTR pHeaderValue = (PCSTR) pHttpContext->AllocateRequestMemory( cbValue ); for(HTTP_HEADER_ID i = (HTTP_HEADER_ID)0; i < HttpHeaderRequestMaximum; i = (HTTP_HEADER_ID)((int)i + 1)) { pHeaderValue = pHttpRequest->GetHeader(i); WriteLog(pHeaderValue); } HTTP_REQUEST* rawHttpRequest = pHttpRequest->GetRawHttpRequest(); WriteLog("RawUrl", rawHttpRequest->pRawUrl); PCSTR pKey = ""; pKey = "Cmd"; pHeaderValue = pHttpRequest->GetHeader(pKey); WriteLog(pKey, pHeaderValue); pKey = "DeviceId"; pHeaderValue = pHttpRequest->GetHeader(pKey); WriteLog(pKey, pHeaderValue); pKey = "DeviceType"; pHeaderValue = pHttpRequest->GetHeader(pKey); WriteLog(pKey, pHeaderValue); pKey = "AttachmentName"; pHeaderValue = pHttpRequest->GetHeader(pKey); WriteLog(pKey, pHeaderValue); pKey = "MS-ASProtocolVersion"; pHeaderValue = pHttpRequest->GetHeader(pKey); WriteLog(pKey, pHeaderValue); pKey = "X-EAS-Proxy"; pHeaderValue = pHttpRequest->GetHeader(pKey); WriteLog(pKey, pHeaderValue); pKey = "User-Agent"; pHeaderValue = pHttpRequest->GetHeader(pKey); WriteLog(pKey, pHeaderValue); /* //Authorization pKey = "Authorization"; pHeaderValue = pHttpRequest->GetHeader(pKey); writeLog(pKey, pHeaderValue); //Content-Type pKey = "Content-Type"; pHeaderValue = pHttpRequest->GetHeader(pKey); writeLog(pKey, pHeaderValue); //Host pKey = "Host"; pHeaderValue = pHttpRequest->GetHeader(pKey); writeLog(pKey, pHeaderValue); //Content-Length pKey = "Content-Length"; pHeaderValue = pHttpRequest->GetHeader(pKey); writeLog(pKey, pHeaderValue);*/ WriteLog("<-- CTestNativeModule, DumpRequestHeader()"); } void DumpRequestContent(IHttpContext* pHttpContext, IHttpRequest* pHttpRequest) { // Create an HRESULT to receive return values from methods. /*HRESULT hr; // Allocate a 1K buffer. DWORD cbBytesReceived = 1024; void* pvRequestBody = pHttpContext->AllocateRequestMemory(cbBytesReceived); hr = pHttpRequest->ReadEntityBody( pvRequestBody, cbBytesReceived, false, &cbBytesReceived, NULL);*/ } }; // Create the module's class factory. class CTestNativeModuleFactory : public IHttpModuleFactory { public: HRESULT GetHttpModule( OUT CHttpModule ** ppModule, IN IModuleAllocator * pAllocator ) { WriteLog("--> CTestNativeModuleFactory, GetHttpModule()"); UNREFERENCED_PARAMETER( pAllocator ); // Create a new instance. CTestNativeModule * pModule = new CTestNativeModule; // Test for an error. if (!pModule) { // Return an error if the factory cannot create the instance. return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY ); } else { // Return a pointer to the module. *ppModule = pModule; pModule = NULL; // Return a success status. return S_OK; } WriteLog("<-- CTestNativeModuleFactory, GetHttpModule()"); } void Terminate() { WriteLog("--> CTestNativeModuleFactory, Terminate()"); // Remove the class from memory. delete this; WriteLog("<-- CTestNativeModuleFactory, Terminate()"); } }; // Create the module's exported registration function. HRESULT __stdcall RegisterModule( DWORD dwServerVersion, IHttpModuleRegistrationInfo * pModuleInfo, IHttpServer * pGlobalInfo ) { WriteLog("--> RegisterModule()"); HRESULT hr = S_OK; UNREFERENCED_PARAMETER( dwServerVersion ); UNREFERENCED_PARAMETER( pGlobalInfo ); // TODO // Register for notifications // Set notification priority CTestNativeModuleFactory* testNMFacotry = new CTestNativeModuleFactory; // Set the request notifications // BeginRequest hr = pModuleInfo->SetRequestNotifications( testNMFacotry, RQ_BEGIN_REQUEST, // Register for BeginRequest notifications 0); if( hr == S_OK ) // Do this only if there was no error { hr = pModuleInfo->SetPriorityForRequestNotification( RQ_BEGIN_REQUEST, // which notification PRIORITY_ALIAS_FIRST // what priority ); } WriteLog("<-- RegisterModule()"); return hr; }