C++调用DotNet类库实现ISAPI Filter
#include "stdafx.h"
#include "mscoree.h"
#include "stdio.h"
#include "string.h"
#include "httpfilt.h"
//CLR宿主控制接口
ICLRControl *clrcontrol=NULL;
//CLR运行时宿主
ICLRRuntimeHost * clr=NULL;
//函数指针用于调用托管委托
//函数指针用于调用DotNet类库中定义的函数
DWORD (* FilterProc)(HTTP_FILTER_CONTEXT *pfc, DWORD NotificationType,VOID *pvNotification);//过滤器过滤事件调用的函数 参数一过滤器上下文(IIS) 事件类型 事件参数
BOOL (* FilterVersion)(HTTP_FILTER_VERSION * pVer);//过滤器初始化时候调用pVer过滤器版本 程序中填充
BOOL (* Terminate)(DWORD dwFlags);//卸载过滤器时候调用
//以下三个方法定义又IIS调用的方法 然后转向调用上边定义的函数指针
__declspec(dllexport) DWORD WINAPI HttpFilterProc(HTTP_FILTER_CONTEXT *pfc,DWORD NotificationType,VOID * pvNotification)
{
if(FilterProc!=NULL)
return FilterProc(pfc,NotificationType,pvNotification);
return 0;
}
__declspec(dllexport) BOOL WINAPI GetFilterVersion(HTTP_FILTER_VERSION * pVer)
{
if(FilterVersion==NULL)
{
Init();
}
if(FilterVersion!=NULL)
{
return FilterVersion(pVer);
}
return FALSE;
}
__declspec(dllexport) BOOL WINAPI TerminateFilter(DWORD dwFlags)
{
if(Terminate!=NULL)
return Terminate(dwFlags);
return TRUE;
}
//次方法用于初始化函数指针以及加载DotNet框架
int Init()
{
//ICLRRuntimeHost接口的指针
LPVOID clrptr;
HRESULT res;
DWORD value=0;
void (* p)(LPWSTR str);
//加载DotNet框架
res=CorBindToRuntimeEx(TEXT("v2.0.50727"),TEXT("svr"),
//启动编译优化
STARTUP_LOADER_OPTIMIZATION_MASK|
//启动多线程GC(垃圾回收器)
STARTUP_CONCURRENT_GC|
//所有程序集归属单个应用程序域(非共享)
STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN,
CLSID_CLRRuntimeHost,
IID_ICLRRuntimeHost,&clrptr);
clr=(ICLRRuntimeHost*)clrptr;
clr->GetCLRControl(&clrcontrol);
//设置CLR应用程序域控制器
res=clrcontrol->SetAppDomainManagerType(TEXT("mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"),TEXT("System.AppDomain"));
res=clr->Start();//启动CLR(所有对CLR的设置都必须在启动前进行)
//在默认应用程序域中执行函数(参数1 类库文件路径 参数2 类型完全限定名(包含命名空间) 参数3 方法名称(静态方法) 参数4 方法的参数) 注意:方法必须返回一个Int类型 参数是一个String类型
res=clr->ExecuteInDefaultAppDomain(TEXT("C:\\FilterProc.dll"),TEXT("ConsoleApplication1.BaseISAPIFilter"),TEXT("GetProcAddr"),TEXT("1"),&value);
FilterVersion=(BOOL (*)(HTTP_FILTER_VERSION * pVer))value;//根据返回的函数指针强制装换 并且设置函数指针
res=clr->ExecuteInDefaultAppDomain(TEXT("C:\\FilterProc.dll"),TEXT("ConsoleApplication1.BaseISAPIFilter"),TEXT("GetProcAddr"),TEXT("2"),&value);
FilterProc=(DWORD (*)(HTTP_FILTER_CONTEXT *pfc,DWORD NotificationType,VOID *pvNotification))value;//根据返回的函数指针强制装换 并且设置函数指针
res=clr->ExecuteInDefaultAppDomain(TEXT("C:\\FilterProc.dll"),TEXT("ConsoleApplication1.BaseISAPIFilter"),TEXT("GetProcAddr"),TEXT("3"),&value);
Terminate=(BOOL (*)(DWORD dwFlags))value;//根据返回的函数指针强制装换 并且设置函数指针
return 0;
}
#include "mscoree.h"//这个头文件在安装VS2008后会在Program files\Windows SDK\v6.0\include
以上参考MSDN编写
经过测试Init方法不能写在DllMain中 可能是我写错了 不过写在DllMain中后无法正常加载!
基本机制:
1、先加载 CLR到当前进程中 得到CLR的宿主接口ICLRRuntimeHost
2、然后通过得到的ICLRRuntimeHost接口获取ICLRControl接口用于控制CLR
3、调用自己编写好的方法返回函数指针 微软设计的很好! 返回值Int类型 可以用作函数指针来使用 函数指针本身也是32位的值类型 Int类型经过强制装换就能得到函数指针。
函数的参数是一个String类型 一系列命令执行对应动作
下来是C#代码
public static BaseISAPIFilter _Share;//定义BaseISAPIFilter对象 不能放对象失去引用除非返回的方法是静态方法
public static int GetProcAddr(string proc)
{
try
{
if (_Share == null)//如果当前应用为空则实例化一个
_Share = new BaseISAPIFilter();
switch (proc)//判断参数
{
case "1":
return Marshal.GetFunctionPointerForDelegate(_Share._getFilterVersion).ToInt32();//返回_Share._getFilterVersion这个委托的地址然后转换成为Int32类型(在C++中进行转换就重新得到这个指针)
case "2":
return Marshal.GetFunctionPointerForDelegate(_Share._httpFilterProc).ToInt32();//同上
case "3":
return Marshal.GetFunctionPointerForDelegate(_Share._terminateFilter).ToInt32();//同上
}
}
catch (Exception e)
{
sw.WriteLine(e.Message);
sw.Flush();
}
finally
{
sw.WriteLine(proc);
sw.Flush();
}
return 0;
}
//委托
#region 定义提供给非托管代码调用的指针
public GetFilterVersion _getFilterVersion;
public TerminateFilter _terminateFilter;
public HttpFilterProc _httpFilterProc;
#endregion
//构造函数 初始化委托
public BaseISAPIFilter()
{
_getFilterVersion = new GetFilterVersion(GetFilterVersionProc);
_terminateFilter = new TerminateFilter(TerminateFilterProc);
_httpFilterProc = new HttpFilterProc(HttpFilterProcProc);
Init();//初始化其他东西
}
以下代码是对这些委托的定义
//以下委托提供回调从托管平台回调
//被[UnmanagedFunctionPointer(CallingConvention.Cdecl)]标志的委托将用于从托管代码传递到非托管代码(函数指针传递)其中Cdecl标志函数将采用C语言的调用方式
///
/// 获取过滤器版本
///
/// 服务器版本信息的数据结构的指针 函数对结构进行填充
/// 确定是否获取成功
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate bool GetFilterVersion(IntPtr pVer);
///
/// 关闭过滤器时调用
///
/// 关闭的标志
/// 确定是否关闭成功
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate bool TerminateFilter(int dwFlags);
///
/// 处理数据的回调函数的委托用于在非托管函数调用
///
/// 过滤器上下文
/// 事件类型
/// 事件的附加结构
/// 处理状态(确定是否继续处理)
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate SF_STATUS_TYPE HttpFilterProc(IntPtr pfc, SF_Notify notificationType, IntPtr pvNotification);
#endregion
DotNet部分也没有多少代码和我上边说的一样根据一个字符串参数返回一个委托的指针(函数指针通过这个指针就能够调用的这个委托)
这些委托必须写以 [UnmanagedFunctionPointer(CallingConvention.StdCall)]否则调用会失败 出现堆栈问题CallingConvention.StdCall这里要和你C++项目的一样
委托参数写的类型都是根据ISAPIFilter中的结构定义出来的代码量比较大一点 而且你也不一定回用这个去写ISAPIFilter吧!
我想需要看这些东西的人必定都算是高手也希望大家能来这个群一起讨论DotNet方面的技术
public static int GetProcAddr(string proc)
假设这个方法的参数字符串是一个函数的地址转换成为字符串类型我们同样可以 在转换成数字然后再转换Intptr类型
在后通过Marshal类型得到一个委托 同样这个委托也可以调用到这个函数的地址。
3COME考试频道为您精心整理,希望对您有所帮助,更多信息在http://www.reader8.com/exam/