动态加载
				DLL
				需要使用
				Windows API
				函数:
				LoadLibrary
				、
				GetProcAddress
				以及
				FreeLibrary
				。我们可以使用
				DllImport
				在
				C#
				中使用这三个函数。
		
		 
		
				[DllImport("Kernel32")]
		
		
				public
				
						static
						extern
						int
						GetProcAddress(inthandle, Stringfuncname);
		
		 
		
				[DllImport("Kernel32")]
		
		
				public
				
						static
						extern
						int
						LoadLibrary(Stringfuncname);
		
		 
		
				[DllImport("Kernel32")]
		
		
				public
				
						static
						extern
						int
						FreeLibrary(inthandle);
		
		 
		
				当我们在
				C++
				中动态调用
				Dll
				中的函数时,我们一般的方法是:
		
		
				假设
				DLL
				中有一个导出函数,函数原型如下:
		
		
				BOOL
				
						__stdcall foo(Object &object, LPVOID lpReserved);
		
		 
		
				1
				、首先定义相应的函数指针:
		
		
				typedef
				
						BOOL (__stdcall *PFOO)(Object &object, LPVOID lpReserved);
		
		 
		
				2
				、调用
				LoadLibrary
				加载
				dll
				:
		
		
				HINSTANCE
				 hInst = ::LoadLibraryW(dllFileName);
		
		 
		
				3
				、调用
				GetProcAddress
				函数获取要调用函数的地址:
		
		
				PFOO
				 foo = (PFOO)GetProcAddress(hInst,"foo");
		
		
				if
				(foo == NULL)
		
		
				{
		
		
				    FreeLibrary(hInst);
		
		
				    return false;
		
		
				}
		
		 
		
				4
				、调用
				foo
				函数:
		
		
				BOOL
				 bRet = foo(object,(LPVOID)NULL);
		
		 
		
				5
				、使用完后应释放
				DLL
				:
		
		
				FreeLibrary(hInst);
		
		 
		
				那么在
				C#
				中应该怎么做呢?方法基本上一样,我们使用委托来代替
				C++
				的函数指针,通过
				.NET Framework 2.0
				新增的函数
				GetDelegateForFunctionPointer
				来得到一个委托的实例:
		
		 
		
				下面封装了一个类,通过该类我们就可以在
				C#
				中动态调用
				Dll
				中的函数了:
		
		 
		
				public class
				
						DLLWrapper
				
		
		
				{
		
		
				    ///<summary>
		
		
				    /// API LoadLibrary
		
		
				    ///</summary>
		
		
				    [DllImport("Kernel32")]
		
		
				    publicstaticexternintLoadLibrary(Stringfuncname);
		
		 
		
				    ///<summary>
		
		
				    /// API GetProcAddress
		
		
				    ///</summary>
		
		
				    [DllImport("Kernel32")]
		
		
				    publicstaticexternintGetProcAddress(inthandle, Stringfuncname);
		
		 
		
				    ///<summary>
		
		
				    /// API FreeLibrary
		
		
				    ///</summary>
		
		
				    [DllImport("Kernel32")]
		
		
				    publicstaticexternintFreeLibrary(inthandle);
		
		 
		
				    ///<summary>
		
		
				    ///
				通过非托管函数名转换为对应的委托
				, by jingzhongrong
		
		
				    ///</summary>
		
		
				    ///<param name="dllModule">
				通过
				LoadLibrary
				获得的
				DLL
				句柄
				</param>
		
		
				    ///<param name="functionName">
				非托管函数名
				</param>
		
		
				    ///<param name="t">
				对应的委托类型
				</param>
		
		
				    ///<returns>
				委托实例,可强制转换为适当的委托类型
				</returns>
		
		
				    publicstaticDelegateGetFunctionAddress(intdllModule, stringfunctionName, Typet)
		
		
				    {
		
		
				       intaddress = GetProcAddress(dllModule, functionName);
		
		
				       if (address == 0)
		
		
				           returnnull;
		
		
				       else
		
		
				           returnMarshal.GetDelegateForFunctionPointer(newIntPtr(address), t);
		
		
				    }
		
		 
		
				    ///<summary>
		
		
				    ///
				将表示函数地址的
				IntPtr
				实例转换成对应的委托
				, by jingzhongrong
		
		
				    ///</summary>
		
		
				    publicstaticDelegateGetDelegateFromIntPtr(IntPtraddress, Typet)
		
		
				    {
		
		
				       if (address == IntPtr.Zero)
		
		
				           returnnull;
		
		
				       else
		
		
				           returnMarshal.GetDelegateForFunctionPointer(address, t);
		
		
				    }
		
		 
		
				    ///<summary>
		
		
				    ///
				将表示函数地址的
				int
				转换成对应的委托,by jingzhongrong
		
		
				    ///</summary>
		
		
				    publicstaticDelegateGetDelegateFromIntPtr(intaddress, Typet)
		
		
				    {
		
		
				       if (address == 0)
		
		
				           returnnull;
		
		
				       else
		
		
				           returnMarshal.GetDelegateForFunctionPointer(newIntPtr(address), t);
		
		
				    }
		
		
				}
		
		 
		
				通过这个类,我们这样调用
				DLL
				:
		
		 
		
				1
				、声明相应的委托(正确声明很重要,否则不能调用成功,后面有详细介绍)。
		
		 
		
				2
				、加载
				DLL
				:
		
		
				int
				 hModule
				 = DLLWrapper.LoadLibrary(dllFilePath);
		
		
				if
				 (hModule == 0)
		
		
				    returnfalse;
		
		 
		
				3
				、获取相应的委托实例:
		
		
				FOO
				
						foo = (FOO)DLLWrapper.GetFunctionAddress(hModule, "foo", typeof(FOO));
		
		
				if
				 (foo == null)
		
		
				{
		
		
				    DLLWrapper.FreeLibrary(hModule);
		
		
				    returnfalse;
		
		
				}
		
		 
		
				4
				、调用函数:
		
		
				foo(...);
		
		 
		
				5
				、
				.NET
				并不能自动释放动态加载的
				DLL
				,因此我们在使用完
				DLL
				后应该自己释放
				DLL
				:
		
		
				DLLWrapper
				.FreeLibrary(hModule);
		
		 
		
				下面我们将就委托应如何声明进行相应的讨论,在实际操作过程中,我发现使用
				DllImport
				方法和动态调用方法两者在
				C#
				中对
				DLL
				中函数原型的声明是有些区别的,下面我介绍动态调用中委托的声明:
		
		 
		
				1
				、首先应该注意的是,
				C++
				中的类型和
				C#
				中类型的对应关系,比如
				C++
				中的
				long
				应该对应
				C#
				中的
				Int32
				而不是
				long
				,否则将导致调用结果出错。
		
		 
		
				2
				、结构的声明使用
				StructLayout对结构的相应布局进行设置,具体的请查看
				MSDN:
		
		 
		
				使用
				LayoutKind
				指定结构中成员的布局顺序,一般可以使用
				Sequential
				:
		
		
				    [StructLayout(LayoutKind.Sequential)]
		
		
				    structStructVersionInfo
		
		
				    {
		
		
				       publicintMajorVersion;
		
		
				       publicintMinorVersion;
		
		
				    }
		
		
				另外,如果单独使用内部类型没有另外使用到字符串、结构、类,可以将结构在
				C#
				中声明为
				class
				:
		
		
				    [StructLayout(LayoutKind.Sequential)]
		
		
				    classStructVersionInfo
		
		
				    {
		
		
				       publicintMajorVersion;
		
		
				       publicintMinorVersion;
		
		
				    }
		
		 
		
				对应
				C++
				中的声明:
		
		
				    typedef
				
						struct
						_VERSION_INFO
				
		
		
				    {
		
		
				        intMajorVersion;
		
		
				        intMinorVersion;
		
		
				    } VERSION_INFO, *PVERSION_INFO;
		
		 
		
				如果结构中使用到了字符串,最好应指定相应的字符集:
		
		
				    
				[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)]
		
		 
		
				部分常用的声明对应关系(在结构中):
		
		
				C++
				:字符串数组
		
		
				    
				wchar_t
				
						Comments[120];
		
		
				C#
				:
		
		
				    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 120)]
		
		
				    publicstringComments;
		
		 
		
				C++
				:结构成员
		
		
				    VERSION_INFO
				 ver;
		
		
				C#
		
		
				    
				public
				StructVersionInfo 
				ver;
		
		 
		
				C++
				:函数指针声明
		
		
				    
				PFOO
				 pFoo; //
				具体声明见文章前面部分
		
		
				C#:
		
		
				    
				public
				IntPtr
				 pFoo; 
				//
				也可以为
				 public int pFoo; 
		
		
				        //
				不同的声明方法可以使用上面
				DLLWrapper
				类的相应函数获取对应的委托实例
		
		 
		
				如果在结构中使用到了
				union
				,那么可以使用
				FieldOffset
				指定具体位置。
		
		 
		
				3
				、委托的声明:
		
		 
		
				当
				C++
				编写的
				DLL
				函数需要通过指针传出将一个结构:如以下声明:
		
		
				    
				void
				 getVersionInfo(
				VERSION_INFO
				 *ver);
		
		
				对于在
				C#
				中声明为
				class
				的结构(当
				VERSION_INFO
				声明为
				class
				)
		
		
				    
				delegate void
				getVersionInfo
				(
				VERSION_INFO
				 ver);
		
		
				如果结构声明为
				struct
				,那么应该使用如下声明:
		
		
				    
				delegate void
				getVersionInfo
				(
				ref
				VERSION_INFO
				 ver);
		
		
				注意:应该使用
				ref
				关键字。
		
		 
		 
		
				如果
				DLL
				函数需要传入一个字符串,比如这样:
		
		
				    BOOL
				
						__stdcall jingzhongrong1(constwchar_t* lpFileName, int* FileNum);
		
		
				那么使用委托来调用函数的时候应该在
				C#
				中如下声明委托:
		
		
				    delegatebooljingzhongrong1(
		
		
				       [MarshalAs(UnmanagedType.LPWStr)]StringFileName, 
		
		
				       refintFileNum);
		
		
				注意:应该使用
				[MarshalAs(UnmanagedType.LPWStr)]
				和
				String
				进行声明。
		
		 
		 
		
				如果要在
				DLL
				函数中传出一个字符串,比如这样:
		
		
				    void
				
						__stdcall jingzhongrong2(
		
		
				    wchar_t* lpFileName, 
				//
				要传出的字符串
		
		
				    int* Length);
		
		
				那么我们如下声明委托:
		
		
				    //
				使用委托从非托管函数的参数中传出的字符串,
		
		
				    //
				应该这样声明,并在调用前为
				StringBuilder
				预备足够的空间
		
		
				    delegatevoidjingzhongrong2(
		
		
				       [MarshalAs(UnmanagedType.LPWStr)] StringBuilderlpFileName,
		
		
				       refintLength,
		
		
				    );
		
		
				在使用函数前,应先为
				StringBuilder
				声明足够的空间用于存放字符串:
		
		
				    
				StringBuilder
				
						fileName = newStringBuilder(FileNameLength);