C#:从 libnotify c 结构中获取值
C#: Get values from libnotify c struct
我正在尝试从我的 c# 代码访问 c libnotify,以便在我的 linux 带有 dotnet 核心的笔记本电脑上使用 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# 代码中创建了两个结构和一个外部方法:
[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 方法中的指针从 libnotify 调用其他函数。
但在最后一行,调试器使用 WriteLine 表示:
The program '...dll' has exited with code 0 (0x00000000).
没有异常,没有错误。怎么了?这是 dotnet 核心的问题?因为它仍处于测试阶段?
如何从属性app_name、摘要、正文中获取文本??
非常感谢您的帮助。
[StructLayout(LayoutKind.Explicit)]
表示 "dear compiler, I know what I'm doing, just... deal with it"。不幸的是,你不知道自己在做什么。 [FieldOffset]
采用以字节为单位的偏移量,而不是成员。
struct _NotifyNotification
{
/*< private >*/
GObject parent_object;
NotifyNotificationPrivate *priv;
};
GObject,推测是指针类型。这意味着它占用 4 个字节 (x86) 或 8 个字节 (amd64)。由于 Linux 的 .NET Core 当前仅在 amd64 上受支持,因此它是 8 个字节。 (除非是其他一些原始结构)。
[FieldOffset(1)]
说 "start reading 1 byte after the pointer"。
你的下一个问题是在 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++),它完全避免了 copying/interpreting 结构(它让 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 会认为它应该在字符串超出范围时清理内存。
我正在尝试从我的 c# 代码访问 c libnotify,以便在我的 linux 带有 dotnet 核心的笔记本电脑上使用 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# 代码中创建了两个结构和一个外部方法:
[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 方法中的指针从 libnotify 调用其他函数。 但在最后一行,调试器使用 WriteLine 表示:
The program '...dll' has exited with code 0 (0x00000000).
没有异常,没有错误。怎么了?这是 dotnet 核心的问题?因为它仍处于测试阶段?
如何从属性app_name、摘要、正文中获取文本??
非常感谢您的帮助。
[StructLayout(LayoutKind.Explicit)]
表示 "dear compiler, I know what I'm doing, just... deal with it"。不幸的是,你不知道自己在做什么。 [FieldOffset]
采用以字节为单位的偏移量,而不是成员。
struct _NotifyNotification
{
/*< private >*/
GObject parent_object;
NotifyNotificationPrivate *priv;
};
GObject,推测是指针类型。这意味着它占用 4 个字节 (x86) 或 8 个字节 (amd64)。由于 Linux 的 .NET Core 当前仅在 amd64 上受支持,因此它是 8 个字节。 (除非是其他一些原始结构)。
[FieldOffset(1)]
说 "start reading 1 byte after the pointer"。
你的下一个问题是在 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++),它完全避免了 copying/interpreting 结构(它让 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 会认为它应该在字符串超出范围时清理内存。