美文网首页
.net调用外部变参函数

.net调用外部变参函数

作者: CodingCode | 来源:发表于2023-12-01 04:04 被阅读0次
    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
    • 当调用dll函数时,同样可以使用CallingConvention更改其值,目标是使得caller和callee使用相同的CallingConvention,否则就会出PInvokeStackImbalance错。
    • 对于变参函数也要特殊处理,不能使用显式的设置成__stdcall,否则和callee的固定的__cdecl不匹配,所以可以设置成__cdecl,或者不设置使用默认是,此时变参的默认值是__cdecl而不是通常固定参数的__stdcall了。

    相关文章

      网友评论

          本文标题:.net调用外部变参函数

          本文链接:https://www.haomeiwen.com/subject/gxhcgdtx.html