namespace DBus { using System; using System.Runtime.InteropServices; using System.Diagnostics; using System.Collections; public class Message : IDisposable { private static Stack stack = new Stack (); static public Message Current { get { return stack.Count > 0 ? (Message) stack.Peek () : null; } } static internal void Push (Message message) { stack.Push (message); } static internal void Pop () { stack.Pop (); } /// /// A pointer to the underlying Message structure /// private IntPtr rawMessage; /// /// The current slot number /// private static int slot = -1; // Keep in sync with C public enum MessageType { Invalid = 0, MethodCall = 1, MethodReturn = 2, Error = 3, Signal = 4 } private Arguments arguments = null; protected Service service = null; protected string pathName = null; protected string interfaceName = null; protected string name = null; private string key= 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 RawMessage = dbus_message_new((int) messageType); if (RawMessage == IntPtr.Zero) { throw new OutOfMemoryException(); } dbus_message_unref(RawMessage); } protected Message(MessageType messageType, Service service) : this(messageType) { this.service = service; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } public void Dispose (bool disposing) { if (disposing) { if (this.arguments != null) this.arguments.Dispose (); } RawMessage = IntPtr.Zero; // free the native object } ~Message() { Dispose (false); } 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; MessageType messageType = (MessageType) dbus_message_get_type(rawMessage); switch (messageType) { case MessageType.Signal: message = new Signal(rawMessage, service); break; case MessageType.MethodCall: message = new MethodCall(rawMessage, service); break; case MessageType.MethodReturn: message = new MethodReturn(rawMessage, service); break; case MessageType.Error: message = new ErrorMessage(rawMessage, service); break; default: throw new ApplicationException("Unknown message type to wrap: " + messageType); } 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 (); Service.Connection.Flush(); } public void Send() { int ignored = 0; Send(ref ignored); } public void SendWithReply() { IntPtr rawPendingCall = IntPtr.Zero; 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(); 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); // Ownership of a ref is passed onto us from // dbus_connection_send_with_reply_and_block(). It gets reffed as // a result of being passed into the MethodReturn ctor, so unref // the extra one here. dbus_message_unref (rawMessage); return methodReturn; } else { throw new DBusException(error); } } public MessageType Type { get { return (MessageType) dbus_message_get_type(RawMessage); } } 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; } } 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)); } 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)); } return this.name; } } public string Key { get { if (this.key == null) { this.key = Name + " " + Arguments; } return this.key; } } public Arguments Arguments { get { if (this.arguments == null) { this.arguments = new Arguments(this); } return this.arguments; } } public string Sender { get { return Marshal.PtrToStringAnsi(dbus_message_get_sender(RawMessage)); } } public string Destination { get { return Marshal.PtrToStringAnsi(dbus_message_get_destination(RawMessage)); } } 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-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-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-1")] private extern static int dbus_message_get_type(IntPtr rawMessage); [DllImport("dbus-1")] private extern static bool dbus_message_set_path(IntPtr rawMessage, string pathName); [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-1")] private extern static IntPtr dbus_message_get_member(IntPtr rawMessage); [DllImport("dbus-1")] private extern static bool dbus_message_set_destination(IntPtr rawMessage, string serviceName); [DllImport("dbus-1")] private extern static IntPtr dbus_message_get_destination(IntPtr rawMessage); [DllImport("dbus-1")] private extern static IntPtr dbus_message_get_sender(IntPtr rawMessage); } }