.net调用外部变参函数

2023-12-01  本文已影响0人  CodingCode
  1. 变参函数定义

假设变参函数userlog定义在DLL文件MyDll.dll里面:

__declspec(dllexport)
int userlog(char* fmt, ...)
{
  ...
}
  1. c#源文件调用
internal class Program
{
    [DllImport("MyDll.dll")]
    public static extern int userlog(string format, __arglist);

    static void Main(string[] args)
    {
        userlog("0000", __arglist());
        userlog("0000:a=%d", __arglist(100));
        userlog("0000:a=%d,b=%d", __arglist(100, 200));
    }
}

注意这里所有的参数必须要用__arglist组织起来,不能想C/C++的传统写法,直接罗列在后面,而且即使没有变参数,也必须使用一个空的__arglist,像第一个例子的写法。

  1. CallingConvention=decl/StdCall的几个试验

下面我们做几个试验:

.NET 结果
[DllImport("dll", CallingConvention=decl))]
int userlog(string format, __arglist)
成功
[DllImport("dll", CallingConvention=StdCall))]
int userlog(string format, __arglist)
失败
Vararg functions must use the
cdecl calling convention
[DllImport("dll")]
int userlog(string format, __arglist)
成功

结论是:对于.net的变参函数调用int userlog(string format, __arglist)

  1. 在c/c++ .dll里面定义的函数的CallingConvention都是__cdecl(后面会有进一步解释)。
  2. 在.net里面引用申明的CallingConvention:
    2.1. Cdecl:匹配,测试成功。
    2.2. StdCall: 不匹配,测试失败:Vararg functions must use the cdecl calling convention
    2.3. <empty>: 使用默认值,我们知道通常.net的默认值是StdCall,应该是不匹配的,但是好像.net为外部变参函数修改了默认值变成Cdecl了;所以使用默认值也能通过。

再做一个固定参数的例子:

.NET 结果
[DllImport("dll", CallingConvention=decl))]
int userlog(string format, int a, int b)
成功
[DllImport("dll", CallingConvention=StdCall))]
int userlog(string format, int a, int b)
失败
PInvokeStackImbalance
[DllImport("dll")]
int userlog(string format, int a, int b)
失败
PInvokeStackImbalance

这里可以看出默认值的差异:

  1. 对应固定参数的默认值是StdCall,而对于变参的默认值是decl

另外我们可以在c/c++的DLL函数声明的时候使用__cdecl/__stdcall关键字来定义CallingConvention:

__declspec(dllexport) int           userlog(char* fmt, ...) { ... }
__declspec(dllexport) int __cdecl   userlog(char* fmt, ...) { ... }
__declspec(dllexport) int __stdcall userlog(char* fmt, ...) { ... }

最后的总结:

  1. c/c++函数缺省的CallingConvention是__cdecl
    • 可以通过属性__cdecl/__stdcall来改变函数的CallingConvention
    • 但是对变参函数无效,变参函数永远是__cdecl,无论用户设置了什么值都被忽略。
  2. .net函数的缺省CallingConvention是__stdcall
上一篇下一篇

猜你喜欢

热点阅读