2017-01-05 3 views
1

Я пытаюсь получить доступ к c libnotify из моего кода C#, чтобы использовать libnotify на моем ноутбуке linux с ядром dotnet.
Но каждый раз возникает проблема с получением значений из библиотеки.C#: Получить значения из libnotify c struct

Это проблематична с код:

typedef struct _NotifyNotification NotifyNotification; 
typedef struct _NotifyNotificationPrivate NotifyNotificationPrivate; 

struct _NotifyNotification 
{ 
    /*<private>*/ 
    GObject     parent_object; 

    NotifyNotificationPrivate *priv; 
}; 

struct _NotifyNotificationPrivate 
{ 
    guint32   id; 
    char   *app_name; 
    char   *summary; 
    char   *body; 

    /* NULL to use icon data. Anything else to have server lookup icon */ 
    char   *icon_name; 

    /* 
    * -1 = use server default 
    * 0 = never timeout 
    * > 0 = Number of milliseconds before we timeout 
    */ 
    gint   timeout; 

    GSList   *actions; 
    GHashTable  *action_map; 
    GHashTable  *hints; 

    gboolean  has_nondefault_actions; 
    gboolean  updates_pending; 

    gulong   proxy_signal_handler; 

    gint   closed_reason; 
}; 

NotifyNotification * 
notify_notification_new (const char *summary, 
        const char *body, 
        const char *icon); 

Теперь я создал два структуры в моих C# код и ехЬегп метод:

[StructLayout(LayoutKind.Explicit)] 
    internal struct NotifyNotification 
    { 
     [FieldOffset(1)] 
     public NotifyNotificationPrivate priv; 
    } 

    [StructLayout(LayoutKind.Explicit)] 
    internal struct NotifyNotificationPrivate 
    { 
     [FieldOffset(0)] 
     public uint id; 

     [FieldOffset(1)] 
     public IntPtr app_name; 

     [FieldOffset(2)] 
     public IntPtr summary; 

     [FieldOffset(5)] 
     public int timeout; 
    } 

    [DllImport("libnotify.so.4", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 
    internal static extern IntPtr notify_notification_new([MarshalAs(UnmanagedType.LPStr)] string summary, 
                  [MarshalAs(UnmanagedType.LPStr)] string body, 
                  [MarshalAs(UnmanagedType.LPStr)] string icon); 

С помощью этого кода я бросил все, чтобы структуры:

NotifyNotification no = (NotifyNotification) Marshal.PtrToStructure(not, typeof(NotifyNotification)); 
Console.WriteLine(Marshal.PtrToStringAnsi(no.priv.summary)); 

Основы работают, и я могу вызывать другие функции из libnotify с помощью poi nter от notify_notification_new-метод. Но в последней строке, с WriteLine, отладчик говорит:

The program '...dll' has exited with code 0 (0x00000000). 

Там не является исключением, и нет ошибок. Что происходит не так? Это проблема с ядром dotnet? Потому что он все еще в бета-версии?

Как получить текст из свойств app_name, summary, body ??

Благодарим вас за помощь.

ответ

1

[StructLayout(LayoutKind.Explicit)] говорит «дорогой компилятор, я знаю, что я делаю, просто ... справляйтесь с ним». К сожалению, вы не знали, что делаете. [FieldOffset] принимает смещение в байтах, а не в членах.

struct _NotifyNotification 
{ 
    /*<private>*/ 
    GObject     parent_object; 

    NotifyNotificationPrivate *priv; 
}; 

GObject, предположительно, является типом указателя. Это означает, что он занимает либо 4 байта (x86), либо 8 байтов (amd64). Так как .NET Core для Linux в настоящее время поддерживается только на amd64, это 8 байт. (Если это не какая-то другая примитивная структура).

[FieldOffset(1)] говорит «начните читать 1 байт после указателя».

Ваша следующая проблема заключается в том, что в C объявлено, что у структуры два члена, второй - указатель. Ваша структура C# говорит, что второй элемент сам является структурой.

Так что, если память по адресу, на который указывает notify_notification_new выглядел

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 

Читаешь

id = [01 02 03 04] => 0x04030201 (because Little-Endian) 
app_name = [02 03 04 05 06 07 08 09] => 0x0908070605040302 
summary = [03 04 05 06 07 08 09 0A] => 0x0A09080706050403 
timeout = [06 07 08 09] => 0x09080706 

Гораздо лучше было бы использовать Последовательная расположение:

[StructLayout(LayoutKind.Sequential)] 
internal struct NotifyNotification 
{ 
    private IntPtr parent_object; 
    public IntPtr priv; 
} 

[StructLayout(LayoutKind.Sequential)] 
internal struct NotifyNotificationPrivate 
{ 
    public uint id; 
    public IntPtr app_name; 
    public IntPtr summary; 
    private IntPtr body; 
    private IntPtr icon_name; 
    public int timeout; 
    // If you're only ever reading one of these structures 
    // you can skip the rest. If you ever allocate one, be sure to 
    // include all of the fields so the right amount of memory is created 
} 

Тогда :

NotifyNotification no = (NotifyNotification) Marshal.PtrToStructure(not, typeof(NotifyNotification)); 
NotifyNotificationPrivate noPriv = (NotifyNotificationPrivate) Marshal.PtrToStructure(no.priv, typeof(NotifyNotificationPrivate)); 
Console.WriteLine(Marshal.PtrToStringAnsi(noPriv.summary)); 

Edit: Хотя, самый предсказуемый подход, чтобы сделать библиотеку C (или C++ с extern "C" деклараций), который позволяет избежать копирования/интерпретации структуры в целом (это позволяет C компилятор заботиться о том, что) :

extern "C" char* ReadNotificationSummary(NotifyNotification* notification) 
{ 
    if (notification == nullptr || notification.priv == nullptr) 
     return nullptr; 

    return notification.priv->summary; 
} 

Совпадение C# декларация о том, что будет иметь функция объявлена ​​как возвращающая IntPtr и передать, что еще Marshal.PtrToStringAnsi; потому что, если вы объявите его возвратом string, GC подумает, что он должен очистить память, когда строка выходит за пределы области.

+0

Хорошо, спасибо за ваш ответ. Это имеет смысл. Но: После сортировки моего не указателя на NotifyNotification-struct значение priv: _0x0000000000000001_. Поэтому я не могу его маршалировать в NotifyNotificationPrivate-struct. Зачем? Это должно быть другой ценностью, не так ли? – bittnerd

+1

@bittnerd Ах, потому что GObject - это структура, а не указатель. https://github.com/GNOME/glib/blob/master/gobject/gobject.h#L245-L252. Лучше всего сделать небольшую библиотеку C, в которую вы можете передать «NotifyNotification *» и получить «NotifyNotificationPrivate». Таким образом, компилятор C может выполнять вычисления смещения поля, и вам не нужно пытаться работать над структурой GObject, изменяющейся в разных версиях GNOME. – bartonjs

+0

Благодарим за помощь. На самом деле я не хотел так поступать с дополнительной библиотекой. Но я думаю, что это лучший способ, и я не знаю другого способа получить эти ценности. Если вы добавите этот комментарий к своему ответу, я приму его. :) – bittnerd

Смежные вопросы