

Introduction
Windows Vista introduced a powerful Remote Desktop Management API which is relatively unknown. This article will present an easy way to use it, in order to provide professional level help in your Vista applications.
A generic implementaton of this technique is presented in this article and is featured in my "Turbo Remote" experimental application.
Requirements
- Windows Vista or later (if you find any way to run it on XP, tell me!). Admin rights are NOT required.
- Knowledge of COM basics and ActiveX controls.
API Reference
The API reference is provided from Microsoft here.
Contents
- RDP.H / RDP.CPP code for server/client implementation
- Helpers AX.CPP and AX.H, my AX control which describes how to use an ActiveX control)
- Test projects to demonstrate the functionality
- Solutions / VCPROJ files for all the stuff
Features
- Easy API
- Allows both direct and reverse connections
- Easy manipulation of the
IRDP*
classes
- Works in x86/x64
Classes
RAS:: BASERDP
, base for SERVER and CLIENT
RAS :: SERVER
, implements a server
RAS :: CLIENT
, implements a client
RAS :: ATTENDEE
, implements an attendee
RAS :: CHANNEL
, implements a virtual channel
RAS :: S_INVITATION
, implements an invitation
BaseRDP
BaseRDP
is a class that has common code for both server and client, like channels, data transmission, events, etc. Some useful member functions are here:
class BASERDP
{
...
public:
void SetWindowNotification(HWND,UINT);
void SetDataNotification(void (*)(CHANNEL*,ATTENDEE*,char*,int,void*),void*);
void AssignChannelToAttendees(CHANNEL* c,vector<ATTENDEE*>& AssignAtts);
virtual CHANNEL* CreateVirtualChannel(const wchar_t* n,bool Compressed,int Priority);
virtual HRESULT SendData(CHANNEL* C,char* d,int sz,
bool Unc = false,vector<ATTENDEE*>* List = 0);
virtual void OnReceiveData(CHANNEL* C,ATTENDEE* A,char* d,int sz);
vector<CHANNEL>& GetChannels();
vector<ATTENDEE>& GetAttendees();
};
Server Implementation
The server is the application that shares the session. An application, some windows, or an entire desktop can be shared. The steps are:
- Configure parameters and start the server.
- Set the notification to receive events if you want to handle them manually (if you don't set a notification, there are default handlers).
- Create an invitation ticket and (optionally) a password.
- Send this ticket and password to the recipient in your own way.
RAS:: SERVER s;
s.SetConnectionParameters(AF_INET,3389,true,24);
s.Open();
RECT rc = {0};
rc.right = GetSystemMetrics(SM_CXFULLSCREEN);
rc.bottom = GetSystemMetrics(SM_CYFULLSCREEN);
s->SetDesktopRegion(rc);
s.Invite(0,L”password”,L"group",1);
If you wish to connect to the client instead of accepting connections (that might be needed if your PC is behind an IPv4 NAT, for example), you can use SERVER::ReverseOpen
. The ticket you must pass to that function must have been created by the client and transported to you through an external interface.
The S_INVITATION* from SERVER::Invite()
can be used to expire the invitation with S_INVITATION::Revoke()
.
Once you have viewers connected, you can use the ATTENDEE
class.
class ATTENDEE
{
...
public:
void SetControl(int x);
CTRL_LEVEL_INTERACTIVE);
wstring& GetNane();
int GetID();
HRESULT Kill();
int GetProtocol();
wstring GetLocalIP();
wstring GetPeerIP();
long GetLocalPort();
long GetPeerPort();
Client Implementation
The client application is a viewer that connects to the sharer. The steps are:
- Get the ticket from an external interface.
- Create the client, either by passing
0
to the constructor, in which case. it creates it with CoCreateInstance
, or by passing an IUnknown*
to it if you ever manage to create it with OleCreate
.
- If you plan to accept connections to the client, call
CLIENT:: SetReverseConnectionParameters
, taking the invitation ticket (generated by the server). You must give the new connection string to the server by an external method.
- If you plan to connect directly, call
Connect()
using the ticket and password.
RAS::CLIENT* c = new RAS::CLIENT(a);
TCHAR Ticket[1000] = {0};
TCHAR Pwd[1000] = {0};
if (Reverse)
{
wstring s = ReversalString;
wstring y = c->SetReverseConnectionParameters(s.c_str(),
_T("hello"),_T(""));
SendToServerSomeHow(y.c_str());
}
else
{
wstring wTicket = GetTicketSomeHow();
wstring Password = GetPasswordSomeHow();
c->Connect(Ticket.c_str(),L"A Client",Pwd.c_str());
}
Virtual Channels
The CHANNEL
class implements a virtual channel (a way to share binary data between clients). You must call BASERDP::CreateVirtualChannel
with the same name from both the server and clients, so as to establish a communication channel between them. The channel is distinguished by its name.
BASERDP::CreateVirtualChannel
must be called before any connection has occurred. It is not possible to create virtual channels after connections have been established.
In order to set which attendees will receive data from a channel, you must call SERVER::AssignChannelToAttendees
.
After that, you can call BASERDP::SendData()
with the selected channel, the binary data and its size.
The implementation ensures that the data is sent and received in a proper way, and also if the data is delayed, it keeps a queue to resend them, either to all or to specified attendees.
The data comes to BASERDP::OnReceiveData()
, which calls your callback.
Sharable Applications and Region
You can limit the shared region of the desktop by calling SERVER :: SetDesktopRegion(const RECT& rc)
.
You can limit the number of applications shared by calling:
SERVER :: ShareOnlyTheseApplications(vector<int>& pids,bool X)
The vector is an array of the process id of the applications you want to be shared, and X
is true
.
If X
is false
, all applications are shared no matter what pids
contain.
You can get the list of sharable applications, their names and their status by calling:
SERVER :: GetShareableApplications
(vector<int>& pids,vector<wstring>& names,vector<int>& ST);
Interesting Stuff
- The Viewer must be created with
CoCreateInstance()
and not OleCreate()
.
- The COM state must be in a single threading mode (
CoInitializeEx(0, COINIT_APARTMENTTHREADED)
in both the server and the client implementations). This would cost me some hours of debugging.
- Virtual channels must be created before any connection is established.
To Do
- Implementation of more server and client events
History
- 29-9-2008 - Implementation of data callbacks, more member functions.
- 26-9-2008 - Implementation of Virtual Channels, some bug fixes and Implementation of Sharable Applications
- 24-9-2008 - First post