Marshal a va_list

Ilo*_*ari 11 c# dllimport .net-core

我有以下代码:

[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);
Run Code Online (Sandbox Code Playgroud)

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

如果我试图从args中的数组中获取每个指针的值(如此处所示:C#delegate中的Marshal va_list),我得到了分段错误.

有什么建议?

Als*_*ein 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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

输出:

a123bc
Run Code Online (Sandbox Code Playgroud)