这几天做项目时,遇到了 C#调用C++函数指针 的问题,C#调用C++ dll中的函数很简单,函数指针还真没调过,看了几篇文章研究了会儿后把问题搞定了,这里总结一下。
先看两篇文章:
http://jc.nfxxw.com/n/200607/14/n20060714_19510.shtml C#中的函数指针 (实在是找不到出处了)
http://dev.csdn.net/article/69/69261.shtm 如何在C#中使用 Win32和其他库 (这篇我在msdn上找了半天也没找到!)
函数指针搞C++的人应该都知道,效率高,易用性强,隐蔽代码等。在C++里面调用C++写的dll的函数指针那是在容易不过了。使用C#就稍微麻烦点了!那怎么掉呢?通过上面的第一篇文章我们知道应该使用委托 delegate。如果再高级点,定义一个函数指针结构(有点像linux的内核),也同样可以用C#调用。
猴子提示:委托就和C++中的函数指针一样。
下面我只写出了.h和.cs文件,都是伪代码,大家如果能明白意思,可以很简单的用到自己的项目了。
.h文件
// 获得梦之都网站的ip pDes - 梦之都的网址 http://www.dreamdu.com/// 返回值 - false 获取失败 true 获取成功typedef bool (*Dreamdu_PGetIP)(char const* pDes) ; // 获得可爱的猴子的名字 pDes - 可爱的猴子的名字 http://www.dreamdu.com/blog/// 返回值 - false 获取失败 true 获取成功typedef void (*Monkey_PGetName)(char const* pDes) ; // 梦之都的函数指针结构struct Dreamdu_Struct{Dreamdu_PGetIPDreamdu_GetIP;Monkey_PGetName Monkey_GetName;} ;// 获取梦之都的函数指针结构的函数指针// pDreamdu - 梦之都的函数指针结构数组typedef void (*Dreamdu_PGetDreamduStruct)(Dreamdu_Struct *pDreamdu) ;// 获取梦之都函数指针结构的函数 这个就是输出函数啦 哈哈// pDreamdu - 函数指针结构数组extern "C" Dreamdu_DLL_EXT_ void Dreamdu_GetDreamduStruct(Dreamdu_Struct *pDreamdu) ;
.csCS文件
public class Dreamdu{//加载dll[DllImport("kernel32.dll")]private static extern IntPtr LoadLibrary(string lpFileName); //获得函数指针的地址[DllImport("kernel32.dll")]private static extern GetDreamduFuns GetDreamduAddress(IntPtr hModule, string lpProcName);//释放dll[DllImport("kernel32", EntryPoint = "FreeLibrary", SetLastError = true)]private static extern bool FreeLibrary(IntPtr hModule);private delegate void GetDreamduFuns(IntPtr funs);private delegate bool Dreamdu_PGetIP(string name);private delegate void Monkey_PGetName(string name);//函数指针结构在cs中的声明[StructLayout(LayoutKind.Sequential, Pack = 1)]private class Dreamdu_Struct{public Dreamdu_PGetIP GetIP;public Monkey_PGetName GetName;}//装载 Dllpublic static void LoadDll(string lpFileName){hModule = LoadLibrary(lpFileName);if (hModule == IntPtr.Zero)throw (new Exception());}//获得函数指针public static void LoadFun(string lpProcName){if (hModule == IntPtr.Zero)throw (new Exception(""));dreamduStruct = GetProcAddress(hModule, lpProcName);if (farProc == null)throw (new Exception(""));try{pcon = Marshal.AllocHGlobal(Marshal.SizeOf(func));Marshal.StructureToPtr(func, pcon, true);dreamduStruct(pcon);Marshal.PtrToStructure(pcon, func);}finally{Marshal.FreeHGlobal(pcon);}}//卸载 Dllpublic static void UnLoadDll(){bool ret = FreeLibrary(hModule);hModule = IntPtr.Zero;dreamduStruct = null;}public static bool GetIP(string name){return func.Dreamdu_PGetIP(name);}public static void GetName(string name){return func.Monkey_PGetName(name);}private static IntPtr hModule = IntPtr.Zero;private static GetDreamduFuns dreamduStruct = null;private static FUNDreamduStruct func = new Dreamdu_Struct();private static IntPtr pcon = IntPtr.Zero;}
上面的C#接口就已经封装好了,仔细观察GetDreamduAddress函数kernel32.dll中此函数是返回FARPROC指针的,但是在这里我重新定义了一个委托来获取函数指针。
上面的C#接口就已经封装好了,可以使用下面方式调用。
class DreamduDLL{Dreamdu.LoadDll("Dreamdu.dll");Dreamdu.LoadFun("Dreamdu_GetDreamduStruct");//获取ipbool hasIP = Dreamdu.GetIP("www.dreamdu.com");if(hasIP){//获取名称Dreamdu.GetName("www.dreamdu.com/blog/");}//使用完了别忘了卸载dllDreamdu.UnLoadDll();}