Introduction
The simple easy to use desktop sharing application "Netmeeting" in Windows XP is killed in Windows 7. It is replaced with meeting place
in Vista and removed in Windows 7. After moving to Windows 7, I almost felt the need of it at least once in a week, so I started to implement desktop sharing application in Window 7.
Background
The desktop sharing application is built using two concepts:
- Communication using WCF and
- Desktop Sharing using Remote Desktop API
1. Communication using WCF
WCF is used to maintain client and server communication. Server broadcasts the messages to all the clients that are connected. I used Callback in TCP/IP protocol to have the messages sent from server to client.
Code Logic
The application will host a WCF Service. The ServiceContract
for IMMService
and the callback interface is shown below:
[ServiceContract(SessionMode = SessionMode.Required,
CallbackContract = typeof(IServerCallbackToClient))]
public interface IMMService
{
[OperationContract(IsOneWay = true, IsInitiating = true, IsTerminating = false)]
void Say(Person person, String msg, MessageType MsgType);
[OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)]
void Whisper(string to, String msg);
[OperationContract(IsOneWay = false, IsInitiating = true, IsTerminating = false)]
Person[] Subscribe(Person name);
[OperationContract(IsOneWay = false, IsInitiating = false, IsTerminating = true)]
bool Unsubscribe(Person name);
}
- Function
Say
will send the message to the server, which in turn will broadcast the message to all the connected clients.
- Function
Whisper
will send the message to the server for some action. The message will not be broadcasted.
- Function
Subscribe
and Unsubscribe
are for joining and leaving the meeting group.
public interface IServerCallbackToClient
{
[OperationContract(IsOneWay = true)]
void Receive(Person sender, String message, MessageType MsgType);
[OperationContract(IsOneWay = true)]
void ReceiveWhisper(Person sender, string message);
[OperationContract(IsOneWay = true)]
void UserEnter(Person person);
[OperationContract(IsOneWay = true)]
void UserLeave(Person person);
}
Function Receive
is the callback from server to client to inform that the message is received and the same acknowledgements for other operations. The callback channels are stored in the server in the subscribe
function. Receive
will callback to all the clients using these call back channels.
The date about the user is passed from client and server. The class Person
holds this data.
[DataContract]
public class Person : INotifyPropertyChanged
{
private string imageURL;
private string name;
public event PropertyChangedEventHandler PropertyChanged;
public Person()
{
}
public Person(string imageURL, string name)
{
this.imageURL = imageURL;
this.name = name;
}
[DataMember]
public string ImageURL
{
get { return imageURL; }
set
{
imageURL = value;
OnPropertyChanged("ImageURL");
}
}
[DataMember]
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged("Name");
}
}
protected void OnPropertyChanged(string propValue)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propValue));
}
}
}
The implementation of this service will be the server which basically works as a Messenger server. The client will login with Subscribe(Person p)
. The class Person
holds the data of the individual systems. Once subscribed, any message sent from any one in the group will be broadcasted to every other one in the group. The Service
calls Receive()
function to send messages to all connected clients.
2. Desktop Sharing using Remote Desktop API
Remote Desktop APIs are used to broadcast the desktop to all the clients connected.
Using the Code
Class RDPSession
is used to share the desktop. When the desktop is shared, it generates an invitation string
. The desktop Viewer needs this invitation string
to view the shared desktop.
m_pRdpSession.OnAttendeeConnected +=
new RDPCOMAPILib._IRDPSessionEvents_OnAttendeeConnectedEventHandler
(OnAttendeeConnected);
m_pRdpSession.OnAttendeeDisconnected +=
new RDPCOMAPILib._IRDPSessionEvents_OnAttendeeDisconnectedEventHandler
(OnAttendeeDisconnected);
m_pRdpSession.OnControlLevelChangeRequest +=
new RDPCOMAPILib._IRDPSessionEvents_OnControlLevelChangeRequestEventHandler
(OnControlLevelChangeRequest);
m_pRdpSession.Open();
IRDPSRAPIInvitation pInvitation = m_pRdpSession.Invitations.CreateInvitation
("DesktopShareServer", "Internal", "", 5);
string invitationString = pInvitation.ConnectionString;
When one of the systems shares the desktop, the generated invitation string
is broadcasted to all others. The invitation string
can then be used to view the desktop.
pRdpViewer.Connect(ConnectionString, "Viewer1", "");
pRdpViewer.Dock = DockStyle.Fill;
pRdpViewer.SmartSizing = true;
Points of Interest
Since the remote desktop is used to share the desktop, the refresh rate is faster than VNC or RADMIN, and also less burden on the CPU.
History
- 20th April, 2011: Initial post.