3.1.4.13 R_StartTransactionalReceive (Opnum 13)

The R_StartTransactionalReceive method peeks or receives a message from the opened queue.<29> If a transaction identifier is provided, a message is received inside the specified transaction.

If the R_StartTransactionalReceive method is invoked with a Peek action type, as specified in the ulAction parameter, the operation completes when the R_StartTransactionalReceive method returns.

If the R_StartTransactionalReceive method is invoked with a Receive action type, as specified in the ulAction parameter, the client MUST pair each call to the R_StartTransactionalReceive method with a call to the R_EndTransactionalReceive (Opnum 15) (section 3.1.4.15) method to complete the operation or to the R_CancelReceive (Opnum 8) (section 3.1.4.8) method to cancel the operation. The call to the R_EndTransactionalReceive method or the R_CancelReceive method is correlated to a call to the R_StartTransactionalReceive method through matching dwRequestId parameters.

If the client specifies a nonzero ulTimeout parameter and a message is not available in the queue at the time of the call, the server waits up to the specified time-out for a message to become available in the queue before responding to the call. The client can call the R_CancelReceive method with a matching dwRequestId parameter to cancel the pending R_StartTransactionalReceive method request.

The message to be returned can be specified in one of three ways:

  • LookupId: A nonzero LookupId parameter value that specifies the unique identifier for the message to be returned. The ulAction parameter further specifies whether the message to be returned is the one identified by the LookupId parameter or the first unlocked message immediately preceding or following it. For more details, see the description of the ulAction parameter.

  • Cursor: A nonzero cursor handle that specifies the cursor to be used to identify the message to be returned. The cursor specifies a location in the queue. The ulAction parameter further specifies whether the message to be returned is the one identified by the cursor or the first unlocked message immediately following it. For more details, see the description of the ulAction parameter.

  • First: If the LookupId parameter is set to 0x0000000000000000 and hCursor is set to 0x00000000, the first unlocked message in the queue can be returned. For more details, see the description of the ulAction parameter.

The ppPacketSections parameter is the address of one or more pointers to one or more SectionBuffer (section 2.2.6) structures. The pSectionBuffer member of the first SectionBuffer structure points to the beginning of the message packet. If more than one SectionBuffer structure is present, the packet sections are concatenated in the order in which they appear in the array to form the entire packet. The size of each section is stored in the SectionSizeAlloc member of the SectionBuffer structure.

 HRESULT R_StartTransactionalReceive(
   [in] handle_t hBind,
   [in] QUEUE_CONTEXT_HANDLE_NOSERIALIZE phContext,
   [in] ULONGLONG LookupId,
   [in] DWORD hCursor,
   [in] DWORD ulAction,
   [in] DWORD ulTimeout,
   [in] DWORD dwRequestId,
   [in] DWORD dwMaxBodySize,
   [in] DWORD dwMaxCompoundMessageSize,
   [in] XACTUOW* pTransactionId,
   [out] DWORD* pdwArriveTime,
   [out] ULONGLONG* pSequenceId,
   [out] DWORD* pdwNumberOfSections,
   [out, size_is(, *pdwNumberOfSections)] 
     SectionBuffer** ppPacketSections
 );

hBind: MUST be an RPC binding handle parameter, as specified in [MS-RPCE] section 2.

phContext: MUST be set by the client to a QUEUE_CONTEXT_HANDLE_NOSERIALIZE (section 2.2.4.1) handle of the queue from which to read a message. The handle MUST have been returned by the server in the pphQueue output parameter of a prior call to the R_OpenQueue (Opnum 2) (section 3.1.4.2) method with the dwAccess parameter set to RECEIVE_ACCESS and MUST NOT have been closed through a prior call to the R_CloseQueue (Opnum 3) (section 3.1.4.3) method. NULL is invalid for this parameter.

LookupId: If nonzero, specifies the lookup identifier of the message to be acted on.

If the client sets the LookupId parameter to a nonzero value, the valid values for other parameters are as follows:

  • ulTimeout set to 0x00000000

  • hCursor set to 0x00000000

  • ulAction set to one of the following:

    • MQ_LOOKUP_PEEK_PREV (pTransactionId set to NULL)

    • MQ_LOOKUP_PEEK_CURRENT (pTransactionId set to NULL)

    • MQ_LOOKUP_PEEK_NEXT (pTransactionId set to NULL)

    • MQ_LOOKUP_RECEIVE_PREV

    • MQ_LOOKUP_RECEIVE_CURRENT

    • MQ_LOOKUP_RECEIVE_NEXT

      If the client sets the LookupId parameter to 0x0000000000000000, all of the preceding values of the ulAction parameter are invalid.

hCursor: If nonzero, specifies a handle to a cursor that MUST have been obtained from a prior call to the R_CreateCursor (Opnum 4) (section 3.1.4.4) method. The handle MUST NOT have been closed through a prior call to the R_CloseCursor (Opnum 5) (section 3.1.4.5) method.

If the client sets the hCursor parameter to a nonzero value, the valid values for other parameters are as follows:

  • LookupId set to 0x0000000000000000.

  • ulAction set to one of the following:

    • MQ_ACTION_RECEIVE

    • MQ_ACTION_PEEK_CURRENT (pTransactionId set to NULL)

    • MQ_ACTION_PEEK_NEXT (pTransactionId set to NULL)

ulAction: Specifies the action to perform on the message. The following table lists possible actions.

Type / Value

Meaning

MQ_ACTION_RECEIVE

0x00000000

If the hCursor parameter is nonzero, read and remove the message at the current cursor location from the queue, and advance the cursor.

If the hCursor parameter is 0x00000000, read and remove the message from the front of the queue.

The valid values for other parameters are as follows:

  • LookupId set to 0x0000000000000000.

MQ_ACTION_PEEK_CURRENT

0x80000000

If the hCursor parameter is nonzero, read the message at the current cursor location, but do not remove it from the queue.

If the hCursor parameter is 0x00000000, read the message at the front of the queue, but do not remove it from the queue.

The valid values for other parameters are as follows:

  • LookupId set to 0x0000000000000000.

  • pTransactionId set to NULL.

MQ_ACTION_PEEK_NEXT

0x80000001

If the hCursor parameter is nonzero, advance the cursor to the next position, and read the message, but do not remove it from the queue.

The valid values for other parameters are as follows:

  • LookupId set to 0x0000000000000000.

  • hCursor set to a nonzero cursor handle obtained from the R_CreateCursor method.

  • pTransactionId set to NULL.

MQ_LOOKUP_PEEK_CURRENT

0x40000010

Read the message specified by the LookupId parameter, but do not remove it from the queue.

The valid values for other parameters are as follows:

  • LookupId set to a nonzero value.

  • hCursor set to 0x00000000.

  • ulTimeout set to 0x00000000.

  • pTransactionId set to NULL.

MQ_LOOKUP_PEEK_NEXT

0x40000011

Read the message following the message specified by the LookupId parameter, but do not remove it.

The valid values for other parameters are as follows:

  • LookupId set to a nonzero value.

  • hCursor set to 0x00000000.

  • ulTimeout set to 0x00000000.

  • pTransactionId set to NULL.

MQ_LOOKUP_PEEK_PREV

0x40000012

Read the message preceding the message specified by the LookupId parameter, but do not remove it from the queue.

The valid values for other parameters are as follows:

  • LookupId set to a nonzero value.

  • hCursor set to 0x00000000.

  • ulTimeout set to 0x00000000.

  • pTransactionId set to NULL.

MQ_LOOKUP_RECEIVE_CURRENT

0x40000020

Read the message specified by the LookupId parameter, and remove it from the queue.

The valid values for other parameters are as follows:

  • LookupId set to a nonzero value.

  • hCursor set to 0x00000000.

  • ulTimeout set to 0x00000000.

MQ_LOOKUP_RECEIVE_NEXT

0x40000021

Read the message following the message specified by the LookupId parameter, and remove it from the queue.

The valid values for other parameters are as follows:

  • LookupId set to a nonzero value.

  • hCursor set to 0x00000000.

  • ulTimeout set to 0x00000000.

MQ_LOOKUP_RECEIVE_PREV

0x40000022

Read the message preceding the message specified by the LookupId parameter, and remove it from the queue.

The valid values for other parameters are as follows:

  • LookupId set to a nonzero value.

  • hCursor set to 0x00000000.

  • ulTimeout set to 0x00000000.

If the hCursor parameter is 0x00000000 and the LookupId parameter is 0x0000000000000000, the valid values for the ulAction parameter are as follows:

  • MQ_ACTION_RECEIVE

  • MQ_ACTION_PEEK_CURRENT (pTransactionId set to NULL)

ulTimeout: Specifies the time-out, in milliseconds, to wait for a message to become available in the queue. The valid value for this parameter is 0x00000000 if the LookupId parameter value is nonzero or if the action is not MQ_ACTION_RECEIVE, MQ_ACTION_PEEK_CURRENT, or MQ_ACTION_PEEK_NEXT.

dwRequestId: MUST be set by the client to a unique correlation identifier for the receive request. This value MUST be used in a subsequent call to the R_EndTransactionalReceive method or the R_CancelReceive method to correlate that call with the call to the R_StartTransactionalReceive method. The value MUST NOT be used in another R_StartTransactionalReceive method call on the same QUEUE_CONTEXT_HANDLE_NOSERIALIZE handle until a call to either the R_EndTransactionalReceive method or the R_CancelReceive method with the same dwRequestId parameter value has been completed.

dwMaxBodySize: MUST be set by the client to the maximum size, in bytes, of the message body to be returned. The server SHOULD ignore this parameter when the message is not a Binary Message (section 2.2.5.1.1).

dwMaxCompoundMessageSize: MUST be set by the client to the maximum size, in bytes, of the CompoundMessageHeader (section 2.2.5.1.2.2). The server SHOULD ignore this parameter when the message is not an SRMP Message (section 2.2.5.1.2).

pTransactionId: Set to NULL or set by the client to a transaction identifier that was registered with the server through a prior call to the R_QMEnlistRemoteTransaction (Opnum 12) (section 3.1.4.12) method.

pdwArriveTime: The server MUST set this value to the time that the message was added to the queue ([MS-MQDMPR] section 3.1.7.3.1), expressed as the number of seconds elapsed since midnight 00:00:00.0, January 1, 1970 UTC.

pSequenceId: The server MUST set this parameter to the lower 7 bytes of the Message.LookupIdentifier of the message that is received by this request.

pdwNumberOfSections: MUST be set by the server to the number of entries in the array that are pointed to by the ppPacketSections parameter.

ppPacketSections: MUST be set by the server to an array of pointers to SectionBuffer (section 2.2.6) structures. The server MUST fill this array in the following manner:

  • Create two local variables of type DWORD called maxMessageSize and actualMessageSize. Assign the following values to these variables:

    If the message is a Binary Message:

    • maxMessageSize := dwMaxBodySize

    • actualMessageSize := message packet body size

      If the message is an SRMP Message:

    • maxMessageSize := dwMaxCompoundMessageSize

    • actualMessageSize := size in bytes of CompoundMessageHeader

  • If the value of maxMessageSize is greater than or equal to actualMessageSize, the ppPacketSections parameter MUST contain a single entry as follows:

    • The SectionBufferType member MUST be set to stFullPacket (0x00000000).

    • The SectionSize and SectionSizeAlloc members MUST be set to the message packet size.

    • The pSectionBuffer member MUST contain the entire message packet.

  • If the value of maxMessageSize is less than actualMessageSize, the array MUST contain a first entry as follows:

    The SectionBufferType member MUST be set to one of the following:

    • stBinaryFirstSection if the message packet is a binary packet.

    • stSrmpFirstSection if the message packet is an SRMP packet.

    • The pSectionBuffer member MUST contain the message packet headers concatenated with the first maxMessageSize bytes of the message body.

    • The SectionSizeAlloc member MUST be set to the message packet headers plus actualMessageSize.

    • The SectionSize member MUST be set to the size of the pSectionBuffer member.

  • If the value of maxMessageSize is less than actualMessageSize and the message packet trailers are not empty, the array MUST contain a second entry as follows:

    The SectionBufferType member MUST be set to one of the following:

    • stBinarySecondSection if the message packet is a binary packet.

    • stSrmpSecondSection if the message packet is an SRMP packet.

    • The pSectionBuffer member MUST contain the message packet trailers.

    • The SectionSize and the SectionSizeAlloc members MUST be equal and MUST be set to the message packet trailers size.

  • For the first entry in this array, the pSectionBuffer member points to a Message Packet Structure (section 2.2.5). Within this structure, set UserMessage.BaseHeader.TimeToReachQueue to UserHeader.SentTime + UserMessage.BaseHeader.TimeToReachQueue.

Return Values: On success, this method MUST return MQ_OK (0x00000000).

If an error occurs, the server MUST return a failure HRESULT, and the client MUST treat all failure HRESULTs identically. The client MUST disregard all output parameter values when any failure HRESULT is returned.

Return value/code

Description

0x00000000

MQ_OK

0xC00E0007

MQ_ERROR_INVALID_HANDLE

0xC00E0088

MQ_ERROR_MESSAGE_NOT_FOUND

0xC00E001B

MQ_ERROR_IO_TIMEOUT

0xC00E0050

MQ_ERROR_TRANSACTION_USAGE

0xC00E0008

MQ_ERROR_OPERATION_CANCELLED

0xC00E0006

MQ_ERROR_INVALID_PARAMETER

Exceptions Thrown:

No exceptions are thrown except those thrown by the underlying RPC protocol, as specified in [MS-RPCE].

While processing this method, the server MUST:

  • If any of the input parameter values is invalid, return MQ_ERROR_INVALID_PARAMETER (0xC00E0006).

  • If the pTransactionId parameter is NULL:

    • Call the R_StartReceive (Opnum 7) (section 3.1.4.7) method with the following parameters:

      • hBind := hBind

      • phContext := phContext

      • LookupId := LookupId

      • hCursor := hCursor

      • ulAction := ulAction

      • ulTimeout := ulTimeout

      • dwRequestId := dwRequestId

      • dwMaxBodySize := dwMaxBodySize

      • dwMaxCompoundMessageSize := dwMaxCompoundMessageSize

      • pdwArriveTime := pdwArriveTime

      • pSequenceId := pSequenceId

      • pdwNumberOfSections := pdwNumberOfSections

      • ppPacketSections := ppPacketSections

    • Return the result from the R_StartReceive method, and take no further action.

  • Find the corresponding OpenQueueDescriptor ADM element instance by comparing the phContext parameter with the Handle ADM attribute for all OpenQueueDescriptor ADM element instances maintained by the local QueueManager ADM element instance.

  • If not found, return a failure HRESULT, and perform no further actions; otherwise, assign the found OpenQueueDescriptor ADM element instance to the local variable queueDesc.

  • If the hCursor parameter is a nonzero value, find the corresponding Cursor ADM element instance by comparing the hCursor parameter with the Handle ADM attribute for all Cursor ADM element instances maintained by the local QueueManager ADM element instance. If not found, or the Cursor ADM element instance has previously been closed by a call to the R_CloseCursor method, return STATUS_INVALID_HANDLE (0xC0000008).

  • If  queueDesc.QueueReference.Transactional is FALSE, the queue does not support transactional operations. Return MQ_ERROR_TRANSACTION_USAGE (0xC00E0050).

  • If the ulAction parameter is MQ_ACTION_PEEK_CURRENT, MQ_ACTION_PEEK_NEXT, MQ_LOOKUP_PEEK_CURRENT, MQ_LOOKUP_PEEK_NEXT, or MQ_LOOKUP_PEEK_PREV, return MQ_ERROR_TRANSACTION_USAGE.

  • Find the corresponding Transaction ADM element instance, referred to as lpTransaction, by comparing the pTransactionId parameter with the Identifier ADM attribute for all Transaction ADM element instances in the TransactionCollection ADM attribute of the local QueueManager ADM element instance.

  • If a Transaction ADM element instance cannot be found:

    • Generate a Create Transaction ([MS-MQDMPR] section 3.1.7.1.8) event with the following input:

      • iTransactionIdentifier := NULL

    • On return, set lpTransaction to rTransaction.

  • If the ulAction parameter is MQ_ACTION_RECEIVE, perform the following steps:

    • Create a new PendingRequestEntry (section 3.1.1.2) ADM element instance with:

      • The RequestId ADM attribute set to the dwRequestId parameter.

      • The QueueContextHandle ADM attribute set to the phContext parameter.

      • The LookupIdentifier ADM attribute set to zero.

      • The TimeStamp ADM attribute set to the current system time, in milliseconds, since the operating system was started.

    • The server MUST create a new instance of the Pending Request Cleanup Timer (section 3.1.2.2) associated with the new PendingRequestEntry ADM element instance and MUST start it.

    • Add the new PendingRequestEntry ADM element instance to the PendingRequestTable (section 3.1.1.3) ADM element.

    • Generate a Dequeue Message Begin ([MS-MQDMPR] section 3.1.7.1.11) event with the following inputs:

      • iQueueDesc := queueDesc

      • iTimeout := ulTimeout

      • iCursor := Cursor only if hCursor is a nonzero value

      • iTag := dwRequestId

      • iTransaction := lpTransaction

    • If the rStatus value returned from the Dequeue Message Begin event is MQ_OK (0x00000000), the server MUST set the LookupIdentifier ADM attribute of the new PendingRequestEntry ADM element instance to rMessage.LookupIdentifier.

  • If the ulAction parameter is MQ_LOOKUP_RECEIVE_CURRENT, generate a Read Message By Lookup Identifier ([MS-MQDMPR] section 3.1.7.1.13) event with the following inputs:

    • iQueueDesc := queueDesc

    • iLookupId := LookupId

    • iPeekOperation := False

    • iLookupOperation := MessageSeekAction.SeekCurrent

    • iTransaction := lpTransaction

    • iTwoPhaseRead := True

  • If the ulAction parameter is MQ_LOOKUP_RECEIVE_NEXT, generate a Read Message By Lookup Identifier event with the following inputs:

    • iQueueDesc := queueDesc

    • iLookupId := LookupId

    • iPeekOperation := False

    • iLookupOperation := MessageSeekAction.SeekNext

    • iTransaction := lpTransaction

    • iTwoPhaseRead := True

  • If the ulAction parameter is MQ_LOOKUP_RECEIVE_PREV, generate a Read Message By Lookup Identifier event with the following inputs:

    • iQueueDesc := queueDesc

    • iLookupId := LookupId

    • iPeekOperation := False

    • iLookupOperation := MessageSeekAction.SeekPrev

    • iTransaction := lpTransaction

    • iTwoPhaseRead := True

  • If the rStatus value returned from the preceding events is MQ_OK (Ox00000000), the server MUST:

    • Use rMessage to fill the ppPacketSections parameter array as specified in the ppPacketSections parameter description.

    • Set the pdwArriveTime parameter to Message.ArrivalTime.

  • Return rStatus.