Marshal a va_list

时间:2017-08-02 14:19:19

标签: c# .net-core dllimport

我有以下代码:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void PanicFuncDelegate(string str, IntPtr args);

private void PanicFunc(string str, IntPtr args)
{
    LogFunc("PANIC", str, args);
}

public void LogFunc(string severity, string str, IntPtr args)
{
    vprintf($"[{severity}] "+ str,args);
}

[DllImport("libc.so.6")]
private static extern int vprintf(string format, IntPtr args);

这会将正确格式化的消息打印到控制台。我想从args中检索值,以便在我自己的记录器中使用它们。

如果我尝试从args中的数组中获取每个指针的值(如此处所示:Marshal va_list in C# delegate),我会遇到分段错误。

有什么建议吗?

1 个答案:

答案 0 :(得分:0)

只需考虑C程序如何从va_list中获取变量,就有解决方案:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace VaTest
{
    class Program
    {
        static void Main(string[] args)
        {
            MarshalVaArgs(vaList => vprintf("%c%d%s", vaList), false, 'a', 123, "bc");
        }

        [DllImport("msvcrt")]  //windows
      //[DllImport("c")]       //linux
        private static extern int vprintf(string format, IntPtr vaList);

        private static int IntSizeOf(Type t)
        {
            return (Marshal.SizeOf(t) + IntPtr.Size - 1) & ~(IntPtr.Size - 1);
        }

        public static void MarshalVaArgs(Action<IntPtr> action, bool? isUnicode, params object[] args)
        {
            var sizes = new int[args.Length];
            for (var i = 0; i < args.Length; i++)
            {
                sizes[i] = args[i] is string ? IntPtr.Size : IntSizeOf(args[i].GetType());
            }

            var allocs = new List<IntPtr>();
            var offset = 0;

            var result = Marshal.AllocHGlobal(sizes.Sum());
            allocs.Add(result);

            for (var i = 0; i < args.Length; i++)
            {
                if (args[i] is string)
                {
                    var s = (string)args[i];
                    var data = default(IntPtr);
                    if (isUnicode.HasValue)
                    {
                        if (isUnicode.Value)
                        {
                            data = Marshal.StringToHGlobalUni(s);
                        }
                        else
                        {
                            data = Marshal.StringToHGlobalAnsi(s);
                        }
                    }
                    else
                    {
                        data = Marshal.StringToHGlobalAuto(s);
                    }
                    allocs.Add(data);
                    Marshal.WriteIntPtr(result, offset, data);
                    offset += sizes[i];
                }
                else
                {
                    Marshal.StructureToPtr(args[i], result + offset, false);
                    offset += sizes[i];
                }
            }

            action(result);

            foreach (var ptr in allocs)
            {
                Marshal.FreeHGlobal(ptr);
            }
        }
    }
}

代码是使用.NET Core 3.0 preview 5编写和测试的,并且与.NET Framework 4.0C# 3.0兼容。

输出:

a123bc