/// <summary>
        /// The method EcDoRpcExt2 passes generic remote operation (ROP) commands to the server for processing within a Session Context. Each call can contain multiple ROP commands. 
        /// </summary>
        /// <param name="pcxh">The unique value to be used as a CXH</param>
        /// <param name="pulFlags">Flags that tell the server how to build the rgbOut parameter.</param>
        /// <param name="rgbIn">This buffer contains the ROP request payload. </param>
        /// <param name="rgbOut">On Output, this parameter contains the response payload.</param>
        /// <param name="pcbOut">On Output, this parameter contains the size of response payload.</param>
        /// <param name="rgbAuxIn">This parameter contains an auxiliary payload buffer. </param>
        /// <param name="pcbAuxOut">On input, this parameter contains the maximum length of the rgbAuxOut buffer. On output, this parameter contains the size of the data to be returned in the rgbAuxOut buffer.</param>
        /// <param name="response">The ROP response returned in this RPC method and parsed by adapter.</param>
        /// <param name="responseSOHTable">The Response SOH Table returned by ROP call, provides information like object handle.</param>
        /// <param name="payloadCount">The count of payload that ROP response contains.</param>
        /// <param name="rgbAuxOut">The data of the output buffer rgbAuxOut. </param>
        /// <returns>If success, it return 0, else return the error code.</returns>
        public uint EcDoRpcExt2(
            ref IntPtr pcxh,
            PulFlags pulFlags,
            byte[] rgbIn, 
            ref byte[] rgbOut,
            ref uint pcbOut,
            byte[] rgbAuxIn, 
            ref uint pcbAuxOut,
            out IDeserializable response,
            ref List<List<uint>> responseSOHTable,
            out uint payloadCount,
            ref byte[] rgbAuxOut)
        {
            #region Variables
            uint inputPcbOutValue = pcbOut;
            uint inputPcbAuxOutValue = pcbAuxOut;
            IntPtr pcxhInput = pcxh;
            int pcxhValue = pcxh.ToInt32();
            uint flags = (uint)pulFlags;
            payloadCount = 0;
            uint returnValue = 0;
            uint pulTransTime;
            uint inputROPSize = 0;
            if (rgbIn != null)
            {
                inputROPSize = (uint)rgbIn.Length;
            }
            else
            {
                rgbIn = new byte[0];
            }

            uint inputAuxSize = 0;
            if (rgbAuxIn != null)
            {
                inputAuxSize = (uint)rgbAuxIn.Length;
            }
            else
            {
                rgbAuxIn = new byte[0];
            }

            rgbOut = new byte[pcbOut];
            response = null;
            #endregion

            IntPtr rgbInPtr = Marshal.AllocHGlobal(rgbIn.Length);
            IntPtr rgbAuxInPtr = Marshal.AllocHGlobal(rgbAuxIn.Length);

            try
            {
                Marshal.Copy(rgbIn, 0, rgbInPtr, rgbIn.Length);
                Marshal.Copy(rgbAuxIn, 0, rgbAuxInPtr, rgbAuxIn.Length);

                // Record whether the server is busy and needs to retry later.
                bool needRetry = false;
                int retryCount = 0;
                int maxRetryCount = int.Parse(Common.GetConfigurationPropertyValue("RetryCount", this.Site));
                do
                {
                    IntPtr rgbOutPtr = Marshal.AllocHGlobal((int)pcbOut);
                    IntPtr rgbAuxOutPtr = Marshal.AllocHGlobal((int)pcbAuxOut);

                    needRetry = false;
                    returnValue = NativeMethods.EcDoRpcExt2(
                        ref pcxhInput,
                        ref flags,
                        rgbInPtr,
                        inputROPSize,
                        rgbOutPtr,
                        ref pcbOut,
                        rgbAuxInPtr,
                        inputAuxSize,
                        rgbAuxOutPtr,
                        ref pcbAuxOut,
                        out pulTransTime);

                    rgbOut = new byte[(int)pcbOut];
                    Marshal.Copy(rgbOutPtr, rgbOut, 0, (int)pcbOut);
                    Marshal.FreeHGlobal(rgbOutPtr);

                    rgbAuxOut = new byte[(int)pcbAuxOut];
                    Marshal.Copy(rgbAuxOutPtr, rgbAuxOut, 0, (int)pcbAuxOut);
                    Marshal.FreeHGlobal(rgbAuxOutPtr);

                    #region Verify requirements while EcDoRpcExt2 success

                    if (returnValue == 0 && pcbOut > 0)
                    {
                        RPC_HEADER_EXT[] rpcHeaderExts;
                        byte[][] rops;
                        uint[][] serverHandleObjectsTables;

                        byte[] rawData = new byte[pcbOut];
                        Array.Copy(rgbOut, rawData, pcbOut);
                        int size;
                        int actualSize;
                        int payLoadSizeCount = 0;
                        int payLoadActualSizeCount = 0;
                        short flag;
                        bool isCompressed = false;
                        List<byte> actualData = new List<byte>();
                        List<short> flagValuesBeforeDecompressing = new List<short>();

                        #region Decompress or revert rgbOut if it is compressed or obfuscated
                        do
                        {
                            flag = BitConverter.ToInt16(rawData, payLoadSizeCount + ConstValues.RpcHeaderExtVersionByteSize);
                            flagValuesBeforeDecompressing.Add(flag);
                            size = BitConverter.ToInt16(rawData, payLoadSizeCount + ConstValues.RpcHeaderExtVersionByteSize + ConstValues.RpcHeaderExtFlagsByteSize);
                            actualSize = BitConverter.ToInt16(rawData, payLoadSizeCount + ConstValues.RpcHeaderExtVersionByteSize + ConstValues.RpcHeaderExtFlagsByteSize + ConstValues.RpcHeaderExtSizeByteSize);
                            byte[] resultByte = new byte[actualSize + AdapterHelper.RPCHeaderExtlength];
                            Array.Copy(rawData, payLoadSizeCount, resultByte, 0, size + AdapterHelper.RPCHeaderExtlength);

                            // Compressed (0x00000001) means the data that follows the RPC_HEADER_EXT is compressed. 
                            if ((flag & (short)RpcHeaderExtFlags.Compressed) == (short)RpcHeaderExtFlags.Compressed)
                            {
                                isCompressed = true;
                                byte[] rgbOriOut = new byte[size + AdapterHelper.RPCHeaderExtlength];
                                Array.Copy(rawData, payLoadSizeCount, rgbOriOut, 0, size + AdapterHelper.RPCHeaderExtlength);
                                bool decompressResult = false;
                                try
                                {
                                    resultByte = Common.DecompressStream(rgbOriOut);
                                    decompressResult = true;
                                }
                                catch (ArgumentException)
                                {
                                    decompressResult = false;
                                }
                                catch (InvalidOperationException)
                                {
                                    decompressResult = false;
                                }

                                this.VerifyCompressionAlgorithm(decompressResult, false, false, flag);
                                this.VerifyDIRECT2EncodingAlgorithm(decompressResult);
                            }

                            // XorMagic(0x00000002) means that the data follows the RPC_HEADER_EXT has been obfuscated.
                            if ((flag & (short)RpcHeaderExtFlags.XorMagic) == (short)RpcHeaderExtFlags.XorMagic)
                            {
                                byte[] rgbXorOut = null;
                                byte[] rgbOriOut = new byte[size + AdapterHelper.RPCHeaderExtlength];
                                Array.Copy(rawData, payLoadSizeCount, rgbOriOut, 0, size + AdapterHelper.RPCHeaderExtlength);

                                // According to the Open Specification, every byte of the data to be obfuscated has XOR applied with the value 0xA5.
                                bool obfuscationResult = false;
                                rgbXorOut = Common.XOR(rgbOriOut);
                                if (rgbXorOut != null)
                                {
                                    obfuscationResult = true;
                                }

                                Array.Copy(rawData, payLoadSizeCount, resultByte, 0, AdapterHelper.RPCHeaderExtlength);
                                Array.Copy(rgbXorOut, 8, resultByte, AdapterHelper.RPCHeaderExtlength, actualSize);

                                // The first false means current method is EcDoRpcExt2
                                // The second false means current field is rgbOut
                                this.VerifyObfuscationAlgorithm(obfuscationResult, false, false, flag);
                            }

                            actualData.AddRange(resultByte);
                            payLoadSizeCount += size + AdapterHelper.RPCHeaderExtlength;
                            payLoadActualSizeCount += actualSize + AdapterHelper.RPCHeaderExtlength;
                        }
                        while ((flag & (short)RpcHeaderExtFlags.Last) != (short)RpcHeaderExtFlags.Last);
                        rgbOut = actualData.ToArray();
                        #endregion

                        #region Resize rgbOut
                        if (rgbOut != null)
                        {
                            if (isCompressed)
                            {
                                Array.Resize<byte>(ref rgbOut, payLoadActualSizeCount + 1);
                            }
                            else
                            {
                                Array.Resize<byte>(ref rgbOut, (int)pcbOut);
                            }
                        }
                        #endregion

                        this.ParseResponseBuffer(rgbOut, out rpcHeaderExts, out rops, out serverHandleObjectsTables);

                        #region Verify RPC_HEADER_EXTs in rgbOut buffer

                        for (int i = 0; i < rpcHeaderExts.Length; i++)
                        {
                            this.VerifyRPCHeaderExt(flagValuesBeforeDecompressing, rpcHeaderExts[i], false, false, i == rpcHeaderExts.Length - 1);
                        }

                        this.VerifyMultiRPCHeader(rpcHeaderExts.Length);

                        #endregion

                        #region Deserialize Rops

                        List<IDeserializable> responseRops = new List<IDeserializable>();

                        int ropID = 0;
                        if (rops != null)
                        {
                            // Verify the length of payload in the rgbOut
                            this.VerifyPayloadLengthResponse(rops);

                            // Only contains one ROP response
                            if (rops.Length == 1)
                            {
                                ropID = rops[0][0];
                                if (ropID == (int)RopId.RopBackoff)
                                {
                                    needRetry = true;

                                    // Revert the value of variables whose type is a reference type and used by method EcDoRpcExt2.
                                    pcxh = pcxhInput;
                                    flags = (uint)pulFlags;
                                    pcbOut = inputPcbOutValue;
                                    pcbAuxOut = inputPcbAuxOutValue;

                                    // Wait a period of time before retrying since the server is busy now.
                                    System.Threading.Thread.Sleep(int.Parse(Common.GetConfigurationPropertyValue("WaitTime", this.Site)));
                                    retryCount++;
                                    continue;
                                }
                                
                                IDeserializable ropRes = null;
                                RopDeserializer.Deserialize(rops[0], ref ropRes);
                                payloadCount = 1;

                                // True means RopDeserializer.Deserialize method is successful(Because there is no exception thrown and code can arrive here).
                                // 1 means one ROP response
                                this.VerifyIsRopResponse(true, 1);
                                responseRops.Add(ropRes);
                            }
                            else
                            {
                                // Contains more than one ROP response
                                ropID = rops[0][0];
                                foreach (byte[] rop in rops)
                                {
                                    if (rop[0] == (byte)RopId.RopBackoff)
                                    {
                                        needRetry = true;

                                        // Revert the value of variables whose type is a reference type and used by method EcDoRpcExt2.
                                        pcxh = pcxhInput;
                                        flags = (uint)pulFlags;
                                        pcbOut = inputPcbOutValue;
                                        pcbAuxOut = inputPcbAuxOutValue;

                                        // Wait a period of time before retrying since the server is busy now.
                                        System.Threading.Thread.Sleep(int.Parse(Common.GetConfigurationPropertyValue("WaitTime", this.Site)));
                                        retryCount++;
                                        break;
                                    }

                                    IDeserializable ropRes = null;
                                    RopDeserializer.Deserialize(rop, ref ropRes);
                                    payloadCount = (uint)rops.Length;

                                    // True means RopDeserializer.Deserialize method is successful(Because there is no exception thrown and code can arrive here).
                                    this.VerifyIsRopResponse(true, rops.Length);
                                    switch (rop[0])
                                    {
                                        // RopReadStream Response Buffer.
                                        case (int)RopId.RopReadStream:
                                            RopReadStreamResponse readStreamResponse = (RopReadStreamResponse)ropRes;
                                            responseRops.Add(readStreamResponse);
                                            break;

                                        // RopQueryRows Response Buffer.
                                        case (int)RopId.RopQueryRows:
                                            RopQueryRowsResponse queryRowsResponse = (RopQueryRowsResponse)ropRes;
                                            responseRops.Add(queryRowsResponse);
                                            break;

                                        // RopFastTransferSourceGetBuffer Response Buffer.
                                        case (int)RopId.RopFastTransferSourceGetBuffer:
                                            RopFastTransferSourceGetBufferResponse bufferResponse = (RopFastTransferSourceGetBufferResponse)ropRes;
                                            responseRops.Add(bufferResponse);
                                            break;
                                        default:
                                            throw new InvalidCastException("The returned ROP ID is {0}.", ropID);
                                    }
                                }

                                switch (ropID)
                                {
                                    // RopReadStream Response Buffer.
                                    case (int)RopId.RopReadStream:
                                        for (int i = 0; i < responseRops.Count; i++)
                                        {
                                            for (int j = i + 1; j < responseRops.Count; j++)
                                            {
                                                RopReadStreamResponse readStreamResponse0 = (RopReadStreamResponse)responseRops[i];
                                                RopReadStreamResponse readStreamResponse1 = (RopReadStreamResponse)responseRops[j];
                                                this.VerifyRopReadStreamResponse(readStreamResponse0.DataSize, readStreamResponse1.DataSize, responseRops.Count);
                                            }
                                        }

                                        break;

                                    // RopQueryRows Response Buffer.
                                    case (int)RopId.RopQueryRows:
                                        for (int i = 0; i < responseRops.Count; i++)
                                        {
                                            for (int j = i + 1; j < responseRops.Count; j++)
                                            {
                                                RopQueryRowsResponse queryRowsResponse0 = (RopQueryRowsResponse)responseRops[i];
                                                RopQueryRowsResponse queryRowsResponse1 = (RopQueryRowsResponse)responseRops[j];
                                                this.VerifyRopQueryRowsResponse(queryRowsResponse0.RowCount, queryRowsResponse1.RowCount, responseRops.Count);
                                            }
                                        }

                                        break;

                                    // RopFastTransferSourceGetBuffer Response Buffer.
                                    case (int)RopId.RopFastTransferSourceGetBuffer:
                                        this.VerifyRopFastTransferSourceGetBufferResponse(responseRops.Count);
                                        break;
                                    default:
                                        throw new InvalidCastException("The returned ROP ID is {0}.", ropID);
                                }
                            }

                            if (responseRops != null)
                            {
                                if (responseRops.Count > 0)
                                {
                                    response = responseRops[0];
                                }
                            }
                            else
                            {
                                response = null;
                            }
                        }

                        #endregion

                        #region Deserialize SOH

                        if (needRetry)
                        {
                            continue;
                        }

                        responseSOHTable = new List<List<uint>>();

                        // Deserialize serverHandleObjectsTables
                        if (serverHandleObjectsTables != null)
                        {
                            foreach (uint[] serverHandleObjectsTable in serverHandleObjectsTables)
                            {
                                List<uint> serverHandleObjectList = new List<uint>();
                                foreach (uint serverHandleObject in serverHandleObjectsTable)
                                {
                                    serverHandleObjectList.Add(serverHandleObject);
                                }

                                responseSOHTable.Add(serverHandleObjectList);
                            }
                        }

                        #endregion

                        #region Parse rgbAuxOut

                        #region Capture code

                        #region Verify rgbAuxOut
                        this.VerifyIsRgbSupportedOnEcDoRpcExt2(pcbAuxOut);

                        List<short> flagValues = new List<short>();

                        if (pcbAuxOut != 0)
                        {
                            isCompressed = false;

                            // Decompress or revert if the rgbAuxOut is compressed or obfuscated
                            flag = BitConverter.ToInt16(rgbAuxOut, ConstValues.RpcHeaderExtVersionByteSize);
                            size = BitConverter.ToInt16(rgbAuxOut, ConstValues.RpcHeaderExtFlagsByteSize + ConstValues.RpcHeaderExtSizeByteSize) + AdapterHelper.RPCHeaderExtlength;
                            actualSize = BitConverter.ToInt16(rgbAuxOut, ConstValues.RpcHeaderExtSizeByteSize + ConstValues.RpcHeaderExtFlagsByteSize + ConstValues.RpcHeaderExtVersionByteSize) + AdapterHelper.RPCHeaderExtlength;
                            flagValues.Add(flag);

                            // Decompress or revert rgbAuxOut if it is compressed or obfuscated
                            // Compressed (0x00000001) means the data that follows the RPC_HEADER_EXT is compressed.
                            if ((flag & (short)RpcHeaderExtFlags.Compressed) == (short)RpcHeaderExtFlags.Compressed)
                            {
                                isCompressed = true;
                                byte[] rgbOriAuxOut = new byte[size];
                                Array.Copy(rgbAuxOut, 0, rgbOriAuxOut, 0, size);
                                bool decompressResult = false;
                                try
                                {
                                    rgbAuxOut = Common.DecompressStream(rgbOriAuxOut);
                                    decompressResult = true;
                                }
                                catch (ArgumentException)
                                {
                                    decompressResult = false;
                                }
                                catch (InvalidOperationException)
                                {
                                    decompressResult = false;
                                }

                                // False means current method is EcDoRpcExt2
                                // True means current field is rgbAuxOut
                                this.VerifyCompressionAlgorithm(decompressResult, false, true, flag);
                                this.VerifyDIRECT2EncodingAlgorithm(decompressResult);
                            }

                            // XorMagic(0x00000002) means that the data follows the RPC_HEADER_EXT has been obfuscated.
                            if ((flag & (short)RpcHeaderExtFlags.XorMagic) == (short)RpcHeaderExtFlags.XorMagic)
                            {
                                byte[] rgbXorAuxOut = null;
                                byte[] rgbOriAuxOut = new byte[size];
                                Array.Copy(rgbAuxOut, AdapterHelper.RPCHeaderExtlength, rgbOriAuxOut, 0, size);

                                // Every byte of the data to be obfuscated has XOR applied with the value 0xA5.
                                bool obfuscationResult = false;
                                rgbXorAuxOut = Common.XOR(rgbOriAuxOut);
                                if (rgbXorAuxOut != null)
                                {
                                    obfuscationResult = true;
                                }

                                Array.Copy(rgbXorAuxOut, 0, rgbAuxOut, AdapterHelper.RPCHeaderExtlength, size - AdapterHelper.RPCHeaderExtlength);
                                this.VerifyObfuscationAlgorithm(obfuscationResult, false, true, flag);
                            }

                            if (rgbAuxOut != null)
                            {
                                if (isCompressed)
                                {
                                    Array.Resize<byte>(ref rgbAuxOut, actualSize);
                                }
                                else
                                {
                                    Array.Resize<byte>(ref rgbAuxOut, (int)pcbAuxOut);
                                }
                            }
                            #region Verify RPC header
                            ExtendedBuffer[] buffer = ExtractExtendedBuffer(rgbAuxOut);
                            this.VerifyRPCHeaderExt(flagValues, buffer[0].Header, false, true, true);
                            #endregion
                            List<AUX_SERVER_TOPOLOGY_STRUCTURE> rgbAuxOutValue = this.ParseRgbAuxOut(rgbAuxOut);
                            this.VerifyRgbAuxOutOnEcDoRpcExt2(rgbAuxOutValue);
                        }
                        #endregion

                        #endregion Capture code

                        #endregion
                    }

                    // Verify method EcDoRpcExt2.
                    this.VerifyEcDoRpcExt2(pcxhValue, pcxhInput.ToInt32(), pcbOut, pcbAuxOut, inputPcbOutValue, rgbOut, inputPcbAuxOutValue, rgbAuxOut, returnValue, (uint)pulFlags, flags);
                    #endregion
                }
                while (needRetry && retryCount < maxRetryCount);
                if (needRetry && retryCount == maxRetryCount)
                {
                    Site.Assert.Fail("Server is still busy after retrying {0} times which is defined in the common configuration file and the returned ROP is RopBackOff.", retryCount);
                }
            }
            catch (SEHException e)
            {
                // Uses try...catch... code structure to get the error code of RPC call
                returnValue = NativeMethods.RpcExceptionCode(e);

                Site.Log.Add(LogEntryKind.Comment, "EcDoRpcExt2 throws exception, system error code is {0}, the error message is: {1}", returnValue, (new Win32Exception((int)returnValue)).ToString());
            }
            finally
            {
                Marshal.FreeHGlobal(rgbInPtr);
                Marshal.FreeHGlobal(rgbAuxInPtr);
            }

            return returnValue;
        }
 /// <summary>
 /// The method EcDoRpcExt2 passes generic remote operation (ROP) commands to the server for processing within a Session Context. Each call can contain multiple ROP commands. 
 /// </summary>
 /// <param name="pcxh">The unique value points to a CXH.</param>
 /// <param name="pulFlags">Flags that tell the server how to build the rgbOut parameter.</param>
 /// <param name="rgbIn">This buffer contains the ROP request payload. </param>
 /// <param name="pcbOut">On input, this parameter contains the maximum size of the rgbOut buffer. On output, this parameter contains the size of the ROP response payload.</param>
 /// <param name="rgbAuxIn">This parameter contains an auxiliary payload buffer. </param>
 /// <param name="pcbAuxOut">On input, this parameter contains the maximum length of the rgbAuxOut buffer. On output, this parameter contains the size of the data to be returned in the rgbAuxOut buffer.</param>
 /// <param name="response">The ROP response returned in this RPC method and parsed by adapter.</param>
 /// <param name="responseSOHTable">The Response SOH Table returned by ROP call, provides information like object handle.</param>
 /// <returns>If success, it returns 0, else returns the error code specified in the Open Specification.</returns>
 public uint EcDoRpcExt2(
     ref IntPtr pcxh,
     PulFlags pulFlags, 
     byte[] rgbIn,
     ref uint pcbOut,
     byte[] rgbAuxIn,
     ref uint pcbAuxOut,
     out IDeserializable response,
     ref List<List<uint>> responseSOHTable)
 {
     byte[] rgbOut = new byte[pcbOut];
     byte[] rgbAuxOut = new byte[pcbAuxOut];
     uint payloadCount = 0;
     return this.EcDoRpcExt2(ref pcxh, pulFlags, rgbIn, ref rgbOut, ref pcbOut, rgbAuxIn, ref pcbAuxOut, out response, ref responseSOHTable, out payloadCount, ref rgbAuxOut);
 }