C#P/Invoke:Varargs委托回调

Zot*_*tta 9 c# pinvoke variadic-functions

我只是想做一些托管/非托管互操作.为了获得扩展的错误信息,我决定注册dll提供的日志回调:


[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public unsafe delegate void LogCallback(void* arg1,int level,byte* fmt);
Run Code Online (Sandbox Code Playgroud)

这个定义有效,但我得到的字符串如"格式%s探测大小=%d和得分=%d".我尝试添加__arglist关键字,但代表不允许这样做.

嗯,这对我来说并不那么引人注目,但我只是好奇,有人可以在C#中获得varargs参数.我知道我可以使用c ++进行互操作.所以:有没有办法在C#中做到这一点,合理的efford?

编辑:对于那些仍然没有得到它的人:我没有导入 varargs函数但是将它作为回调导出,然后将其称为我的本机代码.我一次只能指定一个 - >只能进行一次重载而__arglist不起作用.

gho*_*ord 5

这是处理它的方法。它可能适用也可能不适用于您的情况,具体取决于您的回调参数是否旨在与printf函数系列一起使用。

首先,导入vsprintf_vscprintf来自msvcrt

[DllImport("msvcrt.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern int vsprintf(
    StringBuilder buffer,
    string format,
    IntPtr args);

[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int _vscprintf(
    string format,
       IntPtr ptr);
Run Code Online (Sandbox Code Playgroud)

IntPtr接下来,使用args 指针声明您的委托:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public unsafe delegate void LogCallback(
    void* arg1,
    int level, 
    [In][MarshalAs(UnmanagedType.LPStr)] string fmt,
    IntPtr args);
Run Code Online (Sandbox Code Playgroud)

现在,当通过本机代码调用您的委托时,只需使用vsprintf正确的格式设置消息:

private void LogCallback(void* data, int level, string fmt, IntPtr args)
{
    var sb = new StringBuilder(_vscprintf(fmt, args) + 1);
    vsprintf(sb, fmt, args);

    //here formattedMessage has the value your are looking for
    var formattedMessage = sb.ToString();

    ...
}
Run Code Online (Sandbox Code Playgroud)


shf*_*301 4

不,没有办法做到这一点。这是不可能的原因是变量参数列表在 C 中的工作方式。

在 C 中,变量参数只是作为额外参数推送到堆栈(在我们的例子中是非托管堆栈)。C 不会在任何地方记录堆栈上的参数数量,被调用函数采用其最后一个形式参数(可变参数之前的最后一个参数)获取其位置并开始从堆栈中弹出参数。

它知道从堆栈中弹出多少变量的方式完全基于约定 - 其他一些参数指示堆栈上有多少变量参数。对于 printf 来说,它通过解析格式字符串并在每次看到格式代码时从堆栈中弹出来实现这一点。看来你的回调是相似的。

为了让 CLR 处理这个问题,它必须能够知道正确的约定来确定需要拾取多少个参数。您无法编写自己的处理程序,因为它需要访问您无权访问的非托管堆栈。所以你无法从 C# 中做到这一点。

有关这方面的更多信息,您需要阅读 C 调用约定。