Click here to Skip to main content
12,767,637 members (52,102 online)
Click here to Skip to main content
Add your own
alternative version


65 bookmarked
Posted 14 Oct 2002

Share the Clipboard Using .NET Remoting

, 14 Oct 2002
Rate this:
Please Sign up or sign in to vote.
Use .NET remoting to send the contents of your clipboard to another computer.

Sample Image


Do 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 Application

ClipShare 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 Remoting

This 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, RemoteClipboard (discussed later), with the RemotingConfiguration so that it can be activated remotely:

TcpChannel channel = new TcpChannel(4820);

This application also serves as a remoting client. To send the clipboard data, the client activates an instance of the RemoteClipboard class on the server using the same port number and service name that was registered above. We will use this instance to invoke the SendClipboard method, so we save it in a member variable for use later:

private void InitRemoteObject()
        string location = "tcp://" + computerName.Text + ":4820/ClipShare";
        m_remoteClipboard = (RemoteClipboard) Activator.GetObject(
                                typeof(RemoteClipboard), location);
        computerName.Modified = false;
    catch (Exception ex)

The RemoteClipboard Class

The RemoteClipboard class is activated remotely and used to transfer the clipboard contents from one computer to the other. It must inherit from MarshalByRefObject so that it can be activated remotely. It has one method, SendClipboard, that takes an ArrayList containing the clipboard data passed in from the client, and places it on the server's clipboard.

Problems Right off the Bat

Unfortunately, the SendClipboard method isn't allowed to place data on the clipboard. The method from the .NET framework used to set the contents of the clipboard, Clipboard.SetDataObject(), can only be run in a Single Threaded Apartment (STA), but since the RemoteClipboard object is activated remotely, it's running in a Multi Threaded Apartment (MTA). If you try to call Clipboard.SetDataObject() directly from SendClipboard, an exception is thrown.

Delegates to the Rescue

In order to get around this problem, we need to have the form place the data on the clipboard instead of the RemoteClipboard object. Since Form.Main() is declared with the [STAThread] attribute, it can call SetDataObject without any problem. To get the form to process the data in its own thread, we need to call the Invoke method on the form, passing in a delegate to one of the form's methods (AddToClip). The RemoteClipboard class has a static method, SetOnClipReceive, called once during initialization, to give it the form and delegate to use when calling Invoke. Here's a picture to help describe what's going on conceptually, followed by the RemoteClipboard class in its entirety:

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 SetOnClipReceive function is called from the form's constructor:

RemoteClipboard.SetOnClipReceive(this, new ClipEventHandler(this.AddToClip));

Special note to Forms developers: I included the RemoteClipboard class in the same source file as the form since it is small and convenient to do so. I originally had the RemoteClipboard class defined above the form in the source file. As soon as I did this (although I never made that connection), the form could no longer access its resources, such as the icon for the system tray. It took me quite awhile to figure out that the Form must be defined first in the source file. According to Microsoft, this is by design (see Q318603).

Packaging up the Clipboard Contents

To access the clipboard data, use Clipboard.GetDataObject(), which returns an instance of the IDataObject interface. Unfortunately, this object is not serializable, so it can't be passed as a parameter to the SendClipboard method. Instead, we iterate through each format on the clipboard, and if it is serlializable, put the clipboard data item in an array list, paired with its format string. The ArrayList is then passed to the RemoteClipboard object using the SendClipboard method.

private void SendClipboardToRemote()
        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]);
                Console.WriteLine("ignoring {0}", formats[i]);

        if (dataObjects.Count > 0)
            Cursor.Current = Cursors.WaitCursor;
            Cursor.Current = Cursors.Default;
            MessageBox.Show(this, "Nothing on clipboard, or contents not supported", 
    catch (Exception ex)
        string message = String.Format("Unable to send data: {0}", ex.Message);
        MessageBox.Show(this, message, "ClipShare");

Receiving the Clipboard

On the receive end, we iterate through the array list and add each clipboard data item to a new DataObject, which then gets placed on the clipboard via Clipboard.SetDataObject. The AddToClip method shown here is the delegate that gets invoked by the SendClipboard method (see above):

public void AddToClip(ArrayList theData)
    if (!allowIncomingCB.Checked)
        throw new Exception("Remote computer has disabled clipboard sharing");

        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)

You may have noticed that the call to SendClipboard by the client is in a try/catch block, so the exception that is thrown in AddToClip on the server: ("Remote computer has disabled clipboard sharing") will get propagated back to the client and shown in a message box. Also interesting to note, but not surprising, is that if AddToClip is invoked asynchronously with BeginInvoke instead of Invoke, the exception will not get propagated and you will get an unhandled exception error.


Unfortunately, not all formats retrieved from the IDataObject are serializable. For example, the windows metafile format is not, so transferring to or from drawing programs is limited to bitmap formats. Also, if you copy a file or directory, the location placed on the clipboard uses drive letters instead of UNC so they can't be pasted on the remote computer. I imagine that it wouldn't be hard to add pre-processing to change the path to use UNC before the clipboard is sent. Finally, I made a half-hearted attempt at getting a left mouse click on the system tray icon to show the context menu in addition to a right mouse click (this is commented out in the source file if you download it). This doesn't seem to be directly supported by the NotifyIcon class, so is not easily done.


When 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!)


This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


About the Author

Douglas Earl
Architect Serlio Software
United States United States
Doug is co-founder of Serlio Software, creators of CaseComplete use case authoring and requirements management tool. Prior to Serlio, he worked at Rational Software on the Rose and XDE products. He's old enough to remember when Rose fit on a single floppy.

You may also be interested in...

Comments and Discussions

GeneralDo you know a similar sample in ? specially SetClipboardViewer Pin
yemulss26-Mar-08 4:02
memberyemulss26-Mar-08 4:02 
QuestionHi i want to implement the samething in VC++, MFC Pin
Member #383530616-Feb-07 1:37
memberMember #383530616-Feb-07 1:37 
QuestionI m getting an error called : "Because of security restrictions, the type System.Runtime.Remoting.ObjRef cannot be accessed" Pin
Kirtan Gor29-Jun-06 20:20
memberKirtan Gor29-Jun-06 20:20 
AnswerRe: I m getting an error called : "Because of security restrictions, the type System.Runtime.Remoting.ObjRef cannot be accessed" Pin
Douglas Earl30-Jun-06 5:52
memberDouglas Earl30-Jun-06 5:52 
GeneralKVM switch Pin
araud28-Mar-05 21:05
memberaraud28-Mar-05 21:05 
GeneralUnable to send IO.MemoryStream data Pin
Jon Ide23-Sep-04 10:18
memberJon Ide23-Sep-04 10:18 
GeneralReally share it... Pin
ProgrammerTim8-Mar-04 20:43
memberProgrammerTim8-Mar-04 20:43 
Years ago I wrote a very similar program using vb3 with a socket control to transfer the data. Recently, I rewrote my app, but my computers are only available to each other via a shared drive, so I have to use the FileSystemWatcher to watch for the file changing.

Something that made things more useable for me in both apps is calling the SetClipboardViewer API call. This call allows you to hook into the clipboard chain, so you don't have to click the "Send Clipboard" button. Anytime you put something on the clipboard, it is automatically sent. It basically means that both computers are then using the same clipboard.
Below are my code snippets to use the API (in, because I don't feel like converting it to C#)

In the declares section (class scope):
	Const WM_DRAWCLIPBOARD as integer = &H308
	Const WM_CHANGECBCHAIN as integer = &H30D
	Declare Function ChangeClipboardChain Lib "user32" (ByVal hWndRemove as Integer, ByVal hWndNewNext as Integer) as Boolean
	Declare Function SetClipboardViewer Lib "user32" Alias "SetClipboardViewer" (ByVal hwnd As Integer) As Integer
	Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hWnd as Integer, Msg as Integer, wParam as Integer, lParam as Integer) as Boolean

	Dim miNexthWnd as Integer
	Dim mbLoading as Boolean

This is in the Form Load event handler...
		miNexthWnd = SetClipboardViewer(me.Handle.ToInt32)

The subclasser. I don't know how the subclasser would be declared in C#, but
this is it in VB. A lot simpler than VB3...
	Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
		if m.Msg = WM_DRAWCLIPBOARD then
			'We have to check this.  If we don't, we will
			'save the clipboard, our sister computer will
			'load it, thereby generating a draw message
			'on that computer, which will save the clipboard
			'and send it to us.  We will then load the
			'clipboard, generating a Draw message here and
			'starting the cycle again.  So, we want to
			'make sure that if the clipboard is changing
			'because we are loading it, we don't save it
			'back out.
			if mbLoading = False then
				'We could call SendMessage, but I don't see any reason to wait for the next window to return.
				PostMessage(miNexthWnd, m.Msg, m.WParam.ToInt32, m.LParam.toInt32)
			End if
		ElseIf m.Msg = WM_CHANGECBCHAIN then
			'If the window we have listed as our
			'next window is the one being removed,
			'we reset our handle and return 0 to
			'say we processed the message.
			'Otherwise, we just pass the message to
			'the next window.
			'We probably are supposed to call
			'SendMessage for that, but there is
			'no defined behavior in the SDK to
			'handle the return value,
			'So I'm not worrying about it.
			if m.wParam.ToInt32 <> miNexthWnd then
				PostMessage(miNexthWnd, m.Msg, m.WParam.ToInt32, m.LParam.toInt32)
				m.Result = m.Result.Zero
				miNexthWnd = m.LParam.ToInt32
			end if
		End if
	End Sub

Finally, this code goes in your Form Closing or Closed event handler. I have it in my Closing because I haven't guaranteed that the form's hWnd still exists on Closed, and I wouldn't want windows trying to send a message to the form after it is gone. You shouldn't have to worry about this propogating to your form. Windows will send the WM_CHANGECBCHAIN message to the first form
in the chain, which will propogate it on until it gets to the form right before yours. That form will update its internal variable and NOT propogate the message.
		ChangeClipboardChain(Me.Handle.ToInt32, miNexthWnd)

Hardly Foreal Custom Software
GeneralRe: Really share it... Pin
ProgrammerTim8-Mar-04 20:51
memberProgrammerTim8-Mar-04 20:51 
GeneralProblem accessing objects outside the RemoteObject Pin
Bernhard Mähr9-Jan-04 12:20
memberBernhard Mähr9-Jan-04 12:20 
GeneralClipShare Pin
baltman9-Jul-03 0:43
sussbaltman9-Jul-03 0:43 
GeneralRe: ClipShare with .net 1.1 Pin
Douglas Earl10-Jul-03 10:15
memberDouglas Earl10-Jul-03 10:15 
GeneralVery useful! Pin
PJ Arends15-Oct-02 22:09
memberPJ Arends15-Oct-02 22:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170217.1 | Last Updated 15 Oct 2002
Article Copyright 2002 by Douglas Earl
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid