最近在项目中碰到需要调用非托管C++生成的dll,下面将自己遇到的问题,以及解决的办法总结如下:
1.
问题:我们通常去映射dll的方法是使用
publicconststringdllPath=path;
[DllImport(dllPath+"test.dll",EntryPoint="test()",SetLastError=true,
CharSet=CharSet.Ansi,ExactSpelling=true,CallingConvention=CallingConvention.Cdecl)]
publicstaticexterninttest(); 这种方式有一个致命的缺陷,dll文件路径dllPath必须为const,而const是编译时常量,也就是说dllPath赋值必须是字符串常量,如果你想动态指定dll文件路径用这种方式基本上没办法实现,下面是一种解决方案。
解决方案:[DllImport("Kernel32.dll")]
publicstaticexternIntPtrLoadLibrary(stringlpFileName);
[DllImport("kernel32.dll",SetLastError=true)]
publicstaticexternintGetProcAddress(
IntPtrhModule,stringlpProcName);
[DllImport("kernel32.dll",EntryPoint="FreeLibrary",SetLastError=true)]
publicstaticexternboolFreeLibrary(IntPtrhModule);
这种方式有点像C#的反射,先引入这3个API 函数:
LoadLibrary(string lpFileName)是加载指定的dll文件,参数lpFileName为dll文件的路径,返回的为该dll的实例(指针)
GetProcAddress(IntPtr hModule,string lpProcName)是获取dll中指定的方法委托,参数hModule为LoadLibrary()方法返回的值,lpProcName方法名,返回值为指定方法的委托,注意要强制转换.
FreeLibrary(InPtr hModule)是释放加载的dll文件,参数hModule为LoadLibrary()返回值.
有了这些方法就能很容易的实现动态加载dll。
代码://dll实例
staticprivateIntPtrinstance;
//要加载方法的委托
delegatevoidTest();
//导入引擎dll
[DllImport("Kernel32.dll")]
publicstaticexternIntPtrLoadLibrary(stringlpFileName);
[DllImport("kernel32.dll",SetLastError=true)]
publicstaticexternintGetProcAddress(
IntPtrhModule,stringlpProcName);
[DllImport("kernel32.dll",EntryPoint="FreeLibrary",SetLastError=true)]
publicstaticexternboolFreeLibrary(IntPtrhModule);
//获取方法指针
staticprivateDelegateGetAddress(IntPtrdllModule,stringfunctionname,Typet)
{
intaddr=GetProcAddress(instance,functionname);
if(addr==0)
returnnull;
else
returnMarshal.GetDelegateForFunctionPointer(newIntPtr(addr),t);
}
//加载DLL
staticpublicvoidLoadLib()
{
stringdllPath;
dllPath=ConfigurationSettings.AppSettings["dllPath"].ToString();
instance=LoadLibrary(dllPath);
if(instance.ToInt32()==0)
{
thrownewLoadLibraryException("请在App.Config中配置引擎DLL的路径!");
}
}
///<summary>
///卸载DLL
///</summary>
staticpublicvoidFreeLib()
{
FreeLibrary(instance);
}
///<summary>
///实例
///</summary>
///<paramname="iptr"></param>
///<returns></returns>
staticpublicvoidtest()
{
try
{
Testtemp=(Test)GetAddress(instance,"test",typeof(Test));
returntemp();
}
catch(Exceptionex)
{
thrownewLoadLibraryException("");
}
}
//mydll.cpp
int f(int a, char* b, int* c)
{
return(10);
}
//mydll.def
LIBRARY MyDll
EXPORTS
f @1
//c#
[DllImport("mydll.dll")]
public static extern int f(int a,StringBuilder b,ref int c);
static void Main(string[] args)
{
StringBuilder b=new StringBuilder(10);
int c=0;
Console.WriteLine("{0}",f(10,b,ref c));
}