演练:使用本机代码创建Request-Level HTTP 模块
本演练演示如何使用 C++ 创建示例请求级 HTTP 模块,该模块在 IIS 7 中实现新的请求处理体系结构。 当你在早期版本的 ASP.NET HTTP 模块和 ISAPI 筛选器或扩展上编写 IIS 应用程序时,此新体系结构扩展了本机代码编程的功能。 有关使用新的请求处理体系结构设计 HTTP 模块的详细信息,请参阅 设计Native-Code HTTP 模块。
在本演练中,你将为 HTTP 模块创建一个 C++ 项目,添加“Hello World”项目所需的代码,然后编译并测试该模块。
先决条件
需要以下软件才能完成示例中的步骤:
IIS 7。
Visual Studio 2005。
Windows 软件开发工具包 (SDK) 。
注意 可以使用 Visual Studio .NET 2003 或更早版本,但演练步骤可能并不相同。
创建模块
在本演练的这一部分中,将为 HTTP 模块创建一个空的 C++ DLL 项目。
创建新的 C++ DLL 项目
打开 Visual Studio 2005。
验证全局选项是否具有指向 SDK 包括文件的所有正确路径:
在 “工具” 菜单上,单击 “选项” 。
在树视图中展开 “项目和解决方案 ”节点,然后单击“ VC++ 目录”。
在 “显示目录” 下拉框中,选择“ 包括文件”。
验证安装 Windows SDK 的路径是否列出了包含文件。 如果未列出路径,请单击“ 新建行 ”图标,然后添加安装 SDK 包含文件的路径。 默认安装目录为 $ (VCInstallDir) PlatformSDK\bin。
单击 “确定” 。
创建新的 C++ 项目:
在 “文件” 菜单上,指向 “新建” ,然后单击 “项目” 。
“新建项目” 对话框随即打开。
在“ 项目类型 ”窗格中,展开 “Visual C++ ”节点,然后单击“ Win32”。
在 “模板 ”窗格中,选择“ Win32 项目”。
在 “名称 ”框中,键入 “HelloWorld”。
在“ 位置 ”框中,键入示例的路径。
单击 “确定” 。
Win32 应用程序向导随即打开。
单击“应用程序设置”。
在 “应用程序类型”下,单击“ DLL”。
在 “其他选项”下,单击“ 空项目”。
单击“完成”。
添加代码和源文件
下一步是将所需的 C++ 和模块定义文件添加到项目中。
将源文件添加到项目
创建模块定义文件以导出 RegisterModule 函数:
在解决方案资源管理器中,右键单击“源文件”,指向“添加”,然后单击“新建项”。
此时将打开“添加新项”对话框。
在“类别”窗格中展开“Visual C++”节点,然后单击“代码”。
在 “模板 ”窗格中,选择“ 模块定义文件” 模板。
在“ 名称 ”框中,键入 “HelloWorld”,并在“ 位置 ”框中保留文件的默认路径。
单击“添加”。
添加具有 和
RegisterModule
的EXPORTS
行。 文件应如以下代码所示:LIBRARY"HelloWorld" EXPORTS RegisterModule
注意
可以使用 /EXPORT:RegisterModule 开关导出 RegisterModule 函数,而不是创建模块定义文件。
创建 C++ 文件:
在解决方案资源管理器中,右键单击“源文件”,指向“添加”,然后单击“新建项”。
此时将打开“添加新项”对话框。
在“类别”窗格中展开“Visual C++”节点,然后单击“代码”。
在 “模板 ”窗格中,选择“ C++ 文件” 模板。
在“ 名称 ”框中,键入 “HelloWorld”,并在“ 位置 ”框中保留文件的默认路径。
单击“添加”。
添加以下代码:
#define _WINSOCKAPI_ #include <windows.h> #include <sal.h> #include <httpserv.h> // Create the module class. class CHelloWorld : public CHttpModule { public: REQUEST_NOTIFICATION_STATUS OnBeginRequest( IN IHttpContext * pHttpContext, IN IHttpEventProvider * pProvider ) { UNREFERENCED_PARAMETER( pProvider ); // Create an HRESULT to receive return values from methods. HRESULT hr; // Retrieve a pointer to the response. IHttpResponse * pHttpResponse = pHttpContext->GetResponse(); // Test for an error. if (pHttpResponse != NULL) { // Clear the existing response. pHttpResponse->Clear(); // Set the MIME type to plain text. pHttpResponse->SetHeader( HttpHeaderContentType,"text/plain", (USHORT)strlen("text/plain"),TRUE); // Create a string with the response. PCSTR pszBuffer = "Hello World!"; // Create a data chunk. HTTP_DATA_CHUNK dataChunk; // Set the chunk to a chunk in memory. dataChunk.DataChunkType = HttpDataChunkFromMemory; // Buffer for bytes written of data chunk. DWORD cbSent; // Set the chunk to the buffer. dataChunk.FromMemory.pBuffer = (PVOID) pszBuffer; // Set the chunk size to the buffer size. dataChunk.FromMemory.BufferLength = (USHORT) strlen(pszBuffer); // Insert the data chunk into the response. hr = pHttpResponse->WriteEntityChunks( &dataChunk,1,FALSE,TRUE,&cbSent); // Test for an error. if (FAILED(hr)) { // Set the HTTP status. pHttpResponse->SetStatus(500,"Server Error",0,hr); } // End additional processing. return RQ_NOTIFICATION_FINISH_REQUEST; } // Return processing to the pipeline. return RQ_NOTIFICATION_CONTINUE; } }; // Create the module's class factory. class CHelloWorldFactory : public IHttpModuleFactory { public: HRESULT GetHttpModule( OUT CHttpModule ** ppModule, IN IModuleAllocator * pAllocator ) { UNREFERENCED_PARAMETER( pAllocator ); // Create a new instance. CHelloWorld * pModule = new CHelloWorld; // 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; } } void Terminate() { // Remove the class from memory. delete this; } }; // Create the module's exported registration function. HRESULT __stdcall RegisterModule( DWORD dwServerVersion, IHttpModuleRegistrationInfo * pModuleInfo, IHttpServer * pGlobalInfo ) { UNREFERENCED_PARAMETER( dwServerVersion ); UNREFERENCED_PARAMETER( pGlobalInfo ); // Set the request notifications and exit. return pModuleInfo->SetRequestNotifications( new CHelloWorldFactory, RQ_BEGIN_REQUEST, 0 ); }
编译和测试模块
编译和测试项目
编译 HTTP 模块:
在 “生成” 菜单上,单击 “生成解决方案” 。
验证 Visual Studio 是否未返回任何错误或警告。
将包含完整路径) 的 HelloWorld.dll 模块 (添加到
globalModules
%windir%\system32\inetsrv\config\applicationHost.config 文件的 部分。
使用 Internet Explorer 浏览到网站;应会看到“开始请求示例”,其中显示了请求计数。
注意
在后续生成中链接项目之前,需要停止 IIS。
设置疑难解答
如果模块未编译或未按预期工作,则可以检查以下几个方面:
确保已为导出的函数指定
__stdcall
,或者已使用__stdcall (/Gz)
调用约定配置编译。确保 IIS 已加载HelloWorld.dll:
在 IIS 管理器中,单击“连接”窗格中的“默认网站”。
在工作区 (中心窗格) ,选择“ 功能视图”。
在“ 分组依据 ”框中,选择“ 类别”。
在“ 服务器组件” 类别中,双击“ 模块”。
验证是否已列出 HelloWorld 模块。
确保已将正确的
RegisterModule
导出添加到定义文件。确保已将定义文件添加到项目设置。 若要将文件添加到项目设置,请完成以下步骤:
在 “项目” 菜单上,单击 “属性” 。
在树视图中展开 “配置属性” 节点,展开 “链接器 ”节点,然后单击“ 输入”。
对于 “模块定义文件 ”设置,请确保已列出定义文件。