/// <summary> /// Asks MSMQ to attempt to deliver a message. /// To ensure the message reached the queue you need to check acknowledgement messages sent to the <see cref="Message.AdministrationQueue"/> /// </summary> /// <param name="message">The message to try to send</param> /// <param name="transaction">can be NULL for no transaction, a <see cref="QueueTransaction"/>, <see cref="QueueTransaction.Single"/>, or <see cref="QueueTransaction.Dtc"/>.</param> public void Write(Message message, QueueTransaction transaction = null) { Contract.Requires(message != null); message.Props.PrepareToSend(); var props = message.Props.Allocate(); try { int res; IntPtr txnHandle; if (transaction.TryGetHandle(out txnHandle)) { res = Native.SendMessage(_handle, props, txnHandle); } else { res = Native.SendMessage(_handle, props, transaction.InternalTransaction); } if (Native.IsError(res)) { throw new QueueException(res); } } finally { message.Props.Free(); } }
internal static bool TryGetHandle(this QueueTransaction transaction, out IntPtr handle) { if (transaction == null) { handle = IntPtr.Zero; return(true); } var fake = transaction as QueueTransaction.SpecialTransaction; if (fake != null) { handle = fake.SpecialId; return(true); } handle = IntPtr.Zero; return(false); }
/// <summary>Tries to read the current message from the queue without removing the message from the queue.</summary> /// <remarks>Within a transaction you cannot peek a message that you moved to a subqueue</remarks> /// <param name="properties">The properties to read</param> /// <param name="timeout">The time allowed, defaults to infinite. Use <see cref="TimeSpan.Zero"/> to return without waiting</param> /// <param name="transaction">can be NULL for no transaction, a <see cref="QueueTransaction"/>, <see cref="QueueTransaction.Single"/>, or <see cref="QueueTransaction.Dtc"/>.</param> /// <returns>The message, or NULL if the receive times out</returns> public Message Peek(Properties properties = Properties.All, TimeSpan?timeout = null, QueueTransaction transaction = null) { return(Receive(properties, ReadAction.PeekCurrent, timeout, transaction, CursorHandle.None)); }
/// <summary>Tries to peek (or receive) a message using the queue-specific <paramref name="lookupId"/></summary> /// <remarks>Within a transaction you cannot receive a message that you moved to a subqueue within the same transaction</remarks> /// <param name="properties">The properties to read</param> /// <param name="lookupId">The <see cref="Message.LookupId"/> of the message to read</param> /// <param name="action">Receive or peek a message?</param> /// <param name="timeout">The time allowed, defaults to infinite. Use <see cref="TimeSpan.Zero"/> to return without waiting</param> /// <param name="transaction">can be NULL for no transaction, a <see cref="QueueTransaction"/>, <see cref="QueueTransaction.Single"/>, or <see cref="QueueTransaction.Dtc"/>.</param> /// <returns>The message, or NULL if the message was not found or the receive times out</returns> public unsafe Message Lookup(Properties properties, long lookupId, LookupAction action = LookupAction.ReceiveCurrent, TimeSpan?timeout = null, QueueTransaction transaction = null) { if (IsClosed) { throw new ObjectDisposedException(nameof(Queue)); } uint timeoutMS = TimeoutInMs(timeout); var msg = new Message(); int res; msg.Props.SetForRead(properties); for (;;) // loop because we might need to adjust memory size { var props = msg.Props.Allocate(); try { IntPtr txnHandle; if (transaction.TryGetHandle(out txnHandle)) { res = Native.ReceiveMessageByLookupId(_handle, lookupId, action, props, null, null, txnHandle); } else { res = Native.ReceiveMessageByLookupId(_handle, lookupId, action, props, null, null, transaction.InternalTransaction); } } finally { msg.Props.Free(); } if ((ErrorCode)res == ErrorCode.IOTimeout || (ErrorCode)res == ErrorCode.MessageNotFound) { return(null); } if (Native.NotEnoughMemory(res)) { msg.Props.IncreaseBufferSize(); continue; // try again } if (Native.IsError(res)) { throw new QueueException(res); } msg.Props.ResizeBody(); return(msg); } }
/// <summary> /// Uses a <see cref="QueueCursor"/> to look for messages with a matching <paramref name="correlationId"/> /// Returns the matching message or NULL if no matching message can be found with the allowed <paramref name="timeout"/>. /// </summary> public static async Task <Message> ReadByCorrelationIdAsync(this QueueReader queue, MessageId correlationId, Properties properties = Properties.All, TimeSpan?timeout = null, QueueTransaction transaction = null) { timeout = timeout ?? QueueReader.Infinite; var start = DateTime.UtcNow; using (var cur = new QueueCursor(queue)) { var msg = await cur.PeekAsync(Properties.CorrelationId | Properties.LookupId, timeout); for (;;) { if (msg == null) { return(null); } if (msg.CorrelationId == correlationId) { return(queue.Lookup(properties, msg.LookupId, LookupAction.ReceiveCurrent, TimeSpan.Zero, transaction)); } var elapsed = DateTime.UtcNow - start; var remaining = timeout - elapsed; if (remaining <= TimeSpan.Zero) { return(null); } msg = await cur.PeekNextAsync(Properties.CorrelationId | Properties.LookupId, remaining); } } }
/// <summary>Tries to receive a message from the queue</summary> /// <remarks>Within a transaction you cannot receive a message that you moved to a subqueue</remarks> /// <param name="properties">The properties to read</param> /// <param name="timeout">The time allowed, defaults to infinite. Use <see cref="TimeSpan.Zero"/> to return without waiting</param> /// <param name="transaction">can be NULL for no transaction, a <see cref="QueueTransaction"/>, <see cref="QueueTransaction.Single"/>, or <see cref="QueueTransaction.Dtc"/>.</param> /// <returns>The message, or NULL if the receive times out</returns> public Message Read(Properties properties = Properties.All, TimeSpan?timeout = null, QueueTransaction transaction = null) { return(_reader.Receive(properties, ReadAction.Receive, timeout, transaction, _cursorHandle)); }
/// <summary>Move the message specified by <paramref name="lookupId"/> from <paramref name="sourceQueue"/> to the <paramref name="targetQueue"/>.</summary> /// <remarks> /// Moving message is 10 to 100 times faster than sending the message to another queue. /// Within a transaction you cannot receive a message that you moved to a subqueue. /// </remarks> public static void MoveMessage(QueueReader sourceQueue, SubQueue targetQueue, long lookupId, QueueTransaction transaction = null) { Contract.Requires(sourceQueue != null); Contract.Requires(targetQueue != null); if (sourceQueue.IsClosed) { throw new ObjectDisposedException(nameof(sourceQueue)); } if (targetQueue.IsClosed) { throw new ObjectDisposedException(nameof(targetQueue)); } int res; IntPtr txnHandle; if (transaction.TryGetHandle(out txnHandle)) { res = Native.MoveMessage(sourceQueue._handle, targetQueue.MoveHandle, lookupId, txnHandle); } else { res = Native.MoveMessage(sourceQueue._handle, targetQueue.MoveHandle, lookupId, transaction.InternalTransaction); } if (Native.IsError(res)) { throw new QueueException(res); } }