1

我正在尝试从我的 c# 代码访问 c libnotify,以便在我的带有 dotnet 核心的 linux 笔记本电脑上使用 libnotify。
但是每次从库中获取值都有问题。

这是有问题的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 方法:

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

基础工作正常,我可以使用来自notify_notification_new -method的指针从 libnotify 调用其他函数。但在最后一行,使用 WriteLine,调试器说:

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

没有例外,也没有错误。怎么了?dotnet核心有问题吗?因为它还处于测试阶段?

如何从属性app_name、summary、body中获取文本?

非常感谢您的帮助。

4

1 回答 1

1

[StructLayout(LayoutKind.Explicit)]说“亲爱的编译器,我知道我在做什么,只是......处理它”。不幸的是,你不知道自己在做什么。 [FieldOffset]以字节为单位的偏移量,而不是成员。

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

    NotifyNotificationPrivate *priv;
};

GObject 大概是一种指针类型。这意味着它占用 4 个字节 (x86) 或 8 个字节 (amd64)。由于目前仅在 amd64 上支持 .NET Core for Linux,因此它是 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));

编辑:虽然,最可预测的方法是制作一个 C 库(或带有extern "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; 因为如果你将它声明为返回一个stringGC 会认为它应该在字符串超出范围时清理内存。

于 2017-01-05T22:34:12.327 回答