linux 进程间通信 dbus-glib实例详解四(上) C库 dbus-glib 使用(附代码)(编写接口描述文件.xml,dbus-binding-tool工具生成绑定文件)(列集散集函数)
Posted Dontla
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux 进程间通信 dbus-glib实例详解四(上) C库 dbus-glib 使用(附代码)(编写接口描述文件.xml,dbus-binding-tool工具生成绑定文件)(列集散集函数)相关的知识,希望对你有一定的参考价值。
linux 进程间通信 dbus-glib【实例】详解一(附代码)(d-feet工具使用)
linux 进程间通信 dbus-glib【实例】详解二(上) 消息和消息总线(附代码)
linux 进程间通信 dbus-glib【实例】详解二(下) 消息和消息总线(ListActivatableNames和服务器的自动启动)(附代码)
linux 进程间通信 dbus-glib【实例】详解三(下) 数据类型和dteeth(类型签名type域)(层级结构:服务Service --> Node(对象、object) 等 )(附代码)
linux 进程间通信 dbus-glib【实例】详解四(上) C库 dbus-glib 使用(附代码)(编写接口描述文件.xml,dbus-binding-tool工具生成绑定文件)
文章目录
dbus实例讲解(四上):使用dbus-glib
dbus-glib是dbus底层接口的一个封装。本讲我们用dbus-glib做一个dus接口,并写一个客户程序。
1、接口
1.1、编写接口描述文件
首先编写接口描述文件。我们要实现的连接的公共名是"org.freesmartphone.ogsmd",接口描述文件如下:
$ cat smss.xml
<?xml version="1.0" encoding="UTF-8" ?>
<node name="/org/freesmartphone/GSM/Device">
<interface name="org.freesmartphone.GSM.SMS">
<method name="SendMessage">
<arg name="number" type="s"/>
<arg name="contents" type="s"/>
<arg name="featuremap" type="asv"/>
<arg type="i" direction="out"/>
</method>
<signal name="IncomingMessage">
<arg name="address" type="s"/>
<arg name="contents" type="s"/>
<arg name="features" type="asv"/>
</signal>
</interface>
</node>
我们要在连接"org.freesmartphone.ogsmd"
中的实现对象"/org/freesmartphone/GSM/Device"
。 这个对象有接口"org.freesmartphone.GSM.SMS"
。这个接口有一个SendMessage方法和一个IncomingMessage信号。
SendMessage方法和IncomingMessage信号除了两个字符串参数外,还有一个asv
参数,这是一个哈希表,即python的字典。 键-值对的键类型是字符串,值类型是VARIANT。这个接口是openmoko fso接口的一部分。 但为简单起见,本例在哈希表部分,只用三个键值。
键"alphabet"对应的值类型是字符串。
键"csm_num"对应的值类型是INT32。
键"csm_seq"对应的值类型是INT32。
请注意方法和信号名应采用单词连写,首字母大写的格式。
1.2、由接口描述文件生成绑定文件
有一个叫dbus-binding-tool
的工具,它读入接口描述文件,产生一个绑定文件。这个文件包含了dbus对象的接口信息。 在主程序中我们通过dbus_g_object_type_install_info
函数向dbus-glib登记对象信息(DBusGObjectInfo结构)。
本例使用了autotool,在Makefile.am中可以这样调用dbus-binding-tool:
smss-glue.h: smss.xml
$(LIBTOOL) --mode=execute dbus-binding-tool --prefix=gsm_sms --mode=glib-server --output=smss-glue.h $(srcdir)/smss.xml
"--prefix"
参数定义了对象前缀
。设对象前缀是$(prefix)
,则生成的DBusGObjectInfo结构变量名就是dbus_glib_$(prefix)_object_info
。 绑定文件会为接口方法定义回调函数。回调函数的名称是这样的:首先将xml中的方法名称转换到全部小写,下划线分隔的格式,然后增加前缀"$(prefix)_"
。 例如:如果xml中有方法SendMessage,绑定文件就会引用一个名称为$(prefix)_send_message
的函数。
绑定文件还会为接口方法生成用于散集(Unmarshaling)(反序列化?)的函数。在dbus消息中,方法参数是以流格式
存在的。 该函数将方法参数由数据流还原到glib的数据格式,并传入方法的回调函数。 本例中,dbus-binding-tool生成以下的smss-glue.h:
Dontla:先照着前面创建smss.xml
文件,然后我在ubuntu上终端执行:dbus-binding-tool --mode=glib-server --prefix=gsm_sms smss.xml>smss-glue.h
参照:https://dontla.blog.csdn.net/article/details/122531787
(smss-glue.h)
/* Generated by dbus-binding-tool; do not edit! */
#ifndef __dbus_glib_marshal_gsm_sms_MARSHAL_H__
#define __dbus_glib_marshal_gsm_sms_MARSHAL_H__
#include <glib-object.h>
G_BEGIN_DECLS
#ifdef G_ENABLE_DEBUG
#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
#define g_marshal_value_peek_char(v) g_value_get_schar (v)
#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
#define g_marshal_value_peek_int(v) g_value_get_int (v)
#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
#define g_marshal_value_peek_long(v) g_value_get_long (v)
#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
#define g_marshal_value_peek_float(v) g_value_get_float (v)
#define g_marshal_value_peek_double(v) g_value_get_double (v)
#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
#define g_marshal_value_peek_param(v) g_value_get_param (v)
#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
#define g_marshal_value_peek_object(v) g_value_get_object (v)
#define g_marshal_value_peek_variant(v) g_value_get_variant (v)
#else /* !G_ENABLE_DEBUG */
/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
* Do not access GValues directly in your code. Instead, use the
* g_value_get_*() functions
*/
#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
#define g_marshal_value_peek_char(v) (v)->data[0].v_int
#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
#define g_marshal_value_peek_int(v) (v)->data[0].v_int
#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
#define g_marshal_value_peek_long(v) (v)->data[0].v_long
#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
#define g_marshal_value_peek_float(v) (v)->data[0].v_float
#define g_marshal_value_peek_double(v) (v)->data[0].v_double
#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer
#endif /* !G_ENABLE_DEBUG */
/* BOOLEAN:STRING,STRING,BOXED,POINTER,POINTER */
extern void dbus_glib_marshal_gsm_sms_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
void
dbus_glib_marshal_gsm_sms_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER (GClosure *closure,
GValue *return_value G_GNUC_UNUSED,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint G_GNUC_UNUSED,
gpointer marshal_data)
typedef gboolean (*GMarshalFunc_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER) (gpointer data1,
gpointer arg_1,
gpointer arg_2,
gpointer arg_3,
gpointer arg_4,
gpointer arg_5,
gpointer data2);
GMarshalFunc_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER callback;
GCClosure *cc = (GCClosure*) closure;
gpointer data1, data2;
gboolean v_return;
g_return_if_fail (return_value != NULL);
g_return_if_fail (n_param_values == 6);
if (G_CCLOSURE_SWAP_DATA (closure))
data1 = closure->data;
data2 = g_value_peek_pointer (param_values + 0);
else
data1 = g_value_peek_pointer (param_values + 0);
data2 = closure->data;
callback = (GMarshalFunc_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
v_return = callback (data1,
g_marshal_value_peek_string (param_values + 1),
g_marshal_value_peek_string (param_values + 2),
g_marshal_value_peek_boxed (param_values + 3),
g_marshal_value_peek_pointer (param_values + 4),
g_marshal_value_peek_pointer (param_values + 5),
data2);
g_value_set_boolean (return_value, v_return);
G_END_DECLS
#endif /* __dbus_glib_marshal_gsm_sms_MARSHAL_H__ */
#include <dbus/dbus-glib.h>
static const DBusGMethodInfo dbus_glib_gsm_sms_methods[] =
(GCallback) gsm_sms_send_message, dbus_glib_marshal_gsm_sms_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER, 0 ,
;
const DBusGObjectInfo dbus_glib_gsm_sms_object_info = 1,
dbus_glib_gsm_sms_methods,
1,
"org.freesmartphone.GSM.SMS\\0SendMessage\\0S\\0number\\0I\\0s\\0contents\\0I\\0s\\0featuremap\\0I\\0asv\\0arg3\\0O\\0F\\0N\\0i\\0\\0\\0",
"org.freesmartphone.GSM.SMS\\0IncomingMessage\\0\\0",
"\\0"
;
在包含绑定文件
前,我们必须声明绑定文件要引用的回调函数。(什么意思?)
2 对象
2.1 对象定义
dbus-glib用GObject实现dbus对象。所以我们首先要实现一个对象。在本例中,我们实现一个GsmSms对象,它继承了GObject:
(gsm_sms.h)
$ cat gsm_sms.h
#ifndef GSM_SMS_H
#define GSM_SMS_H
typedef struct GsmSms GsmSms;
typedef struct GsmSmsClass GsmSmsClass;
struct GsmSms
GObject parent;
;
struct GsmSmsClass
GObjectClass parent;
;
#define GSM_SMS_TYPE (gsm_sms_get_type ())
GType gsm_sms_get_type (void);
gboolean gsm_sms_send_message (GsmSms *obj, const char *number, const char *contents, GHashTable *featuremap, int *ret, GError **error);
void gsm_sms_emit_incoming_message(GsmSms *obj, const char * address, const char * contents, GHashTable *hash);
#endif
GObject的对象定义虽然繁琐,但有固定的套路。依样画葫芦,画多了就习惯了。我们在gsm_sms.h中声明了gsm_sms_send_message函数。 gsm_sms_send_message函数是在gsm_sms.c中实现的,在绑定文件smss-glue.h中用到。 因为主程序要使用绑定文件中的对象信息,所以应由主程序包含绑定文件。 主程序只要在包含绑定文件前包含gsm_sms.h,编译器就不会抱怨gsm_sms_send_message函数未声明。
2.2 信号的列集函数
列集(Marshaling)是将数据从某种格式存为流格式的操作;散集(Unmarshaling)则是列集的反操作,将信息从流格式中还原出来。 在绑定文件中,dbus-binding-tool自动生成函数将方法参数从dbus消息中还原出来,即实现了散集。 那么我们怎么把信号参数由glib的数据结构转换到消息中的数据流呢?
因为GsmSms对象有一个信号,所以在对象类初始化函数gsm_sms_class_init
中,我们要调用g_signal_new
创建信号。 g_signal_new要求我们提供一个列集函数。
glib有一些标准的列集函数,在gmarshal.h中定义。例如g_cclosure_marshal_VOID__STRING
,这个函数适合只有一个字符串参数的信号。 如果gmarshal.h没有提供适合的列集函数,我们可以用一个叫glib-genmarshal
的工具自动生成列集函数。 后面我们会看到,无论是标准列集函数还是生成的列集函数都是既可以用于列集也可以用于散集,这些函数通常都被称作列集函数
。
使用glib-genmarshal前,我们同样要准备一个输入文件:
$ cat sms-marshal.list
# see glib-genmarshal(1) for a detailed description of the file format,
# possible parameter types are:
# VOID indicates no return type, or no extra
# parameters. if VOID is used as the parameter
# list, no additional parameters may be present.
# BOOLEAN for boolean types (gboolean)
# CHAR for signed char types (gchar)
# UCHAR for unsigned char types (guchar)
# INT for signed integer types (gint)
# UINT for unsigned integer types (guint)
# LONG for signed long integer types (glong)
# ULONG for unsigned long integer types (gulong)
# ENUM for enumeration types (gint)
# FLAGS for flag enumeration types (guint)
# FLOAT for single-precision float types (gfloat)
# DOUBLE for double-precision float types (gdouble)
# STRING for string types (gchar*)
# PARAM for GParamSpec or derived types (GParamSpec*)
# BOXED for boxed (anonymous but reference counted) types (GBoxed*)
# POINTER for anonymous pointer types (gpointer)
# OBJECT for GObject or derived types (GObject*)
# NONE deprecated alias for VOID
# BOOL deprecated alias for BOOLEAN
VOID:STRING,STRING,BOXED
我们需要的函数返回类型是VOID,参数是STRING,STRING,BOXED。在Makefile.am中可以这样调用glib-genmarshal:
sms-marshal.h: sms-marshal.list
$(LIBTOOL) --mode=execute glib-genmarshal --header sms-marshal.list --prefix=sms_marshal > sms-marshal.h
sms-marshal.c: sms-marshal.list
$(LIBTOOL) --mode=execute glib-genmarshal --body sms-marshal.list --prefix=sms_marshal > sms-marshal.c
"--prefix"
和函数原型决定输出函数名。如果"--prefix=sms_marshal"
,函数原型是"OID:STRING,STRING,BOXED"
, 生成的列集函数名就必然是sms_marshal_VOID__STRING_STRING_BOXED
。
在本例中自动生成的文件内容如下:
$ cat sms-marshal.h
#ifndef __sms_marshal_MARSHAL_H__
#define __sms_marshal_MARSHAL_H__
#include <glib-object.h>
G_BEGIN_DECLS
/* VOID:STRING,STRING,BOXED (sms-marshal.list:24) */
extern void sms_marshal_VOID__STRING_STRING_BOXED (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
G_END_DECLS
#endif /* __sms_marshal_MARSHAL_H__ */
$ cat sms-marshal.c
#include <glib-object.h>
#ifdef G_ENABLE_DEBUG
#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
#define g_marshal_value_peek_char(v) g_value_get_char (v)
#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
#define g_marshal_value_peek_int(v) g_value_get_int (v)
#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
#define g_marshal_value_peek_long(v) g_value_get_long (v)
#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
#define g_marshal_value_peek_float(v) g_value_get_float (v)
#define g_marshal_value_peek_double(v) g_value_get_double (v)
#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
#define g_marshal_value_peek_paramlinux 进程间通信 dbus-glib实例详解二(下) 消息和消息总线(ListActivatableNames和服务器的自动启动)(附代码)
linux 进程间通信 dbus-glib实例详解三(下) 数据类型和dteeth(类型签名type域)(层级结构:服务Service --> Node(对象object) 等 )(附代码)
linux 进程间通信 dbus-glib实例详解四(上) C库 dbus-glib 使用(附代码)(编写接口描述文件.xml,dbus-binding-tool工具生成绑定文件)(列集散集函数)