|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionDo you have two computers at your desk? If so, have you ever wanted to Ctrl-C on one computer and Ctrl-V on the other? It's especially tempting to do this when the two computers share a common keyboard and monitor via a KVM switch. This application makes it easy to share the clipboard between two computers on a local network. The application is written in C# using .NET remoting. This article will explain the remoting implementation and the clipboard processing, as well as explaining the use of a delegate to get around a problem with threading. Using the ApplicationClipShare acts as both a client and server, so it must be running on both computers that will be sharing the clipboard. On the computer that has the clipboard data that you want to share (the client), specify the name of the computer you want to send to and then select the "Send Clipboard" button. The clipboard will be packaged up, sent over the LAN, and automatically placed on the clipboard of the computer at the receiving end (the server). For convenience, the application has an icon in the system tray so you can also select "send clipboard" from the icon's context menu. If you want to temporarily disallow other computers from sending their clipboard to your computer, deselect the "allow incoming" checkbox or context menu item. Setting Up .NET RemotingThis application uses .NET remoting in order to transfer the clipboard
contents. First we need to do some initialization to make the application
a remoting server. This code snippet (from the form's constructor)
enables remoting and then registers a class, TcpChannel channel = new TcpChannel(4820);
hannelServices.RegisterChannel(channel);
RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemoteClipboard),
"ClipShare",
WellKnownObjectMode.Singleton);
This application also serves as a remoting client. To send the clipboard
data, the client activates an instance of the private void InitRemoteObject()
{
try
{
string location = "tcp://" + computerName.Text + ":4820/ClipShare";
m_remoteClipboard = (RemoteClipboard) Activator.GetObject(
typeof(RemoteClipboard), location);
computerName.Modified = false;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
The RemoteClipboard ClassThe Problems Right off the BatUnfortunately, the Delegates to the RescueIn order to get around this problem, we need to have the form place the data on
the clipboard instead of the
public delegate void ClipEventHandler(ArrayList clipData);
public class RemoteClipboard : MarshalByRefObject
{
private static ClipEventHandler m_OnClipReceive;
private static Form m_receiverForm;
// set the object and method that gets a callback when a clipboard is sent.
public static void SetOnClipReceive(Form receiver, ClipEventHandler theCallback)
{
m_receiverForm = receiver;
m_OnClipReceive = theCallback;
}
// this is the method that will be invoked remotely by the other computer.
public void SendClipboard(ArrayList clipData)
{
object[] clipObjects = {clipData};
m_receiverForm.Invoke(m_OnClipReceive, clipObjects);
}
}
The RemoteClipboard.SetOnClipReceive(this, new ClipEventHandler(this.AddToClip));
Special note to Forms developers: I included the Packaging up the Clipboard ContentsTo access the clipboard data, use private void SendClipboardToRemote()
{
try
{
...
ArrayList dataObjects = new ArrayList();
IDataObject clipboardData = Clipboard.GetDataObject();
string[] formats = clipboardData.GetFormats();
for (int i=0; i < formats.Length; i++)
{
object clipboardItem = clipboardData.GetData(formats[i]);
if (clipboardItem != null && clipboardItem.GetType().IsSerializable)
{
Console.WriteLine("sending {0}", formats[i]);
dataObjects.Add(formats[i]);
dataObjects.Add(clipboardItem);
}
else
Console.WriteLine("ignoring {0}", formats[i]);
}
if (dataObjects.Count > 0)
{
Cursor.Current = Cursors.WaitCursor;
m_remoteClipboard.SendClipboard(dataObjects);
Cursor.Current = Cursors.Default;
}
else
MessageBox.Show(this, "Nothing on clipboard, or contents not supported",
"ClipShare");
}
catch (Exception ex)
{
string message = String.Format("Unable to send data: {0}", ex.Message);
MessageBox.Show(this, message, "ClipShare");
}
}
Receiving the ClipboardOn the receive end, we iterate through the array list and add each clipboard
data item to a new public void AddToClip(ArrayList theData)
{
if (!allowIncomingCB.Checked)
throw new Exception("Remote computer has disabled clipboard sharing");
try
{
DataObject dataObj = new DataObject();
for (int i = 0; i < theData.Count; i++)
{
string format = (string)theData[i++];
dataObj.SetData(format, theData[i]);
}
Clipboard.SetDataObject(dataObj, true);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
You may have noticed that the call to LimitationsUnfortunately, not all formats retrieved from the ConclusionWhen I started writing this application, I thought it would serve as a quick introduction to remoting, but as is often the case, especially when coming up to speed on a new programming environment, it turned out to take longer than I expected. But running into problems isn't all bad since solving them is part of the learning process. (Next time I'll know not to define a class above my form!)
|
||||||||||||||||||||||