我正在尝试通过 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/