查看完整版本: C#动态调用C++编写的DLL函数

Teenits 2008-5-15 23:51

C#动态调用C++编写的DLL函数

[table=98%][tr][td][/td]          [/tr]          [tr]            [td]
[url=http://bbs.zxbc.cn/]
[/url][/td]          [/tr]          [tr]            [td]            [font=宋体][C#[font=宋体]动态调用[/font]C++[font=宋体]编写的[/font]DLL[font=宋体]函数][/font]
[/font]
[font=宋体]        by jingzhongrong  2008-05-08[/font]


[font=宋体]动态加载[/font]DLL[font=宋体]需要使用[/font]Windows API[font=宋体]函数:[/font]LoadLibrary[font=宋体]、[/font]GetProcAddress[font=宋体]以及[/font]FreeLibrary[font=宋体]。我们可以使用[/font]DllImport[font=宋体]在[/font]C#[font=宋体]中使用这三个函数。[/font]

[align=left][[color=#2b91af]DllImport[/color]([color=#a31515]"Kernel32"[/color])][/align][align=left][color=blue]public[/color] [color=blue]static[/color] [color=blue]extern[/color] [color=blue]int[/color] [color=#303]GetProcAddress[/color]([color=blue]int[/color] [color=#303]handle[/color], [color=#2b91af]String[/color] [color=#303]funcname[/color]);[/align]
[align=left][[color=#2b91af]DllImport[/color]([color=#a31515]"Kernel32"[/color])][/align][align=left][color=blue]public[/color] [color=blue]static[/color] [color=blue]extern[/color] [color=blue]int[/color] [color=#303]LoadLibrary[/color]([color=#2b91af]String[/color] [color=#303]funcname[/color]);[/align]
[align=left][[color=#2b91af]DllImport[/color]([color=#a31515]"Kernel32"[/color])][/align][color=blue]public[/color] [color=blue]static[/color] [color=blue]extern[/color] [color=blue]int[/color] [color=#303]FreeLibrary[/color]([color=blue]int[/color] [color=#303]handle[/color]);

[font=宋体]当我们在[/font]C++[font=宋体]中动态调用[/font]Dll[font=宋体]中的函数时,我们一般的方法是:[/font]
[font=宋体]假设[/font]DLL[font=宋体]中有一个导出函数,函数原型如下:[/font]
[color=blue]BOOL[/color] [color=blue]__stdcall[/color] foo([color=blue]Object[/color] &object, [color=blue]LPVOID[/color] lpReserved);

1[font=宋体]、首先定义相应的函数指针:[/font]
[color=blue]typedef[/color] [color=blue]BOOL[/color] ([color=blue]__stdcall[/color] *PFOO)([color=blue]Object[/color] &object, [color=blue]LPVOID[/color] lpReserved);

2[font=宋体]、调用[/font]LoadLibrary[font=宋体]加载[/font]dll[font=宋体]:[/font]
[color=blue]HINSTANCE[/color] hInst = ::LoadLibraryW(dllFileName);

3[font=宋体]、调用[/font]GetProcAddress[font=宋体]函数获取要调用函数的地址:[/font]
[color=blue]PFOO[/color] foo = ([color=blue]PFOO[/color])GetProcAddress(hInst,"foo");
[color=blue]if[/color](foo == NULL)
{
    FreeLibrary(hInst);
    [color=blue]return[/color] false;
}

4[font=宋体]、调用[/font]foo[font=宋体]函数:[/font]
[color=blue]BOOL[/color] bRet = foo(object,([color=blue]LPVOID[/color])NULL);

5[font=宋体]、使用完后应释放[/font]DLL[font=宋体]:[/font]
FreeLibrary(hInst);

[font=宋体]那么在[/font]C#[font=宋体]中应该怎么做呢?方法基本上一样,我们使用委托来代替[/font]C++[font=宋体]的函数指针,通过[/font].NET Framework 2.0[font=宋体]新增的函数[/font]GetDelegateForFunctionPointer[font=宋体]来得到一个委托的实例:[/font]

[font=宋体]下面封装了一个类,通过该类我们就可以在[/font]C#[font=宋体]中动态调用[/font]Dll[font=宋体]中的函数了:[/font]

[align=left][color=blue]public class[/color] [color=#2b91af]DLLWrapper[/color][/align][align=left]{[/align][align=left]    [color=gray]///[/color][color=gray]<summary>[/color][/align][align=left]    [color=gray]///[/color][color=green] API LoadLibrary[/color][/align][align=left]    [color=gray]///[/color][color=gray]</summary>[/color][/align][align=left]    [[color=#2b91af]DllImport[/color]([color=#a31515]"Kernel32"[/color])][/align][align=left]    [color=blue]public[/color] [color=blue]static[/color] [color=blue]extern[/color] [color=blue]int[/color] [color=#303]LoadLibrary[/color]([color=#2b91af]String[/color] [color=#303]funcname[/color]);[/align]
[align=left]    [color=gray]///[/color][color=gray]<summary>[/color][/align][align=left]    [color=gray]///[/color][color=green] API GetProcAddress[/color][/align][align=left]    [color=gray]///[/color][color=gray]</summary>[/color][/align][align=left]    [[color=#2b91af]DllImport[/color]([color=#a31515]"Kernel32"[/color])][/align][align=left]    [color=blue]public[/color] [color=blue]static[/color] [color=blue]extern[/color] [color=blue]int[/color] [color=#303]GetProcAddress[/color]([color=blue]int[/color] [color=#303]handle[/color], [color=#2b91af]String[/color] [color=#303]funcname[/color]);[/align]
[align=left]    [color=gray]///[/color][color=gray]<summary>[/color][/align][align=left]    [color=gray]///[/color][color=green] API FreeLibrary[/color][/align][align=left]    [color=gray]///[/color][color=gray]</summary>[/color][/align][align=left]    [[color=#2b91af]DllImport[/color]([color=#a31515]"Kernel32"[/color])][/align][align=left]    [color=blue]public[/color] [color=blue]static[/color] [color=blue]extern[/color] [color=blue]int[/color] [color=#303]FreeLibrary[/color]([color=blue]int[/color] [color=#303]handle[/color]);[/align]
[align=left]    [color=gray]///[/color][color=gray]<summary>[/color][/align][align=left]    [color=gray]///[/color][color=green][font=宋体]通过非托管函数名转换为对应的委托[/font][/color][color=green], by jingzhongrong[/color][/align][align=left]    [color=gray]///[/color][color=gray]</summary>[/color][/align][align=left]    [color=gray]///[/color][color=gray]<param name="dllModule">[/color][color=green][font=宋体]通过[/font][/color][color=green]LoadLibrary[/color][color=green][font=宋体]获得的[/font][/color][color=green]DLL[/color][color=green][font=宋体]句柄[/font][/color][color=gray]</param>[/color][/align][align=left]    [color=gray]///[/color][color=gray]<param name="functionName">[/color][color=green][font=宋体]非托管函数名[/font][/color][color=gray]</param>[/color][/align][align=left]    [color=gray]///[/color][color=gray]<param name="t">[/color][color=green][font=宋体]对应的委托类型[/font][/color][color=gray]</param>[/color][/align][align=left]    [color=gray]///[/color][color=gray]<returns>[/color][color=green][font=宋体]委托实例,可强制转换为适当的委托类型[/font][/color][color=gray]</returns>[/color][/align][align=left]    [color=blue]public[/color] [color=blue]static[/color] [color=#2b91af]Delegate[/color] [color=#303]GetFunctionAddress[/color]([color=blue]int[/color] [color=#303]dllModule[/color], [color=blue]string[/color] [color=#303]functionName[/color], [color=#2b91af]Type[/color] [color=#303]t[/color])[/align][align=left]    {[/align][align=left]       [color=blue]int[/color] [color=#303]address[/color] = [color=#303]GetProcAddress[/color]([color=#303]dllModule[/color], [color=#303]functionName[/color]);[/align][align=left]       [color=blue]if[/color] ([color=#303]address[/color] == 0)[/align][align=left]           [color=blue]return[/color] [color=blue]null[/color];[/align][align=left]       [color=blue]else[/color][/align][align=left]           [color=blue]return[/color] [color=#2b91af]Marshal[/color].[color=#303]GetDelegateForFunctionPointer[/color]([color=blue]new[/color] [color=#2b91af]IntPtr[/color]([color=#303]address[/color]), [color=#303]t[/color]);[/align][align=left]    }[/align]
[align=left]    [color=gray]///[/color][color=gray]<summary>[/color][/align][align=left]    [color=gray]///[/color][color=green][font=宋体]将表示函数地址的[/font][/color][color=green]IntPtr[/color][color=green][font=宋体]实例转换成对应的委托[/font][/color][color=green], by jingzhongrong[/color][/align][align=left]    [color=gray]///[/color][color=gray]</summary>[/color][/align][align=left]    [color=blue]public[/color] [color=blue]static[/color] [color=#2b91af]Delegate[/color] [color=#303]GetDelegateFromIntPtr[/color]([color=#2b91af]IntPtr[/color] [color=#303]address[/color], [color=#2b91af]Type[/color] [color=#303]t[/color])[/align][align=left]    {[/align][align=left]       [color=blue]if[/color] ([color=#303]address[/color] == [color=#2b91af]IntPtr[/color].[color=#303]Zero[/color])[/align][align=left]           [color=blue]return[/color] [color=blue]null[/color];[/align][align=left]       [color=blue]else[/color][/align][align=left]           [color=blue]return[/color] [color=#2b91af]Marshal[/color].[color=#303]GetDelegateForFunctionPointer[/color]([color=#303]address[/color], [color=#303]t[/color]);[/align][align=left]    }[/align]
[align=left]    [color=gray]///[/color][color=gray]<summary>[/color][/align][align=left]    [color=gray]///[/color][color=green][font=宋体]将表示函数地址的[/font][/color][color=green]int[/color][color=green][font=宋体]转换成对应的委托,by jingzhongrong[/font][/color][/align][align=left]    [color=gray]///[/color][color=gray]</summary>[/color][/align][align=left]    [color=blue]public[/color] [color=blue]static[/color] [color=#2b91af]Delegate[/color] [color=#303]GetDelegateFromIntPtr[/color]([color=blue]int[/color] [color=#303]address[/color], [color=#2b91af]Type[/color] [color=#303]t[/color])[/align][align=left]    {[/align][align=left]       [color=blue]if[/color] ([color=#303]address[/color] == 0)[/align][align=left]           [color=blue]return[/color] [color=blue]null[/color];[/align][align=left]       [color=blue]else[/color][/align][align=left]           [color=blue]return[/color] [color=#2b91af]Marshal[/color].[color=#303]GetDelegateForFunctionPointer[/color]([color=blue]new[/color] [color=#2b91af]IntPtr[/color]([color=#303]address[/color]), [color=#303]t[/color]);[/align][align=left]    }[/align]}

[font=宋体]通过这个类,我们这样调用[/font]DLL[font=宋体]:[/font]

1[font=宋体]、声明相应的委托(正确声明很重要,否则不能调用成功,后面有详细介绍)。[/font]

2[font=宋体]、加载[/font]DLL[font=宋体]:[/font]
[color=blue]int[/color][color=#303] hModule[/color] = [color=#2b91af]DLLWrapper[/color].[color=#303]LoadLibrary[/color]([color=#303]dllFilePath[/color]);
[align=left][color=blue]if[/color] ([color=#303]hModule[/color] == 0)[/align]    [color=blue]return[/color] [color=blue]false[/color];

3[font=宋体]、获取相应的委托实例:[/font]
[align=left][color=#2b91af]FOO[/color] [color=#303]foo[/color] = ([color=#2b91af]FOO[/color])[color=#2b91af]DLLWrapper[/color].[color=#303]GetFunctionAddress[/color]([color=#303]hModule[/color], [color=#a31515]"foo"[/color], [color=blue]typeof[/color]([color=#2b91af]FOO[/color]));[/align][align=left][color=blue]if[/color] ([color=#303]foo[/color] == [color=blue]null[/color])[/align][align=left]{[/align][align=left]    [color=#2b91af]DLLWrapper[/color].[color=#303]FreeLibrary[/color]([color=#303]hModule[/color]);[/align][align=left]    [color=blue]return[/color] [color=blue]false[/color];[/align]}

4[font=宋体]、调用函数:[/font]
foo(...);

5[font=宋体]、[/font].NET[font=宋体]并不能自动释放动态加载的[/font]DLL[font=宋体],因此我们在使用完[/font]DLL[font=宋体]后应该自己释放[/font]DLL[font=宋体]:[/font]
[color=#2b91af]DLLWrapper[/color].[color=#303]FreeLibrary[/color]([color=#303]hModule[/color]);

[font=宋体]下面我们将就委托应如何声明进行相应的讨论,在实际操作过程中,我发现使用[/font]DllImport[font=宋体]方法和动态调用方法两者在[/font]C#[font=宋体]中对[/font]DLL[font=宋体]中函数原型的声明是有些区别的,下面我介绍动态调用中委托的声明:[/font]

1[font=宋体]、首先应该注意的是,[/font]C++[font=宋体]中的类型和[/font]C#[font=宋体]中类型的对应关系,比如[/font]C++[font=宋体]中的[/font]long[font=宋体]应该对应[/font]C#[font=宋体]中的[/font]Int32[font=宋体]而不是[/font]long[font=宋体],否则将导致调用结果出错。[/font]

2[font=宋体]、结构的声明使用[/font]StructLayout对结构的相应布局进行设置,具体的请查看MSDN:

[font=宋体]使用[/font]LayoutKind[font=宋体]指定结构中成员的布局顺序,一般可以使用[/font][color=#303]Sequential[/color][color=#303][font=宋体]:[/font][/color]
[align=left]    [[color=#2b91af]StructLayout[/color]([color=#2b91af]LayoutKind[/color].[color=#303]Sequential[/color])][/align][align=left]    [color=blue]struct[/color] [color=#2b91af]StructVersionInfo[/color][/align][align=left]    {[/align][align=left]       [color=blue]public[/color] [color=blue]int[/color] [color=#303]MajorVersion[/color];[/align][align=left]       [color=blue]public[/color] [color=blue]int[/color] [color=#303]MinorVersion[/color];[/align]    }
[font=宋体]另外,如果单独使用内部类型没有另外使用到字符串、结构、类,可以将结构在[/font]C#[font=宋体]中声明为[/font]class[font=宋体]:[/font]
[align=left]    [[color=#2b91af]StructLayout[/color]([color=#2b91af]LayoutKind[/color].[color=#303]Sequential[/color])][/align][align=left]    [color=blue]class[/color] [color=#2b91af]StructVersionInfo[/color][/align][align=left]    {[/align][align=left]       [color=blue]public[/color] [color=blue]int[/color] [color=#303]MajorVersion[/color];[/align][align=left]       [color=blue]public[/color] [color=blue]int[/color] [color=#303]MinorVersion[/color];[/align]    }

[font=宋体]对应[/font]C++[font=宋体]中的声明:[/font]
[align=left][color=blue]    typedef[/color] [color=blue]struct[/color] [color=#303]_VERSION_INFO[/color][/align][align=left]    {[/align][align=left]        [color=blue]int[/color] [color=#303]MajorVersion[/color];[/align][align=left]        [color=blue]int[/color] [color=#303]MinorVersion[/color];[/align]    }[color=#303] VERSION_INFO[/color], *[color=#303]PVERSION_INFO[/color];

[font=宋体]如果结构中使用到了字符串,最好应指定相应的字符集:[/font]

[size=10pt][[color=#2b91af]StructLayout[/color]([color=#2b91af]LayoutKind[/color].[color=#303]Sequential[/color],[color=#303]CharSet[/color]=[color=#2b91af]CharSet[/color].[color=#303]Unicode[/color])][/size]

[font=宋体]部分常用的声明对应关系(在结构中):[/font]
C++[font=宋体]:字符串数组[/font]

[size=10pt]wchar_t[/size][size=10pt] [color=#303]Comments[/color][120];[/size]
[align=left]C#[font=宋体]:[/font][/align][align=left][size=10pt]    [[color=#2b91af]MarshalAs[/color]([color=#2b91af]UnmanagedType[/color].[color=#303]ByValTStr[/color], [color=#303]SizeConst[/color] = 120)][/size][/align][align=left][size=10pt]    [color=blue]public[/color] [color=blue]string[/color] [color=#303]Comments[/color];[/size][/align]
[align=left][size=10pt]C++[/size][font=宋体][size=10pt]:结构成员[/size][/font][/align][align=left][size=10pt]    [color=blue]VERSION_INFO[/color][/size][color=#303] ver;[/color][/align][align=left][color=#303]C#[/color][/align][align=left]
[size=10pt]public[/size][color=#2b91af]StructVersionInfo [/color][color=#303]ver;[/color][/align]
[align=left][color=#303]C++[/color][color=#303][font=宋体]:函数指针声明[/font][/color][/align][align=left]
[size=10pt]PFOO[/size][color=#303] pFoo; //[/color][color=#303][font=宋体]具体声明见文章前面部分[/font][/color][/align][align=left][color=#303]C#:[/color][/align][align=left]
[size=10pt]public[/size][color=#2b91af]IntPtr[/color][color=#303] pFoo; [/color][color=green]//[/color][color=green][font=宋体]也可以为[/font][/color][color=green] public int pFoo; [/color][/align][align=left][color=green]        //[/color][color=green][font=宋体]不同的声明方法可以使用上面[/font][/color][color=green]DLLWrapper[/color][color=green][font=宋体]类的相应函数获取对应的委托实例[/font][/color][/align]
[align=left][color=#303][font=宋体]如果在结构中使用到了[/font][/color][color=#303]union[/color][color=#303][font=宋体],那么可以使用[/font][/color][color=#303]FieldOffset[/color][color=#303][font=宋体]指定具体位置。[/font][/color][/align]
[align=left][color=#303]3[/color][color=#303][font=宋体]、委托的声明:[/font][/color][/align]
[align=left][color=#303][font=宋体]当[/font][/color][color=#303]C++[/color][color=#303][font=宋体]编写的[/font][/color][color=#303]DLL[/color][color=#303][font=宋体]函数需要通过指针传出将一个结构:如以下声明:[/font][/color][/align][align=left]
[size=10pt]void[/size][color=#303] getVersionInfo([/color][size=10pt]VERSION_INFO[/size][color=#303] *ver);[/color][/align][align=left][color=#303][font=宋体]对于在[/font][/color][color=#303]C#[/color][color=#303][font=宋体]中声明为[/font][/color][color=#303]class[/color][color=#303][font=宋体]的结构(当[/font][/color][color=#303]VERSION_INFO[/color][color=#303][font=宋体]声明为[/font][/color][color=#303]class[/color][color=#303][font=宋体])[/font][/color][/align][align=left]
[size=10pt]delegate void[/size][color=#2b91af]getVersionInfo[/color][color=#303]([/color][color=#2b91af]VERSION_INFO[/color][color=#303] ver);[/color][/align][align=left][color=#303][font=宋体]如果结构声明为[/font][/color][color=#303]struct[/color][color=#303][font=宋体],那么应该使用如下声明:[/font][/color][/align][align=left]
[size=10pt]delegate void[/size][color=#2b91af]getVersionInfo[/color][color=#303]([/color][size=10pt]ref[/size][color=#2b91af]VERSION_INFO[/color][color=#303] ver);[/color][/align][align=left][color=#303][font=宋体]注意:应该使用[/font][/color][color=#303]ref[/color][color=#303][font=宋体]关键字。[/font][/color][/align]

[align=left][color=#303][font=宋体]如果[/font][/color][color=#303]DLL[/color][color=#303][font=宋体]函数需要传入一个字符串,比如这样:[/font][/color][/align][align=left][size=10pt]    BOOL[/size][size=10pt] [color=blue]__stdcall[/color] jingzhongrong1([color=blue]const[/color] [color=blue]wchar_t[/color]* [color=#303]lpFileName[/color], [color=blue]int[/color]* [color=#303]FileNum[/color]);[/size][/align][align=left][color=#303][font=宋体]那么使用委托来调用函数的时候应该在[/font][/color][color=#303]C#[/color][color=#303][font=宋体]中如下声明委托:[/font][/color][/align][align=left][size=10pt]    [color=blue]delegate[/color] [color=blue]bool[/color] [color=#2b91af]jingzhongrong1[/color]([/size][/align][align=left][size=10pt]       [[color=#2b91af]MarshalAs[/color]([color=#2b91af]UnmanagedType[/color].[color=#303]LPWStr[/color])][color=#2b91af]String[/color] [color=#303]FileName[/color], [/size][/align][align=left][size=10pt]       [color=blue]ref[/color] [color=blue]int[/color] [color=#303]FileNum[/color]);[/size][/align][align=left][color=#303][font=宋体]注意:应该使用[/font][/color][size=10pt][[color=#2b91af]MarshalAs[/color]([color=#2b91af]UnmanagedType[/color].[color=#303]LPWStr[/color])][/size][font=宋体][size=10pt]和[/size][/font][size=10pt]String[/size][font=宋体][size=10pt]进行声明。[/size][/font][/align]

[align=left][color=#303][font=宋体]如果要在[/font][/color][color=#303]DLL[/color][color=#303][font=宋体]函数中传出一个字符串,比如这样:[/font][/color][/align][align=left][size=10pt]    void[/size][size=10pt] [color=blue]__stdcall[/color] jingzhongrong2([/size][/align][align=left][size=10pt]    [color=blue]wchar_t[/color]* [color=#303]lpFileName[/color], [/size][color=green]//[/color][color=green][font=宋体]要传出的字符串[/font][/color][/align][align=left][size=10pt]    [color=blue]int[/color]* [color=#303]Length[/color]);[/size][/align][align=left][color=#303][font=宋体]那么我们如下声明委托:[/font][/color][/align][align=left][size=10pt]    [color=green]//[/color][/size][font=宋体][size=10pt]使用委托从非托管函数的参数中传出的字符串,[/size][/font][/align][align=left][size=10pt]    [color=green]//[/color][/size][font=宋体][size=10pt]应该这样声明,并在调用前为[/size][/font][size=10pt]StringBuilder[/size][font=宋体][size=10pt]预备足够的空间[/size][/font][/align][align=left][size=10pt]    [color=blue]delegate[/color] [color=blue]void[/color] [color=#2b91af]jingzhongrong2[/color]([/size][/align][align=left][size=10pt]       [[color=#2b91af]MarshalAs[/color]([color=#2b91af]UnmanagedType[/color].[color=#303]LPWStr[/color])] [color=#2b91af]StringBuilder[/color] [color=#303]lpFileName[/color],[/size][/align][align=left][size=10pt]       [color=blue]ref[/color] [color=blue]int[/color] [color=#303]Length[/color],[/size][/align][align=left][size=10pt]    );[/size][/align][align=left][color=#303][font=宋体]在使用函数前,应先为[/font][/color][color=#303]StringBuilder[/color][color=#303][font=宋体]声明足够的空间用于存放字符串:[/font][/color][/align][align=left]
[size=10pt]StringBuilder[/size][size=10pt] [color=#303]fileName[/color] = [color=blue]new[/color] [color=#2b91af]StringBuilder[/color]([color=#303]FileNameLength[/color]);[/size][/align][/td][/tr][/table]
页: [1]
查看完整版本: C#动态调用C++编写的DLL函数