2017-01-05 3 views
1

libnotifyをlibnetを使用するためにC#コードからc libnotifyにアクセスしようとしています。
しかし、毎回libから値を取得するのに問題があります。C#:libnotifyから値を取得するstruct

これは、問題のあるCコードです:

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#のコードとのextern方法に2つの構造体を作成しました:このコードで

[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で他の関数を呼び出すことができますから通知します。notify_notification_new -method。 しかし、最後の行では、WriteLineメソッドで、デバッガは言う:

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

も例外とエラーがありません。何がうまくいかない?それはドットネットコアの問題ですか?まだベータ版なので?

プロパティからテキストを取得する方法 app_name、summary、body ??

ご協力いただきありがとうございます。

答えて

1

[StructLayout(LayoutKind.Explicit)]は "私は何をしているのか知っています。ただそれを処理する"と言います。残念ながら、あなたは何をしているのか分からなかった。 [FieldOffset]はメンバではなくバイト単位でオフセットをとります。

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

    NotifyNotificationPrivate *priv; 
}; 

GObjectは、おそらくポインタ型です。つまり、4バイト(x86)または8バイト(amd64)のいずれかを使用します。 Linux用.NET Coreは現在、amd64でのみサポートされているため、8バイトです。 (他の原始的な構造でない限り)。

[FieldOffset(1)]は、 "ポインタの後に1バイト読み込みを開始する"と言います。

次の問題は、Cではstructが2つのメンバーを持つと宣言され、2つ目はポインタです。あなたのC#の構造体は、2番目のメンバ自体が構造体だと言います。その後

[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 
} 

:アドレスのメモリは、あなたが

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 

を読ん

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

のように見えたnotify_notification_newで指される場合

とても良いシーケンシャルレイアウトを使用することです:

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

編集:コピーを回避ものの、最も予測アプローチは、(extern "C"宣言またはC++)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に私のnot-pointerをマーシャリングした後、privの値は_0x0000000000000001_です。 私はそれをNotifyNotificationPrivate-structにマーシャリングできません。どうして?それは他の価値でなければなりません。 – bittnerd

+1

@bittnerdああ、GObjectは構造体だからポインタではないから。 https://github.com/GNOME/glib/blob/master/gobject/gobject.h#L245-L252。あなたの最善の策は、 'NotifyNotificationPrivate *'を 'NotifyNotification *'に渡すことができる小さなCライブラリを作ることです。そうすれば、Cコンパイラはフィールドオフセットの計算を行うことができ、GNOMEフレームワークのバージョン間でGObjectの構造を変更しようとする必要はありません。 – bartonjs

+0

ご協力いただきありがとうございます。 実際、私は余分なライブラリを使ってそのようにしたくありませんでした。しかし、私はこれが最善の方法だと思うし、これらの価値を得る別の方法を知らない。 このコメントを回答に追加すると、私はそれを受け入れます。 :) – bittnerd

関連する問題