2015-04-16 6 views
2

У меня есть новая привязка MonoTouch, частично работающая. Но функция переменных аргументов вызывает сбой при запуске. .h файл:VarArgs Binding in MonoTouch/Xamarin.iOS

FOUNDATION_EXPORT void __BFLog(NSInteger lineNumber, NSString *method, NSString *file, 
    BFLogLevel level, NSString *tag, NSString *format, ...); 

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); 
} 

Потому что я передам "" в arg0 и действительно передать строку в формате секции. Но при вызове, я вижу эту аварию:

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

Цель Находчивых ставить IntPtr переменных аргументы в качестве последнего аргумента по умолчанию. Я попробовал эту строку arg0 и вместо этого перешел в IntPtr.Zero, но все равно сбой.

EDIT # 1: insteading беспокоиться о первом vararg - я собирался просто передать «» к нему - я последовал за TestFlight binding example ventayol и игнорировали это, только определение формата в 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 */ 
); 

и обертка:

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();    
} 

Но я вижу только тег и другие на внутреннем интерфейсе, без сообщения.

ответ

6

Это не привязка Objective-C, а стандартная .NET P/Invoke. Это означает, что Xamarin.iOS не будет выполнять стандартную сортировку типа C# -> Objective-C (например, string -> 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* */)] 

и использовать его как это:

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 */); 

Вам также необходимо создать отдельную P/Invoke для каждого varargs вариант вы используете. Так что если вам нужен один, который принимает две строки 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* */)] 

также в виду, что на arm64 списков параметров имеет другое соглашение о вызовах, все переменные аргументы передаются в стек. Что это означает на практике, что первый переменные аргументы аргумент должен быть аргументом # 8 в P/Invoke:

[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 */)] 

, а затем вы можете предоставить оболочку вокруг экспорта 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(); 
} 
+1

Thanks Rolf. Это сработало, за исключением того, что мне трудно получить сообщение в формате format/args. Я предположил, что бэкэнд-библиотека - у меня нет исходного кода - позволит мне просто форматировать = "% s", а затем я могу просто передать сообщение через arg0. Нет кубиков. Но я могу просто использовать тег вместо этого, когда я экспериментирую с этой услугой (http://bugfender.com/). – t9mike

1

может быть, вы можете проверить, что TestFlight сделал на его связывание здесь: https://github.com/mono/monotouch-bindings/blob/master/TestFlight/binding/testflight-cplusplus.cs

Они имеют ту же функцию, что вы хотите сделать.

+0

По-прежнему не гоните (см. РЕДАКТИРОВАНИЕ № 1 выше).Я действительно думаю, что в таком API лучше всего использовать неформатную версию (например, мне нужно было бы избежать таких вещей, как% s, нет?). TestFlight фактически предоставляет такой простой интерфейс в своем SDK: void TFLogPreFormatted (сообщение NSString *). Это то, что я рыскал по электронной почте :-). – t9mike