对非托管函数进行平台调用,最小要求就是要为DllImport属性传入包含所要调用的非托管函数的DLL的名称。DLL的名称,可以是DLL的绝对路径,如DllImport(@"D:"BookCode"NativeLib.dll")。也可以是DLL的相对路径,如DllImport("NativeLib.dll")。当指定为相对路径时,CLR会在以下搜索路径下搜索这个非托管DLL。首先,CLR会在当前目录下搜索这个非托管DLL。如果没找到,就会转向搜索Windows系统目录。如果还没有找到,则会在PATH环境变量所列出的所有目录中进行搜索。如果在上述所列出的所有目录中都没有找到这个DLL时,CLR就会抛出一个DllNotFoundException。由此可见,如果要调用的非托管函数没有位于所有可供搜索的路径之下,那么就无法对非托管函数进行平台调用。
仔细分析平台调用的过程和原理,就能直观地想出一个进行动态平台调用的方法。那就是在对非托管DLL中的函数进行平台调用之前,也就是CLR开始在默认搜索路径下搜索非托管DLL并使用Win32 API LoadLibrary加载此DLL之前,手动地使用LoadLibrary来加载非托管DLL。这样CLR就能搜索到这个非托管DLL,并对其中指定的非托管函数进行平台调用了。
class DynamicPInvokeViaLoadLib
{
[DllImport("NativeLibForDynamicPInvoke.dll")]
static extern int Multiply(int factorA, int factorB);
public static void Test()
{
string currentDirectory =
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string dllPath = Path.Combine(currentDirectory,
@"nativelibfordynamicpinvoke"NativeLibForDynamicPInvoke.dll");
IntPtr dllAddr = Win32API.LoadLibrary(dllPath);
if (dllAddr == IntPtr.Zero)
{
throw new DllNotFoundException(
string.Format("Can not load {0}, please check.", dllPath));
}
//调用非托管函数
int factorA = 100, factorB = 8;
int result = Multiply(factorA, factorB);
//打印结果
Console.WriteLine(string.Format("{0} * {1} = {2} ", factorA, factorB, result));
Console.WriteLine("Press any key to exit.");
Console.Read();
}
}
其中LoadLibrary是在Win32API类中进行声明的Win32 API函数,其定义如下:
[DllImport("kernel32.dll", EntryPoint = "LoadLibrary")]
public static extern IntPtr LoadLibrary(
string lpLibFileName
);
如同之前进行平台调用时一样,首先在托管代码中对需要调用的非托管函数进行声明,并为其加上DllImport属性。在为DllImport属性传入包含所要调用的非托管函数的DLL的名称时,使用相对路径,只传入了非托管DLL的名称。由于这个DLL并不在CLR的默认搜索路径下,因此CLR就不能搜索到它并使用LoadLibrary来加载它了。解决这个问题的办法就是在对非托管DLL中的函数进行平台调用之前,手动地使用LoadLibrary来加载非托管DLL。上面代码中的粗体部分就演示了这种方法。
运行DynamicPInvokeViaLoadLib类的Test方法进行测试,就能得到如下的结果:
100 * 8 = 800 Press any key to exit. |
未完待续......