c# - 在 C# 委托(delegate)中编码 va_list

标签 c# c++ pinvoke marshalling

我正在尝试通过 C# 实现此功能:

C 头文件:

typedef void (LogFunc) (const char *format, va_list args);

bool Init(uint32 version, LogFunc *log)

C# 实现:

static class NativeMethods
{
    [DllImport("My.dll", SetLastError = true)]
    internal static extern bool Init(uint version, LogFunc log);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl, SetLastError = true)]
    internal delegate void LogFunc(string format, string[] args);
}

class Program
{
    public static void Main(string[] args)
    {
         NativeMethods.Init(5, LogMessage);
         Console.ReadLine();
    }

    private static void LogMessage(string format, string[] args)
    {
         Console.WriteLine("Format: {0}, args: {1}", format, DisplayArgs(args));
    }
}

这里发生的是对 NativeMethods.Init 的调用回调 LogMessage 并将来自非托管代码的数据作为参数传递。这适用于参数为字符串的大多数情况。但是,有一个调用,其格式为:

已加载版本 %d 的插件 %s。

并且 args 仅包含一个字符串(插件名称)。它们不包含版本值,这是有道理的,因为我在委托(delegate)声明中使用了 string[]。问题是,我应该如何编写委托(delegate)来同时获取字符串和整数?

我尝试使用 object[] args 并得到了这个异常: 在从非托管 VARIANT 到托管对象的转换过程中检测到无效的 VARIANT。将无效的 VARIANT 传递给 CLR 会导致意外异常、损坏或数据丢失。

编辑: 我可以将委托(delegate)签名更改为:

internal delegate void LogFunc(string format, IntPtr args);

我可以解析格式并找出期望的参数数量和类型。例如。对于 已加载版本 %d 的插件 %s。 我希望有一个字符串和一个整数。有没有办法从 IntPtr 中取出这两个?

最佳答案

为了以防万一,这里有一个整理参数的解决方案。委托(delegate)声明为:

[UnmanagedFunctionPointer(CallingConvention.Cdecl, SetLastError = true)] // Cdecl is a must
internal delegate void LogFunc(string format, IntPtr argsAddress);

argsAddress 是数组开始的非托管内存地址(我认为)。 format 给出了数组的大小。知道这一点后,我可以创建托管数组并填充它。伪代码:

size <- get size from format
if size = 0 then return

array <- new IntPtr[size]
Marshal.Copy(argsAddress, array, 0, size);
args <- new string[size]

for i = 0 to size-1 do
   placeholder <- get the i-th placeholder from format // e.g. "%s"
   switch (placeholder)
       case "%s": args[i] <- Marshal.PtrToStringAnsi(array[i])
       case "%d": args[i] <- array[i].ToString() // i can't explain why the array contains the value, but it does
       default: throw exception("todo: handle {placeholder}")

说实话,我不确定它是如何工作的。它似乎只是获得了正确的数据。不过,我并不是说它是正确的。

关于c# - 在 C# 委托(delegate)中编码 va_list,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10361369/

相关文章:

c++ - 管道标准输出到 Qt 4.7 中的 QLabel

c# - 为什么后续调用时不能使用IntPtr

c# - 在具有 ui/非 ui 线程差异的 WPF 中使用 PInvoke

c# - 如何通过 IntPtr 将 C++ 数组编码到 C#

c# - 这是在 asp.net 页面之间传递变量的好方法吗

c# - OnActionExecuting 构造局部 View

c# - SqlParameter 构造函数与对象初始值设定项的问题

c++ - 为什么 DECLARE_DYNAMIC 和 IMPLEMENT_DYNAMIC 对 DYNAMIC_DOWNCAST 是必要的?

c# - 如何从 DateTime 获取完整的月份名称

c++ - 如何仅使用其中一个维度在2D vector 中插入值?