MessageEnumerator.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. //------------------------------------------------------------------------------
  2. // <copyright file="MessageEnumerator.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. //------------------------------------------------------------------------------
  6. using Experimental.System.Messaging.Interop;
  7. using System;
  8. using System.Collections;
  9. using System.ComponentModel;
  10. using System.Diagnostics.CodeAnalysis;
  11. namespace Experimental.System.Messaging
  12. {
  13. /// <include file='doc\MessageEnumerator.uex' path='docs/doc[@for="MessageEnumerator"]/*' />
  14. /// <devdoc>
  15. /// <para>Provides (forward-only)
  16. /// cursor semantics to enumerate the messages contained in
  17. /// a queue.</para>
  18. /// <note type="rnotes">
  19. /// Translate into English?
  20. /// </note>
  21. /// </devdoc>
  22. public class MessageEnumerator : MarshalByRefObject, IEnumerator, IDisposable
  23. {
  24. private MessageQueue owner;
  25. private CursorHandle handle = System.Messaging.Interop.CursorHandle.NullHandle;
  26. private int index = 0;
  27. private bool disposed = false;
  28. private bool useCorrectRemoveCurrent = false; //needed in fix for 88615
  29. internal MessageEnumerator(MessageQueue owner, bool useCorrectRemoveCurrent)
  30. {
  31. this.owner = owner;
  32. this.useCorrectRemoveCurrent = useCorrectRemoveCurrent;
  33. }
  34. /// <include file='doc\MessageEnumerator.uex' path='docs/doc[@for="MessageEnumerator.Current"]/*' />
  35. /// <devdoc>
  36. /// <para>Gets the current <see cref='System.Messaging.Message'/> pointed to
  37. /// by this enumerator.</para>
  38. /// </devdoc>
  39. public Message Current
  40. {
  41. get
  42. {
  43. if (this.index == 0)
  44. throw new InvalidOperationException(Res.GetString(Res.NoCurrentMessage));
  45. return this.owner.ReceiveCurrent(TimeSpan.Zero, NativeMethods.QUEUE_ACTION_PEEK_CURRENT, this.Handle,
  46. this.owner.MessageReadPropertyFilter, null,
  47. MessageQueueTransactionType.None);
  48. }
  49. }
  50. /// <include file='doc\MessageEnumerator.uex' path='docs/doc[@for="MessageEnumerator.IEnumerator.Current"]/*' />
  51. /// <internalonly/>
  52. object IEnumerator.Current
  53. {
  54. get
  55. {
  56. return this.Current;
  57. }
  58. }
  59. /// <include file='doc\MessageEnumerator.uex' path='docs/doc[@for="MessageEnumerator.CursorHandle"]/*' />
  60. /// <devdoc>
  61. /// <para>Gets the native Message Queuing cursor handle used to browse messages
  62. /// in the queue.</para>
  63. /// </devdoc>
  64. public IntPtr CursorHandle
  65. {
  66. get { return this.Handle.DangerousGetHandle(); }
  67. }
  68. internal CursorHandle Handle
  69. {
  70. get
  71. {
  72. //Cursor handle doesn't demand permissions since GetEnumerator will demand somehow.
  73. if (this.handle.IsInvalid)
  74. {
  75. //Cannot allocate the a new cursor if the object has been disposed, since finalization has been suppressed.
  76. if (this.disposed)
  77. throw new ObjectDisposedException(GetType().Name);
  78. CursorHandle result;
  79. int status = SafeNativeMethods.MQCreateCursor(this.owner.MQInfo.ReadHandle, out result);
  80. if (MessageQueue.IsFatalError(status))
  81. throw new MessageQueueException(status);
  82. this.handle = result;
  83. }
  84. return this.handle;
  85. }
  86. }
  87. /// <include file='doc\MessageEnumerator.uex' path='docs/doc[@for="MessageEnumerator.Close"]/*' />
  88. /// <devdoc>
  89. /// <para>
  90. /// Frees the resources associated with the enumerator.
  91. /// </para>
  92. /// </devdoc>
  93. public void Close()
  94. {
  95. this.index = 0;
  96. if (!this.handle.IsInvalid)
  97. {
  98. this.handle.Close();
  99. }
  100. }
  101. /// <include file='doc\MessageEnumerator.uex' path='docs/doc[@for="MessageEnumerator.Dispose"]/*' />
  102. /// <devdoc>
  103. /// </devdoc>
  104. [SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed")]
  105. public void Dispose()
  106. {
  107. Dispose(true);
  108. }
  109. /// <include file='doc\MessageEnumerator.uex' path='docs/doc[@for="MessageEnumerator.Dispose1"]/*' />
  110. /// <devdoc>
  111. /// <para>
  112. /// </para>
  113. /// </devdoc>
  114. protected virtual void Dispose(bool disposing)
  115. {
  116. if (disposing)
  117. {
  118. this.Close();
  119. }
  120. this.disposed = true;
  121. }
  122. /// <include file='doc\MessageEnumerator.uex' path='docs/doc[@for="MessageEnumerator.MoveNext"]/*' />
  123. /// <devdoc>
  124. /// <para>Advances the enumerator to the next message in the queue, if one
  125. /// is currently available.</para>
  126. /// </devdoc>
  127. public bool MoveNext()
  128. {
  129. return MoveNext(TimeSpan.Zero);
  130. }
  131. /// <include file='doc\MessageEnumerator.uex' path='docs/doc[@for="MessageEnumerator.MoveNext1"]/*' />
  132. /// <devdoc>
  133. /// <para>Advances the enumerator to the next message in the
  134. /// queue. If the enumerator is positioned at the end of the queue, <see cref='System.Messaging.MessageEnumerator.MoveNext'/> waits until a message is available or the
  135. /// given <paramref name="timeout"/>
  136. /// expires.</para>
  137. /// </devdoc>
  138. public unsafe bool MoveNext(TimeSpan timeout)
  139. {
  140. long timeoutInMilliseconds = (long)timeout.TotalMilliseconds;
  141. if (timeoutInMilliseconds < 0 || timeoutInMilliseconds > UInt32.MaxValue)
  142. throw new ArgumentException(Res.GetString(Res.InvalidParameter, "timeout", timeout.ToString()));
  143. int status = 0;
  144. int action = NativeMethods.QUEUE_ACTION_PEEK_NEXT;
  145. //Peek current or next?
  146. if (this.index == 0)
  147. action = NativeMethods.QUEUE_ACTION_PEEK_CURRENT;
  148. status = owner.StaleSafeReceiveMessage((uint)timeoutInMilliseconds, action, null, null, null, this.Handle, (IntPtr)NativeMethods.QUEUE_TRANSACTION_NONE);
  149. //If the cursor reached the end of the queue.
  150. if (status == (int)MessageQueueErrorCode.IOTimeout)
  151. {
  152. this.Close();
  153. return false;
  154. }
  155. //If all messages were removed.
  156. else if (status == (int)MessageQueueErrorCode.IllegalCursorAction)
  157. {
  158. this.index = 0;
  159. this.Close();
  160. return false;
  161. }
  162. if (MessageQueue.IsFatalError(status))
  163. throw new MessageQueueException(status);
  164. ++this.index;
  165. return true;
  166. }
  167. /// <include file='doc\MessageEnumerator.uex' path='docs/doc[@for="MessageEnumerator.RemoveCurrent"]/*' />
  168. /// <devdoc>
  169. /// <para> Removes the current message from
  170. /// the queue and returns the message to the calling application.</para>
  171. /// </devdoc>
  172. public Message RemoveCurrent()
  173. {
  174. return RemoveCurrent(TimeSpan.Zero, null, MessageQueueTransactionType.None);
  175. }
  176. /// <include file='doc\MessageEnumerator.uex' path='docs/doc[@for="MessageEnumerator.RemoveCurrent1"]/*' />
  177. /// <devdoc>
  178. /// <para> Removes the current message from
  179. /// the queue and returns the message to the calling application.</para>
  180. /// </devdoc>
  181. public Message RemoveCurrent(MessageQueueTransaction transaction)
  182. {
  183. if (transaction == null)
  184. throw new ArgumentNullException("transaction");
  185. return RemoveCurrent(TimeSpan.Zero, transaction, MessageQueueTransactionType.None);
  186. }
  187. /// <include file='doc\MessageEnumerator.uex' path='docs/doc[@for="MessageEnumerator.RemoveCurrent5"]/*' />
  188. /// <devdoc>
  189. /// <para> Removes the current message from
  190. /// the queue and returns the message to the calling application.</para>
  191. /// </devdoc>
  192. public Message RemoveCurrent(MessageQueueTransactionType transactionType)
  193. {
  194. if (!ValidationUtility.ValidateMessageQueueTransactionType(transactionType))
  195. throw new InvalidEnumArgumentException("transactionType", (int)transactionType, typeof(MessageQueueTransactionType));
  196. return RemoveCurrent(TimeSpan.Zero, null, transactionType);
  197. }
  198. /// <include file='doc\MessageEnumerator.uex' path='docs/doc[@for="MessageEnumerator.RemoveCurrent2"]/*' />
  199. /// <devdoc>
  200. /// <para> Removes the current message from
  201. /// the queue and returns the message to the calling application within the timeout specified.</para>
  202. /// </devdoc>
  203. public Message RemoveCurrent(TimeSpan timeout)
  204. {
  205. return RemoveCurrent(timeout, null, MessageQueueTransactionType.None);
  206. }
  207. /// <include file='doc\MessageEnumerator.uex' path='docs/doc[@for="MessageEnumerator.RemoveCurrent3"]/*' />
  208. /// <devdoc>
  209. /// <para> Removes the current message from
  210. /// the queue and returns the message to the calling application within the timeout specified.</para>
  211. /// </devdoc>
  212. public Message RemoveCurrent(TimeSpan timeout, MessageQueueTransaction transaction)
  213. {
  214. if (transaction == null)
  215. throw new ArgumentNullException("transaction");
  216. return RemoveCurrent(timeout, transaction, MessageQueueTransactionType.None);
  217. }
  218. /// <include file='doc\MessageEnumerator.uex' path='docs/doc[@for="MessageEnumerator.RemoveCurrent4"]/*' />
  219. /// <devdoc>
  220. /// <para> Removes the current message from
  221. /// the queue and returns the message to the calling application within the timeout specified.</para>
  222. /// </devdoc>
  223. public Message RemoveCurrent(TimeSpan timeout, MessageQueueTransactionType transactionType)
  224. {
  225. if (!ValidationUtility.ValidateMessageQueueTransactionType(transactionType))
  226. throw new InvalidEnumArgumentException("transactionType", (int)transactionType, typeof(MessageQueueTransactionType));
  227. return RemoveCurrent(timeout, null, transactionType);
  228. }
  229. private Message RemoveCurrent(TimeSpan timeout, MessageQueueTransaction transaction, MessageQueueTransactionType transactionType)
  230. {
  231. long timeoutInMilliseconds = (long)timeout.TotalMilliseconds;
  232. if (timeoutInMilliseconds < 0 || timeoutInMilliseconds > UInt32.MaxValue)
  233. throw new ArgumentException(Res.GetString(Res.InvalidParameter, "timeout", timeout.ToString()));
  234. if (this.index == 0)
  235. return null;
  236. Message message = this.owner.ReceiveCurrent(timeout, NativeMethods.QUEUE_ACTION_RECEIVE,
  237. this.Handle, this.owner.MessageReadPropertyFilter, transaction, transactionType);
  238. if (!useCorrectRemoveCurrent) --this.index;
  239. return message;
  240. }
  241. /// <include file='doc\MessageEnumerator.uex' path='docs/doc[@for="MessageEnumerator.Reset"]/*' />
  242. /// <devdoc>
  243. /// <para> Resets the current enumerator, so it points to
  244. /// the head of the queue.</para>
  245. /// </devdoc>
  246. public void Reset()
  247. {
  248. this.Close();
  249. }
  250. }
  251. }