MonATouch/Xamarin.iOS中的VarArgs绑定

t9m*_*ike 2 xamarin.ios xamarin

我有一个新的MonoTouch绑定部分工作.但是变量arguments函数在运行时会导致崩溃..h文件:

FOUNDATION_EXPORT void __BFLog(NSInteger lineNumber, NSString *method, NSString *file, 
    BFLogLevel level, NSString *tag, NSString *format, ...);
Run Code Online (Sandbox Code Playgroud)

C#:

internal static class CFunctions
{
    // extern void __BFLog (NSInteger lineNumber, NSString * method, 
    // NSString * file, BFLogLevel level, NSString * tag, NSString * format, ...);
    [DllImport ("__Internal", EntryPoint = "__BFLog")]
    internal static extern void BFLog (nint lineNumber, string method, string file, 
        LogLevel level, string tag, string format, string arg0);
}
Run Code Online (Sandbox Code Playgroud)

因为我会在arg0中传递""并真正传递格式部分中的字符串.但是在打电话时,我看到了这个崩溃:

critical:   at <unknown> <0xffffffff>
critical:   at (wrapper managed-to-native) BugfenderSDK.CFunctions.BFLog (System.nint,string,string,BugfenderSDK.LogLevel,string,string,string) <0xffffffff>
...
Run Code Online (Sandbox Code Playgroud)

目标Sharpie默认将IntPtr varArgs作为最后一个参数.我尝试了这个字符串arg0并传入IntPtr.Zero,但仍然崩溃.

编辑#1:担心第一个vararg - 我只是将""传递给它 - 我按照每个ventayol的TestFlight绑定示例而忽略了这一点,只定义了Dllimport中的格式:

 [DllImport ("__Internal", EntryPoint = "__BFLog")]
 internal static extern void BFLog(
     nint lineNumber, /* nint will be marshalled correctly */
     IntPtr method, /* NSString must be declared as IntPtr */
     IntPtr file, /* NSString */
     LogLevel level, /* This may be wrong, depending on the exact LogLevel type */
     IntPtr tag, /* NSString */
     IntPtr format /* NSString */
 );
Run Code Online (Sandbox Code Playgroud)

和包装:

 public static void Log(LogLevel level, nint lineNumber, string method, string file, 
     string tag, string format, params object[] args)
 {
     var nsMethod = new NSString (method);
     var nsFile = new NSString (file);
     var nsTag = new NSString (tag);
     string msg = String.Format(format, args);
     var nsMsg = new NSString(msg);

     BFLog (lineNumber, nsMethod.Handle, nsFile.Handle, level, nsTag.Handle, nsMsg.Handle);

     nsMethod.Dispose ();
     nsFile.Dispose ();
     nsTag.Dispose ();
     nsMsg.Dispose ();            
 }
Run Code Online (Sandbox Code Playgroud)

但我只在后端看到标签和其他人,没有消息.

Rol*_*nge 6

这不是Objective-C绑定,而是标准.NET P/Invoke.这意味着Xamarin.iOS不会执行标准的C# - > Objective-C类型编组(例如字符串 - > NSString).

所以你需要像这样编写P/Invoke:

[DllImport ("__Internal", EntryPoint = "__BFLog")]
internal static extern void BFLog (
    nint lineNumber, /* nint will be marshalled correctly */
    IntPtr method, /* NSString must be declared as IntPtr */
    IntPtr file, /* NSString */
    LogLevel level, /* This may be wrong, depending on the exact LogLevel type */
    IntPtr tag, /* NSString */
    IntPtr format, /* NSString */
    string arg0 /* this is a C-style string, char* */)]
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

BFLog (
    0, /* nint */
    new NSString (method).Handle, /* IntPtr */
    new NSString (file).Handle, /* IntPtr */
    level, /* LogLevel */
    new NSString (tag).Handle,  /* IntPtr */
    new NSString (format).Handle,  /* IntPtr */
    "arg0" /* string */);
Run Code Online (Sandbox Code Playgroud)

您还需要为您正在使用的每个varargs变体创建单独的P/Invoke.因此,如果你需要一个带有两个C风格的字符串,请执行以下操作:

[DllImport ("__Internal", EntryPoint = "__BFLog")]
internal static extern void BFLog (
    nint lineNumber, /* nint will be marshalled correctly */
    IntPtr method, /* NSString must be declared as IntPtr */
    IntPtr file, /* NSString */
    LogLevel level, /* This may be wrong, depending on the exact LogLevel type */
    IntPtr tag, /* NSString */
    IntPtr format, /* NSString */
    string arg0, /* this is a C-style string, char* */
    string arg1 /* second C-style string, char* */)]
Run Code Online (Sandbox Code Playgroud)

还要记住,arm64上的varargs具有不同的调用约定,所有varargs都在堆栈上传递.这在实践中意味着第一个varargs参数必须是P/Invoke中的参数#8:

[DllImport ("__Internal", EntryPoint = "__BFLog")]
internal static extern void BFLog_arm64 (
    nint lineNumber, /* 1st */
    IntPtr method, /* 2nd */
    IntPtr file, /* 3rd */
    LogLevel level, /* 4th */
    IntPtr tag, /* 5th */
    IntPtr format, /* 6th */
    IntPtr dummy1, /* 7th */
    string arg0 /* 8th, the first varargs parameter */)]
Run Code Online (Sandbox Code Playgroud)

然后你可以提供一个围绕BFLog导出的包装器,它将根据体系结构做正确的事情:

using ObjCRuntime;

internal static void Log (nint lineNumber, string method, string file, LogLevel level, string tag, string format, string arg0)
{
    var nsMethod = new NSString (method);
    var nsFile = new NSString (file);
    var nsTag = new NSString (tag);
    var nsFormat = new NSString (format);

    if (Runtime.Arch == ARCH.Device && IntPtr.Size == 8) {
        BFLog_arm64 (lineNumber, nsMethod.Handle, nsFile.Handle, level, nsTag.Handle, nsFormat.Handle, IntPtr.Zero, arg0);
    } else {
        BFLog (lineNumber, nsMethod.Handle, nsFile.Handle, level, nsTag.Handle, nsFormat.Handle, arg0);
    }

    nsMethod.Dispose ();
    nsFile.Dispose ();
    nsTag.Dispose ();
    nsFormat.Dispose ();
}
Run Code Online (Sandbox Code Playgroud)