diff options
Diffstat (limited to 'mono/Arguments.cs')
-rw-r--r-- | mono/Arguments.cs | 304 |
1 files changed, 304 insertions, 0 deletions
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); + } +} |