diff options
44 files changed, 3646 insertions, 337 deletions
@@ -1,3 +1,12 @@ +2004-03-23 Owen Fraser-Green <owen@discobabe.net> + + First checkin of mono bindings. + + * configure.in: + * Makefile.am: + Build stuff for the bindings + * dbus-sharp.pc.in: Added for pkgconfig + 2004-03-21 Havoc Pennington <hp@redhat.com> * test/test-service.c (main): remove debug spew diff --git a/Makefile.am b/Makefile.am index 82fee346..aa6f0ecd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -14,6 +14,7 @@ endif if DBUS_USE_MCS MONO_SUBDIR=mono + MONO_PC=dbus-sharp.pc endif if HAVE_PYTHON @@ -30,16 +31,18 @@ dist-local: SUBDIRS=dbus bus doc $(GLIB_SUBDIR) $(GCJ_SUBDIR) $(MONO_SUBDIR) $(QT_SUBDIR) $(PYTHON_SUBDIR) test tools pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = dbus-1.pc $(GLIB_PC) +pkgconfig_DATA = dbus-1.pc $(GLIB_PC) $(MONO_PC) DISTCLEANFILES = \ dbus-1.pc \ - $(GLIB_PC) + $(GLIB_PC) \ + $(MONO_PC) EXTRA_DIST = \ HACKING \ dbus-1.pc.in \ - dbus-glib-1.pc.in + dbus-glib-1.pc.in \ + dbus-sharp.pc.in all-local: Doxyfile diff --git a/configure.in b/configure.in index 5612f301..e92fdb14 100644 --- a/configure.in +++ b/configure.in @@ -35,7 +35,7 @@ AC_ARG_ENABLE(doxygen-docs, [ --enable-doxygen-docs build DOXYGEN docum AC_ARG_ENABLE(gcov, [ --enable-gcov compile with coverage profiling instrumentation (gcc only)],enable_gcov=$enableval,enable_gcov=no) AC_ARG_ENABLE(abstract-sockets, [ --enable-abstract-sockets use abstract socket namespace (linux only)],enable_abstract_sockets=$enableval,enable_abstract_sockets=auto) AC_ARG_ENABLE(gcj, [ --enable-gcj build gcj bindings],enable_gcj=$enableval,enable_gcj=no) -AC_ARG_ENABLE(mono, [ --enable-mono build mono bindings],enable_mono=$enableval,enable_mono=no) +AC_ARG_ENABLE(mono, [ --enable-mono build mono bindings],enable_mono=$enableval,enable_mono=auto) AC_ARG_ENABLE(python, [ --enable-python build python bindings],enable_python=$enableval,enable_python=auto) @@ -1022,6 +1022,7 @@ gcj/org/Makefile gcj/org/freedesktop/Makefile gcj/org/freedesktop/dbus/Makefile mono/Makefile +mono/example/Makefile bus/Makefile tools/Makefile test/Makefile @@ -1090,6 +1091,7 @@ echo " Building Qt bindings: ${have_qt} Building GLib bindings: ${have_glib} Building Python bindings: ${have_python} + Building Mono bindings: ${enable_mono} Building GTK+ tools: ${have_gtk} Building X11 code: ${enable_x11} Building Doxygen docs: ${enable_doxygen_docs} diff --git a/dbus-sharp.pc.in b/dbus-sharp.pc.in new file mode 100644 index 00000000..c6b7044c --- /dev/null +++ b/dbus-sharp.pc.in @@ -0,0 +1,8 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ + +Name: DBus# +Description: DBus# - D-BUS .NET Bindings +Version: 0.1 + diff --git a/mono/Arguments.cs b/mono/Arguments.cs new file mode 100644 index 00000000..ac88d6a7 --- /dev/null +++ b/mono/Arguments.cs @@ -0,0 +1,304 @@ +using System; +using System.Collections; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace DBus +{ + // Holds the arguments of a message. Provides methods for appending + // arguments and to assist in matching .NET types with D-BUS types. + public class Arguments : IEnumerable + { + // Must follow sizeof(DBusMessageIter) + internal const int DBusMessageIterSize = 14*4; + private static Hashtable dbusTypes = null; + private Message message; + private IntPtr appenderIter = Marshal.AllocCoTaskMem(DBusMessageIterSize); + private IEnumerator enumerator = null; + + internal Arguments() + { + } + + ~Arguments() + { + Marshal.FreeCoTaskMem(appenderIter); + } + + internal Arguments(Message message) + { + this.message = message; + } + + // Checks the suitability of a D-BUS type for supporting a .NET + // type. + public static bool Suits(Type dbusType, Type type) + { + object [] pars = new object[1]; + pars[0] = type; + + return (bool) dbusType.InvokeMember("Suits", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, pars, null); + } + + // Find a suitable match for the given .NET type or throw an + // exception if one can't be found. + public static Type MatchType(Type type) + { + foreach(Type dbusType in DBusTypes.Values) { + if (Suits(dbusType, type)) { + return dbusType; + } + } + + throw new ApplicationException("No suitable DBUS type found for type '" + type + "'"); + } + + // The D-BUS types. + public static Hashtable DBusTypes { + get + { + if (dbusTypes == null) { + dbusTypes = new Hashtable(); + + foreach (Type type in Assembly.GetAssembly(typeof(DBusType.IDBusType)).GetTypes()) { + if (type != typeof(DBusType.IDBusType) && typeof(DBusType.IDBusType).IsAssignableFrom(type)) { + dbusTypes.Add(GetCode(type), type); + } + } + } + + return dbusTypes; + } + } + + // Append an argument + public void Append(DBusType.IDBusType dbusType) + { + if (dbusType.GetType() == typeof(DBusType.ObjectPath)) { + ((DBusType.ObjectPath) dbusType).SetService(message.Service); + } + dbusType.Append(appenderIter); + } + + // Append an argument of the specified type + private void AppendType(Type type, object val) + { + object [] pars = new Object[1]; + pars[0] = val; + DBusType.IDBusType dbusType = (DBusType.IDBusType) Activator.CreateInstance(MatchType(type), pars); + Append(dbusType); + } + + // Append the results of a method call + public void AppendResults(MethodInfo method, object retVal, object [] parameters) + { + InitAppending(); + + if (method.ReturnType != typeof(void)) { + AppendType(method.ReturnType, retVal); + } + + for (int i = 0; i < method.GetParameters().Length; i++) { + ParameterInfo par = method.GetParameters()[i]; + if (par.IsOut || par.ParameterType.ToString().EndsWith("&")) { + // It's an OUT or INOUT parameter. + AppendType(par.ParameterType.UnderlyingSystemType, parameters[i]); + } + } + } + + // Get the parameters + public object[] GetParameters(MethodInfo method) + { + ParameterInfo[] pars = method.GetParameters(); + ArrayList paramList = new ArrayList(); + + enumerator = GetEnumerator(); + foreach (ParameterInfo par in pars) { + if (!par.IsOut) { + // It's an IN or INOUT paramter. + enumerator.MoveNext(); + DBusType.IDBusType dbusType = (DBusType.IDBusType) enumerator.Current; + paramList.Add(dbusType.Get(par.ParameterType)); + } else { + // It's an OUT so just create a parameter for it + object var = null; + paramList.Add(var); + } + } + + return paramList.ToArray(); + } + + // Parse the IN & REF parameters to a method and return the types in a list. + public static object[] ParseInParameters(MethodInfo method) + { + ArrayList types = new ArrayList(); + + ParameterInfo[] pars = method.GetParameters(); + foreach (ParameterInfo par in pars) { + if (!par.IsOut) { + types.Add(MatchType(par.ParameterType)); + } + } + + return types.ToArray(); + } + + // Parse the OUT & REF parameters to a method and return the types in a list. + public static object[] ParseOutParameters(MethodInfo method) + { + ArrayList types = new ArrayList(); + + ParameterInfo[] pars = method.GetParameters(); + foreach (ParameterInfo par in pars) { + if (par.IsOut || par.ParameterType.ToString().EndsWith("&")) { + types.Add(MatchType(par.ParameterType)); + } + } + + return types.ToArray(); + } + + // Get the appropriate constructor for a D-BUS type + public static ConstructorInfo GetDBusTypeConstructor(Type dbusType, Type type) + { + ConstructorInfo constructor = dbusType.GetConstructor(new Type[] {type.UnderlyingSystemType}); + if (constructor == null) + throw new ArgumentException("There is no valid constructor for '" + dbusType + "' from type '" + type + "'"); + + return constructor; + } + + // Get the signature of a method + public static string ParseParameters(MethodInfo method) + { + ParameterInfo[] pars = method.GetParameters(); + string key = ""; + + foreach (ParameterInfo par in pars) { + if (!par.IsOut) { + Type dbusType = MatchType(par.ParameterType); + key += GetCode(dbusType); + } + } + + return key; + } + + // Get the type code for a given D-BUS type + public static char GetCode(Type dbusType) + { + return (char) dbusType.InvokeMember("Code", BindingFlags.Static | BindingFlags.GetField, null, null, null); + } + + // Get a complete method signature + public override string ToString() + { + IntPtr iter = Marshal.AllocCoTaskMem(DBusMessageIterSize); + string key = ""; + + // Iterate through the parameters getting the type codes to a string + dbus_message_iter_init(message.RawMessage, iter); + + do { + char code = (char) dbus_message_iter_get_arg_type(iter); + if (code == '\0') + return key; + + key += code; + } while (dbus_message_iter_next(iter)); + + Marshal.FreeCoTaskMem(iter); + + return key; + } + + // Move to the next parameter + public DBusType.IDBusType GetNext() + { + enumerator.MoveNext(); + return (DBusType.IDBusType) enumerator.Current; + } + + // Begin appending + public void InitAppending() + { + dbus_message_append_iter_init(message.RawMessage, appenderIter); + } + + // Get the enumerator + public IEnumerator GetEnumerator() + { + return new ArgumentsEnumerator(this); + } + + private class ArgumentsEnumerator : IEnumerator + { + private Arguments arguments; + private bool started = false; + private IntPtr iter = Marshal.AllocCoTaskMem(Arguments.DBusMessageIterSize); + + public ArgumentsEnumerator(Arguments arguments) + { + this.arguments = arguments; + Reset(); + } + + ~ArgumentsEnumerator() + { + Marshal.FreeCoTaskMem(iter); + } + + public bool MoveNext() + { + if (started) { + return dbus_message_iter_next(iter); + } else { + started = true; + return true; + } + } + + public void Reset() + { + dbus_message_iter_init(arguments.message.RawMessage, iter); + started = false; + } + + public object Current + { + get + { + object [] pars = new Object[1]; + pars[0] = iter; + + Type type = (Type) DBusTypes[(char) dbus_message_iter_get_arg_type(iter)]; + DBusType.IDBusType dbusType = (DBusType.IDBusType) Activator.CreateInstance(type, pars); + + // Special case for ObjectPath + if (type == typeof(DBusType.ObjectPath)) { + ((DBusType.ObjectPath) dbusType).SetService(arguments.message.Service); + } + + return dbusType; + } + } + } + + [DllImport("dbus-1")] + private extern static void dbus_message_append_iter_init(IntPtr rawMessage, IntPtr iter); + + [DllImport("dbus-1")] + private extern static bool dbus_message_iter_has_next(IntPtr iter); + + [DllImport("dbus-1")] + private extern static bool dbus_message_iter_next(IntPtr iter); + + [DllImport("dbus-1")] + private extern static void dbus_message_iter_init(IntPtr rawMessage, IntPtr iter); + + [DllImport("dbus-1")] + private extern static int dbus_message_iter_get_arg_type(IntPtr iter); + } +} diff --git a/mono/Bus.cs b/mono/Bus.cs new file mode 100644 index 00000000..963e8195 --- /dev/null +++ b/mono/Bus.cs @@ -0,0 +1,51 @@ +namespace DBus +{ + using System; + using System.Runtime.InteropServices; + using System.Diagnostics; + + public class Bus + { + // Keep in sync with C + private enum BusType + { + Session = 0, + System = 1, + Activation = 2 + } + + public static Connection GetSessionBus() + { + return GetBus(BusType.Session); + } + + public static Connection GetSystemBus() + { + return GetBus(BusType.System); + } + + private static Connection GetBus(BusType busType) + { + Error error = new Error(); + error.Init(); + + IntPtr rawConnection = dbus_bus_get((int) busType, ref error); + + if (rawConnection != IntPtr.Zero) { + Connection connection = Connection.Wrap(rawConnection); + connection.SetupWithMain(); + dbus_connection_unref(rawConnection); + + return connection; + } else { + throw new DBusException(error); + } + } + + [DllImport ("dbus-1")] + private extern static IntPtr dbus_bus_get (int which, ref Error error); + + [DllImport ("dbus-1")] + private extern static void dbus_connection_unref (IntPtr ptr); + } +} diff --git a/mono/Connection.cs b/mono/Connection.cs index 56dcb7a2..406e779b 100644 --- a/mono/Connection.cs +++ b/mono/Connection.cs @@ -1,192 +1,180 @@ -namespace DBus { +namespace DBus +{ using System; using System.Runtime.InteropServices; using System.Diagnostics; + using System.Reflection; + using System.IO; + using System.Collections; - public class Connection { + public class Connection + { + /// <summary> + /// A pointer to the underlying Connection structure + /// </summary> + private IntPtr rawConnection; + + /// <summary> + /// The current slot number + /// </summary> + private static int slot = -1; + + private int timeout = -1; - public Connection (string address) { + internal Connection(IntPtr rawConnection) + { + RawConnection = rawConnection; + } + + public Connection(string address) + { // the assignment bumps the refcount - Error error = new Error (); - error.Init (); - raw = dbus_connection_open (address, ref error); - if (raw != IntPtr.Zero) { - dbus_connection_unref (raw); + Error error = new Error(); + error.Init(); + RawConnection = dbus_connection_open(address, ref error); + if (RawConnection != IntPtr.Zero) { + dbus_connection_unref(RawConnection); } else { - Exception e = new Exception (ref error); - error.Free (); - throw e; + throw new DBusException(error); } - dbus_connection_setup_with_g_main (raw, IntPtr.Zero); - } - // Keep in sync with C - public enum BusType { - Session = 0, - System = 1, - Activation = 2 + SetupWithMain(); } - public static Connection GetBus (BusType bus) { - Error error = new Error (); - - error.Init (); - - IntPtr ptr = dbus_bus_get ((int) bus, ref error); - if (ptr != IntPtr.Zero) { - Connection c = Wrap (ptr); - dbus_connection_unref (ptr); - return c; - } else { - Exception e = new Exception (ref error); - error.Free (); - throw e; - } + public void SetupWithMain() + { + dbus_connection_setup_with_g_main(RawConnection, IntPtr.Zero); } - public void Send (Message m, - ref int serial) { - if (!dbus_connection_send (raw, m.raw, ref serial)) - throw new OutOfMemoryException (); - } - - public void Send (Message m) { - int ignored = 0; - Send (m, ref ignored); - } - - public void Flush () { - dbus_connection_flush (raw); - } - - public void Disconnect () { - dbus_connection_disconnect (raw); + ~Connection () + { + if (RawConnection != IntPtr.Zero) + { + dbus_connection_disconnect(rawConnection); + } + RawConnection = IntPtr.Zero; // free the native object } - public static Connection Wrap (IntPtr ptr) { - IntPtr gch_ptr; - - gch_ptr = dbus_connection_get_data (ptr, wrapper_slot); - if (gch_ptr != IntPtr.Zero) { - return (DBus.Connection) ((GCHandle)gch_ptr).Target; - } else { - return new Connection (ptr); - } - } - - // surely there's a convention for this pattern with the property - // and the real member - IntPtr raw_; - internal IntPtr raw { - get { - return raw_; - } - set { - if (value == raw_) - return; - - if (raw_ != IntPtr.Zero) { - IntPtr gch_ptr; - - gch_ptr = dbus_connection_get_data (raw_, - wrapper_slot); - Debug.Assert (gch_ptr != IntPtr.Zero); - - dbus_connection_set_data (raw_, wrapper_slot, - IntPtr.Zero, IntPtr.Zero); - - ((GCHandle) gch_ptr).Free (); - - dbus_connection_unref (raw_); - } - - raw_ = value; - - if (raw_ != IntPtr.Zero) { - GCHandle gch; - - dbus_connection_ref (raw_); - - // We store a weak reference to the C# object on the C object - gch = GCHandle.Alloc (this, GCHandleType.WeakTrackResurrection); - - dbus_connection_set_data (raw_, wrapper_slot, - (IntPtr) gch, IntPtr.Zero); - } - } + internal static Connection Wrap(IntPtr rawConnection) + { + if (slot > -1) { + // If we already have a Connection object associated with this rawConnection then return it + IntPtr rawThis = dbus_connection_get_data (rawConnection, slot); + return (DBus.Connection) ((GCHandle)rawThis).Target; + } + else + { + // If it doesn't exist then create a new connection around it + return new Connection(rawConnection); + } } - ~Connection () { - if (raw != IntPtr.Zero) { - Disconnect (); - } - raw = IntPtr.Zero; // free the native object + public int Timeout + { + get + { + return this.timeout; + } + set + { + this.timeout = value; + } } - Connection (IntPtr r) { - raw = r; + private int Slot + { + get + { + if (slot == -1) + { + // We need to initialize the slot + if (!dbus_connection_allocate_data_slot (ref slot)) + throw new OutOfMemoryException (); + + Debug.Assert (slot >= 0); + } + + return slot; + } } - // static constructor runs before any methods - static Connection () { - DBus.Internals.Init (); - - Debug.Assert (wrapper_slot == -1); - - if (!dbus_connection_allocate_data_slot (ref wrapper_slot)) - throw new OutOfMemoryException (); - - Debug.Assert (wrapper_slot >= 0); + internal IntPtr RawConnection + { + get + { + return rawConnection; + } + set + { + if (value == rawConnection) + return; + + if (rawConnection != IntPtr.Zero) + { + // Get the reference to this + IntPtr rawThis = dbus_connection_get_data (rawConnection, Slot); + Debug.Assert (rawThis != IntPtr.Zero); + + // Blank over the reference + dbus_connection_set_data (rawConnection, Slot, IntPtr.Zero, IntPtr.Zero); + + // Free the reference + ((GCHandle) rawThis).Free(); + + // Unref the connection + dbus_connection_unref(rawConnection); + } + + this.rawConnection = value; + + if (rawConnection != IntPtr.Zero) + { + GCHandle rawThis; + + dbus_connection_ref (rawConnection); + + // We store a weak reference to the C# object on the C object + rawThis = GCHandle.Alloc (this, GCHandleType.WeakTrackResurrection); + + dbus_connection_set_data(rawConnection, Slot, (IntPtr) rawThis, IntPtr.Zero); + } + } } - // slot used to store the C# object on the C object - static int wrapper_slot = -1; + [DllImport("dbus-glib-1")] + private extern static void dbus_connection_setup_with_g_main(IntPtr rawConnection, + IntPtr rawContext); - [DllImport (DBus.Internals.DBusLibname, EntryPoint="dbus_connection_open")] - private extern static IntPtr dbus_connection_open (string address, - ref Error error); - - [DllImport (DBus.Internals.DBusLibname, EntryPoint="dbus_connection_unref")] - private extern static void dbus_connection_unref (IntPtr ptr); - - [DllImport (DBus.Internals.DBusLibname, EntryPoint="dbus_connection_ref")] - private extern static void dbus_connection_ref (IntPtr ptr); - - [DllImport (DBus.Internals.DBusLibname, EntryPoint="dbus_connection_allocate_data_slot")] - private extern static bool dbus_connection_allocate_data_slot (ref int slot); - - [DllImport (DBus.Internals.DBusLibname, EntryPoint="dbus_connection_free_data_slot")] - private extern static void dbus_connection_free_data_slot (ref int slot); - - [DllImport (DBus.Internals.DBusLibname, EntryPoint="dbus_connection_set_data")] - private extern static bool dbus_connection_set_data (IntPtr ptr, - int slot, - IntPtr data, - IntPtr free_data_func); - - [DllImport (DBus.Internals.DBusLibname, EntryPoint="dbus_connection_send")] - private extern static bool dbus_connection_send (IntPtr ptr, - IntPtr message, - ref int client_serial); - - [DllImport (DBus.Internals.DBusLibname, EntryPoint="dbus_connection_flush")] - private extern static void dbus_connection_flush (IntPtr ptr); + [DllImport ("dbus-1")] + private extern static IntPtr dbus_connection_open (string address, ref Error error); - [DllImport (DBus.Internals.DBusLibname, EntryPoint="dbus_bus_get")] - private extern static IntPtr dbus_bus_get (int which, - ref Error error); + [DllImport ("dbus-1")] + private extern static void dbus_connection_unref (IntPtr ptr); - [DllImport (DBus.Internals.DBusLibname, EntryPoint="dbus_connection_get_data")] - private extern static IntPtr dbus_connection_get_data (IntPtr ptr, - int slot); - - [DllImport (DBus.Internals.DBusLibname, EntryPoint="dbus_connection_disconnect")] - private extern static void dbus_connection_disconnect (IntPtr ptr); + [DllImport ("dbus-1")] + private extern static void dbus_connection_ref (IntPtr ptr); + + [DllImport ("dbus-1")] + private extern static bool dbus_connection_allocate_data_slot (ref int slot); + + [DllImport ("dbus-1")] + private extern static void dbus_connection_free_data_slot (ref int slot); + + [DllImport ("dbus-1")] + private extern static bool dbus_connection_set_data (IntPtr ptr, + int slot, + IntPtr data, + IntPtr free_data_func); + + [DllImport ("dbus-1")] + private extern static void dbus_connection_flush (IntPtr ptr); - [DllImport (DBus.Internals.DBusGLibname, EntryPoint="dbus_connection_setup_with_g_main")] - private extern static void dbus_connection_setup_with_g_main (IntPtr ptr, - IntPtr context); + [DllImport ("dbus-1")] + private extern static IntPtr dbus_connection_get_data (IntPtr ptr, + int slot); + [DllImport ("dbus-1")] + private extern static void dbus_connection_disconnect (IntPtr ptr); } } diff --git a/mono/Custom.cs b/mono/Custom.cs new file mode 100644 index 00000000..f96562b9 --- /dev/null +++ b/mono/Custom.cs @@ -0,0 +1,18 @@ +using System; + +using DBus; + +namespace DBus +{ + public struct Custom + { + public string Name; + public byte[] Data; + + public Custom(string name, byte[] data) + { + Name = name; + Data = data; + } + } +} diff --git a/mono/DBus.cs b/mono/DBus.cs deleted file mode 100644 index 377af742..00000000 --- a/mono/DBus.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace DBus { - - using System; - using System.Runtime.InteropServices; - - public class Exception : ApplicationException { - internal Exception (ref Error error) - : base (Runtime.InteropServices.Marshal.PtrToStringAnsi (error.message)) { } - } - - internal class Internals { - internal const string DBusLibname = "libdbus-1.so.0"; - internal const string DBusGLibname = "libdbus-glib-1.so.0"; - internal const string GLibname = "libglib-2.0.so.0"; - internal const string GThreadname = "libgthread-2.0.so.0"; - - internal static void Init () { - dbus_gthread_init (); - } - - [DllImport (DBus.Internals.DBusGLibname, EntryPoint="dbus_gthread_init")] - private extern static void dbus_gthread_init (); - } -} diff --git a/mono/DBusException.cs b/mono/DBusException.cs new file mode 100644 index 00000000..5c912cca --- /dev/null +++ b/mono/DBusException.cs @@ -0,0 +1,12 @@ +namespace DBus +{ + using System; + using System.Runtime.InteropServices; + + public class DBusException : ApplicationException + { + internal DBusException (Error error) : base (error.Message) { + error.Free(); + } + } +} diff --git a/mono/DBusType/Array.cs b/mono/DBusType/Array.cs new file mode 100644 index 00000000..3bce3afa --- /dev/null +++ b/mono/DBusType/Array.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections; +using System.Runtime.InteropServices; +using System.Reflection.Emit; + +using DBus; + +namespace DBus.DBusType +{ + /// <summary> + /// Array. + /// </summary> + public class Array : IDBusType + { + public const char Code = 'a'; + private System.Array val; + private ArrayList elements; + private Type elementType; + + private Array() + { + } + + public Array(System.Array val) + { + this.val = val; + this.elementType = Arguments.MatchType(val.GetType().UnderlyingSystemType); + } + + public Array(IntPtr iter) + { + IntPtr arrayIter = Marshal.AllocCoTaskMem(Arguments.DBusMessageIterSize); + + int elementTypeCode; + dbus_message_iter_init_array_iterator(iter, arrayIter, out elementTypeCode); + this.elementType = (Type) Arguments.DBusTypes[(char) elementTypeCode]; + + elements = new ArrayList(); + + do { + object [] pars = new Object[1]; + pars[0] = arrayIter; + DBusType.IDBusType dbusType = (DBusType.IDBusType) Activator.CreateInstance(elementType, pars); + elements.Add(dbusType); + } while (dbus_message_iter_next(arrayIter)); + + Marshal.FreeCoTaskMem(arrayIter); + } + + public void Append(IntPtr iter) + { + IntPtr arrayIter = Marshal.AllocCoTaskMem(Arguments.DBusMessageIterSize); + + if (!dbus_message_iter_append_array(iter, + arrayIter, + (int) Arguments.GetCode(this.elementType))) { + throw new ApplicationException("Failed to append INT32 argument:" + val); + } + + foreach (object element in this.val) { + object [] pars = new Object[1]; + pars[0] = element; + DBusType.IDBusType dbusType = (DBusType.IDBusType) Activator.CreateInstance(elementType, pars); + dbusType.Append(arrayIter); + } + + Marshal.FreeCoTaskMem(arrayIter); + } + + public static bool Suits(System.Type type) + { + if (type.IsArray) { + return true; + } + + return false; + } + + public static void EmitMarshalIn(ILGenerator generator, Type type) + { + if (type.IsByRef) { + generator.Emit(OpCodes.Ldind_Ref); + } + } + + public static void EmitMarshalOut(ILGenerator generator, Type type, bool isReturn) + { + generator.Emit(OpCodes.Castclass, type); + if (!isReturn) { + generator.Emit(OpCodes.Stind_Ref); + } + } + + public object Get() + { + throw new ArgumentException("Cannot call Get on an Array without specifying type."); + } + + public object Get(System.Type type) + { + if (Arguments.Suits(elementType, type.UnderlyingSystemType)) { + this.val = System.Array.CreateInstance(type.UnderlyingSystemType, elements.Count); + int i = 0; + foreach (DBusType.IDBusType element in elements) { + this.val.SetValue(element.Get(type.UnderlyingSystemType), i++); + } + } else { + throw new ArgumentException("Cannot cast DBus.Type.Array to type '" + type.ToString() + "'"); + } + + return this.val; + } + + [DllImport("dbus-1")] + private extern static void dbus_message_iter_init_array_iterator(IntPtr iter, + IntPtr arrayIter, + out int elementType); + + [DllImport("dbus-1")] + private extern static bool dbus_message_iter_append_array(IntPtr iter, + IntPtr arrayIter, + int elementType); + + [DllImport("dbus-1")] + private extern static bool dbus_message_iter_has_next(IntPtr iter); + + [DllImport("dbus-1")] + private extern static bool dbus_message_iter_next(IntPtr iter); + } +} diff --git a/mono/DBusType/Boolean.cs b/mono/DBusType/Boolean.cs new file mode 100644 index 00000000..ef8ed498 --- /dev/null +++ b/mono/DBusType/Boolean.cs @@ -0,0 +1,86 @@ +using System; +using System.Runtime.InteropServices; +using System.Reflection.Emit; + +using DBus; + +namespace DBus.DBusType +{ + /// <summary> + /// Boolean + /// </summary> + public class Boolean : IDBusType + { + public const char Code = 'b'; + private System.Boolean val; + + private Boolean() + { + } + + public Boolean(System.Boolean val) + { + this.val = val; + } + + public Boolean(IntPtr iter) + { + this.val = dbus_message_iter_get_boolean(iter); + } + + public void Append(IntPtr iter) + { + if (!dbus_message_iter_append_boolean(iter, val)) + throw new ApplicationException("Failed to append BOOLEAN argument:" + val); + } + + public static bool Suits(System.Type type) + { + switch (type.ToString()) { + case "System.Boolean": + case "System.Boolean&": + return true; + } + + return false; + } + + public static void EmitMarshalIn(ILGenerator generator, Type type) + { + if (type.IsByRef) { + generator.Emit(OpCodes.Ldind_I1); + } + } + + public static void EmitMarshalOut(ILGenerator generator, Type type, bool isReturn) + { + generator.Emit(OpCodes.Unbox, type); + generator.Emit(OpCodes.Ldind_I1); + if (!isReturn) { + generator.Emit(OpCodes.Stind_I1); + } + } + + public object Get() + { + return this.val; + } + + public object Get(System.Type type) + { + switch (type.ToString()) { + case "System.Boolean": + case "System.Boolean&": + return this.val; + default: + throw new ArgumentException("Cannot cast DBus.Type.Boolean to type '" + type.ToString() + "'"); + } + } + + [DllImport("dbus-1")] + private extern static System.Boolean dbus_message_iter_get_boolean(IntPtr iter); + + [DllImport("dbus-1")] + private extern static bool dbus_message_iter_append_boolean(IntPtr iter, System.Boolean value); + } +} diff --git a/mono/DBusType/Byte.cs b/mono/DBusType/Byte.cs new file mode 100644 index 00000000..eaffd26e --- /dev/null +++ b/mono/DBusType/Byte.cs @@ -0,0 +1,86 @@ +using System; +using System.Runtime.InteropServices; +using System.Reflection.Emit; + +using DBus; + +namespace DBus.DBusType +{ + /// <summary> + /// Byte + /// </summary> + public class Byte : IDBusType + { + public const char Code = 'y'; + private System.Byte val; + + private Byte() + { + } + + public Byte(System.Byte val) + { + this.val = val; + } + + public Byte(IntPtr iter) + { + this.val = dbus_message_iter_get_byte(iter); + } + + public void Append(IntPtr iter) + { + if (!dbus_message_iter_append_byte(iter, val)) + throw new ApplicationException("Failed to append BYTE argument:" + val); + } + + public static bool Suits(System.Type type) + { + switch (type.ToString()) { + case "System.Byte": + case "System.Byte&": + return true; + } + + return false; + } + + public static void EmitMarshalIn(ILGenerator generator, Type type) + { + if (type.IsByRef) { + generator.Emit(OpCodes.Ldind_U1); + } + } + + public static void EmitMarshalOut(ILGenerator generator, Type type, bool isReturn) + { + generator.Emit(OpCodes.Unbox, type); + generator.Emit(OpCodes.Ldind_U1); + if (!isReturn) { + generator.Emit(OpCodes.Stind_I1); + } + } + + public object Get() + { + return this.val; + } + + public object Get(System.Type type) + { + switch (type.ToString()) { + case "System.Byte": + case "System.Byte&": + return this.val; + default: + throw new ArgumentException("Cannot cast DBus.Type.Byte to type '" + type.ToString() + "'"); + } + } + + [DllImport("dbus-1")] + private extern static System.Byte dbus_message_iter_get_byte(IntPtr iter); + + [DllImport("dbus-1")] + private extern static bool dbus_message_iter_append_byte(IntPtr iter, System.Byte value); + } +} diff --git a/mono/DBusType/Custom.cs b/mono/DBusType/Custom.cs new file mode 100644 index 00000000..d3eb7629 --- /dev/null +++ b/mono/DBusType/Custom.cs @@ -0,0 +1,109 @@ +using System; +using System.Runtime.InteropServices; +using System.Reflection.Emit; + +using DBus; + +namespace DBus.DBusType +{ + /// <summary> + /// A named byte array, used for custom types. + /// </summary> + public class Custom : IDBusType + { + public const char Code = 'c'; + private DBus.Custom val; + + private Custom() + { + } + + public Custom(DBus.Custom val) + { + this.val = val; + } + + public Custom(IntPtr iter) + { + string name; + IntPtr value; + int len; + + if (!dbus_message_iter_get_custom(iter, out name, out value, out len)) { + throw new ApplicationException("Failed to get CUSTOM argument."); + } + + this.val.Name = name; + this.val.Data = new byte[len]; + Marshal.Copy(value, this.val.Data, 0, len); + } + + public void Append(IntPtr iter) + { + IntPtr data = Marshal.AllocCoTaskMem(this.val.Data.Length); + try { + Marshal.Copy(this.val.Data, 0, data, this.val.Data.Length); + if (!dbus_message_iter_append_custom(iter, this.val.Name, data, this.val.Data.Length)) { + throw new ApplicationException("Failed to append CUSTOM argument:" + val); + } + } finally { + Marshal.FreeCoTaskMem(data); + } + } + + public static bool Suits(System.Type type) + { + switch (type.ToString()) { + case "DBus.Custom": + case "DBus.Custom&": + return true; + } + + return false; + } + + public static void EmitMarshalIn(ILGenerator generator, Type type) + { + if (type.IsByRef) { + generator.Emit(OpCodes.Ldobj, type); + } + } + + public static void EmitMarshalOut(ILGenerator generator, Type type, bool isReturn) + { + generator.Emit(OpCodes.Unbox, type); + generator.Emit(OpCodes.Ldobj, type); + if (!isReturn) { + generator.Emit(OpCodes.Stobj, type); + } + } + + public object Get() + { + return this.val; + } + + public object Get(System.Type type) + { + switch (type.ToString()) { + case "DBus.Custom": + case "DBus.Custom&": + return this.val; + default: + throw new ArgumentException("Cannot cast DBus.Type.Custom to type '" + type.ToString() + "'"); + } + } + + [DllImport("dbus-1")] + private extern static bool dbus_message_iter_get_custom(IntPtr iter, + out string name, + out IntPtr value, + out int len); + + [DllImport("dbus-1")] + private extern static bool dbus_message_iter_append_custom(IntPtr iter, + string name, + IntPtr data, + int len); + } +} diff --git a/mono/DBusType/Dict.cs b/mono/DBusType/Dict.cs new file mode 100644 index 00000000..e6fce159 --- /dev/null +++ b/mono/DBusType/Dict.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections; +using System.Runtime.InteropServices; +using System.Reflection.Emit; + +using DBus; + +namespace DBus.DBusType +{ + /// <summary> + /// Dict. + /// </summary> + public class Dict : IDBusType + { + public const char Code = 'm'; + private Hashtable val; + + private Dict() + { + } + + public Dict(IDictionary val) + { + this.val = new Hashtable(); + foreach (DictionaryEntry entry in val) { + this.val.Add(entry.Key, entry.Value); + } + } + + public Dict(IntPtr iter) + { + IntPtr dictIter = Marshal.AllocCoTaskMem(Arguments.DBusMessageIterSize); + + dbus_message_iter_init_dict_iterator(iter, dictIter); + + this.val = new Hashtable(); + + do { + string key = dbus_message_iter_get_dict_key(dictIter); + + // Get the argument type and get the value + Type elementType = (Type) DBus.Arguments.DBusTypes[(char) dbus_message_iter_get_arg_type(dictIter)]; + object [] pars = new Object[1]; + pars[0] = dictIter; + DBusType.IDBusType dbusType = (DBusType.IDBusType) Activator.CreateInstance(elementType, pars); + this.val.Add(key, dbusType); + } while (dbus_message_iter_next(dictIter)); + + Marshal.FreeCoTaskMem(dictIter); + } + + public void Append(IntPtr iter) + { + IntPtr dictIter = Marshal.AllocCoTaskMem(Arguments.DBusMessageIterSize); + + if (!dbus_message_iter_append_dict(iter, + dictIter)) { + throw new ApplicationException("Failed to append DICT argument:" + val); + } + + foreach (DictionaryEntry entry in this.val) { + if (!dbus_message_iter_append_dict_key(dictIter, (string) entry.Key)) { + throw new ApplicationException("Failed to append DICT key:" + entry.Key); + } + + // Get the element type + Type elementType = Arguments.MatchType(entry.Value.GetType()); + object [] pars = new Object[1]; + pars[0] = entry.Value; + DBusType.IDBusType dbusType = (DBusType.IDBusType) Activator.CreateInstance(elementType, pars); + dbusType.Append(dictIter); + } + + Marshal.FreeCoTaskMem(dictIter); + } + + public static bool Suits(System.Type type) + { + if (typeof(IDictionary).IsAssignableFrom(type)) { + return true; + } + + return false; + } + + public static void EmitMarshalIn(ILGenerator generator, Type type) + { + if (type.IsByRef) { + generator.Emit(OpCodes.Ldind_Ref); + } + } + + public static void EmitMarshalOut(ILGenerator generator, Type type, bool isReturn) + { + generator.Emit(OpCodes.Castclass, type); + if (!isReturn) { + generator.Emit(OpCodes.Stind_Ref); + } + } + + public object Get() + { + return Get(typeof(Hashtable)); + } + + public object Get(System.Type type) + { + IDictionary retVal; + + if (Suits(type)) { + retVal = (IDictionary) Activator.CreateInstance(type, new object[0]); + foreach (DictionaryEntry entry in this.val) { + retVal.Add(entry.Key, ((IDBusType) entry.Value).Get()); + } + } else { + throw new ArgumentException("Cannot cast DBus.Type.Dict to type '" + type.ToString() + "'"); + } + + return retVal; + } + + [DllImport("dbus-1")] + private extern static void dbus_message_iter_init_dict_iterator(IntPtr iter, + IntPtr dictIter); + + [DllImport("dbus-1")] + private extern static bool dbus_message_iter_append_dict(IntPtr iter, + IntPtr dictIter); + + [DllImport("dbus-1")] + private extern static bool dbus_message_iter_has_next(IntPtr iter); + + [DllImport("dbus-1")] + private extern static bool dbus_message_iter_next(IntPtr iter); + + [DllImport("dbus-1")] + private extern static string dbus_message_iter_get_dict_key (IntPtr dictIter); + + [DllImport("dbus-1")] + private extern static bool dbus_message_iter_append_dict_key (IntPtr dictIter, + string value); + [DllImport("dbus-1")] + private extern static int dbus_message_iter_get_arg_type(IntPtr iter); + } +} diff --git a/mono/DBusType/Double.cs b/mono/DBusType/Double.cs new file mode 100644 index 00000000..d578822f --- /dev/null +++ b/mono/DBusType/Double.cs @@ -0,0 +1,86 @@ +using System; +using System.Runtime.InteropServices; +using System.Reflection.Emit; + +using DBus; + +namespace DBus.DBusType +{ + /// <summary> + /// IEEE 754 double + /// </summary> + public class Double : IDBusType + { + public const char Code = 'd'; + private System.Double val; + + private Double() + { + } + + public Double(System.Double val) + { + this.val = val; + } + + public Double(IntPtr iter) + { + this.val = dbus_message_iter_get_double(iter); + } + + public void Append(IntPtr iter) + { + if (!dbus_message_iter_append_double(iter, val)) + throw new ApplicationException("Failed to append DOUBLE argument:" + val); + } + + public static bool Suits(System.Type type) + { + switch (type.ToString()) { + case "System.Double": + case "System.Double&": + return true; + } + + return false; + } + + public static void EmitMarshalIn(ILGenerator generator, Type type) + { + if (type.IsByRef) { + generator.Emit(OpCodes.Ldind_R8); + } + } + + public static void EmitMarshalOut(ILGenerator generator, Type type, bool isReturn) + { + generator.Emit(OpCodes.Unbox, type); + generator.Emit(OpCodes.Ldind_R8); + if (!isReturn) { + generator.Emit(OpCodes.Stind_R8); + } + } + + public object Get() + { + return this.val; + } + + public object Get(System.Type type) + { + switch (type.ToString()) { + case "System.Double": + case "System.Double&": + return this.val; + default: + throw new ArgumentException("Cannot cast DBus.Type.Double to type '" + type.ToString() + "'"); + } + } + + [DllImport("dbus-1")] + private extern static System.Double dbus_message_iter_get_double(IntPtr iter); + + [DllImport("dbus-1")] + private extern static bool dbus_message_iter_append_double(IntPtr iter, System.Double value); + } +} diff --git a/mono/DBusType/IDBusType.cs b/mono/DBusType/IDBusType.cs new file mode 100644 index 00000000..447c8208 --- /dev/null +++ b/mono/DBusType/IDBusType.cs @@ -0,0 +1,16 @@ +using System; + +namespace DBus.DBusType +{ + /// <summary> + /// Base class for DBusTypes + /// </summary> + public interface IDBusType + { + object Get(); + + object Get(System.Type type); + + void Append(IntPtr iter); + } +} diff --git a/mono/DBusType/Int32.cs b/mono/DBusType/Int32.cs new file mode 100644 index 00000000..b617a9a0 --- /dev/null +++ b/mono/DBusType/Int32.cs @@ -0,0 +1,86 @@ +using System; +using System.Runtime.InteropServices; +using System.Reflection.Emit; + +using DBus; + +namespace DBus.DBusType +{ + /// <summary> + /// 32-bit integer. + /// </summary> + public class Int32 : IDBusType + { + public const char Code = 'i'; + private System.Int32 val; + + private Int32() + { + } + + public Int32(System.Int32 val) + { + this.val = val; + } + + public Int32(IntPtr iter) + { + this.val = dbus_message_iter_get_int32(iter); + } + + public void Append(IntPtr iter) + { + if (!dbus_message_iter_append_int32(iter, val)) + throw new ApplicationException("Failed to append INT32 argument:" + val); + } + + public static bool Suits(System.Type type) + { + switch (type.ToString()) { + case "System.Int32": + case "System.Int32&": + return true; + } + + return false; + } + + public static void EmitMarshalIn(ILGenerator generator, Type type) + { + if (type.IsByRef) { + generator.Emit(OpCodes.Ldind_I4); + } + } + + public static void EmitMarshalOut(ILGenerator generator, Type type, bool isReturn) + { + generator.Emit(OpCodes.Unbox, type); + generator.Emit(OpCodes.Ldind_I4); + if (!isReturn) { + generator.Emit(OpCodes.Stind_I4); + } + } + + public object Get() + { + return this.val; + } + + public object Get(System.Type type) + { + switch (type.ToString()) { + case "System.Int32": + case "System.Int32&": + return this.val; + default: + throw new ArgumentException("Cannot cast DBus.Type.Int32 to type '" + type.ToString() + "'"); + } + } + + [DllImport("dbus-1")] + private extern static System.Int32 dbus_message_iter_get_int32(IntPtr iter); + + [DllImport("dbus-1")] + private extern static bool dbus_message_iter_append_int32(IntPtr iter, System.Int32 value); + } +} diff --git a/mono/DBusType/Int64.cs b/mono/DBusType/Int64.cs new file mode 100644 index 00000000..0905af74 --- /dev/null +++ b/mono/DBusType/Int64.cs @@ -0,0 +1,86 @@ +using System; +using System.Runtime.InteropServices; +using System.Reflection.Emit; + +using DBus; + +namespace DBus.DBusType +{ + /// <summary> + /// 64-bit integer. + /// </summary> + public class Int64 : IDBusType + { + public const char Code = 'x'; + private System.Int64 val; + + private Int64() + { + } + + public Int64(System.Int64 val) + { + this.val = val; + } + + public Int64(IntPtr iter) + { + this.val = dbus_message_iter_get_int64(iter); + } + + public void Append(IntPtr iter) + { + if (!dbus_message_iter_append_int64(iter, val)) + throw new ApplicationException("Failed to append INT64 argument:" + val); + } + + public static bool Suits(System.Type type) + { + switch (type.ToString()) { + case "System.Int64": + case "System.Int64&": + return true; + } + + return false; + } + + public static void EmitMarshalIn(ILGenerator generator, Type type) + { + if (type.IsByRef) { + generator.Emit(OpCodes.Ldind_I8); + } + } + + public static void EmitMarshalOut(ILGenerator generator, Type type, bool isReturn) + { + generator.Emit(OpCodes.Unbox, type); + generator.Emit(OpCodes.Ldind_I8); + if (!isReturn) { + generator.Emit(OpCodes.Stind_I8); + } + } + + public object Get() + { + return this.val; + } + + public object Get(System.Type type) + { + switch (type.ToString()) { + case "System.Int64": + case "System.Int64&": + return this.val; + default: + throw new ArgumentException("Cannot cast DBus.Type.Int64 to type '" + type.ToString() + "'"); + } + } + + [DllImport("dbus-1")] + private extern static System.Int64 dbus_message_iter_get_int64(IntPtr iter); + + [DllImport("dbus-1")] + private extern static bool dbus_message_iter_append_int64(IntPtr iter, System.Int64 value); + } +} diff --git a/mono/DBusType/Nil.cs b/mono/DBusType/Nil.cs new file mode 100644 index 00000000..e39b64a9 --- /dev/null +++ b/mono/DBusType/Nil.cs @@ -0,0 +1,68 @@ +using System; +using System.Runtime.InteropServices; +using System.Reflection.Emit; + +using DBus; + +namespace DBus.DBusType +{ + /// <summary> + /// Marks a "void"/"unset"/"nonexistent"/"null" argument. + /// </summary> + public class Nil : IDBusType + { + public const char Code = 'v'; + + private Nil() + { + } + + public Nil(object nil) + { + } + + public Nil(IntPtr iter) + { + } + + public void Append(IntPtr iter) + { + if (!dbus_message_iter_append_nil(iter)) + throw new ApplicationException("Failed to append NIL argument"); + } + + public static bool Suits(System.Type type) + { + return false; + } + + public static void EmitMarshalIn(ILGenerator generator, Type type) + { + if (type.IsByRef) { + generator.Emit(OpCodes.Ldind_I1); + } + } + + public static void EmitMarshalOut(ILGenerator generator, Type type, bool isReturn) + { + generator.Emit(OpCodes.Unbox, type); + generator.Emit(OpCodes.Ldind_I1); + if (!isReturn) { + generator.Emit(OpCodes.Stind_I1); + } + } + + public object Get() + { + return null; + } + + public object Get(System.Type type) + { + throw new ArgumentException("Cannot cast DBus.Type.Nil to type '" + type.ToString() + "'"); + } + + [DllImport("dbus-1")] + private extern static bool dbus_message_iter_append_nil(IntPtr iter); + } +} diff --git a/mono/DBusType/ObjectPath.cs b/mono/DBusType/ObjectPath.cs new file mode 100644 index 00000000..e20ae18d --- /dev/null +++ b/mono/DBusType/ObjectPath.cs @@ -0,0 +1,110 @@ +using System; +using System.Runtime.InteropServices; +using System.Reflection.Emit; + +using DBus; + +namespace DBus.DBusType +{ + /// <summary> + /// An object path. + /// </summary> + public class ObjectPath : IDBusType + { + public const char Code = 'o'; + private string pathName = null; + private object val = null; + private Service service = null; + + private ObjectPath() + { + } + + public ObjectPath(object val) + { + this.val = val; + } + + public ObjectPath(IntPtr iter) + { + + this.pathName = Marshal.PtrToStringAnsi(dbus_message_iter_get_object_path(iter)); + } + + public void SetService(Service service) + { + this.service = service; + } + + private string PathName + { + get { + if (this.pathName == null && this.val != null) { + Handler handler = this.service.GetHandler(this.val); + this.pathName = handler.PathName; + } + + return this.pathName; + } + } + + public void Append(IntPtr iter) + { + if (PathName == null) { + throw new ApplicationException("Unable to append ObjectPath before calling SetService()"); + } + + if (!dbus_message_iter_append_object_path(iter, Marshal.StringToHGlobalAnsi(PathName))) + throw new ApplicationException("Failed to append OBJECT_PATH argument:" + val); + } + + public static bool Suits(System.Type type) + { + object[] attributes = type.GetCustomAttributes(typeof(InterfaceAttribute), true); + if (attributes.Length == 1) { + return true; + } else { + return false; + } + } + + public static void EmitMarshalIn(ILGenerator generator, Type type) + { + if (type.IsByRef) { + generator.Emit(OpCodes.Ldind_Ref); + } + } + + public static void EmitMarshalOut(ILGenerator generator, Type type, bool isReturn) + { + generator.Emit(OpCodes.Castclass, type); + if (!isReturn) { + generator.Emit(OpCodes.Stind_Ref); + } + } + + public object Get() + { + throw new ArgumentException("Cannot call Get on an ObjectPath without specifying type."); + } + + public object Get(System.Type type) + { + if (this.service == null) { + throw new ApplicationException("Unable to get ObjectPath before calling SetService()"); + } + + try { + return this.service.GetObject(type, PathName); + } catch(Exception ex) { + throw new ArgumentException("Cannot cast object pointed to by Object Path to type '" + type.ToString() + "': " + ex); + } + } + + [DllImport("dbus-1")] + private extern static IntPtr dbus_message_iter_get_object_path(IntPtr iter); + + [DllImport("dbus-1")] + private extern static bool dbus_message_iter_append_object_path(IntPtr iter, IntPtr pathName); + } +} diff --git a/mono/DBusType/String.cs b/mono/DBusType/String.cs new file mode 100644 index 00000000..1eda1f25 --- /dev/null +++ b/mono/DBusType/String.cs @@ -0,0 +1,86 @@ +using System; +using System.Runtime.InteropServices; +using System.Reflection.Emit; + +using DBus; + +namespace DBus.DBusType +{ + /// <summary> + /// A string. + /// </summary> + public class String : IDBusType + { + public const char Code = 's'; + private string val; + + private String() + { + } + + public String(string val) + { + this.val = val; + } + + public String(IntPtr iter) + { + this.val = Marshal.PtrToStringAnsi(dbus_message_iter_get_string(iter)); + } + + public void Append(IntPtr iter) + { + if (!dbus_message_iter_append_string(iter, Marshal.StringToHGlobalAnsi(val))) + throw new ApplicationException("Failed to append STRING argument:" + val); + } + + public static bool Suits(System.Type type) + { + switch (type.ToString()) { + case "System.String": + case "System.String&": + return true; + } + + return false; + } + + public static void EmitMarshalIn(ILGenerator generator, Type type) + { + if (type.IsByRef) { + generator.Emit(OpCodes.Ldind_Ref); + } + } + + public static void EmitMarshalOut(ILGenerator generator, Type type, bool isReturn) + { + generator.Emit(OpCodes.Castclass, type); + if (!isReturn) { + generator.Emit(OpCodes.Stind_Ref); + } + } + + public object Get() + { + return this.val; + } + + public object Get(System.Type type) + { + switch (type.ToString()) + { + case "System.String": + case "System.String&": + return this.val; + default: + throw new ArgumentException("Cannot cast DBus.Type.String to type '" + type.ToString() + "'"); + } + } + + [DllImport("dbus-1")] + private extern static IntPtr dbus_message_iter_get_string(IntPtr iter); + + [DllImport("dbus-1")] + private extern static bool dbus_message_iter_append_string(IntPtr iter, IntPtr value); + } +} diff --git a/mono/DBusType/UInt32.cs b/mono/DBusType/UInt32.cs new file mode 100644 index 00000000..9c0e350a --- /dev/null +++ b/mono/DBusType/UInt32.cs @@ -0,0 +1,87 @@ +using System; +using System.Runtime.InteropServices; +using System.Reflection.Emit; + +using DBus; + +namespace DBus.DBusType +{ + /// <summary> + /// 32-bit unsigned integer. + /// </summary> + public class UInt32 : IDBusType + { + public const char Code = 'u'; + private System.UInt32 val; + + private UInt32() + { + } + + public UInt32(System.UInt32 val) + { + this.val = val; + } + + public UInt32(IntPtr iter) + { + this.val = dbus_message_iter_get_uint32(iter); + } + + public void Append(IntPtr iter) + { + if (!dbus_message_iter_append_uint32(iter, val)) + throw new ApplicationException("Failed to append UINT32 argument:" + val); + } + + public static bool Suits(System.Type type) + { + switch (type.ToString()) { + case "System.UInt32": + case "System.UInt32&": + return true; + } + + return false; + } + + public static void EmitMarshalIn(ILGenerator generator, Type type) + { + if (type.IsByRef) { + generator.Emit(OpCodes.Ldind_U4); + } + } + + public static void EmitMarshalOut(ILGenerator generator, Type type, bool isReturn) + { + generator.Emit(OpCodes.Unbox, type); + generator.Emit(OpCodes.Ldind_U4); + if (!isReturn) { + generator.Emit(OpCodes.Stind_I4); + } + } + + public object Get() + { + return this.val; + } + + public object Get(System.Type type) + { + switch (type.ToString()) + { + case "System.UInt32": + case "System.UInt32&": + return this.val; + default: + throw new ArgumentException("Cannot cast DBus.Type.UInt32 to type '" + type.ToString() + "'"); + } + } + + [DllImport("dbus-1")] + private extern static System.UInt32 dbus_message_iter_get_uint32(IntPtr iter); + + [DllImport("dbus-1")] + private extern static bool dbus_message_iter_append_uint32(IntPtr iter, System.UInt32 value); + } +} diff --git a/mono/DBusType/UInt64.cs b/mono/DBusType/UInt64.cs new file mode 100644 index 00000000..2e474795 --- /dev/null +++ b/mono/DBusType/UInt64.cs @@ -0,0 +1,87 @@ +using System; +using System.Runtime.InteropServices; +using System.Reflection.Emit; + +using DBus; + +namespace DBus.DBusType +{ + /// <summary> + /// 64-bit unsigned integer. + /// </summary> + public class UInt64 : IDBusType + { + public const char Code = 't'; + private System.UInt64 val; + + private UInt64() + { + } + + public UInt64(System.UInt64 val) + { + this.val = val; + } + + public UInt64(IntPtr iter) + { + this.val = dbus_message_iter_get_uint64(iter); + } + + public void Append(IntPtr iter) + { + if (!dbus_message_iter_append_uint64(iter, val)) + throw new ApplicationException("Failed to append UINT64 argument:" + val); + } + + public static bool Suits(System.Type type) + { + switch (type.ToString()) { + case "System.UInt64": + case "System.UInt64&": + return true; + } + + return false; + } + + public static void EmitMarshalIn(ILGenerator generator, Type type) + { + if (type.IsByRef) { + generator.Emit(OpCodes.Ldind_I8); + } + } + + public static void EmitMarshalOut(ILGenerator generator, Type type, bool isReturn) + { + generator.Emit(OpCodes.Unbox, type); + generator.Emit(OpCodes.Ldind_I8); + if (!isReturn) { + generator.Emit(OpCodes.Stind_I8); + } + } + + public object Get() + { + return this.val; + } + + public object Get(System.Type type) + { + switch (type.ToString()) + { + case "System.UInt64": + case "System.UInt64&": + return this.val; + default: + throw new ArgumentException("Cannot cast DBus.Type.UInt64 to type '" + type.ToString() + "'"); + } + } + + [DllImport("dbus-1")] + private extern static System.UInt64 dbus_message_iter_get_uint64(IntPtr iter); + + [DllImport("dbus-1")] + private extern static bool dbus_message_iter_append_uint64(IntPtr iter, System.UInt64 value); + } +} diff --git a/mono/Error.cs b/mono/Error.cs index dab4df1f..d89a013a 100644 --- a/mono/Error.cs +++ b/mono/Error.cs @@ -1,29 +1,60 @@ -namespace DBus { - +namespace DBus +{ + using System; using System.Runtime.InteropServices; + using System.Diagnostics; // FIXME add code to verify that size of DBus.Error // matches the C code. - + [StructLayout (LayoutKind.Sequential)] - internal struct Error { + internal struct Error + { internal IntPtr name; internal IntPtr message; private int dummies; private IntPtr padding1; - - internal void Init () { - dbus_error_init (ref this); + + public void Init() + { + dbus_error_init(ref this); + } + + public void Free() + { + dbus_error_free(ref this); } - internal void Free () { - dbus_error_free (ref this); + public string Message + { + get + { + return System.Runtime.InteropServices.Marshal.PtrToStringAnsi(message); + } } - [DllImport (DBus.Internals.DBusLibname, EntryPoint="dbus_error_init")] + public string Name + { + get + { + return System.Runtime.InteropServices.Marshal.PtrToStringAnsi(name); + } + } + + public bool IsSet + { + get + { + return (name != IntPtr.Zero); + } + } + + + [DllImport ("dbus-1", EntryPoint="dbus_error_init")] private extern static void dbus_error_init (ref Error error); - [DllImport (DBus.Internals.DBusLibname, EntryPoint="dbus_error_free")] + + [DllImport ("dbus-1", EntryPoint="dbus_error_free")] private extern static void dbus_error_free (ref Error error); } } diff --git a/mono/Handler.cs b/mono/Handler.cs new file mode 100644 index 00000000..d565b7ec --- /dev/null +++ b/mono/Handler.cs @@ -0,0 +1,256 @@ +namespace DBus +{ + using System; + using System.Runtime.InteropServices; + using System.Diagnostics; + using System.Reflection; + using System.Collections; + + internal class Handler + { + private string[] path = null; + private string pathName = null; + private Introspector introspector = null; + private object handledObject = null; + private Hashtable handledMethods = null; + private DBusObjectPathVTable vTable; + private Connection connection; + private Service service; + private DBusHandleMessageFunction filterCalled; + + internal delegate void DBusObjectPathUnregisterFunction(IntPtr rawConnection, + IntPtr userData); + + internal delegate int DBusObjectPathMessageFunction(IntPtr rawConnection, + IntPtr rawMessage, + IntPtr userData); + + internal delegate int DBusHandleMessageFunction(IntPtr rawConnection, + IntPtr rawMessage, + IntPtr userData); + + + private enum Result + { + Handled = 0, + NotYetHandled = 1, + NeedMemory = 2 + } + + [StructLayout (LayoutKind.Sequential)] + private struct DBusObjectPathVTable + { + public DBusObjectPathUnregisterFunction unregisterFunction; + public DBusObjectPathMessageFunction messageFunction; + public IntPtr padding1; + public IntPtr padding2; + public IntPtr padding3; + public IntPtr padding4; + + public DBusObjectPathVTable(DBusObjectPathUnregisterFunction unregisterFunction, + DBusObjectPathMessageFunction messageFunction) + { + this.unregisterFunction = unregisterFunction; + this.messageFunction = messageFunction; + this.padding1 = IntPtr.Zero; + this.padding2 = IntPtr.Zero; + this.padding3 = IntPtr.Zero; + this.padding4 = IntPtr.Zero; + } + } + + ~Handler() + { + if (Connection != null && Connection.RawConnection != IntPtr.Zero && path != null) { + dbus_connection_unregister_object_path(Connection.RawConnection, + Path); + } + } + + public Handler(object handledObject, + string pathName, + Service service) + { + Service = service; + Connection = service.Connection; + HandledObject = handledObject; + + // Strip the leading / off if there is one and get the path as an array + pathName = pathName.TrimStart('/'); + this.path = pathName.Split('/'); + this.pathName = "/" + pathName; + + // Create the vTable and register the path + vTable = new DBusObjectPathVTable(new DBusObjectPathUnregisterFunction(Unregister_Called), + new DBusObjectPathMessageFunction(Message_Called)); + + if (!dbus_connection_register_object_path(Connection.RawConnection, + Path, + ref vTable, + IntPtr.Zero)) + throw new OutOfMemoryException(); + + // Setup the filter function + this.filterCalled = new DBusHandleMessageFunction(Filter_Called); + if (!dbus_connection_add_filter(Connection.RawConnection, + this.filterCalled, + IntPtr.Zero, + IntPtr.Zero)) + throw new OutOfMemoryException(); + } + + private void RegisterMethod(MethodInfo method) + { + string key = method.Name + " " + Arguments.ParseParameters(method); + handledMethods.Add(key, method); + } + + public object HandledObject + { + get + { + return this.handledObject; + } + + set + { + this.handledObject = value; + + object[] attributes; + + // Register the methods + this.handledMethods = new Hashtable(); + this.introspector = new Introspector(value.GetType()); + + foreach (MethodInfo method in this.introspector.Methods) { + RegisterMethod(method); + } + } + } + + public int Filter_Called(IntPtr rawConnection, + IntPtr rawMessage, + IntPtr userData) + { + Message message = Message.Wrap(rawMessage, Service); + + if (message.Type == Message.MessageType.Signal) { + Signal signal = (Signal) message; + } else if (message.Type == Message.MessageType.MethodCall) { + MethodCall methodCall = (MethodCall) message; + } + + return (int) Result.NotYetHandled; + } + + public void Unregister_Called(IntPtr rawConnection, + IntPtr userData) + { + System.Console.WriteLine("FIXME: Unregister called."); + } + + private int Message_Called(IntPtr rawConnection, + IntPtr rawMessage, + IntPtr userData) + { + Message message = Message.Wrap(rawMessage, Service); + + switch (message.Type) { + case Message.MessageType.Signal: + System.Console.WriteLine("FIXME: Signal called."); + break; + case Message.MessageType.MethodCall: + return (int) HandleMethod((MethodCall) message); + } + + return (int) Result.NotYetHandled; + } + + private Result HandleMethod(MethodCall methodCall) + { + methodCall.Service = service; + + // Check the interface name matches + if (methodCall.InterfaceName != this.introspector.InterfaceName) { + return Result.NotYetHandled; + } + + // Iterate through getting the type codes + string key = methodCall.Name + " " + methodCall.Arguments; + + // Check it's one of our methods + if (!handledMethods.Contains(key)) { + return Result.NotYetHandled; + } + + // Got it! + MethodInfo method = (MethodInfo) handledMethods[key]; + + // Now call the method. FIXME: Error handling + object [] args = methodCall.Arguments.GetParameters(method); + object retVal = method.Invoke(this.handledObject, args); + + // Create the reply and send it + MethodReturn methodReturn = new MethodReturn(methodCall); + methodReturn.Arguments.AppendResults(method, retVal, args); + methodReturn.Send(); + + return Result.Handled; + } + + internal string[] Path + { + get + { + return path; + } + } + + public string PathName + { + get + { + return pathName; + } + } + + internal Connection Connection + { + get + { + return connection; + } + + set + { + this.connection = value; + } + } + + public Service Service + { + get + { + return service; + } + + set + { + this.service = value; + } + } + + [DllImport ("dbus-1")] + private extern static bool dbus_connection_register_object_path (IntPtr rawConnection, string[] path, ref DBusObjectPathVTable vTable, IntPtr userData); + + [DllImport ("dbus-1")] + private extern static void dbus_connection_unregister_object_path (IntPtr rawConnection, string[] path); + + [DllImport ("dbus-1")] + private extern static bool dbus_connection_add_filter(IntPtr rawConnection, + DBusHandleMessageFunction filter, + IntPtr userData, + IntPtr freeData); + + } +} diff --git a/mono/InterfaceAttribute.cs b/mono/InterfaceAttribute.cs new file mode 100644 index 00000000..2a6e9bb0 --- /dev/null +++ b/mono/InterfaceAttribute.cs @@ -0,0 +1,23 @@ +using System; + +namespace DBus +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=true)] + public class InterfaceAttribute : Attribute + { + private string interfaceName; + + public InterfaceAttribute(string interfaceName) + { + this.interfaceName = interfaceName; + } + + public string InterfaceName + { + get + { + return this.interfaceName; + } + } + } +} diff --git a/mono/Introspector.cs b/mono/Introspector.cs new file mode 100644 index 00000000..c7b9d05b --- /dev/null +++ b/mono/Introspector.cs @@ -0,0 +1,106 @@ +namespace DBus +{ + + using System; + using System.Runtime.InteropServices; + using System.Diagnostics; + using System.Collections; + using System.Reflection; + + internal class Introspector + { + private Type type; + private string interfaceName; + + public Introspector(Type type) { + object[] attributes = type.GetCustomAttributes(typeof(InterfaceAttribute), true); + if (attributes.Length != 1) + throw new ApplicationException("Type '" + type + "' is not a D-BUS interface."); + + InterfaceAttribute interfaceAttribute = (InterfaceAttribute) attributes[0]; + + this.interfaceName = interfaceAttribute.InterfaceName; + this.type = type; + } + + public string InterfaceName + { + get + { + return this.interfaceName; + } + } + + public ConstructorInfo Constructor + { + get + { + ConstructorInfo ret = this.type.GetConstructor(new Type[0]); + if (ret != null) { + return ret; + } else { + return typeof(object).GetConstructor(new Type[0]); + } + } + } + + public IntrospectorMethods Methods + { + get + { + return new IntrospectorMethods(this.type); + } + } + + public class IntrospectorMethods : IEnumerable + { + private Type type; + + public IntrospectorMethods(Type type) + { + this.type = type; + } + + public IEnumerator GetEnumerator() + { + return new MethodEnumerator(this.type.GetMethods(BindingFlags.Public|BindingFlags.Instance).GetEnumerator()); + } + + private class MethodEnumerator : IEnumerator + { + private IEnumerator enumerator; + + public MethodEnumerator(IEnumerator enumerator) + { + this.enumerator = enumerator; + } + + public bool MoveNext() + { + while (enumerator.MoveNext()) { + MethodInfo method = (MethodInfo) enumerator.Current; + object[] attributes = method.GetCustomAttributes(typeof(MethodAttribute), true); + if (attributes.GetLength(0) > 0) { + return true; + } + } + + return false; + } + + public void Reset() + { + enumerator.Reset(); + } + + public object Current + { + get + { + return enumerator.Current; + } + } + } + } + } +} diff --git a/mono/Makefile.am b/mono/Makefile.am index efd61b4b..6483f4ed 100644 --- a/mono/Makefile.am +++ b/mono/Makefile.am @@ -1,9 +1,41 @@ DESTDIR= DLLS=dbus-sharp.dll -NOINST_EXES=test-dbus-sharp +NOINST_EXES=test-dbus-sharp.exe + +DBUS_SHARP_FILES= \ + Arguments.cs \ + Bus.cs \ + Connection.cs \ + Custom.cs \ + DBusException.cs \ + Error.cs \ + Handler.cs \ + InterfaceAttribute.cs \ + Introspector.cs \ + Message.cs \ + MethodAttribute.cs \ + MethodCall.cs \ + MethodReturn.cs \ + ProxyBuilder.cs \ + Server.cs \ + Service.cs \ + Signal.cs \ + DBusType/IDBusType.cs \ + DBusType/Array.cs \ + DBusType/Boolean.cs \ + DBusType/Byte.cs \ + DBusType/Custom.cs \ + DBusType/Dict.cs \ + DBusType/Double.cs \ + DBusType/Int32.cs \ + DBusType/Int64.cs \ + DBusType/Nil.cs \ + DBusType/ObjectPath.cs \ + DBusType/String.cs \ + DBusType/UInt32.cs \ + DBusType/UInt64.cs -DBUS_SHARP_FILES= DBus.cs Message.cs Connection.cs Error.cs TEST_DBUS_SHARP_FILES=Test.cs all: $(DLLS) $(NOINST_EXES) @@ -11,8 +43,8 @@ all: $(DLLS) $(NOINST_EXES) dbus-sharp.dll: $(DBUS_SHARP_FILES) $(MCS) $(MCSFLAGS) --unsafe --target library -o dbus-sharp.dll $(DBUS_SHARP_FILES) -test-dbus-sharp: $(TEST_DBUS_SHARP_FILES) - $(MCS) $(MCSFLAGS) --unsafe --target exe -L . -r dbus-sharp.dll -o test-dbus-sharp $(TEST_DBUS_SHARP_FILES) +test-dbus-sharp.exe: $(TEST_DBUS_SHARP_FILES) + $(MCS) $(MCSFLAGS) --unsafe --target exe -L . -r dbus-sharp.dll -r gtk-sharp.dll -o test-dbus-sharp.exe $(TEST_DBUS_SHARP_FILES) clean: rm -f $(DLLS) $(NOINST_EXES) diff --git a/mono/Message.cs b/mono/Message.cs index 8f6db007..2f5270dd 100644 --- a/mono/Message.cs +++ b/mono/Message.cs @@ -1,129 +1,358 @@ -namespace DBus { +namespace DBus +{ using System; using System.Runtime.InteropServices; using System.Diagnostics; + using System.Collections; - public class Message { + public class Message + { + + /// <summary> + /// A pointer to the underlying Message structure + /// </summary> + private IntPtr rawMessage; + + /// <summary> + /// The current slot number + /// </summary> + private static int slot = -1; + + // Keep in sync with C + public enum MessageType + { + Invalid = 0, + MethodCall = 1, + MethodReturn = 2, + Error = 3, + Signal = 4 + } - public Message (string name, - string dest_service) { + private Arguments arguments = null; + + protected Service service = null; + protected string pathName = null; + protected string interfaceName = null; + protected string name = null; + + protected Message() + { + // An empty constructor for the sake of sub-classes which know how to construct theirselves. + } + + protected Message(IntPtr rawMessage, Service service) + { + RawMessage = rawMessage; + this.service = service; + } + + protected Message(MessageType messageType) + { // the assignment bumps the refcount - raw = dbus_message_new (name, dest_service); - if (raw == IntPtr.Zero) - throw new OutOfMemoryException (); - dbus_message_unref (raw); + RawMessage = dbus_message_new((int) messageType); + + if (RawMessage == IntPtr.Zero) { + throw new OutOfMemoryException(); + } + + dbus_message_unref(RawMessage); } - - public string Name { - get { - return dbus_message_get_name (raw); + + protected Message(MessageType messageType, Service service) : this(messageType) + { + this.service = service; + } + + ~Message() + { + RawMessage = IntPtr.Zero; // free the native object + } + + public static Message Wrap(IntPtr rawMessage, Service service) + { + if (slot > -1) { + // If we already have a Message object associated with this rawMessage then return it + IntPtr rawThis = dbus_message_get_data(rawMessage, slot); + if (rawThis != IntPtr.Zero) + return (DBus.Message) ((GCHandle)rawThis).Target; + } + // If it doesn't exist then create a new Message around it + Message message = null; + + switch ((MessageType) dbus_message_get_type(rawMessage)) { + case MessageType.Signal: + message = new Signal(rawMessage, service); + break; + case MessageType.MethodCall: + message = new MethodCall(rawMessage, service); + break; } + + return message; + } + + internal IntPtr RawMessage + { + get + { + return rawMessage; + } + set + { + if (value == rawMessage) + return; + + if (rawMessage != IntPtr.Zero) + { + // Get the reference to this + IntPtr rawThis = dbus_message_get_data(rawMessage, Slot); + Debug.Assert (rawThis != IntPtr.Zero); + + // Blank over the reference + dbus_message_set_data(rawMessage, Slot, IntPtr.Zero, IntPtr.Zero); + + // Free the reference + ((GCHandle) rawThis).Free(); + + // Unref the connection + dbus_message_unref(rawMessage); + } + + this.rawMessage = value; + + if (rawMessage != IntPtr.Zero) + { + GCHandle rawThis; + + dbus_message_ref(rawMessage); + + // We store a weak reference to the C# object on the C object + rawThis = GCHandle.Alloc(this, GCHandleType.WeakTrackResurrection); + + dbus_message_set_data(rawMessage, Slot, (IntPtr) rawThis, IntPtr.Zero); + } + } + } + + public void Send(ref int serial) + { + if (!dbus_connection_send (Service.Connection.RawConnection, RawMessage, ref serial)) + throw new OutOfMemoryException (); + } + + public void Send() + { + int ignored = 0; + Send(ref ignored); } - public static Message Wrap (IntPtr ptr) { - IntPtr gch_ptr; + public void SendWithReply() + { + IntPtr rawPendingCall = IntPtr.Zero; - gch_ptr = dbus_message_get_data (ptr, wrapper_slot); - if (gch_ptr != IntPtr.Zero) { - return (DBus.Message) ((GCHandle)gch_ptr).Target; - } else { - return new Message (ptr); - } + if (!dbus_connection_send_with_reply (Service.Connection.RawConnection, RawMessage, rawPendingCall, Service.Connection.Timeout)) + throw new OutOfMemoryException(); } + + public MethodReturn SendWithReplyAndBlock() + { + Error error = new Error(); + error.Init(); - // surely there's a convention for this pattern with the property - // and the real member - IntPtr raw_; - internal IntPtr raw { - get { - return raw_; - } - set { - if (value == raw_) - return; - - if (raw_ != IntPtr.Zero) { - IntPtr gch_ptr; - - gch_ptr = dbus_message_get_data (raw_, - wrapper_slot); - Debug.Assert (gch_ptr != IntPtr.Zero); - - dbus_message_set_data (raw_, wrapper_slot, - IntPtr.Zero, IntPtr.Zero); - - ((GCHandle) gch_ptr).Free (); - - dbus_message_unref (raw_); - } - - raw_ = value; - - if (raw_ != IntPtr.Zero) { - GCHandle gch; - - dbus_message_ref (raw_); - - // We store a weak reference to the C# object on the C object - gch = GCHandle.Alloc (this, GCHandleType.WeakTrackResurrection); - - dbus_message_set_data (raw_, wrapper_slot, - (IntPtr) gch, IntPtr.Zero); - } + IntPtr rawMessage = dbus_connection_send_with_reply_and_block(Service.Connection.RawConnection, + RawMessage, + Service.Connection.Timeout, + ref error); + + if (rawMessage != IntPtr.Zero) { + MethodReturn methodReturn = new MethodReturn(rawMessage, Service); + return methodReturn; + } else { + throw new DBusException(error); } } - ~Message () { - raw = IntPtr.Zero; // free the native object + public MessageType Type + { + get + { + return (MessageType) dbus_message_get_type(RawMessage); + } } - Message (IntPtr r) { - raw = r; + public Service Service + { + set + { + if (this.service != null && (value.Name != this.service.Name)) { + if (!dbus_message_set_destination(RawMessage, value.Name)) { + throw new OutOfMemoryException(); + } + } + + this.service = value; + } + get + { + return this.service; + } } - // static constructor runs before any methods - static Message () { - DBus.Internals.Init (); - - Debug.Assert (wrapper_slot == -1); - - if (!dbus_message_allocate_data_slot (ref wrapper_slot)) - throw new OutOfMemoryException (); + protected virtual string PathName + { + set + { + if (value != this.pathName) + { + if (!dbus_message_set_path(RawMessage, value)) { + throw new OutOfMemoryException(); + } + + this.pathName = value; + } + } + get + { + if (this.pathName == null) { + this.pathName = Marshal.PtrToStringAnsi(dbus_message_get_path(RawMessage)); + } + + return this.pathName; + } + } + + protected virtual string InterfaceName + { + set + { + if (value != this.interfaceName) + { + dbus_message_set_interface (RawMessage, value); + this.interfaceName = value; + } + } + get + { + if (this.interfaceName == null) { + this.interfaceName = Marshal.PtrToStringAnsi(dbus_message_get_interface(RawMessage)); + } - Debug.Assert (wrapper_slot >= 0); + return this.interfaceName; + } } + + protected virtual string Name + { + set + { + if (value != this.name) + { + dbus_message_set_member (RawMessage, value); + this.name = value; + } + } + get + { + if (this.name == null) { + this.name = Marshal.PtrToStringAnsi(dbus_message_get_member(RawMessage)); + } + - // slot used to store the C# object on the C object - static int wrapper_slot = -1; + return this.name; + } + } + + public Arguments Arguments + { + get + { + if (this.arguments == null) { + this.arguments = new Arguments(this); + } + + return this.arguments; + } + } + + protected int Slot + { + get + { + if (slot == -1) + { + // We need to initialize the slot + if (!dbus_message_allocate_data_slot (ref slot)) + throw new OutOfMemoryException (); + + Debug.Assert (slot >= 0); + } + + return slot; + } + } + + [DllImport ("dbus-1", EntryPoint="dbus_message_new")] + protected extern static IntPtr dbus_message_new (int messageType); + + [DllImport ("dbus-1", EntryPoint="dbus_message_unref")] + protected extern static void dbus_message_unref (IntPtr ptr); + + [DllImport ("dbus-1", EntryPoint="dbus_message_ref")] + protected extern static void dbus_message_ref (IntPtr ptr); + + [DllImport ("dbus-1", EntryPoint="dbus_message_allocate_data_slot")] + protected extern static bool dbus_message_allocate_data_slot (ref int slot); + + [DllImport ("dbus-1", EntryPoint="dbus_message_free_data_slot")] + protected extern static void dbus_message_free_data_slot (ref int slot); - [DllImport (DBus.Internals.DBusLibname, EntryPoint="dbus_message_new")] - private extern static IntPtr dbus_message_new (string name, - string dest_service); + [DllImport ("dbus-1", EntryPoint="dbus_message_set_data")] + protected extern static bool dbus_message_set_data (IntPtr ptr, + int slot, + IntPtr data, + IntPtr free_data_func); + + [DllImport ("dbus-1", EntryPoint="dbus_message_get_data")] + protected extern static IntPtr dbus_message_get_data (IntPtr ptr, + int slot); + + [DllImport ("dbus-1", EntryPoint="dbus_connection_send")] + private extern static bool dbus_connection_send (IntPtr ptr, + IntPtr message, + ref int client_serial); + + [DllImport ("dbus-1", EntryPoint="dbus_connection_send_with_reply")] + private extern static bool dbus_connection_send_with_reply (IntPtr rawConnection, IntPtr rawMessage, IntPtr rawPendingCall, int timeout); - [DllImport (DBus.Internals.DBusLibname, EntryPoint="dbus_message_unref")] - private extern static void dbus_message_unref (IntPtr ptr); + [DllImport ("dbus-1", EntryPoint="dbus_connection_send_with_reply_and_block")] + private extern static IntPtr dbus_connection_send_with_reply_and_block (IntPtr rawConnection, IntPtr message, int timeout, ref Error error); - [DllImport (DBus.Internals.DBusLibname, EntryPoint="dbus_message_ref")] - private extern static void dbus_message_ref (IntPtr ptr); + [DllImport("dbus-1")] + private extern static int dbus_message_get_type(IntPtr rawMessage); - [DllImport (DBus.Internals.DBusLibname, EntryPoint="dbus_message_get_name")] - private extern static string dbus_message_get_name (IntPtr ptr); + [DllImport("dbus-1")] + private extern static bool dbus_message_set_path(IntPtr rawMessage, string pathName); - [DllImport (DBus.Internals.DBusLibname, EntryPoint="dbus_message_allocate_data_slot")] - private extern static bool dbus_message_allocate_data_slot (ref int slot); + [DllImport("dbus-1")] + private extern static IntPtr dbus_message_get_path(IntPtr rawMessage); + + [DllImport("dbus-1")] + private extern static bool dbus_message_set_interface (IntPtr rawMessage, string interfaceName); + + [DllImport("dbus-1")] + private extern static IntPtr dbus_message_get_interface(IntPtr rawMessage); + + [DllImport("dbus-1")] + private extern static bool dbus_message_set_member (IntPtr rawMessage, string name); - [DllImport (DBus.Internals.DBusLibname, EntryPoint="dbus_message_free_data_slot")] - private extern static void dbus_message_free_data_slot (ref int slot); + [DllImport("dbus-1")] + private extern static IntPtr dbus_message_get_member(IntPtr rawMessage); - [DllImport (DBus.Internals.DBusLibname, EntryPoint="dbus_message_set_data")] - private extern static bool dbus_message_set_data (IntPtr ptr, - int slot, - IntPtr data, - IntPtr free_data_func); + [DllImport("dbus-1")] + private extern static bool dbus_message_set_destination(IntPtr rawMessage, string serviceName); - [DllImport (DBus.Internals.DBusLibname, EntryPoint="dbus_message_get_data")] - private extern static IntPtr dbus_message_get_data (IntPtr ptr, - int slot); + [DllImport("dbus-1")] + private extern static IntPtr dbus_message_get_destination(IntPtr rawMessage); } } diff --git a/mono/MethodAttribute.cs b/mono/MethodAttribute.cs new file mode 100644 index 00000000..db5da24e --- /dev/null +++ b/mono/MethodAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace DBus +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple=false, Inherited=true)] + public class MethodAttribute : Attribute + { + public MethodAttribute() + { + } + } +} diff --git a/mono/MethodCall.cs b/mono/MethodCall.cs new file mode 100644 index 00000000..ab7a4a36 --- /dev/null +++ b/mono/MethodCall.cs @@ -0,0 +1,80 @@ +namespace DBus +{ + using System; + using System.Runtime.InteropServices; + using System.Diagnostics; + + public class MethodCall : Message + { + public MethodCall() : base(MessageType.MethodCall) + { + } + + internal MethodCall(IntPtr rawMessage, Service service) : base(rawMessage, service) + { + } + + public MethodCall(Service service) : base(MessageType.MethodCall, service) + { + } + + public MethodCall(Service service, string pathName, string interfaceName, string name) + { + this.service = service; + + RawMessage = dbus_message_new_method_call(service.Name, pathName, interfaceName, name); + + if (RawMessage == IntPtr.Zero) { + throw new OutOfMemoryException(); + } + + this.pathName = pathName; + this.interfaceName = interfaceName; + this.name = name; + + dbus_message_unref(RawMessage); + } + + public new string PathName + { + get + { + return base.PathName; + } + + set + { + base.PathName = value; + } + } + + public new string InterfaceName + { + get + { + return base.InterfaceName; + } + + set + { + base.InterfaceName = value; + } + } + + public new string Name + { + get + { + return base.Name; + } + + set + { + base.Name = value; + } + } + + [DllImport("dbus-1")] + private extern static IntPtr dbus_message_new_method_call(string serviceName, string pathName, string interfaceName, string name); + } +} diff --git a/mono/MethodReturn.cs b/mono/MethodReturn.cs new file mode 100644 index 00000000..1e7731df --- /dev/null +++ b/mono/MethodReturn.cs @@ -0,0 +1,57 @@ +namespace DBus +{ + using System; + using System.Runtime.InteropServices; + using System.Diagnostics; + + public class MethodReturn : Message + { + private MethodReturn() : base(MessageType.MethodReturn) + { + } + + internal MethodReturn(IntPtr rawMessage, Service service) : base(rawMessage, service) + { + } + + public MethodReturn(MethodCall methodCall) + { + this.service = methodCall.Service; + + RawMessage = dbus_message_new_method_return(methodCall.RawMessage); + + if (RawMessage == IntPtr.Zero) { + throw new OutOfMemoryException(); + } + + dbus_message_unref(RawMessage); + } + + public new string PathName + { + get + { + return base.PathName; + } + } + + public new string InterfaceName + { + get + { + return base.InterfaceName; + } + } + + public new string Name + { + get + { + return base.Name; + } + } + + [DllImport("dbus-1")] + private extern static IntPtr dbus_message_new_method_return(IntPtr rawMessage); + } +} diff --git a/mono/ProxyBuilder.cs b/mono/ProxyBuilder.cs new file mode 100644 index 00000000..8e170c74 --- /dev/null +++ b/mono/ProxyBuilder.cs @@ -0,0 +1,301 @@ +namespace DBus +{ + using System; + using System.Runtime.InteropServices; + using System.Diagnostics; + using System.Collections; + using System.Threading; + using System.Reflection; + using System.Reflection.Emit; + + internal class ProxyBuilder + { + private Service service= null; + private string pathName = null; + private Type type = null; + private Introspector introspector = null; + private AssemblyBuilder proxyAssembly; + + private static MethodInfo Service_NameMI = typeof(Service).GetMethod("get_Name", + new Type[0]); + private static MethodInfo Service_ConnectionMI = typeof(Service).GetMethod("get_Connection", + new Type[0]); + private static MethodInfo Message_ArgumentsMI = typeof(Message).GetMethod("get_Arguments", + new Type[0]); + private static MethodInfo Arguments_InitAppendingMI = typeof(Arguments).GetMethod("InitAppending", + new Type[0]); + private static MethodInfo Arguments_AppendMI = typeof(Arguments).GetMethod("Append", + new Type[] {typeof(DBusType.IDBusType)}); + private static MethodInfo Message_SendWithReplyAndBlockMI = typeof(Message).GetMethod("SendWithReplyAndBlock", + new Type[0]); + private static MethodInfo Arguments_GetEnumeratorMI = typeof(Arguments).GetMethod("GetEnumerator", + new Type[0]); + private static MethodInfo IEnumerator_MoveNextMI = typeof(System.Collections.IEnumerator).GetMethod("MoveNext", + new Type[0]); + private static MethodInfo IEnumerator_CurrentMI = typeof(System.Collections.IEnumerator).GetMethod("get_Current", + new Type[0]); + private static MethodInfo Type_GetTypeFromHandleMI = typeof(System.Type).GetMethod("GetTypeFromHandle", + new Type[] {typeof(System.RuntimeTypeHandle)}); + private static MethodInfo IDBusType_GetMI = typeof(DBusType.IDBusType).GetMethod("Get", + new Type[] {typeof(System.Type)}); + private static ConstructorInfo MethodCall_C = typeof(MethodCall).GetConstructor(new Type[] {typeof(Service), + typeof(string), + typeof(string), + typeof(string)}); + + + + public ProxyBuilder(Service service, Type type, string pathName) + { + this.service = service; + this.pathName = pathName; + this.type = type; + this.introspector = new Introspector(type); + } + + private void BuildMethod(MethodInfo method, + ref TypeBuilder typeB, + FieldInfo serviceF, + FieldInfo interfaceF, + FieldInfo pathF) + { + ParameterInfo[] pars = method.GetParameters(); + Type[] parTypes = new Type[pars.Length]; + for (int parN = 0; parN < pars.Length; parN++) { + parTypes[parN] = pars[parN].ParameterType; + } + + // Generate the code + MethodBuilder methodBuilder = typeB.DefineMethod(method.Name, + MethodAttributes.Public | + MethodAttributes.HideBySig | + MethodAttributes.Virtual, + method.ReturnType, + parTypes); + ILGenerator generator = methodBuilder.GetILGenerator(); + + for (int parN = 0; parN < pars.Length; parN++) { + methodBuilder.DefineParameter(parN + 1, pars[parN].Attributes, pars[parN].Name); + } + + // Generate the locals + LocalBuilder methodCallL = generator.DeclareLocal(typeof(MethodCall)); + methodCallL.SetLocalSymInfo("methodCall"); + LocalBuilder replyL = generator.DeclareLocal(typeof(MethodReturn)); + replyL.SetLocalSymInfo("reply"); + LocalBuilder enumeratorL = generator.DeclareLocal(typeof(System.Collections.IEnumerator)); + enumeratorL.SetLocalSymInfo("enumerator"); + + if (method.ReturnType != typeof(void)) { + LocalBuilder retvalL = generator.DeclareLocal(method.ReturnType); + retvalL.SetLocalSymInfo("retval"); + } + + //generator.EmitWriteLine("MethodCall methodCall = new MethodCall(...)"); + generator.Emit(OpCodes.Ldsfld, serviceF); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldfld, pathF); + generator.Emit(OpCodes.Ldsfld, interfaceF); + generator.Emit(OpCodes.Ldstr, method.Name); + generator.Emit(OpCodes.Newobj, MethodCall_C); + generator.Emit(OpCodes.Stloc_0); + + //generator.EmitWriteLine("methodCall.Arguments.InitAppending()"); + generator.Emit(OpCodes.Ldloc_0); + generator.EmitCall(OpCodes.Callvirt, Message_ArgumentsMI, null); + generator.EmitCall(OpCodes.Callvirt, Arguments_InitAppendingMI, null); + + for (int parN = 0; parN < pars.Length; parN++) { + ParameterInfo par = pars[parN]; + if (!par.IsOut) { + EmitIn(generator, par.ParameterType, parN); + } + } + + //generator.EmitWriteLine("MethodReturn reply = methodCall.SendWithReplyAndBlock()"); + generator.Emit(OpCodes.Ldloc_0); + generator.EmitCall(OpCodes.Callvirt, Message_SendWithReplyAndBlockMI, null); + generator.Emit(OpCodes.Stloc_1); + + //generator.EmitWriteLine("IEnumerator enumeartor = reply.Arguments.GetEnumerator()"); + generator.Emit(OpCodes.Ldloc_1); + generator.EmitCall(OpCodes.Callvirt, Message_ArgumentsMI, null); + generator.EmitCall(OpCodes.Callvirt, Arguments_GetEnumeratorMI, null); + generator.Emit(OpCodes.Stloc_2); + + // handle the return value + if (method.ReturnType != typeof(void)) { + EmitOut(generator, method.ReturnType, 0); + } + + for (int parN = 0; parN < pars.Length; parN++) { + ParameterInfo par = pars[parN]; + if (par.IsOut || par.ParameterType.ToString().EndsWith("&")) { + EmitOut(generator, par.ParameterType, parN); + } + } + + if (method.ReturnType != typeof(void)) { + generator.Emit(OpCodes.Ldloc_3); + } + + generator.Emit(OpCodes.Ret); + + // Generate the method + typeB.DefineMethodOverride(methodBuilder, method); + } + + private void EmitIn(ILGenerator generator, Type parType, int parN) + { + Type inParType = Arguments.MatchType(parType); + //generator.EmitWriteLine("methodCall.Arguments.Append(...)"); + generator.Emit(OpCodes.Ldloc_0); + generator.EmitCall(OpCodes.Callvirt, Message_ArgumentsMI, null); + generator.Emit(OpCodes.Ldarg_S, parN + 1); + + // Call the DBusType EmitMarshalIn to make it emit itself + object[] pars = new object[] {generator, parType}; + inParType.InvokeMember("EmitMarshalIn", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, pars, null); + + generator.Emit(OpCodes.Newobj, Arguments.GetDBusTypeConstructor(inParType, parType)); + generator.EmitCall(OpCodes.Callvirt, Arguments_AppendMI, null); + } + + private void EmitOut(ILGenerator generator, Type parType, int parN) + { + Type outParType = Arguments.MatchType(parType); + //generator.EmitWriteLine("enumerator.MoveNext()"); + generator.Emit(OpCodes.Ldloc_2); + generator.EmitCall(OpCodes.Callvirt, IEnumerator_MoveNextMI, null); + + //generator.EmitWriteLine("return (" + parType + ") ((DBusType.IDBusType) enumerator.Current).Get(typeof(" + parType + "))"); + generator.Emit(OpCodes.Pop); + if (parN > 0) { + generator.Emit(OpCodes.Ldarg_S, parN + 1); + } + + generator.Emit(OpCodes.Ldloc_2); + generator.EmitCall(OpCodes.Callvirt, IEnumerator_CurrentMI, null); + generator.Emit(OpCodes.Castclass, typeof(DBusType.IDBusType)); + generator.Emit(OpCodes.Ldtoken, parType); + generator.EmitCall(OpCodes.Call, Type_GetTypeFromHandleMI, null); + generator.EmitCall(OpCodes.Callvirt, IDBusType_GetMI, null); + + // Call the DBusType EmitMarshalOut to make it emit itself + object[] pars = new object[] {generator, parType, parN == 0}; + outParType.InvokeMember("EmitMarshalOut", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, pars, null); + + if (parN == 0) { + generator.Emit(OpCodes.Stloc_3); + } + } + + public void BuildConstructor(ref TypeBuilder typeB, FieldInfo serviceF, FieldInfo interfaceF, FieldInfo pathF) + { + Type[] pars = {typeof(Service), typeof(string), typeof(string)}; + ConstructorBuilder constructor = typeB.DefineConstructor(MethodAttributes.RTSpecialName | + MethodAttributes.Public, + CallingConventions.Standard, pars); + + ILGenerator generator = constructor.GetILGenerator(); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Call, this.introspector.Constructor); + generator.Emit(OpCodes.Ldarg_1); + generator.Emit(OpCodes.Stsfld, serviceF); + generator.Emit(OpCodes.Ldarg_2); + generator.Emit(OpCodes.Stsfld, interfaceF); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldarg_3); + generator.Emit(OpCodes.Stfld, pathF); + + generator.Emit(OpCodes.Ret); + } + + public object GetProxy() + { + + // Build the type + TypeBuilder typeB = ServiceModuleBuilder.DefineType(ProxyName, TypeAttributes.Public, this.type); + //type.AddInterfaceImplementation(typeof(IProxy)); + + FieldBuilder serviceF = typeB.DefineField("service", + typeof(Service), + FieldAttributes.Private | + FieldAttributes.Static); + FieldBuilder interfaceF = typeB.DefineField("interfaceName", + typeof(string), + FieldAttributes.Private | + FieldAttributes.Static); + FieldBuilder pathF = typeB.DefineField("pathName", + typeof(string), + FieldAttributes.Private); + + BuildConstructor(ref typeB, serviceF, interfaceF, pathF); + + // Build the methods + foreach (MethodInfo method in this.introspector.Methods) { + BuildMethod(method, ref typeB, serviceF, interfaceF, pathF); + } + + Type [] parTypes = new Type[] {typeof(Service), typeof(string), typeof(string)}; + object [] pars = new object[] {Service, this.introspector.InterfaceName, pathName}; + + Type proxyType = typeB.CreateType(); + + // Uncomment the following line to produce a DLL of the + // constructed assembly which can then be examined using + // monodis. Note that in order for this to work you should copy + // the client assembly as a dll file so that monodis can pick it + // up. + //ProxyAssembly.Save("proxy.dll"); + + ConstructorInfo constructor = proxyType.GetConstructor(parTypes); + object instance = constructor.Invoke(pars); + return instance; + } + + private ModuleBuilder ServiceModuleBuilder + { + get + { + if (Service.module == null) { + Service.module = ProxyAssembly.DefineDynamicModule(Service.Name, "proxy.dll", true); + } + + return Service.module; + } + } + + private Service Service + { + get + { + return this.service; + } + } + + private string ProxyName + { + get + { + return this.introspector.InterfaceName + ".Proxy"; + } + } + + private AssemblyBuilder ProxyAssembly + { + get + { + if (this.proxyAssembly == null){ + AssemblyName assemblyName = new AssemblyName(); + assemblyName.Name = "DBusProxy"; + this.proxyAssembly = Thread.GetDomain().DefineDynamicAssembly(assemblyName, + AssemblyBuilderAccess.RunAndSave); + } + + return this.proxyAssembly; + } + } + } +} + diff --git a/mono/README b/mono/README new file mode 100644 index 00000000..2d1b08b6 --- /dev/null +++ b/mono/README @@ -0,0 +1,118 @@ +D-BUS Mono Bindings +=== + +These bindings are a 'thick' wrapper around the D-BUS API. For now +they rely on the main loop provided by the GLib bindings but this +dependancy will be removed in the near future. + +The wrapper serves two main functions: firstly, it has the know-how to +introspect live objects passed to it by a server and service requests +to those objects via the D-BUS. Secondly, it can create a proxy for +clients who can pretend they are making calls to the regular +objects. This latter piece of magic is implemented using +Reflection.Emit to create an assembly on-the-fly containing +sub-classes of the classes the client thinks it's really using. These +sub-classes simply marshal each method's parameters off to the D-BUS, +demarshal the results and return them to the client as if nothing +happened. + +Usage +=== + +A server do should something like this: + + namespace Foo + { + using System; + using DBus; + using Gtk; + + public class MyServer + { + public static int Main(string [] args) + { + Application.Init(); + +1 Connection connection = Bus.GetSessionBus(); +2 Service service = new Service(connection, "org.foo"); +3 MyObject myObject = new MyObject(); +4 service.RegisterObject(myObject, "/org/foo/MyObject"); + + Application.Run(); + + return 0; + } + } + } + +In line 1 we get a connection to the session bus. Then, in line 2 we +create a service which will listen for requests to org.foo to +service. In line 3 we create a MyObject object and register it with an +object path in line 4. It's almost that simple. All that's missing is +to mark MyObject in such a way that dbus-sharp knows how to export +it. This is done using the attributes, Interface and Method, +as in the following example: + + namespace Foo + { + using System; + using DBus; + + [Interface("org.foo.MyObject")] + public class MyObject + { + [Method] + public virtual string Echo(string message) + { + return "Reply: " + message; + } + } + } + +Note that the Methods should also be declared virtual in order for +the client to use same class declaration. + +Now for the client: + + namespace Foo + { + using System; + using DBus; + + public class MyClient + { + public static int Main(string [] args) + { +1 Connection connection = Bus.GetSessionBus(); +2 Service service = Service.Get(connection, "org.foo"); +3 MyObject myObject = (MyObject) + service.GetObject(typeof(MyObject), "/org/foo/MyObject"); +4 System.Console.WriteLine(testObject.Echo("Hello world!")); + + return 0; + } + } + } + +Here we start off the same by getting a connection to the session +bus. This time though, in line 2, we get the existing service rather +than creating it. In line 3, we ask the service to get the object +"/org/foo/MyObject" as registered by the server and that return it as +a MyObject. Once obtained we can use it like any normal object as in +line 4. This supposes, of course, that you've just written MyObject +and happen to have it readily available. If that were not the case, +for example if you wanted to call a method on one of the well-known +services, then you will need to write a stub class, like the MyObject +class above, which has the method calls you need correctly defined but +needn't actually have any implementation. + + +Working Example +=== + +The example directory contains a working example similar to that +described above. It uses the session bus so first run dbus-launch and +then export DBUS_SESSION_BUS_ADDRESS, as displayed by dbus-launch, to +two terminals, one to run the server and one for the client. Then, +start the server in one terminal, the client in the other and cross +your fingers. diff --git a/mono/Server.cs b/mono/Server.cs new file mode 100644 index 00000000..e39b7ca2 --- /dev/null +++ b/mono/Server.cs @@ -0,0 +1,148 @@ +namespace DBus +{ + using System; + using System.Runtime.InteropServices; + using System.Diagnostics; + + public class Server + { + private IntPtr rawServer; + + /// <summary> + /// The current slot number + /// </summary> + private static int slot = -1; + + private string address = null; + + private Server(IntPtr rawServer) + { + RawServer = rawServer; + } + + public Server(string address) + { + Error error = new Error(); + error.Init(); + RawServer = dbus_server_listen(address, ref error); + if (RawServer != IntPtr.Zero){ + dbus_server_unref(RawServer); + } else { + throw new DBusException(error); + } + } + + ~Server() + { + if (RawServer != IntPtr.Zero) { + dbus_server_unref(rawServer); + } + + RawServer = IntPtr.Zero; + } + + public string Address + { + get + { + if (address == null) { + address = dbus_server_get_address(rawServer); + } + + return address; + } + } + + private int Slot + { + get + { + if (slot == -1) + { + // We need to initialize the slot + if (!dbus_server_allocate_data_slot (ref slot)) + throw new OutOfMemoryException (); + + Debug.Assert (slot >= 0); + } + + return slot; + } + } + + internal IntPtr RawServer + { + get + { + return rawServer; + } + set + { + if (value == rawServer) + return; + + if (rawServer != IntPtr.Zero) + { + // Get the reference to this + IntPtr rawThis = dbus_server_get_data (rawServer, Slot); + Debug.Assert (rawThis != IntPtr.Zero); + + // Blank over the reference + dbus_server_set_data (rawServer, Slot, IntPtr.Zero, IntPtr.Zero); + + // Free the reference + ((GCHandle) rawThis).Free(); + + // Unref the connection + dbus_server_unref(rawServer); + } + + this.rawServer = value; + + if (rawServer != IntPtr.Zero) + { + GCHandle rawThis; + + dbus_server_ref (rawServer); + + // We store a weak reference to the C# object on the C object + rawThis = GCHandle.Alloc (this, GCHandleType.WeakTrackResurrection); + + dbus_server_set_data(rawServer, Slot, (IntPtr) rawThis, IntPtr.Zero); + } + } + } + + [DllImport ("dbus-1", EntryPoint="dbus_server_listen")] + private extern static IntPtr dbus_server_listen(string address, ref Error error); + + [DllImport ("dbus-1", EntryPoint="dbus_server_unref")] + private extern static IntPtr dbus_server_unref(IntPtr rawServer); + + [DllImport ("dbus-1", EntryPoint="dbus_server_ref")] + private extern static void dbus_server_ref(IntPtr rawServer); + + [DllImport ("dbus-1", EntryPoint="dbus_server_disconnect")] + private extern static void dbus_server_disconnect(IntPtr rawServer); + + [DllImport ("dbus-1", EntryPoint="dbus_server_get_address")] + private extern static string dbus_server_get_address(IntPtr rawServer); + + [DllImport ("dbus-1", EntryPoint="dbus_server_set_data")] + private extern static bool dbus_server_set_data(IntPtr rawServer, + int slot, + IntPtr data, + IntPtr freeDataFunc); + + [DllImport ("dbus-1", EntryPoint="dbus_server_get_data")] + private extern static IntPtr dbus_server_get_data(IntPtr rawServer, + int slot); + + [DllImport ("dbus-1", EntryPoint="dbus_server_allocate_data_slot")] + private extern static bool dbus_server_allocate_data_slot (ref int slot); + + [DllImport ("dbus-1", EntryPoint="dbus_server_free_data_slot")] + private extern static void dbus_server_free_data_slot (ref int slot); + + } +} diff --git a/mono/Service.cs b/mono/Service.cs new file mode 100644 index 00000000..a3c2a31f --- /dev/null +++ b/mono/Service.cs @@ -0,0 +1,115 @@ +namespace DBus +{ + using System; + using System.Runtime.InteropServices; + using System.Diagnostics; + using System.Collections; + using System.Reflection; + using System.Reflection.Emit; + + public class Service + { + private Connection connection; + private string name; + private bool local = false; + private Hashtable registeredHandlers = new Hashtable(); + internal ModuleBuilder module = null; + + internal Service(string name, Connection connection) + { + this.name = name; + this.connection = connection; + } + + public Service(Connection connection, string name) + { + Error error = new Error(); + error.Init(); + + // This isn't used for now + uint flags = 0; + + if (dbus_bus_acquire_service(connection.RawConnection, name, flags, ref error) == -1) { + throw new DBusException(error); + } + + this.connection = connection; + this.name = name; + this.local = true; + } + + public static bool Exists(Connection connection, string name) + { + Error error = new Error(); + error.Init(); + + if (dbus_bus_service_exists(connection.RawConnection, + name, + ref error)) { + return true; + } else { + if (error.IsSet) { + throw new DBusException(error); + } + return false; + } + } + + public static Service Get(Connection connection, string name) + { + if (Exists(connection, name)) { + return new Service(name, connection); + } else { + throw new ApplicationException("Service '" + name + "' does not exist."); + } + } + + public void RegisterObject(object handledObject, + string pathName) + { + Handler handler = new Handler(handledObject, + pathName, + this); + registeredHandlers.Add(handledObject, handler); + } + + internal Handler GetHandler(object handledObject) + { + return (Handler) registeredHandlers[handledObject]; + } + + public object GetObject(Type type, string pathName) + { + ProxyBuilder builder = new ProxyBuilder(this, type, pathName); + object proxy = builder.GetProxy(); + return proxy; + } + + public string Name + { + get + { + return this.name; + } + } + + public Connection Connection + { + get + { + return connection; + } + + set + { + this.connection = value; + } + } + + [DllImport ("dbus-1")] + private extern static int dbus_bus_acquire_service (IntPtr rawConnection, string serviceName, uint flags, ref Error error); + + [DllImport ("dbus-1")] + private extern static bool dbus_bus_service_exists (IntPtr rawConnection, string serviceName, ref Error error); + } +} diff --git a/mono/Signal.cs b/mono/Signal.cs new file mode 100644 index 00000000..a9209fec --- /dev/null +++ b/mono/Signal.cs @@ -0,0 +1,60 @@ +namespace DBus +{ + using System; + using System.Runtime.InteropServices; + using System.Diagnostics; + + public class Signal : Message + { + public Signal() : base(MessageType.Signal) + { + } + + internal Signal(IntPtr rawMessage, Service service) : base(rawMessage, service) + { + } + + public Signal(Service service) : base(MessageType.Signal, service) + { + } + + public new string PathName + { + get + { + return base.PathName; + } + + set + { + base.PathName = value; + } + } + + public new string InterfaceName + { + get + { + return base.InterfaceName; + } + + set + { + base.InterfaceName = value; + } + } + + public new string Name + { + get + { + return base.Name; + } + + set + { + base.Name = value; + } + } + } +} diff --git a/mono/TODO b/mono/TODO new file mode 100644 index 00000000..e44c2a92 --- /dev/null +++ b/mono/TODO @@ -0,0 +1,8 @@ +- Clean up memory leakages. Call _unref functions etc. + +- Convert strings to/from UTF-8 + +- Implement own main loop. The current implementation depends on the + GLib mainloop. + +- Get test working. Probably a threading issue. diff --git a/mono/Test.cs b/mono/Test.cs index e92176f2..028986c9 100644 --- a/mono/Test.cs +++ b/mono/Test.cs @@ -1,53 +1,70 @@ - using System; -using System.Runtime.InteropServices; +using System.Threading; +using DBus; +using Gtk; -class Test { - static void Main() { - g_thread_init (IntPtr.Zero); +namespace DBus.Test +{ + public class Test + { + public static Service service = null; + public static Connection connection = null; - DBus.Connection c; + public static int Main(string [] args) + { + TestServer testServer = new TestServer(); + Thread serverThread = new Thread(new ThreadStart(testServer.StartServer)); + serverThread.Start(); - // c = new DBus.Connection ("unix:path=/tmp/foobar"); + connection = Bus.GetSessionBus(); + service = Service.Get(connection, "org.freedesktop.Test"); - try { - c = DBus.Connection.GetBus (DBus.Connection.BusType.Session); - } - catch (DBus.Exception e) { - Console.Error.WriteLine ("Failed to open connection: {0}", - e.Message); - return; - } + TestObject testObject = (TestObject) service.GetObject(typeof(TestObject), "/org/freedesktop/Test/TestObject"); - DBus.Message m = new DBus.Message ("org.freedesktop.Foo", - "org.freedesktop.DBus.Broadcast"); + System.Console.WriteLine(testObject.Test1("Hello")); - c.Send (m); - c.Flush (); + //RunTests(testObject); - IntPtr loop = g_main_loop_new (IntPtr.Zero, false); - - g_main_loop_run (loop); + return 0; + } - g_main_loop_unref (loop); + public static void RunTests(TestObject testObject) + { + System.Console.WriteLine(testObject.Test1("Hello")); + } } - internal const string GLibname = "libglib-2.0.so.0"; - internal const string GThreadname = "libgthread-2.0.so.0"; - - [DllImport (GLibname, EntryPoint="g_main_loop_new")] - private extern static IntPtr g_main_loop_new (IntPtr context, - bool is_running); + public class TestServer + { + public Connection connection; + public Service service; - [DllImport (GLibname, EntryPoint="g_main_loop_unref")] - private extern static void g_main_loop_unref (IntPtr loop); + public TestServer() + { + Application.Init(); + + System.Console.WriteLine("Starting server..."); - [DllImport (GLibname, EntryPoint="g_main_loop_run")] - private extern static void g_main_loop_run (IntPtr loop); + connection = Bus.GetSessionBus(); + service = new Service(connection, "org.freedesktop.Test"); + TestObject testObject = new TestObject(); + service.RegisterObject(testObject, "/org/freedesktop/Test/TestObject"); + } + + public void StartServer() + { + Application.Run(); + } + } - [DllImport (GLibname, EntryPoint="g_main_loop_quit")] - private extern static void g_main_loop_quit (IntPtr loop); - - [DllImport (GThreadname, EntryPoint="g_thread_init")] - private extern static void g_thread_init (IntPtr vtable); + [Interface("org.freedesktop.Test.TestObject")] + public class TestObject + { + [Method] + public virtual int Test1(string x) + { + System.Console.WriteLine("Called: " + x); + return 5; + } + } } diff --git a/mono/example/EchoClient.cs b/mono/example/EchoClient.cs new file mode 100644 index 00000000..dc20771a --- /dev/null +++ b/mono/example/EchoClient.cs @@ -0,0 +1,19 @@ +namespace Foo +{ + using System; + using DBus; + + public class EchoClient + { + public static int Main(string [] args) + { + Connection connection = Bus.GetSessionBus(); + Service service = Service.Get(connection, "org.freedesktop.Test"); + Echoer echoer = (Echoer) + service.GetObject(typeof(Echoer), "/org/freedesktop/Test/Echoer"); + System.Console.WriteLine(echoer.Echo("Hello world!")); + + return 0; + } + } +} diff --git a/mono/example/EchoServer.cs b/mono/example/EchoServer.cs new file mode 100644 index 00000000..243a2730 --- /dev/null +++ b/mono/example/EchoServer.cs @@ -0,0 +1,23 @@ +namespace Foo +{ + using System; + using DBus; + using Gtk; + + public class EchoServer + { + public static int Main(string [] args) + { + Application.Init(); + + Connection connection = Bus.GetSessionBus(); + Service service = new Service(connection, "org.freedesktop.Test"); + Echoer echoer = new Echoer(); + service.RegisterObject(echoer, "/org/freedesktop/Test/Echoer"); + + Application.Run(); + + return 0; + } + } +} diff --git a/mono/example/Echoer.cs b/mono/example/Echoer.cs new file mode 100644 index 00000000..bc5a843d --- /dev/null +++ b/mono/example/Echoer.cs @@ -0,0 +1,16 @@ +namespace Foo +{ + using System; + using DBus; + + [Interface("org.freedesktop.Test.Echoer")] + public class Echoer + { + [Method] + public virtual string Echo(string message) + { + System.Console.WriteLine("I received: " + message); + return "Reply: " + message; + } + } +} diff --git a/mono/example/Makefile.am b/mono/example/Makefile.am new file mode 100644 index 00000000..2355bf31 --- /dev/null +++ b/mono/example/Makefile.am @@ -0,0 +1,19 @@ +DESTDIR= + +NOINST_EXES=echo-server.exe echo-client.exe + +all: $(NOINST_EXES) + +echo-server.exe: EchoServer.cs Echoer.cs + $(MCS) $(MCSFLAGS) --unsafe --target exe -L .. -r dbus-sharp.dll -r gtk-sharp -o echo-server.exe EchoServer.cs Echoer.cs + +echo-client.exe: EchoClient.cs Echoer.cs + $(MCS) $(MCSFLAGS) --unsafe --target exe -L .. -r dbus-sharp.dll -o echo-client.exe EchoClient.cs Echoer.cs + +clean: + rm -f $(NOINST_EXES) + +install: all + +EXTRA_DIST=EchoServer.cs EchoClient.cs Echoer.cs + |