none
MS-RDPRFX C# implementation RRS feed

  • Question

  • I apologize if this is not the right forum for this, but I have looked at all the available forums on msdn and this appears to be the most appropriate.

    Does anyone have a version of this in C# that I can look at? I am trying to implmement this in C# and I am getting stuck with a result that starts out correct, but by the time it is done running - it is much larger than it is supposed to be.

    I am attempting to implement MS-RDPRFX and after coding it out, the results start out correct for a single color plane, but once s equals 0, the code automatically switches from RLGR mode to GR ONLY mode and things start to go wrong. At that point, I start getting results that differ from what I am expecting, according to this example: http://blogs.msdn.com/b/openspecification/archive/2011/04/29/remotefx-rlgr3-decoding.aspx. I also have reviewed and referenced http://www.freerdp.com/api/rfx__decode_8c.html, http://research.microsoft.com/pubs/102069/Malvar_DCC06.pdf , the MS-RDPRFX pdf, and a US Patent filing by Malvar detailing the specifics (including flowcharts) of the RFX encoding. All of these documents are heavy on the encoding instructions and light to non-existent on the decoding instructions. The code that I have implemented is as follows:

    Any Help on this would be very helpful. I am still trying to figure this out, as I am a bit new to the RDP protocol development field - normally work in ASP.NET. Code for what I am working on is below.

    Thanks in advance.

    Code within a button1_Click event in a Form1.cs file that is part of a winforms application:

    // Get bytes according - according to example (http://blogs.msdn.com/b/openspecification/archive/2011/04/29/remotefx-rlgr3-decoding.aspx)
    byte[] bytes = new byte[116];
    bytes[3] = 0x06;
    bytes[2] = 0x20;
    bytes[1] = 0xda;
    bytes[0] = 0x17;

    bytes[7] = 0x42;
    bytes[6] = 0xe8;
    bytes[5] = 0xfa;
    bytes[4] = 0x00;

    bytes[11] = 0x1f;
    bytes[10] = 0xfc;
    bytes[9] = 0x80;
    bytes[8] = 0x64;

    bytes[15] = 0x06;
    bytes[14] = 0x40;
    bytes[13] = 0xc8;
    bytes[12] = 0x32;

    bytes[19] = 0x0c;
    bytes[18] = 0x86;
    bytes[17] = 0x46;
    bytes[16] = 0x4c;

    bytes[23] = 0x99;
    bytes[22] = 0x67;
    bytes[21] = 0xc5;
    bytes[20] = 0xf8;

    bytes[27] = 0xba;
    bytes[26] = 0x5d;
    bytes[25] = 0x2e;
    bytes[24] = 0x96;

    bytes[31] = 0x4b;
    bytes[30] = 0x45;
    bytes[29] = 0x00;
    bytes[28] = 0x00;

    bytes[35] = 0x01;
    bytes[34] = 0xba;
    bytes[33] = 0x44;
    bytes[32] = 0x03;

    bytes[39] = 0x30;
    bytes[38] = 0xe8;
    bytes[37] = 0x80;
    bytes[36] = 0xcc;

    bytes[43] = 0xe8;
    bytes[42] = 0x83;
    bytes[41] = 0x97;
    bytes[40] = 0x80;

    bytes[47] = 0x39;
    bytes[46] = 0x88;
    bytes[45] = 0x02;
    bytes[44] = 0x01;

    bytes[51] = 0x01;
    bytes[50] = 0x04;
    bytes[49] = 0x10;
    bytes[48] = 0x8f;

    bytes[55] = 0x2f;
    bytes[54] = 0x29;
    bytes[53] = 0x1f;
    bytes[52] = 0xff;

    bytes[59] = 0xff;
    bytes[58] = 0xff;
    bytes[57] = 0x9f;
    bytes[56] = 0x73;

    bytes[63] = 0xee;
    bytes[62] = 0x7d;
    bytes[61] = 0xcf;
    bytes[60] = 0xb9;

    bytes[67] = 0xf7;
    bytes[66] = 0x3e;
    bytes[65] = 0x17;
    bytes[64] = 0x80;

    bytes[71] = 0x00;
    bytes[70] = 0x02;
    bytes[69] = 0x00;
    bytes[68] = 0x03;

    bytes[75] = 0x67;
    bytes[74] = 0x8c;
    bytes[73] = 0x7a;
    bytes[72] = 0xa7;

    bytes[79] = 0x0a;
    bytes[78] = 0x91;
    bytes[77] = 0xc0;
    bytes[76] = 0x70;

    bytes[83] = 0x1c;
    bytes[82] = 0x1c;
    bytes[81] = 0x38;
    bytes[80] = 0xe2;

    bytes[87] = 0xff;
    bytes[86] = 0xfc;
    bytes[85] = 0x0f;
    bytes[84] = 0xec;

    bytes[91] = 0xdf;
    bytes[90] = 0x33;
    bytes[89] = 0x7c;
    bytes[88] = 0x4d;

    bytes[95] = 0x89;
    bytes[94] = 0x00;
    bytes[93] = 0x00;
    bytes[92] = 0x2f;

    bytes[99] = 0x07;
    bytes[98] = 0x86;
    bytes[97] = 0x10;
    bytes[96] = 0x96;

    bytes[103] = 0x90;
    bytes[102] = 0x8b;
    bytes[101] = 0xcf;
    bytes[100] = 0xf2;

    bytes[107] = 0x8f;
    bytes[106] = 0xa1;
    bytes[105] = 0x64;
    bytes[104] = 0xb8;

    bytes[111] = 0xc7;
    bytes[110] = 0x81;
    bytes[109] = 0x00;
    bytes[108] = 0x8c;

    bytes[115] = 0x30;
    bytes[114] = 0x03;
    bytes[113] = 0x10;
    bytes[112] = 0x00;

    List<int> values = Methods.Decode(bytes);


    // End of Form1.cs file

    Additional code in next post

    Sunday, July 29, 2012 2:55 PM

Answers

All replies

  • The following code is within a class file called Methods.cs

            public static List<int> Decode(byte[] bytes)
            {
                // Container for output byte array
                //StringBuilder output = new StringBuilder();
                List<int> output = new List<int>();

                BitArray bitArray = new BitArray(bytes);

                StringBuilder bitArrayString = new StringBuilder();
                foreach (bool b in bitArray)
                {
                    if (b)
                    {
                        bitArrayString.Append("1");
                    }
                    else
                    {
                        bitArrayString.Append("0");
                    }
                }

                BitArray bitArrayReverse = Reverse(bitArray);

                StringBuilder bitArrayReverseString = new StringBuilder();
                foreach (bool b in bitArrayReverse)
                {
                    if (b)
                    {
                        bitArrayReverseString.Append("1");
                    }
                    else
                    {
                        bitArrayReverseString.Append("0");
                    }
                }

                string binaryArrayToString = bitArray.ToString();

                // Create constants - these can be tweaked to find a more efficient decoding process
                const int A1 = 3; // (s = 0): GR mode // |x| = 0: S = S + A1
                const int B1 = 3; // (s = 0): GR mode // |x| > 0: S = S - B1
    const int A2 = 4; // (s > 0): RLGR mode // Run of r = 2^s values of x = 0: S = S + A2
                const int B2 = 6; // (s > 0): RLGR mode // Run of r < 2^s values of x = 0, followed by a value x <> 0: S = S + B2
                const int B3 = 2;
                const int LBitShift = 3;
                const int L = 16;

                // Initialize parameters - Fig 11/1115
                int s = 1;      // Encoding mode parameter
                int S = s << LBitShift; // Scaled Encoding parameter
                int k = 1;      // G/R parameter
                int K = k << LBitShift; // Scaled G/R parameter

                // Initialize counter and run variables
                int n = 0;
                uint mask = 0x80000000;

                // Get number of bytes in array
                int numBytes = bytes.Length;

                // Byte index
                int i = 0;

                while(i < numBytes)
                {
                    // Read input - Fig 11/1110
                    uint binary = BitConverter.ToUInt32(bytes, i);

                    while (n < 32)
                    {
                        // GR Only mode
                        if (s == 0) // Fig 11/1125
                        {
                            List<int> ones = new List<int>();
                            int remainder = 0;
                            int u = 0;
                            int q = 0;

                            // Determine number of 1's before we hit another 0
                            while ((binary & mask) != 0)
                            {
                                ones.Add(1);
                                UpdateIndexers(ref binary, ref i, ref n, ref mask, bytes);
                            }

                            if (ones.Count > 0)
                            {
                                // numberOfOnes = u/2^k
                                u = ones.Count * Math2Pow(k);

                                // Determine the value of q; used for parameter adaptation
                                q = u >> k;

                                // Increment n to move past the 0 used as a delimiter for the string of ones
                                UpdateIndexers(ref binary, ref i, ref n, ref mask, bytes);

                                StringBuilder binaryRemainder = new StringBuilder();

                                // Get the remainder - k bits represent the remainder
                                for (int p = 0; p < k; p++)
                                {
                                    if ((binary & mask) != 0)
                                    {
                                        binaryRemainder.Append(1);
                                    }
                                    else
                                    {
                                        binaryRemainder.Append(0);
                                    }

                                    UpdateIndexers(ref binary, ref i, ref n, ref mask, bytes);
                                }

                                // Convert binary to integer
                                remainder = Convert.ToInt32(binaryRemainder.ToString(), 2);

                                // Get least number of bits to represent u
                                double leastNumBits = Math.Log((double)u, (double)2);
                                int intLeastNumBits = (int)leastNumBits;

                                if (leastNumBits > intLeastNumBits)
                                {
                                    intLeastNumBits++;
                                }

                                StringBuilder bits = new StringBuilder();
                                for (int z = 0; z < intLeastNumBits; z++)
                                {
                                    if ((binary & mask) != 0)
                                    {
                                        bits.Append(1);
                                    }
                                    else
                                    {
                                        bits.Append(0);
                                    }

                                    UpdateIndexers(ref binary, ref i, ref n, ref mask, bytes);
                                }

                                int firstCoe = Convert.ToInt32(bits.ToString(), 2);

                                int secondCoe = u - firstCoe;

                                // Calculate output coefficients (-2y-1=x)
                                // where x is the initial Coe and y is the output Coe
                                int firstCoeOutput = firstCoe + 1; // Add one to firstCoe to make equation (-2y-1+1 = x+1) = (-2y = x+1)
                                firstCoeOutput = firstCoeOutput / 2; // Divide both sides by 2 (-y = (x+1)/2)
                                output.Add(-firstCoeOutput); // Negate both side to get answer ( y = -((x+1)/2) )

                                // Repeat for the secondCoe
                                // Calculate output coefficients (-2y-1=x)
                                // where x is the initial Coe and y is the output Coe
                                int secondCoeOutput = secondCoe + 1; // Add one to secondCoe to get equation (-2y-1+1 = x+1) = (-2y = x+1)
                                secondCoeOutput = secondCoeOutput / 2; // Divide both sides by 2 (-y = (x+1)/2)
                                output.Add(-secondCoeOutput); // Negate both side to get answer ( y = -((x+1)/2) )

                                K = K + firstCoe;
                                k = K >> LBitShift;

                                if (Math.Abs(firstCoeOutput) > 0)
                                {
                                    S -= B1;

                                    if (S < 0)
                                    {
                                        // S cannot go lower than 0, correct if it occurs
                                        S = 0;
                                    }
                                }
                            }
                            else
                            {
                                S += A1;

                                if (S > 80)
                                {
                                    S = 80;
                                }
                            }

                            if (q == 1)
                            {
                                // Do Nothing - parameters remain constant
                            }
                            else
                            {
                                if (q != 0)
                                {
                                    K -= B3;
                                }

                                k = K >> LBitShift;
                            }

                            s = S >> LBitShift;
                        }
                        // RLGR Mode
                        else // s > 0 // Fig 11/1125
                        {
                            // Read next bit from array
                            bool nextBitValue = (binary & mask) != 0; //binaryVector[31 - n++/* Fig11/1140 */]; // increment index while indexing
                            UpdateIndexers(ref binary, ref i, ref n, ref mask, bytes);
                            //mask >>= 1;
                            //n++;

                            // Fig 11/1130
                            if (nextBitValue) // nextBitValue = 1 // GR mode // 1 + bin(m, k) + GR(u, kR)
                            {
                                StringBuilder binaryRepresentationOfR = new StringBuilder();

                                // Build binary representation of r, which indicates the length of the incomplete code
                                for (int m = 0; m < (int)Math.Abs((decimal)s); m++)
                                {
                                    bool nextBit = (binary & mask) != 0;
                                    UpdateIndexers(ref binary, ref i, ref n, ref mask, bytes);

                                    if (nextBit)
                                    {
                                        binaryRepresentationOfR.Append(1);
                                    }
                                    else
                                    {
                                        binaryRepresentationOfR.Append(0);
                                    }
                                }

                                // Convert binary to integer
                                int lengthOfIncompleteCodeword = Convert.ToInt32(binaryRepresentationOfR.ToString(), 2);

                                // Output values to output buffer
                                for (int p = 0; p < lengthOfIncompleteCodeword; p++)
                                {
                                    output.Add(0);
                                }

                                // Read next bit from array
                                bool grSignBitValue = (binary & mask) != 0;
                                UpdateIndexers(ref binary, ref i, ref n, ref mask, bytes);

                                List<int> ones = new List<int>();

                                // Determine number of 1's before we hit another 0
                                while ((binary & mask) != 0)
                                {
                                    ones.Add(1);
                                    UpdateIndexers(ref binary, ref i, ref n, ref mask, bytes);
                                }

                                // Determine the value of u if (numberOfOnes = u/2^k)
                                int u = ones.Count * Math2Pow(k);

                                // Increment n to move past the 0 used as a delimiter for the string of ones
                                UpdateIndexers(ref binary, ref i, ref n, ref mask, bytes);

                                StringBuilder binaryRemainder = new StringBuilder();

                                // Get the remainder - k bits represent the remainder
                                for (int p = 0; p < k; p++)
                                {
                                    bool nextBit = (binary & mask) != 0;
                                    UpdateIndexers(ref binary, ref i, ref n, ref mask, bytes);

                                    if (nextBit)
                                    {
                                        binaryRemainder.Append(1);
                                    }
                                    else
                                    {
                                        binaryRemainder.Append(0);
                                    }
                                }

                                // Convert binary to integer
                                int remainder = Convert.ToInt32(binaryRemainder.ToString(), 2);

                                // Magnitude - 1 = u
                                int magnitude = u + remainder + 1;

                                if (grSignBitValue) // grSignBitValue = 1 / negative sign
                                {
                                    magnitude = -magnitude;
                                }
                                else // grSignBitValue = 0 / positive sign
                                {
                                    // do nothing, value is already positive
                                }

                                output.Add(magnitude);

                                // Decrement Encoding Mode Parameter
                                S -= B2;
                                if (S > 80)
                                {
                                    S = 80;
                                }
                            }
                            // Fig 11/1135
                            else // firstBitValue = 0 // RL mode
                            {
                                int calculatedValue = Math2Pow(s);
                                // Output values to output buffer
                                for (int j = 0; j < calculatedValue; j++)
                                {
                                    output.Add(0);
                                }

                                // Increment Encoding Mode Parameter
                                S += A2;
                                if (S > 80)
                                {
                                    S = 80;
                                }
                            }
                           
                            s = S >> LBitShift;
                        }
                    }
                }

                return output;
            }

            public static int Math2Pow(int value)
            {
                return (int)Math.Pow(2.0, (double)value);
            }

    public static void UpdateIndexers(ref uint binary, ref int byteIndex, ref int bitIndex, ref uint mask, byte[] bytes)
    {
         // Update indexers
         bitIndex++;
         mask >>= 1;

         if (bitIndex == 32 && (byteIndex + 4) < bytes.Length)
         {
      byteIndex += 4;
                    binary = BitConverter.ToUInt32(bytes, byteIndex);
                    bitIndex = 0;
                    mask = 0x80000000;

                    if (byteIndex > 20)
                    {
                        string empty = string.Empty;
                    }
                }
            }

    // End of Methods class file

    Sunday, July 29, 2012 2:59 PM
  • Hi David:

    I have alerted open specifications team regarding your inquiry. A member of the team will be in touch soon.


    Regards, Obaid Farooqi

    Sunday, July 29, 2012 5:55 PM
    Owner
  • Hi David:

    I have alerted open specifications team regarding your inquiry. A member of the team will be in touch soon.


    Regards, Obaid Farooqi

    Sunday, July 29, 2012 5:56 PM
    Owner
  • David,

    Regarding “Does anyone have a version of this in C# that I can look at?”, the answer is no.  There is RDP Client Reference Source Code available per http://blogs.msdn.com/b/rds/archive/2010/08/16/rdp-client-reference-source-code-now-available-to-rdp-licensees.aspx, however, that is in C++.  If you want to pursue that option, please contact “iplicreq (at) Microsoft (dot) com”, as specified in that blog.

    We would not be in a position to review or debug your code.  However, if there is a specific operation in the specification you believe is unclear or you believe may be contributing to the issue you are having, we may be able to help with that.  For instance, since you are using Obaid’s encoded input (“0x06, 0x20, 0xda, 0x17…”) from his blog at http://blogs.msdn.com/b/openspecification/archive/2011/04/29/remotefx-rlgr3-decoding.aspx, is there a step where his result diverges from yours?  That may help you zero in on a specific section of [MS-RDPRFX] or [ARLGR].


    Bryan S. Burgin Senior Escalation Engineer Microsoft Protocol Open Specifications Team

    Tuesday, July 31, 2012 5:43 PM
    Moderator
  • Yes. That is essentially what I was driving at with my original question. Once the decoder switches from RLGR mode to GR-Only mode I start running into issues.

    His example is a good one, but the decoding instructions for the GR-Only mode are a bit vague. I am not sure if it is an issue with the parameter adaptation or what, but basically my output vector contains 56596 elements when implemented with the following values: A1 = 3; B1 = 3; A2 = 4; B2 = 6; B3 = 3; When I modify those parameters, I am getting fluctuations in the size of the output vector, but only a few other sizes: 58705, 57683, and 58708 - which are obviously not anywhere close to the expected size of 4096. This is where I could use some help if possible.

    AS a side note - The reason I posted the code is simply because I am usually asked for it at some point and I thought it might speed things along. I wasn't really expecting for you to debug it, more for reference. 

    Thanks for the reference to the RDP Client License. I will look into it to see if it can be used to shed some light on my issue. Looking forward to your answer as well.

    Tuesday, July 31, 2012 6:51 PM
  • Can you contact me off-line by e-mailing "dochelp (at) microsoft (dot) com"?


    Bryan S. Burgin Senior Escalation Engineer Microsoft Protocol Open Specifications Team

    Tuesday, July 31, 2012 10:17 PM
    Moderator