Saturday, April 11, 2009

Code Snippets

back to top
A simple example code on connecting to RDP hosts desktop
Well the basic thing you need to know for you to connect on your server is ..

Connection Settings
// for example, I have my AxMsRdpClient control named rdpClient.
rdpClient.Server = "sever name here";
rdpClient.UserName = "your username on remote pc";
rdpClient.AdvancedSettings2.ClearTextPassword = "you password on remote pc";
// optional
rdpClient.ColorDepth = 16; // int value can be 8, 15, 16, or 24

rdpClient.DesktopWidth = 1024; // int value
rdpClient.DesktopHeight = 768; // int value
rdpClient.FullScreen = true|false; // boolean value that can be True or False
// and connect
rdpClient.Connect();

Going Fullscreen in runtime? Fairly Easy!
// just set the Fullsceen property to TRUE
rdpClient.Fullscreen = true;

// strecth the screen
rdpClient.AdvanceSettings3.SmartSizing = true;

Points of interest

I implemented a Reconnect feature. But its not easy as calling Disconnect() and Connect().
You should wait until it properly disconnected and call Connect().

How to do is? Simple, in AxMsRdpClient, there's a Connect property which acts as a Connection Status:

  • 1 = Connected
  • 0 = Disconnected

Now reconnecting can be done by doing this:
// call Disconnect() method on AxMsRdpClient
// wait for the server to properly disconnect
while (rdpClient.Connected != 0)
{
System.Threading.Thread.Sleep(1000);
Application.DoEvents();
}

// call Connect() method on AxMsRdpClient


RDPFile Reader version 1.0 class - Which is used to read RDP files
/*
Author: Jayson Ragasa | aka: Nullstring
Application Developer - Anomalist Designs LLC
* ---
* RDPFileReader 1.0
*
* RDP File Settings - http://dev.remotenetworktechnology.com/ts/rdpfile.htm
* Terminal Services Team Blog - http://blogs.msdn.com/ts/archive/2008/09/02/specifying-the-ts-client-start-location-on-the-virtual-desktop.aspx
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;

namespace RDPFileReader
{
public class RDPFile
{
#region enum

public enum KeyboardHooks
{
ON_THE_LOCAL_COMPUTER = 0,
ON_THE_REMOTE_COMPUTER = 1,
IN_FULL_SCREEN_MODE_ONLY = 2
};

public enum AudioModes
{
BRING_TO_THIS_COMPUTER = 0,
DO_NOT_PLAY = 1,
LeAVE_AT_REMOTE_COMOPUTER = 2
};

public enum WindowState : int
{
NORMAL = 1,
MAXMIZE = 3
}

public enum SessionBPPs
{
BPP_8 = 8,
BPP_15 = 15,
BPP_16 = 16,
BPP_24 = 24

}

#endregion

#region structs

public struct RECT
{
public int Top;
public int Left;
public int Width;
public int Height;
}

public struct WindowsPosition
{
public WindowState WinState;
public RECT Rect;
}

#endregion

#region variables

private string _filename = string.Empty;

#region RDP template
string[] _rdpTemplate = {
"screen mode id:i:{0}",
"desktopwidth:i:{1}",
"desktopheight:i:{2}",
"session bpp:i:{3}",
"winposstr:s:0,{4},{5},{6},{7},{8}",
"full address:s:{9}",
"compression:i:{10}",
"keyboardhook:i:{11}",
"audiomode:i:{12}",
"redirectdrives:i:{13}",
"redirectprinters:i:{14}",
"redirectcomports:i:{15}",
"redirectsmartcards:i:{16}",
"displayconnectionbar:i:{17}",
"autoreconnection enabled:i:{18}",
"username:s:{19}",
"domain:s:{20}",
"alternate shell:s:{21}",
"shell working directory:s:{22}",
"password 51:b:{23}",
"disable wallpaper:i:{24}",
"disable full window drag:i:{25}",
"disable menu anims:i:{26}",
"disable themes:i:{27}",
"disable cursor setting:i:{28}",
"bitmapcachepersistenable:i:{29}"
};
#endregion

#region member fields

int _screenMode = 0;
int _desktopWidth = 0;
int _desktopHeight = 0;
SessionBPPs _sessionBPP = 0;
WindowsPosition _winPosStr;
string _fullAddress = string.Empty;
int _compression = 0;
KeyboardHooks _keyboardHook = 0;
AudioModes _audiomode = 0;
int _redirectDrives = 0;
int _redirectPrinters = 0;
int _redirectComPorts = 0;
int _redirectSmartCards = 0;
int _displayConnectionBar = 0;
int _autoReconnectionEnabled = 0;
string _username = string.Empty;
string _domain = string.Empty;
string _alternateShell = string.Empty;
string _shellWorkingDirectory = string.Empty;
string _password = string.Empty;
int _disableWallpaper = 0;
int _disableFullWindowDrag = 0;
int _disableMenuAnims = 0;
int _disableThemes = 0;
int _disableCursorSettings = 0;
int _bitmapCachePersistEnable = 0;

#endregion

#endregion

#region properties

public int ScreenMode
{
get
{
return this._screenMode;
}
set
{
this._screenMode = value;
}
}

public int DesktopWidth
{
get
{
return this._desktopWidth;
}
set
{
this._desktopWidth = value;
}
}

public int DesktopHeight
{
get
{
return this._desktopHeight;
}
set
{
this._desktopHeight = value;
}
}

public SessionBPPs SessionBPP
{
get
{
return this._sessionBPP;
}
set
{
this._sessionBPP = value;
}
}

public WindowsPosition WinPosStr
{
get
{
return this._winPosStr;
}
set
{
this._winPosStr = value;
}
}

public string FullAddress
{
get
{
return this._fullAddress;
}
set
{
this._fullAddress = value;
}
}

public int Compression
{
get
{
return this._compression;
}
set
{
this._compression = value;
}
}

public KeyboardHooks KeyboardHook
{
get
{
return this._keyboardHook;
}
set
{
this._keyboardHook = value;
}
}

public AudioModes AudioMode
{
get
{
return this._audiomode;
}
set
{
this._audiomode = value;
}
}

public int RedirectDrives
{
get
{
return this._redirectDrives;
}
set
{
this._redirectDrives = value;
}
}

public int RedirectPrinters
{
get
{
return this._redirectPrinters;
}
set
{
this._redirectPrinters = value;
}
}

public int RedirectComPorts
{
get
{
return this._redirectComPorts;
}
set
{
this._redirectComPorts = value;
}
}

public int RedirectSmartCards
{
get
{
return this._redirectSmartCards;
}
set
{
this._redirectSmartCards = value;
}
}

public int DisplayConnectionBar
{
get
{
return this._displayConnectionBar;
}
set
{
this._displayConnectionBar = value;
}
}

public int AutoReconnectionEnabled
{
get
{
return this._autoReconnectionEnabled;
}
set
{
this._autoReconnectionEnabled = value;
}
}

public string Username
{
get
{
return this._username;
}
set
{
this._username = value;
}
}

public string Domain
{
get
{
return this._domain;
}
set
{
this._domain = value;
}
}

public string AlternateShell
{
get
{
return this._alternateShell;
}
set
{
this._alternateShell = value;
}
}

public string ShellWorkingDirectory
{
get
{
return this._shellWorkingDirectory;
}
set
{
this._shellWorkingDirectory = value;
}
}

public string Password
{
get
{
return this._password;
}
set
{
this._password = value;
}
}

public int DisableWallpaper
{
get
{
return this._disableWallpaper;
}
set
{
this._disableWallpaper = value;
}
}

public int DisableFullWindowDrag
{
get
{
return this._disableFullWindowDrag;
}
set
{
this._disableFullWindowDrag = value;
}
}

public int DisableMenuAnims
{
get
{
return this._disableMenuAnims;
}
set
{
this._disableMenuAnims = value;
}
}

public int DisableThemes
{
get
{
return this._disableThemes;
}
set
{
this._disableThemes = value;
}
}

public int DisableCursorSettings
{
get
{
return this._disableCursorSettings;
}
set
{
this._displayConnectionBar = value;
}
}

public int BitmapCachePersistEnable
{
get
{
return this._bitmapCachePersistEnable;
}
set
{
this._bitmapCachePersistEnable = value;
}
}

#endregion

#region methods

public void Read(string filepath)
{
this._filename = filepath;

string data = string.Empty;

using (StreamReader reader = new StreamReader(filepath))
{
data = reader.ReadToEnd();
}

string[] settings = data.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);

foreach (string thisSetting in settings)
{
string regex = "(?<type>.*)\\:(?<dtype>\\w)\\:(?<value>.*)";

RegexOptions options = ((RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline) | RegexOptions.IgnoreCase);
Regex reg = new Regex(regex, options);

if (reg.IsMatch(thisSetting))
{
Match m = reg.Match(thisSetting);

string v = m.Groups["value"].Value;

switch (m.Groups["type"].Value)
{
case "screen mode id":
this._screenMode = int.Parse(v);
break;

case "desktopwidth":
this._desktopWidth = int.Parse(v);
break;

case "desktopheight":
this._desktopHeight = int.Parse(v);
break;

case "session bpp":
this._sessionBPP = (SessionBPPs)int.Parse(v);
break;

case "winposstr":
string[] vals = v.Split(',');

this._winPosStr.WinState = (WindowState)int.Parse(vals[1]);

this._winPosStr.Rect.Top = int.Parse(vals[2]);
this._winPosStr.Rect.Left = int.Parse(vals[3]);
this._winPosStr.Rect.Width = int.Parse(vals[4]);
this._winPosStr.Rect.Height = int.Parse(vals[5]);

break;

case "full address":
this._fullAddress = v;
break;

case "compression":
this._compression = int.Parse(v);
break;

case "keyboardhook":
this._keyboardHook = (KeyboardHooks)int.Parse(v);
break;

case "audiomode":
this._audiomode = (AudioModes)int.Parse(v);
break;

case "redirectdrives":
this._redirectDrives = int.Parse(v);
break;

case "redirectprinters":
this._redirectPrinters = int.Parse(v);
break;

case "redirectcomports":
this._redirectComPorts = int.Parse(v);
break;

case "redirectsmartcards":
this._redirectSmartCards = int.Parse(v);
break;

case "displayconnectionbar":
this._displayConnectionBar = int.Parse(v);
break;

case "autoreconnection enabled":
this._autoReconnectionEnabled = int.Parse(v);
break;

case "username":
this._username = v;
break;

case "domain":
this._domain = v;
break;

case "alternate shell":
this._alternateShell = v;
break;

case "shell working directory":
this._shellWorkingDirectory = v;
break;

case "password 51":
this._password = v;
break;

case "disable wallpaper":
this._disableWallpaper = int.Parse(v);
break;

case "disable full window drag":
this._disableFullWindowDrag = int.Parse(v);
break;

case "disable menu anims":
this._disableMenuAnims = int.Parse(v);
break;

case "disable themes":
this._disableThemes = int.Parse(v);
break;

case "disable cursor setting":
this._disableCursorSettings = int.Parse(v);
break;

case "bitmapcachepersistenable":
this._bitmapCachePersistEnable = int.Parse(v);
break;
}
}
}
}

public void Update()
{
Save(this._filename);
}

public void Save(string filepath)
{
this._filename = filepath;

string template = string.Empty;

foreach (string temp in this._rdpTemplate)
{
template += temp + "\r\n";
}

string data = string.Format(template,
this._screenMode,
this._desktopWidth,
this._desktopHeight,
(int)this._sessionBPP,
(int)this._winPosStr.WinState, this._winPosStr.Rect.Top, this._winPosStr.Rect.Left, this._winPosStr.Rect.Width, this._winPosStr.Rect.Height,
this._fullAddress,
this._compression,
(int)this._keyboardHook,
(int)this._audiomode,
this._redirectDrives,
this._redirectPrinters,
this._redirectComPorts,
this._redirectSmartCards,
this._displayConnectionBar,
this._autoReconnectionEnabled,
this._username,
this._domain,
this._alternateShell,
this._shellWorkingDirectory,
this._password,
this._disableWallpaper,
this._disableFullWindowDrag,
this._disableMenuAnims,
this._disableThemes,
this._disableCursorSettings,
this._bitmapCachePersistEnable
);

using (StreamWriter writer = new StreamWriter(filepath))
{
writer.Write(data);
}
}

#endregion
}
}

We also have DataProtection class which was provided by Microsoft and a little bit of modification and some methods to implement such as converting Byte[] < to > Hex blob. We could create a MSTSC valid password.

Thanks to
Building Secure ASP.NET Applications: Authentication, Authorization, and Secure Communication
http://msdn.microsoft.com/en-us/library/aa302402.aspx#secnetht07_topic4
- Implementing CryptProtectData and CryptUnprotectData from Crypt32.DLL


Remko Weijnen - "psw" descriptor on CryptProtectData
http://www.remkoweijnen.nl/blog/2007/10/18/how-rdp-passwords-are-encrypted/#comment-900

Now I have created a wrapper called DataProtectionForRDPWrapper to easily Encrypt and Decrypt RDP password CREATED in DataProtection class. Why did I emphasize the "created" word. Some limitation on DataProtection class is, we can't decrypt the password created by MSTSC and still under research.

Here's the DataProtectionForRDPWrapper wrapper:
/*
Author: Jayson Ragasa | aka: Nullstring
Application Developer - Anomalist Designs LLC
*
* --
* Made a wrapper for DataProtector so I could
* Encrypt/Decrypt valid password for RDP
*
* TAKE NOTE:
* This can't Decrypt MSTSC Password!
*/
using System;
using System.Collections.Generic;
using System.Text;

namespace DataProtection
{
public class DataProtectionForRDPWrapper
{
static DataProtection.DataProtector dp = new DataProtector(DataProtector.Store.USE_USER_STORE);

public static string Encrypt(string text_password)
{
byte[] e = dp.Encrypt(GetBytes(text_password), null, "psw");
return GetHex(e);
}

public static string Decrypt(string enc_password)
{
byte[] b = ToByteArray(enc_password);
byte[] d = dp.Decrypt(b, null, "psw");
return GetString(d);
}

static byte[] GetBytes(string text)
{
return UnicodeEncoding.Unicode.GetBytes(text);
}

static string GetString(byte[] byt)
{
System.Text.Encoding enc = System.Text.Encoding.Unicode;
return enc.GetString(byt);
}

static string GetHex(byte[] byt_text)
{
string ret = string.Empty;

for (int i = 0; i < byt_text.Length; i++)
{
ret += Convert.ToString(byt_text[i], 16).PadLeft(2, '0').ToUpper();
}

return ret;
}

static byte[] ToByteArray(String HexString)
{
try
{
int NumberChars = HexString.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
{
bytes[i / 2] = Convert.ToByte(HexString.Substring(i, 2), 16);
}
return bytes;
}
catch (Exception ex)
{
// this occures everytime we decrypt MSTSC generated password.
// so let's just throw an exception for now
throw new Exception("Problem converting Hex to Bytes", ex);
}
}
}
}


Creating a new RDP File is simply done by doing this.

// RDP Key descriptions found at
// http://dev.remotenetworktechnology.com/ts/rdpfile.htm

RDPFile rdp =
new RDPFile();

// we wan't the window to be on a Maximize state
// 1 - windowed
// 2 - fullscreen
rdp.ScreenMode = 1;

// remote desktop resolution
rdp.DesktopWidth = 1024;
rdp.DesktopHeight = 768;

/* remote desktop color depth
public enum SessionBPPs
{
BPP_8 = 8,
BPP_15 = 15,
BPP_16 = 16,
BPP_24 = 24
}
*/
rdp.SessionBPP = SessionBPPs.BPP_16

/* how the window will look?
Terminal Services Team Blog explained the "winposstr" key!
The location on the virtual desktop where the TS Client initially positions itself can be controlled via the winposstr setting in the RDP file
winposstr:s:0,ShowCmd,Left,Top,Right,Bottom
*/
RDPFile.WindowsPosition winpos =
new RDPFile.WindowsPosition();
RDPFile.RECT r =
new RDPFile.RECT();
r.Top = 0;
r.Left = 0;
r.Width = ss.DesktopWidth;
r.Height = ss.DesktopHeight;
winpos.Rect = r;
/* this is equal to ShowCmd from Terminal Services Team Blog
public enum WindowState : int
{
NORMAL = 1,
MAXMIZE = 3
}
*/
winpos.WinState = RDPFile.WindowState.MAXMIZE;

rdp.WinPosStr = winpos;
// set all our winposstr from the obove configuration
rdp.FullAddress =
"192.168.1.1" // your server name or ip address;
rdp.Compression = 1;
// RemoteNetworkTechnology didn't fully explanied this but looks like this is needed for faster data transfer

/* For applying standard key combinations
public enum KeyboardHooks
{
ON_THE_LOCAL_COMPUTER = 0,
ON_THE_REMOTE_COMPUTER = 1,
IN_FULL_SCREEN_MODE_ONLY = 2
};
*/
rdp.KeyboardHook = RDPFile.KeyboardHooks.ON_THE_REMOTE_COMPUTER;

/* How will the audio from the remote pc be played
public enum AudioModes
{
BRING_TO_THIS_COMPUTER = 0,
DO_NOT_PLAY = 1,
LeAVE_AT_REMOTE_COMOPUTER = 2
};
*/
rdp.AudioMode = RDPFile.AudioModes.BRING_TO_THIS_COMPUTER;

rdp.RedirectDrives = 0;
// should we share our local drives in the remote pc?
rdp.RedirectPrinters = 0;
// should we share our printers in the remote pc?
rdp.RedirectComPorts = 0;
// should we share our com ports in the remoe pc?
rdp.RedirectSmartCards = 0;
// should we share our smart cards in the remote pc?
rdp.DisplayConnectionBar = 1;
// will the Connection bar visible when in Fullscreen mode?
rdp.AutoReconnectionEnabled = 1;
// do we need to automatically connect?
rdp.Username =
"Admin"; // remote pc Username
rdp.Domain =
"DomainName"; // remote pc Domain
rdp.AlternateShell =
string.Empty; // are we going to use different shell other than C:\Windows\Explorer.exe?
rdp.ShellWorkingDirectory =
string.Empty; // Working directory if an alternate shell was specified.

// here's the password implementing our DataProtection and the wrapper
rdp.Password = (ss.Password ==
string.Empty ? string.Empty : DataProtectionForRDPWrapper.Encrypt(ss.Password));

rdp.DisableWallpaper = 1;
// should we disable wallpaper in the remote pc?
rdp.DisableFullWindowDrag = 1;
// should we disable the full window drag in the remote pc and just show the box while dragging?
rdp.DisableMenuAnims = 1;
// should we disable animations?
rdp.DisableThemes = 1;
// should we disable Windows Visual Themes?
rdp.DisableCursorSettings = 1;
// should we disable mouse cursor effects?
rdp.BitmapCachePersistEnable = 1;
// This setting determines whether bitmaps are cached on the local computer

#region try exporting the file
{
try
{
rdp.Save(
@"D:\My Documents\MyRDPConnection.RDP");
}
catch (Exception ex)
{
MessageBox.Show(
"An error occured while saving the configuration for '" + rdp.FullAddress + "'.\r\n\r\nError Message: " + ex.Message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
System.Diagnostics.Debug.WriteLine(ex.Message +
"\r\n" + ex.StackTrace);

continue;
}
}
#endregion


Reading RDP Files

string thisFile = @"D:\My Documents\MyRDPConnection.RDP";

#region Read RDP File

RDPFile rdpfile;
{
try
{
rdpfile =
new RDPFile();
rdpfile.Read(thisFile);
}
catch (Exception ex)
{
MessageBox.Show(
"An error occured while reading '" + Path.GetFileName(thisFile) + "' and it will be skipped.\r\n\r\nError Message: " + ex.Message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
System.Diagnostics.Debug.WriteLine(ex.Message +
"\r\n" + ex.StackTrace);
}
}

#endregion

Console.Writeline(
"RDP Username: " + rdpfile.Username);

#region Try decrypting the password from RDP file
{
try
{
System.Diagnostics.Debug.WriteLine(
"reading password " + thisFile);
Console.Writeline(
"RDP Password: " + DataProtectionForRDPWrapper.Decrypt(rdpfile.Password));
System.Diagnostics.Debug.WriteLine(
"reading password done");
}
catch (Exception Ex)
{
ss.Password =
string.Empty;

if (Ex.Message == "Problem converting Hex to Bytes")
{
MessageBox.Show(
"This RDP File '" + Path.GetFileNameWithoutExtension(thisFile) + "' contains a secured password which is currently unsported by this application.\r\nThe importing can still continue but without the password.\r\nYou can edit the password later by selecting a server in 'All Listed Servers' and click 'Edit Settings' button on the toolbar", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
}
else if (Ex.Message.Contains("Exception decrypting"))
{
MessageBox.Show(
"Failed to decrypt the password from '" + Path.GetFileNameWithoutExtension(thisFile) + "'", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
MessageBox.Show(
"An unknown error occured while decrypting the password from '" + Path.GetFileNameWithoutExtension(thisFile) + "'", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
#endregion

Console.Writeline(
"RDP Desktop Width: " + rdpfile.DesktopWidth);
Console.Writeline(
"RDP Desktop Height: " + rdpfile.DesktopHeight);


Updating an RDP File

//RDP File Reader test
RDPFileReader.RDPFile rdp =
new RDPFileReader.RDPFile();
rdp.Read(
@"D:\My Documents\RDP\Application Server (1120x700-16bitc).rdp");

Console.WriteLine(rdp.WinPosStr.WinState);

// set new Window State
// make window mode maxmize
RDPFileReader.RDP.WindowsPosition wpos =
new RDPFileReader.RDP.WindowsPosition();
wpos.Rect = rdp.WinPosStr.Rect;
wpos.WinState = RDPFileReader.RDP.WindowState.MAXMIZE;
// change the window state when in window mode.

rdp.WinPosStr = wpos;
rdp.SessionBPP = RDPFileReader.RDP.SessionBPPs.BPP_8;
// change the color depth

// and call Update
rdp.Update();

No comments:

Post a Comment