MS-RDPRFX C# implementation
-
Sunday, July 29, 2012 2:55 PM
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 fileAdditional code in next post
All Replies
-
Sunday, July 29, 2012 2:59 PM
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 5:55 PMModerator
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 PMModerator
Hi David:
I have alerted open specifications team regarding your inquiry. A member of the team will be in touch soon.
Regards, Obaid Farooqi
-
Tuesday, July 31, 2012 5:43 PMModerator
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
- Proposed As Answer by Bryan S. BurginMicrosoft Employee, Moderator Tuesday, July 31, 2012 5:43 PM
-
Tuesday, July 31, 2012 6:51 PM
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 10:17 PMModerator
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

