今天蛋疼试验了下这方面的东东...
试图着在.net下像在C++里一样载入一个Win32dll后调用它的函数.
当然,要实现这样的功能.LoadLibrary和GetProcAddress是必须的.
于是乎先写出了这样的声明:
[DllImport("kernel32.dll", CharSet = CharSet.Auto)] static extern IntPtr LoadLibrary(string fname); [DllImport("kernel32.dll", CharSet = CharSet.Ansi)] static extern Delegate GetProcAddress(IntPtr hmodule, string funcName); delegate int msgboxcall(int a, int b, int c, int d);
然后试着像在C++里一样先载入user32.dll后查找MessageBoxA的地址.再强转为msgboxcall.
结果程序直接抛出了一个ExecutionEngineException后崩掉了囧....
看来虽然.net里是拿委托当函数指针用但直接用委托的基类当返回类型还是不行啊...
于是我改了下GetProcAddress的声明,把返回类型直接改成了msgboxcall.这回行了,调用得到的委托
也弹出了需要的空对话框.
但是这样有个问题...如果我有n条参数不同的函数调用,岂不是要写n个GetProcAddress?很显然.这样的写法很不适合
做成库之类的东东.
.net里凡是涉及平台调用的东东基本都会扯到Marshal.我查了一下.果然,Marshal提供了一个方法GetDelegateForFunctionPointer,
用于由函数指针生成指定的委托对象.接下来就好办了,改了下声明.写了这么条泛型函数:
[DllImport("kernel32.dll", CharSet = CharSet.Auto)] static extern IntPtr LoadLibrary(string fname); [DllImport("kernel32.dll", CharSet = CharSet.Ansi)] static extern IntPtr GetProcAddress(IntPtr hmodule, string funcName); public static T GetProcAddress<T>(string libname, string funcname) where T : class { if (typeof(T).BaseType != typeof(MulticastDelegate)) return null; IntPtr hlib = LoadLibrary(libname); if (hlib == IntPtr.Zero) return null; IntPtr pproc = GetProcAddress(hlib, funcname); if (pproc == IntPtr.Zero) return null; var ret = Marshal.GetDelegateForFunctionPointer(pproc, typeof(T)) as T; return ret; }
泛型约束不能用Delegate之类的特殊类.所以只好在函数的开头做了个基类的检查,以保证后面不会出错.
接下来试着调用
var c = ISAPICaller.GetProcAddress<msgboxcall>("user32.dll", "MessageBoxA");
c(0, 0, 0, 0);
成功的弹出了对话框.接下来解决字符串编码的问题.之前的msgboxcall为了测试方便把中间两个参数声明成了int后直接传0.
现在改成
delegate int msgboxcall(int a, string b, string c, int d);
然后用之前的代码,不过对c的调用改成
c(0,"test","测试",0);
编译后运行成功.不过当我把查找的函数改成MessageBoxW后,预想中的乱码果然出现了...
委托的声明不像直接声明API那样可以直接指定封送时使用的字符串编码.我试着指定模块的默认封送编码也没用.困扰了十多分钟
后才突然反应过来:
既然默认是ANSI.我就逐个指明是UNICODE就ok了嘛....
于是我把委托改成了这样的声明:
delegate int msgboxcall(int a, [MarshalAs(UnmanagedType.LPWStr)] string b, [MarshalAs(UnmanagedType.LPWStr)] string c, int d);
接下来再用同样的代码调用MessageBoxW终于成功了.这样这段代码基本也能实用了.
不过这段代码仍然有它的缺陷...它无法指定函数的调用规则.从对MessageBox的成功调用来看是使用了stdcall.但dll的导出函数可以是任何
调用规则.在.net中声明的时候可以通过DllImportAttribute.CallingConvertion来指明使用何种方式,但我查了半天MSDN没找到如何指定
GetDelegateForFunctionPointer得到的委托调用规则的方法:(估计要实现这样的功能得用到自己动态生成IL之类的东东了吧.