The TS_FRAME_ACKNOWLEDGE_PDU structure is a client-to-server PDU sent to the server whenever the client receives a Frame Marker Command ([MS-RDPBCGR] section 2.2.9.2.3) with the frameAction field set to SURFACECMD_FRAMEACTION_END (0x0001) and it has finished processing this particular frame (that is, the surface bits have been rendered on the screen).
Inheritance: RdpbcgrClientPdu
 /// <summary>
 /// Verify TS_FRAME_ACKNOWLEDGE_PDU
 /// </summary>
 /// <param name="ackPdu">The TS_FRAME_ACKNOWLEDGE_PDU to be verified</param>
 private void VerifyTS_FRAME_ACKNOWLEDGE_PDU(TS_FRAME_ACKNOWLEDGE_PDU ackPdu)
 {
     this.Site.Log.Add(LogEntryKind.Comment, "The frameID of TS_FRAME_ACKNOWLEDGE_PDU is {0}", ackPdu.frameID);
     if (ackPdu.frameID == 0xFFFFFFFF)
     {
         this.Site.Assert.IsTrue(ackPdu.frameID == 0xFFFFFFFF, "[TS_FRAME_ACKNOWLEDGE_PDU] If frameID has the value 0xFFFFFFFF, the server SHOULD assume that all in-flight frames have been acknowledged.");
     }
     else
     {
         this.Site.Assert.AreEqual<uint>(this.frameMakerFrameId, ackPdu.frameID, "[TS_FRAME_ACKNOWLEDGE_PDU][frameID] This field specifies the 32-bit identifier of the frame that was sent to the client using a Frame Marker Command and is being acknowledged as delivered");
     }
 }
        /// <summary>
        /// Decode Persistent Key List PDU 
        /// </summary>
        /// <param name="data">data to be parsed</param>
        /// <param name="decryptedUserData">decrypted user data to be parsed</param>
        /// <param name="type">security header type</param>
        /// <returns>decoded Control PDU</returns>
        public StackPacket DecodeFrameAcknowledgePDU(
            byte[] data,
            byte[] decryptedUserData,
            SecurityHeaderType type)
        {
            TS_FRAME_ACKNOWLEDGE_PDU pdu = new TS_FRAME_ACKNOWLEDGE_PDU();

            // data index
            int dataIndex = 0;

            // SlowPathOutputPDU: commonHeader
            pdu.commonHeader = ParseMcsCommonHeader(data, ref dataIndex, type);

            // user data index
            int userDataIndex = 0;

            // Share Data Header
            pdu.shareDataHeader = ParseTsShareDataHeader(decryptedUserData, ref userDataIndex);

            //frame id
            pdu.frameID = ParseUInt32(decryptedUserData, ref userDataIndex, false);

            // ETW Provider Dump Message
            if (pdu.commonHeader.securityHeader != null)
            {
                // RDP Standard Security
                string messageName = "RDPBCGR:" + pdu.GetType().Name;
                ExtendedLogger.DumpMessage(messageName, RdpbcgrUtility.DumpLevel_Layer3, pdu.GetType().Name, decryptedUserData);
            }

            // Check if data length exceeded expectation
            VerifyDataLength(decryptedUserData.Length, userDataIndex, ConstValue.ERROR_MESSAGE_DATA_LENGTH_EXCEEDED);

            return pdu;
        }