<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" media="screen" href="/styles/xslt/rss.xslt"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:media="http://search.yahoo.com/mrss/" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:c9="http://channel9.msdn.com">
<channel>
	<title>Channel 9</title>
    <atom:link rel="self" type="application/rss+xml" href="http://channel9.msdn.com/Niners/c4f.Brian-Trautman/Posts/RSS"></atom:link>
    <itunes:summary></itunes:summary>
    <itunes:author>Microsoft</itunes:author>
    <itunes:subtitle></itunes:subtitle>
    <image>
      <url>http://mschnlnine.vo.llnwd.net/d1/Dev/App_Themes/C9/images/feedimage.png</url>
      <title>Channel 9</title>
      <link>http://channel9.msdn.com/Niners/c4f.Brian-Trautman/Posts</link>
    </image>
    <itunes:image href=""></itunes:image>
    <itunes:category text="Technology"></itunes:category>
    <description>Channel 9 keeps you up to date with the latest news and behind the scenes info from Microsoft that developers love to keep up with. From LINQ to SilverLight – Watch videos and hear about all the cool technologies coming and the people behind them.</description>
    <link>http://channel9.msdn.com/Niners/c4f.Brian-Trautman/Posts</link>
    <language>en</language>
    <pubDate>Fri, 24 May 2013 04:28:05 GMT</pubDate>
    <lastBuildDate>Fri, 24 May 2013 04:28:05 GMT</lastBuildDate>
    <generator>Rev9</generator>
    <c9:totalResults>1</c9:totalResults>
    <c9:pageCount>1</c9:pageCount>
    <c9:pageSize>25</c9:pageSize>
  <item>
      <title>Windows Clipboard Sharing Through Web Services</title>
      <description><![CDATA[<span id="c4fmetadata">
<table border="0" cellspacing="0" cellpadding="1" width="100%">
<tbody>
<tr class="entry_overview">
<td width="50">&nbsp;</td>
<td><span class="entry_description">This application transfers the contents of a machine's clipboard to another machine through the user of ASP .NET Web Services.</span></td>
</tr>
<tr>
<td colspan="2">
<div class="entry_author">Brian Trautman</div>
<div class="entry_company"><a href=""></a></div>
<br>
<div class="entry_details"><b>Difficulty: </b><span class="entry_details_input">Easy</span></div>
<div class="entry_details"><b>Time Required:</b> <span class="entry_details_input">
1-3 hours</span></div>
<div class="entry_details"><b>Cost: </b><span class="entry_details_input">Free</span></div>
<div class="entry_details"><b>Software: </b><span class="entry_details_input"><a href="http://msdn.com/express/">Visual Basic or Visual C# Express Editions</a></span></div>
<div class="entry_details"><b>Hardware: </b><span class="entry_details_input">None</span></div>
<div class="entry_details"><b>Download: </b><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1848530/clipboard.zip">Download</a>
<ul>
</ul>
</div>
</td>
</tr>
</tbody>
</table>
</span>
<h3>Background</h3>
<p>Have you ever worked on multiple machines and wished you could copy the clipboard contents from one machine to the other? I've often found myself wishing that there was a quick and easy way to move text snippets, screenshots, or even files to another machine
 with a simple copy and paste. If this sounds interesting to you then read on. </p>
<p>I wanted this to work regardless of whether both machines were online at the same time, and not be halted by firewalls, NAT's, etc. so I opted for a server based rather than a peer to peer architecture. The architecture involved a client application to transfer
 the clipboard contents to the server via a web services call, a web service to cache the clipboard contents, and another client component to retrieve the clipboard contents from the server and place them on the local machine's clipboard.
</p>
<p>To tackle this problem we need programmatic access to the clipboard on both the machine to copy from and also the machine we want to copy to. Luckily .NET provides a managed wrapper around the native Windows Clipboard API that gives us access to this. The
 relevant namespaces are Clipboard for C# and my.Computer.Clipboard. Since we're interested in moving clipboard objects from one machine to another we first need to determine what type of objects get placed onto the clipboard for our various actions (copy text,
 images, and files). Writing a quick code snippet with the Clipboard namespace allows us to iterate through all objects on the clipboard for various types of actions to see what we're dealing with.
</p>
<pre class="csharpcode">C#</pre>
<div class="csharpcode">
<pre class="alt">        IDataObject clipData = Clipboard.GetDataObject();</pre>
<pre>&nbsp;</pre>
<pre class="alt">        <span class="rem">//retrieve an array of strings for all the formats available on the clipboard.</span></pre>
<pre>        <span class="kwrd">string</span>[] formats = clipData.GetFormats();</pre>
<pre class="alt">&nbsp;</pre>
<pre>        <span class="rem">//iterate through the list of formats on the clipboard</span></pre>
<pre class="alt">        <span class="kwrd">foreach</span> (<span class="kwrd">string</span> format <span class="kwrd">in</span> formats)</pre>
<pre>        {</pre>
<pre class="alt">            <span class="rem">//add each objeoct to an arraylist so we can inspect the object types</span></pre>
<pre>            <span class="kwrd">object</span> dataObject = clipData.GetData(format);</pre>
<pre class="alt">&nbsp;</pre>
<pre>        }</pre>
</div>
<style type="text/css">
<!--
.csharpcode
	{background-color:#ffffff;
	font-family:consolas,"Courier New",courier,monospace;
	color:black;
	font-size:small}
.csharpcode 
	{background-color:#ffffff;
	font-family:consolas,"Courier New",courier,monospace;
	color:black;
	font-size:small}
.csharpcode 
	{margin:0em}
.csharpcode .rem
	{color:#008000}
.csharpcode .kwrd
	{color:#0000ff}
.csharpcode .str
	{color:#006080}
.csharpcode .op
	{color:#0000c0}
.csharpcode .preproc
	{color:#cc6633}
.csharpcode .asp
	{background-color:#ffff00}
.csharpcode .html
	{color:#800000}
.csharpcode .attr
	{color:#ff0000}
.csharpcode .alt
	{background-color:#f4f4f4;
	margin:0em;
	width:100%}
.csharpcode .lnum
	{color:#606060}
-->
</style>
<pre class="csharpcode">VB</pre>
<div class="csharpcode">
<pre class="alt">    <span class="kwrd">Dim</span> clipData <span class="kwrd">As</span> IDataObject = Clipboard.GetDataObject</pre>
<pre>&nbsp;</pre>
<pre class="alt">    <span class="kwrd">Dim</span> formats() <span class="kwrd">As</span> <span class="kwrd">String</span> = clipData.GetFormats</pre>
<pre>&nbsp;</pre>
<pre class="alt">    <span class="rem">'iterate through the list of formats on the clipboard</span></pre>
<pre>    <span class="kwrd">For</span> <span class="kwrd">Each</span> format <span class="kwrd">As</span> <span class="kwrd">String</span> <span class="kwrd">In</span> formats</pre>
<pre class="alt">    <span class="rem">'add each objeoct to an arraylist so we can inspect the object types</span></pre>
<pre>          <span class="kwrd">Dim</span> dataObject <span class="kwrd">As</span> <span class="kwrd">Object</span> = clipData.GetData(Format)</pre>
<pre class="alt">    Next</pre>
</div>
<p>The screenshot below shows the results for having some text from Word copied onto the clipboard. Each format in the string array is a representation of the data on the clipboard. Since the target application (where you're going to paste to) is unknown the
 clipboard has multiple formats each containing the same data. The objective with this project is to replicate all these formats on the target machine's clipboard.</p>
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1848530/image02.png"><img border="0" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1848530/image0_thumb.png" width="717" height="212"></a>
</p>
<p>After inspection of the objects on the Clipboard for text, images, and file objects it was easy to determine that the primary object types we need to be concerned with. These are &quot;System.IO.MemoryStream&quot;, “System.IO.FileStream”, &quot;System.Drawing.Bitmap&quot;,
 and &quot;System.String&quot; . Since all this information would be transferred to the server via web services it's simple to serialize all objects to bytes for transmission. This is necessary for a number of reasons including the fact that complex objects such as MemoryStreams
 cannot be simply serialized and sent over the web service call as Strings can. In addition, some of the objects are larger than the web service call allows, and would need to be broken up into smaller pieces for transmission and then reassembled on the server
 side in the correct order. Again, when the client requests the clipboard items we'll need to disassemble each object, send it over the web service return result to the client, and then reassemble it.</p>
<p>The first item to build is a base function that handles the breaking up of these large streams into more manageable byte arrays for transmission to the web service. The function below performs this task by sending chunks of the MemoryStream limited in size
 by the “byteCount” constant. Once this limit is reached the buffer is sent over the web service call for storage and assembly on the server. Once we have either 0 bytes left to send, or a number less than “byteCount” we send the remaining elements of the buffer
 and signal the web service that this particular object is complete with the “isFinalTransaction” flag.
</p>
<p>C#</p>
<div class="csharpcode">
<pre class="alt">        <span class="kwrd">private</span> <span class="kwrd">void</span> UploadStreamBlock(<span class="kwrd">string</span> format, <span class="kwrd">string</span> objectType, MemoryStream memStream)</pre>
<pre>        {</pre>
<pre class="alt">            <span class="rem">//each time we enter this function we have a new transaction beginning.  </span></pre>
<pre>            <span class="rem">//  A transaction represents a comlete</span></pre>
<pre class="alt">            <span class="rem">//object on the clipboard and we'll use this on the server side to know </span></pre>
<pre>            <span class="rem">//  how to put the stream back together</span></pre>
<pre class="alt">            <span class="kwrd">string</span> transactionGuid = System.Guid.NewGuid().ToString();</pre>
<pre>            memStream.Position = 0;</pre>
<pre class="alt">&nbsp;</pre>
<pre>            <span class="kwrd">byte</span>[] buffer = <span class="kwrd">new</span> <span class="kwrd">byte</span>[byteCount];</pre>
<pre class="alt">            <span class="kwrd">bool</span> isFinalTransaction = <span class="kwrd">false</span>;</pre>
<pre>&nbsp;</pre>
<pre class="alt">            <span class="rem">//while the current stream position plus our byte count is less than the </span></pre>
<pre>            <span class="rem">//  length of the stream continue sending as much as we can.</span></pre>
<pre class="alt">            <span class="kwrd">while</span> ((memStream.Position &#43; byteCount) &lt;= memStream.Length)</pre>
<pre>            {</pre>
<pre class="alt">                <span class="rem">//if we happen to be on the last byte of the stream set the final </span></pre>
<pre>                <span class="rem">// transaction flag to true so the server will know that this is the </span></pre>
<pre class="alt">                <span class="rem">//  last bit of this transaction to expect.</span></pre>
<pre>                <span class="kwrd">if</span> (memStream.Position &#43; byteCount == memStream.Length)</pre>
<pre class="alt">                {</pre>
<pre>                    isFinalTransaction = <span class="kwrd">true</span>;</pre>
<pre class="alt">                }</pre>
<pre>                <span class="rem">//read the stream into our buffer for transmission over the web service.</span></pre>
<pre class="alt">                memStream.Read(buffer, 0, byteCount);</pre>
<pre>                ws.InsertMessageStream(buffer, format, objectType, transactionGuid, </pre>
<pre class="alt">                    isFinalTransaction, clipBoardGUID);</pre>
<pre>            }</pre>
<pre class="alt">&nbsp;</pre>
<pre>            <span class="kwrd">long</span> remainingBytes = memStream.Length - memStream.Position;</pre>
<pre class="alt">            <span class="rem">//if we still have remaining bytes left figure out how many and transmit the </span></pre>
<pre>            <span class="rem">//  last bit of this ojbect over the web service.</span></pre>
<pre class="alt">            <span class="kwrd">if</span> ((<span class="kwrd">int</span>)remainingBytes &gt; 0)</pre>
<pre>            {</pre>
<pre class="alt">                <span class="kwrd">byte</span>[] remainingBuffer = <span class="kwrd">new</span> <span class="kwrd">byte</span>[(<span class="kwrd">int</span>)remainingBytes];</pre>
<pre>&nbsp;</pre>
<pre class="alt">                memStream.Read(remainingBuffer, 0, (<span class="kwrd">int</span>)remainingBytes);</pre>
<pre>                ws.InsertMessageStream(remainingBuffer, format, objectType, </pre>
<pre class="alt">                    transactionGuid, <span class="kwrd">true</span>, clipBoardGUID);</pre>
<pre>            }</pre>
<pre class="alt">        }</pre>
</div>
<p>VB</p>
<div class="csharpcode">
<pre class="alt">    <span class="kwrd">Private</span> <span class="kwrd">Sub</span> UploadStreamBlock(<span class="kwrd">ByVal</span> format <span class="kwrd">As</span> <span class="kwrd">String</span>, <span class="kwrd">ByVal</span> objectType <span class="kwrd">As</span> <span class="kwrd">String</span>, _</pre>
<pre>                                  <span class="kwrd">ByVal</span> memStream <span class="kwrd">As</span> MemoryStream)</pre>
<pre class="alt">        <span class="rem">'each time we enter this function we have a new transaction beginning. </span></pre>
<pre>        <span class="rem">' A transaction represents a comlete</span></pre>
<pre class="alt">        <span class="rem">'object on the clipboard and we'll use this on the server side to know how </span></pre>
<pre>        <span class="rem">' to put the stream back together</span></pre>
<pre class="alt">        <span class="kwrd">Dim</span> transactionGuid <span class="kwrd">As</span> <span class="kwrd">String</span> = System.Guid.NewGuid.ToString</pre>
<pre>        memStream.Position = 0</pre>
<pre class="alt">        <span class="kwrd">Dim</span> buffer() <span class="kwrd">As</span> <span class="kwrd">Byte</span> = <span class="kwrd">New</span> <span class="kwrd">Byte</span>((byteCount) - 1) {}</pre>
<pre>        <span class="kwrd">Dim</span> isFinalTransaction <span class="kwrd">As</span> <span class="kwrd">Boolean</span> = <span class="kwrd">False</span></pre>
<pre class="alt">        <span class="rem">'while the current stream position plus our byte count is less than the </span></pre>
<pre>        <span class="rem">' length of the stream continue sending as much as we can.</span></pre>
<pre class="alt">&nbsp;</pre>
<pre>        <span class="kwrd">While</span> ((memStream.Position &#43; byteCount) _</pre>
<pre class="alt">                    &lt;= memStream.Length)</pre>
<pre>            <span class="rem">'if we happen to be on the last byte of the stream set the final </span></pre>
<pre class="alt">            <span class="rem">' transaction flag to true so the server will know that this is the </span></pre>
<pre>            <span class="rem">' last bit of this transaction to expect.</span></pre>
<pre class="alt">            <span class="kwrd">If</span> ((memStream.Position &#43; byteCount) _</pre>
<pre>                        = memStream.Length) <span class="kwrd">Then</span></pre>
<pre class="alt">                isFinalTransaction = <span class="kwrd">True</span></pre>
<pre>            <span class="kwrd">End</span> <span class="kwrd">If</span></pre>
<pre class="alt">            <span class="rem">'ream the stream into our buffer for transmission over the web service.</span></pre>
<pre>            memStream.Read(buffer, 0, byteCount)</pre>
<pre class="alt">            clipService.InsertMessageStream(buffer, format, objectType, transactionGuid, _</pre>
<pre>                                            isFinalTransaction, clipBoardGUID)</pre>
<pre class="alt">&nbsp;</pre>
<pre>        <span class="kwrd">End</span> <span class="kwrd">While</span></pre>
<pre class="alt">        <span class="kwrd">Dim</span> remainingBytes <span class="kwrd">As</span> <span class="kwrd">Long</span> = (memStream.Length - memStream.Position)</pre>
<pre>        <span class="rem">'if we still have remaining bytes left figure out how many and </span></pre>
<pre class="alt">        <span class="rem">' transmit the last bit of this ojbect over the web service.</span></pre>
<pre>        <span class="kwrd">If</span> (<span class="kwrd">CType</span>(remainingBytes, <span class="kwrd">Integer</span>) &gt; 0) <span class="kwrd">Then</span></pre>
<pre class="alt">            <span class="kwrd">Dim</span> remainingBuffer() <span class="kwrd">As</span> <span class="kwrd">Byte</span> = <span class="kwrd">New</span> <span class="kwrd">Byte</span>((<span class="kwrd">CType</span>(remainingBytes, <span class="kwrd">Integer</span>)) - 1) {}</pre>
<pre>            memStream.Read(remainingBuffer, 0, <span class="kwrd">CType</span>(remainingBytes, <span class="kwrd">Integer</span>))</pre>
<pre class="alt">            clipService.InsertMessageStream(remainingBuffer, format, objectType, _</pre>
<pre>                                            transactionGuid, <span class="kwrd">True</span>, clipBoardGUID)</pre>
<pre class="alt">        <span class="kwrd">End</span> <span class="kwrd">If</span></pre>
</div>
<p>The Server side of the web service needs to put the whole clipboard back together from a number of byte arrays so it's important that all the objects, their types, and formats be preserved for the clipboard to work properly on the target machine. We use
 the clipBoardGuid to determine if we are on a new clipboard posting or adding objects to an already existing instance. We use the isFinalTranaction flag to know if this byte array should be part of an existing transaction, or is the first in a new transaction.
 All clipboard items are saved to disk for later retrieval by any client requesting them. The code for this is below.
</p>
<p>C# </p>
<div class="csharpcode">
<pre class="alt">    [WebMethod]</pre>
<pre>    <span class="kwrd">public</span> <span class="kwrd">void</span> InsertMessageStream(<span class="kwrd">byte</span>[] buffer, <span class="kwrd">string</span> format, <span class="kwrd">string</span> objectType, </pre>
<pre class="alt">        <span class="kwrd">string</span> transactionGuid, <span class="kwrd">bool</span> isFinalTransaction, <span class="kwrd">string</span> clipBoardGUID)</pre>
<pre>    {</pre>
<pre class="alt">        <span class="rem">//always base the current directory on the clipboard that we're sending now.</span></pre>
<pre>        <span class="kwrd">string</span> clipBoardGUIDDirectory = </pre>
<pre class="alt">            System.Web.HttpContext.Current.Request.PhysicalApplicationPath &#43; clipBoardGUID;</pre>
<pre>&nbsp;</pre>
<pre class="alt">        <span class="kwrd">try</span></pre>
<pre>        {</pre>
<pre class="alt">            <span class="rem">//if the directory does not exist then delete all the other directories </span></pre>
<pre>            <span class="rem">// (clipboard instances) and create a new directory if the directory already </span></pre>
<pre class="alt">            <span class="rem">// exists then this particular transaction is part of the same clipboard so </span></pre>
<pre>            <span class="rem">// don't do anything. this works because othe clipboardDirectory is based </span></pre>
<pre class="alt">            <span class="rem">// off of the GUID sent from the client.</span></pre>
<pre>            <span class="kwrd">if</span> (!Directory.Exists(clipBoardGUIDDirectory))</pre>
<pre class="alt">            {</pre>
<pre>                <span class="kwrd">string</span>[] dirs = Directory.GetDirectories(</pre>
<pre class="alt">                    System.Web.HttpContext.Current.Request.PhysicalApplicationPath);</pre>
<pre>                <span class="kwrd">foreach</span> (<span class="kwrd">string</span> dir <span class="kwrd">in</span> dirs)</pre>
<pre class="alt">                {</pre>
<pre>                    Directory.Delete(dir, <span class="kwrd">true</span>);</pre>
<pre class="alt">                }</pre>
<pre>                Directory.CreateDirectory(clipBoardGUIDDirectory);</pre>
<pre class="alt">            }</pre>
<pre>        }</pre>
<pre class="alt">        <span class="kwrd">catch</span></pre>
<pre>        {</pre>
<pre class="alt">        }</pre>
<pre>        <span class="rem">//create the filename based on the current transaction, format, and object type.  </span></pre>
<pre class="alt">        <span class="rem">// We will parse this out later so we know how to add this back to the target clipboard.</span></pre>
<pre>        <span class="kwrd">string</span> fileName = clipBoardGUIDDirectory &#43; <span class="str">&quot;\\&quot; &#43; transactionGuid </pre>
<pre class="alt">            &#43; &quot;</span>_<span class="str">&quot; &#43; format &#43; &quot;</span>_&quot; &#43; objectType;</pre>
<pre>        FileStream fs = <span class="kwrd">new</span> FileStream(fileName, FileMode.Append, FileAccess.Write);</pre>
<pre class="alt">        fs.Position = fs.Length;</pre>
<pre>        fs.Write(buffer, 0, buffer.Length);</pre>
<pre class="alt">        fs.Close();</pre>
<pre>    }</pre>
</div>
<pre class="csharpcode">VB</pre>
<div class="csharpcode">
<pre class="alt">    &lt;WebMethod()&gt; <span class="kwrd">Public</span> <span class="kwrd">Sub</span> InsertMessageStream(<span class="kwrd">ByVal</span> buffer() <span class="kwrd">As</span> <span class="kwrd">Byte</span>, <span class="kwrd">ByVal</span> _</pre>
<pre>            format <span class="kwrd">As</span> <span class="kwrd">String</span>, <span class="kwrd">ByVal</span> objectType <span class="kwrd">As</span> <span class="kwrd">String</span>, <span class="kwrd">ByVal</span> transactionGuid _</pre>
<pre class="alt">            <span class="kwrd">As</span> <span class="kwrd">String</span>, <span class="kwrd">ByVal</span> isFinalTransaction <span class="kwrd">As</span> <span class="kwrd">Boolean</span>, <span class="kwrd">ByVal</span> clipBoardGUID <span class="kwrd">As</span> <span class="kwrd">String</span>)</pre>
<pre>        <span class="rem">'always base the current directory on the clipboard that we're sending now.</span></pre>
<pre class="alt">        <span class="kwrd">Dim</span> clipBoardDataDirectory <span class="kwrd">As</span> <span class="kwrd">String</span> = _</pre>
<pre>        (System.Web.HttpContext.Current.Request.PhysicalApplicationPath _</pre>
<pre class="alt">         &#43; <span class="str">&quot;\\Clipboard_Data&quot;</span>)</pre>
<pre>        <span class="kwrd">Dim</span> clipBoardGUIDDirectory <span class="kwrd">As</span> <span class="kwrd">String</span> = (clipBoardDataDirectory &#43; _</pre>
<pre class="alt">                                                (<span class="str">&quot;\\&quot;</span> &#43; clipBoardGUID))</pre>
<pre>        <span class="kwrd">Try</span></pre>
<pre class="alt">            <span class="rem">'if the directory does not exist then delete all the other directories (clipboard </span></pre>
<pre>            <span class="rem">' instances) and create a new directory if the directory already exists then this </span></pre>
<pre class="alt">            <span class="rem">' particular transaction is part of the same clipboard so don't do anything. this </span></pre>
<pre>            <span class="rem">' works because othe clipboardDirectory is based off of the GUID sent from the client.</span></pre>
<pre class="alt">            <span class="kwrd">If</span> <span class="kwrd">Not</span> Directory.Exists(clipBoardGUIDDirectory) <span class="kwrd">Then</span></pre>
<pre>                <span class="kwrd">Dim</span> dirs() <span class="kwrd">As</span> <span class="kwrd">String</span> = Directory.GetDirectories(clipBoardDataDirectory)</pre>
<pre class="alt">                <span class="kwrd">For</span> <span class="kwrd">Each</span> dir <span class="kwrd">As</span> <span class="kwrd">String</span> <span class="kwrd">In</span> dirs</pre>
<pre>                    Directory.Delete(dir, <span class="kwrd">True</span>)</pre>
<pre class="alt">                <span class="kwrd">Next</span></pre>
<pre>                Directory.CreateDirectory(clipBoardGUIDDirectory)</pre>
<pre class="alt">            <span class="kwrd">End</span> <span class="kwrd">If</span></pre>
<pre>        <span class="kwrd">Catch</span></pre>
<pre class="alt">&nbsp;</pre>
<pre>        <span class="kwrd">End</span> <span class="kwrd">Try</span></pre>
<pre class="alt">        <span class="rem">'create the filename based on the current transaction, format, and object type.  We </span></pre>
<pre>        <span class="rem">' will parse this out later so we know how to add this back to the target clipboard.</span></pre>
<pre class="alt">        <span class="kwrd">Dim</span> fileName <span class="kwrd">As</span> <span class="kwrd">String</span> = (clipBoardGUIDDirectory &#43; (<span class="str">&quot;\\&quot;</span> _</pre>
<pre>                    &#43; (transactionGuid &#43; (<span class="str">&quot;_&quot;</span> _</pre>
<pre class="alt">                    &#43; (format &#43; (<span class="str">&quot;_&quot;</span> &#43; objectType))))))</pre>
<pre>        <span class="kwrd">Dim</span> fs <span class="kwrd">As</span> FileStream = <span class="kwrd">New</span> FileStream(fileName, FileMode.Append, FileAccess.Write)</pre>
<pre class="alt">        fs.Position = fs.Length</pre>
<pre>        fs.Write(buffer, 0, buffer.Length)</pre>
<pre class="alt">        fs.Close()</pre>
<pre>    <span class="kwrd">End</span> Sub</pre>
</div>
<p>Each clipgoard format object is stored on disk for later retrievel by the client. Note in the screenshot below how the filename is used to store the unique transactionID for the object, the object type and also the clipboard format. All these pieces of information
 are necessary to reassemble the items correctly and place onto the target clipboard.
</p>
<p><a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1848530/image05.png"><img border="0" src="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1848530/image0_thumb1.png" width="728" height="198"></a>
</p>
<p>Now that we have a representation of each clipboard format object on the server we need a way to get each item back onto the target clipboard. The following web service method provides a return result of type “ClipboardStream”. The ClipboardStream object
 contains all relevant information necessary to reassemble each item onto the target clipboard. Since a web service is a request-response type relationship the web service expects the client to continue to call the web service until the all the clipboard items
 have been successfully received. In addition, further complexity is introduced because each individual clipboard item may be split into multiple items if they exceed the maximum length set by our contstant “byteCount”, therefore the target machine must keep
 track of each request and tell the server where the last transaction left off via the variable named “currentByte”. The web service code is shown below.
</p>
<p>C# </p>
<div class="csharpcode">
<pre class="alt">    [WebMethod]</pre>
<pre>    <span class="kwrd">public</span> ClipboardStream GetMessageStream(<span class="kwrd">string</span> transactionGUID, <span class="kwrd">string</span>[] </pre>
<pre class="alt">        previousTransactionGUIDs, <span class="kwrd">string</span> clipBoardGUID, <span class="kwrd">long</span> currentByte)</pre>
<pre>    {</pre>
<pre class="alt">        <span class="kwrd">string</span> clipBoardDataDirectory = </pre>
<pre>            System.Web.HttpContext.Current.Request.PhysicalApplicationPath &#43; </pre>
<pre class="alt">            <span class="str">&quot;Clipboard_Data&quot;</span>;</pre>
<pre>        <span class="kwrd">string</span> clipBoardGUIDDirectory = clipBoardDataDirectory &#43; <span class="str">&quot;\\&quot; &#43; </pre>
<pre class="alt">            clipBoardGUID;</pre>
<pre>        string currentTransaction = &quot;</span><span class="str">&quot;;</pre>
<pre class="alt">        bool isLastTransaction = false;</pre>
<pre>&nbsp;</pre>
<pre class="alt">&nbsp;</pre>
<pre>        //if the clipBoardGUID is not empty then we only need to make sure </pre>
<pre class="alt">        // that the directory still exists.</pre>
<pre>        if (clipBoardGUID != &quot;</span><span class="str">&quot;)</pre>
<pre class="alt">        {</pre>
<pre>            //if the directory does not exist throw an exception, it must </pre>
<pre class="alt">            // have already been deleted.</pre>
<pre>            if (!Directory.Exists(clipBoardGUIDDirectory))</pre>
<pre class="alt">            {</pre>
<pre>                throw new Exception(&quot;</span>Requested clipboard does not exist.<span class="str">&quot; &#43;</pre>
<pre class="alt">                        &quot;</span>It must have been deleted.&quot;);</pre>
<pre>            }</pre>
<pre class="alt">        }</pre>
<pre>        <span class="rem">//if the clipboardGUID is empty then this is the client's first contact </span></pre>
<pre class="alt">        <span class="rem">// with the server and we need</span></pre>
<pre>        <span class="rem">//to select the available clipboard GUID to return to the user.</span></pre>
<pre class="alt">        <span class="kwrd">else</span></pre>
<pre>        {</pre>
<pre class="alt">            <span class="kwrd">string</span>[] availableClipBoard = </pre>
<pre>                Directory.GetDirectories(clipBoardDataDirectory)[0].Split(<span class="str">'\\');</pre>
<pre class="alt">            clipBoardGUID = availableClipBoard[availableClipBoard.Length - 1];</pre>
<pre>            clipBoardGUIDDirectory &#43;= clipBoardGUID;</pre>
<pre class="alt">        }</pre>
<pre>&nbsp;</pre>
<pre class="alt">        //we need to get the next transaction.  Each time we finish a transaction </pre>
<pre>        // we add it to previousTransactionGUIDs at the client end so we know not </pre>
<pre class="alt">        // to send it again.</pre>
<pre>        currentTransaction = GetCurrentTransaction(clipBoardGUIDDirectory, </pre>
<pre class="alt">            previousTransactionGUIDs);</pre>
<pre>&nbsp;</pre>
<pre class="alt">        //if the current transaction is null then we'</span>re done and there are no </pre>
<pre>        <span class="rem">// more to send to the client</span></pre>
<pre class="alt">        <span class="kwrd">if</span> (currentTransaction == <span class="kwrd">null</span>)</pre>
<pre>        {</pre>
<pre class="alt">            <span class="kwrd">return</span> <span class="kwrd">null</span>;</pre>
<pre>        }</pre>
<pre class="alt">&nbsp;</pre>
<pre>        <span class="rem">//open the filestream and set it to the position requested by the client.</span></pre>
<pre class="alt">        FileStream fs = <span class="kwrd">new</span> FileStream(currentTransaction, FileMode.Open);</pre>
<pre>        fs.Position = currentByte;</pre>
<pre class="alt">&nbsp;</pre>
<pre>        <span class="rem">//determind if this is the last transaction or not for this object </span></pre>
<pre class="alt">        <span class="rem">// so we can let the client know.</span></pre>
<pre>        <span class="kwrd">long</span> numBytesToRead = fs.Length - currentByte;</pre>
<pre class="alt">        <span class="kwrd">if</span> (numBytesToRead &gt; byteCount)</pre>
<pre>        {</pre>
<pre class="alt">            numBytesToRead = byteCount;</pre>
<pre>            isLastTransaction = <span class="kwrd">false</span>;</pre>
<pre class="alt">        }</pre>
<pre>        <span class="kwrd">else</span></pre>
<pre class="alt">        {</pre>
<pre>            isLastTransaction = <span class="kwrd">true</span>;</pre>
<pre class="alt">        }</pre>
<pre>&nbsp;</pre>
<pre class="alt">        <span class="rem">//read the filestream bytes to the buffer and populate the object to </span></pre>
<pre>        <span class="rem">// return to the client.</span></pre>
<pre class="alt">        <span class="kwrd">byte</span>[] buffer = <span class="kwrd">new</span> <span class="kwrd">byte</span>[numBytesToRead];</pre>
<pre>        fs.Read(buffer, 0, (<span class="kwrd">int</span>)numBytesToRead);</pre>
<pre class="alt">        fs.Close();</pre>
<pre>&nbsp;</pre>
<pre class="alt">&nbsp;</pre>
<pre>        FileInfo fi = <span class="kwrd">new</span> FileInfo(currentTransaction);</pre>
<pre class="alt">        ClipboardStream clipboardStream = <span class="kwrd">new</span> ClipboardStream();</pre>
<pre>        clipboardStream.Buffer = buffer;</pre>
<pre class="alt">        clipboardStream.ClipBoardID = clipBoardGUID;</pre>
<pre>        clipboardStream.Format = fi.Name.Split(<span class="str">'_'</span>)[1];</pre>
<pre class="alt">        clipboardStream.ObjectType = fi.Name.Split(<span class="str">'_'</span>)[2];</pre>
<pre>        clipboardStream.IsLastTransaction = isLastTransaction;</pre>
<pre class="alt">        clipboardStream.TransactionID = currentTransaction;</pre>
<pre>&nbsp;</pre>
<pre class="alt">        <span class="kwrd">return</span> clipboardStream;</pre>
<pre>    }</pre>
</div>
<pre class="csharpcode">VB</pre>
<div class="csharpcode">
<pre class="alt">    &lt;WebMethod()&gt; <span class="kwrd">Public</span> <span class="kwrd">Function</span> GetMessageStream(<span class="kwrd">ByVal</span> transactionGUID <span class="kwrd">As</span> <span class="kwrd">String</span>, _</pre>
<pre>            <span class="kwrd">ByVal</span> previousTransactionGUIDs() <span class="kwrd">As</span> <span class="kwrd">String</span>, <span class="kwrd">ByVal</span> clipBoardGUID <span class="kwrd">As</span> <span class="kwrd">String</span>, _</pre>
<pre class="alt">            <span class="kwrd">ByVal</span> currentByte <span class="kwrd">As</span> <span class="kwrd">Long</span>) <span class="kwrd">As</span> ClipboardStream</pre>
<pre>        <span class="kwrd">Dim</span> clipBoardDataDirectory <span class="kwrd">As</span> <span class="kwrd">String</span> = _</pre>
<pre class="alt">            (System.Web.HttpContext.Current.Request.PhysicalApplicationPath &#43; <span class="str">&quot;Clipboard_Data&quot;</span>)</pre>
<pre>        <span class="kwrd">Dim</span> clipBoardGUIDDirectory <span class="kwrd">As</span> <span class="kwrd">String</span> = clipBoardDataDirectory</pre>
<pre class="alt">        <span class="kwrd">Dim</span> currentTransaction <span class="kwrd">As</span> <span class="kwrd">String</span> = <span class="str">&quot;&quot;</span></pre>
<pre>        <span class="kwrd">Dim</span> isLastTransaction <span class="kwrd">As</span> <span class="kwrd">Boolean</span> = <span class="kwrd">False</span></pre>
<pre class="alt">        <span class="rem">'if the clipBoardGUID is not empty then we only need to make sure that </span></pre>
<pre>        <span class="rem">' the directory still exists.</span></pre>
<pre class="alt">        <span class="kwrd">If</span> (clipBoardGUID &lt;&gt; <span class="str">&quot;&quot;</span>) <span class="kwrd">Then</span></pre>
<pre>            <span class="rem">'if the directory does not exist throw an exception, it must have </span></pre>
<pre class="alt">            <span class="rem">' already been deleted.</span></pre>
<pre>            <span class="kwrd">If</span> <span class="kwrd">Not</span> Directory.Exists(clipBoardGUIDDirectory) <span class="kwrd">Then</span></pre>
<pre class="alt">                <span class="kwrd">Throw</span> <span class="kwrd">New</span> Exception(<span class="str">&quot;Requested clipboard does not exist.&quot;</span> &#43; _</pre>
<pre>                                    <span class="str">&quot;It must have been deleted.&quot;</span>)</pre>
<pre class="alt">            <span class="kwrd">End</span> <span class="kwrd">If</span></pre>
<pre>        <span class="kwrd">End</span> <span class="kwrd">If</span></pre>
<pre class="alt">        <span class="rem">'if the clipboardGUID is empty then this is the client's first contact </span></pre>
<pre>        <span class="rem">' with the server and we need</span></pre>
<pre class="alt">        <span class="rem">'to select the available clipboard GUID to return to the user.</span></pre>
<pre>        <span class="kwrd">Dim</span> availableClipBoard() <span class="kwrd">As</span> <span class="kwrd">String</span> = Directory.GetDirectories _</pre>
<pre class="alt">            (clipBoardDataDirectory)(0).Split(Microsoft.VisualBasic.ChrW(92))</pre>
<pre>        clipBoardGUID = availableClipBoard((availableClipBoard.Length - 1))</pre>
<pre class="alt">        clipBoardGUIDDirectory = (clipBoardGUIDDirectory &#43; <span class="str">&quot;\&quot;</span> &#43; clipBoardGUID)</pre>
<pre>        <span class="rem">'we need to get the next transaction.  Each time we finish a transaction </span></pre>
<pre class="alt">        <span class="rem">' we add it to previousTransactionGUIDs</span></pre>
<pre>        <span class="rem">'at the client end so we know not to send it again.</span></pre>
<pre class="alt">        currentTransaction = GetCurrentTransaction(clipBoardGUIDDirectory, _</pre>
<pre>                                                   previousTransactionGUIDs)</pre>
<pre class="alt">        <span class="rem">'if the current transaction is null then we're done and there are no </span></pre>
<pre>        <span class="rem">' more to send to the client</span></pre>
<pre class="alt">        <span class="kwrd">If</span> (currentTransaction <span class="kwrd">Is</span> <span class="kwrd">Nothing</span>) <span class="kwrd">Then</span></pre>
<pre>            <span class="kwrd">Return</span> <span class="kwrd">Nothing</span></pre>
<pre class="alt">        <span class="kwrd">End</span> <span class="kwrd">If</span></pre>
<pre>        <span class="rem">'open the filestream and set it to the position requested by the client.</span></pre>
<pre class="alt">        <span class="kwrd">Dim</span> fs <span class="kwrd">As</span> FileStream = <span class="kwrd">New</span> FileStream(currentTransaction, FileMode.Open)</pre>
<pre>        fs.Position = currentByte</pre>
<pre class="alt">        <span class="rem">'determind if this is the last transaction or not for this object so </span></pre>
<pre>        <span class="rem">' we can let the client know.</span></pre>
<pre class="alt">        <span class="kwrd">Dim</span> numBytesToRead <span class="kwrd">As</span> <span class="kwrd">Long</span> = (fs.Length - currentByte)</pre>
<pre>        <span class="kwrd">If</span> (numBytesToRead &gt; byteCount) <span class="kwrd">Then</span></pre>
<pre class="alt">            numBytesToRead = byteCount</pre>
<pre>            isLastTransaction = <span class="kwrd">False</span></pre>
<pre class="alt">        <span class="kwrd">Else</span></pre>
<pre>            isLastTransaction = <span class="kwrd">True</span></pre>
<pre class="alt">        <span class="kwrd">End</span> <span class="kwrd">If</span></pre>
<pre>        <span class="rem">'read the filestream bytes to the buffer and populate the object to </span></pre>
<pre class="alt">        <span class="rem">' return to the client.</span></pre>
<pre>        <span class="kwrd">Dim</span> buffer() <span class="kwrd">As</span> <span class="kwrd">Byte</span> = <span class="kwrd">New</span> <span class="kwrd">Byte</span>((numBytesToRead) - 1) {}</pre>
<pre class="alt">        fs.Read(buffer, 0, <span class="kwrd">CType</span>(numBytesToRead, <span class="kwrd">Integer</span>))</pre>
<pre>        fs.Close()</pre>
<pre class="alt">&nbsp;</pre>
<pre>        <span class="kwrd">Dim</span> fi <span class="kwrd">As</span> FileInfo = <span class="kwrd">New</span> FileInfo(currentTransaction)</pre>
<pre class="alt">        <span class="kwrd">Dim</span> clipboardStream <span class="kwrd">As</span> ClipboardStream = <span class="kwrd">New</span> ClipboardStream</pre>
<pre>        clipboardStream.Buffer = buffer</pre>
<pre class="alt">        clipboardStream.ClipBoardID = clipBoardGUID</pre>
<pre>        clipboardStream.Format = fi.Name.Split(Microsoft.VisualBasic.ChrW(95))(1)</pre>
<pre class="alt">        clipboardStream.ObjectType = fi.Name.Split(Microsoft.VisualBasic.ChrW(95))(2)</pre>
<pre>        clipboardStream.IsLastTransaction = isLastTransaction</pre>
<pre class="alt">        clipboardStream.TransactionID = currentTransaction</pre>
<pre>        <span class="kwrd">Return</span> clipboardStream</pre>
<pre class="alt">    <span class="kwrd">End</span> Function</pre>
</div>
<p>The last remaining piece for our project to function correctly is the client code that receives the clipboard contents from the server and reassembles each item in the correct order, adding it to the clipboard with the correct format as each is completed.
 The client code for this is shown below.&nbsp;&nbsp;</p>
<pre class="csharpcode">C#</pre>
<div class="csharpcode">
<pre class="alt">        <span class="kwrd">string</span>[] transactionGuids = <span class="kwrd">null</span>;</pre>
<pre>&nbsp;</pre>
<pre class="alt">        ClipboardService.ClipboardStream clipBoardStream = </pre>
<pre>            <span class="kwrd">new</span> WindowsApplication1.ClipboardService.ClipboardStream();</pre>
<pre class="alt">        DataObject dataObject = <span class="kwrd">new</span> DataObject();</pre>
<pre>        clipBoardStream.ClipBoardID = <span class="str">&quot;&quot;</span>;</pre>
<pre class="alt">        clipBoardStream.IsLastTransaction = <span class="kwrd">false</span>;</pre>
<pre>        clipBoardStream.TransactionID = <span class="str">&quot;&quot;</span>;</pre>
<pre class="alt">        <span class="kwrd">long</span> currentByte = 0;</pre>
<pre>&nbsp;</pre>
<pre class="alt">        Clipboard.Clear();</pre>
<pre>&nbsp;</pre>
<pre class="alt">        <span class="rem">//while we don't get null back keep on contacting the web service </span></pre>
<pre>        <span class="rem">// to get the next ojbect.</span></pre>
<pre class="alt">        <span class="kwrd">while</span> (clipBoardStream != <span class="kwrd">null</span>)</pre>
<pre>        {</pre>
<pre class="alt">            MemoryStream memStream = <span class="kwrd">new</span> MemoryStream();</pre>
<pre>            <span class="rem">//while this is not the last transaction keep on contacting </span></pre>
<pre class="alt">            <span class="rem">// the web service to get the rest of this particular object.</span></pre>
<pre>            <span class="kwrd">while</span> (clipBoardStream.IsLastTransaction == <span class="kwrd">false</span>)</pre>
<pre class="alt">            {</pre>
<pre>                <span class="rem">//contact the web service to get the next transaction</span></pre>
<pre class="alt">                clipBoardStream = clipService.GetMessageStream(</pre>
<pre>                    clipBoardStream.TransactionID, transactionGuids, </pre>
<pre class="alt">                    clipBoardStream.ClipBoardID, currentByte);</pre>
<pre>&nbsp;</pre>
<pre class="alt">                <span class="kwrd">if</span> (clipBoardStream != <span class="kwrd">null</span>)</pre>
<pre>                {</pre>
<pre class="alt">                    <span class="rem">//write the results to the memory stream</span></pre>
<pre>                    memStream.Write(clipBoardStream.Buffer, 0, </pre>
<pre class="alt">                        clipBoardStream.Buffer.Length);</pre>
<pre>                    <span class="rem">//increment the current byte so next time we contact </span></pre>
<pre class="alt">                    <span class="rem">// the webservice we'll pick up where we left off</span></pre>
<pre>                    currentByte = memStream.Position;</pre>
<pre class="alt">&nbsp;</pre>
<pre>                    <span class="rem">//if it is the last transaction then we need to place </span></pre>
<pre class="alt">                    <span class="rem">// this item onto the clipboard.</span></pre>
<pre>                    <span class="kwrd">if</span> (clipBoardStream.IsLastTransaction)</pre>
<pre class="alt">                    {</pre>
<pre>                        <span class="rem">//handle the clipBoardStream appropriately and add it </span></pre>
<pre class="alt">                        <span class="rem">// to the dataObject for posting to the clipblard.</span></pre>
<pre>                        HandleFinalTransaction(clipBoardStream, memStream, <span class="kwrd">ref</span> dataObject);</pre>
<pre class="alt">&nbsp;</pre>
<pre>                        <span class="rem">//resize the transactionGuids array as necessary and </span></pre>
<pre class="alt">                        <span class="rem">// add the current transaction so next time we contact </span></pre>
<pre>                        <span class="rem">// the web service we won't get this one again.</span></pre>
<pre class="alt">                        <span class="kwrd">if</span> (transactionGuids == <span class="kwrd">null</span>)</pre>
<pre>                        {</pre>
<pre class="alt">                            Array.Resize(<span class="kwrd">ref</span> transactionGuids, 1);</pre>
<pre>                        }</pre>
<pre class="alt">                        <span class="kwrd">else</span></pre>
<pre>                        {</pre>
<pre class="alt">                            Array.Resize(<span class="kwrd">ref</span> transactionGuids, </pre>
<pre>                                transactionGuids.Length &#43; 1);</pre>
<pre class="alt">                        }</pre>
<pre>                        transactionGuids[transactionGuids.Length - 1] = </pre>
<pre class="alt">                            clipBoardStream.TransactionID;</pre>
<pre>                    }</pre>
<pre class="alt">                }</pre>
<pre>                <span class="kwrd">else</span></pre>
<pre class="alt">                {</pre>
<pre>                    <span class="kwrd">break</span>;</pre>
<pre class="alt">                }</pre>
<pre>            }</pre>
<pre class="alt">&nbsp;</pre>
<pre>            <span class="kwrd">if</span> (clipBoardStream != <span class="kwrd">null</span>)</pre>
<pre class="alt">            {</pre>
<pre>                clipBoardStream.IsLastTransaction = <span class="kwrd">false</span>;</pre>
<pre class="alt">                currentByte = 0;</pre>
<pre>            }</pre>
<pre class="alt">        }</pre>
<pre>&nbsp;</pre>
<pre class="alt">        Clipboard.SetDataObject(dataObject, <span class="kwrd">true</span>);</pre>
</div>
<pre class="csharpcode">VB</pre>
<div class="csharpcode">
<pre class="alt">        <span class="kwrd">Dim</span> transactionGuids() <span class="kwrd">As</span> <span class="kwrd">String</span> = <span class="kwrd">Nothing</span></pre>
<pre>        <span class="kwrd">Dim</span> clipBoardStream <span class="kwrd">As</span> ClipboardService.ClipboardStream = _</pre>
<pre class="alt">            <span class="kwrd">New</span> ClipboardVB.ClipboardService.ClipboardStream</pre>
<pre>        <span class="kwrd">Dim</span> dataObject <span class="kwrd">As</span> DataObject = <span class="kwrd">New</span> DataObject</pre>
<pre class="alt">        clipBoardStream.ClipBoardID = <span class="str">&quot;&quot;</span></pre>
<pre>        clipBoardStream.IsLastTransaction = <span class="kwrd">False</span></pre>
<pre class="alt">        clipBoardStream.TransactionID = <span class="str">&quot;&quot;</span></pre>
<pre>        <span class="kwrd">Dim</span> currentByte <span class="kwrd">As</span> <span class="kwrd">Long</span> = 0</pre>
<pre class="alt">        Clipboard.Clear()</pre>
<pre>        <span class="rem">'while we don't get null back keep on contacting the web service </span></pre>
<pre class="alt">        <span class="rem">' to get the next ojbect.</span></pre>
<pre>&nbsp;</pre>
<pre class="alt">        <span class="kwrd">While</span> (<span class="kwrd">Not</span> (clipBoardStream) <span class="kwrd">Is</span> <span class="kwrd">Nothing</span>)</pre>
<pre>            <span class="kwrd">Dim</span> memStream <span class="kwrd">As</span> MemoryStream = <span class="kwrd">New</span> MemoryStream</pre>
<pre class="alt">            <span class="rem">'while this is not the last transaction keep on contacting the</span></pre>
<pre>            <span class="rem">' web service to get the rest of this particular object.</span></pre>
<pre class="alt">&nbsp;</pre>
<pre>            <span class="kwrd">While</span> (clipBoardStream.IsLastTransaction = <span class="kwrd">False</span>)</pre>
<pre class="alt">                <span class="rem">'contact the web service to get the next transaction</span></pre>
<pre>&nbsp;</pre>
<pre class="alt">                clipBoardStream = clipService.GetMessageStream( _</pre>
<pre>                    clipBoardStream.TransactionID, transactionGuids, _</pre>
<pre class="alt">                    clipBoardStream.ClipBoardID, currentByte)</pre>
<pre>                <span class="kwrd">If</span> (<span class="kwrd">Not</span> (clipBoardStream) <span class="kwrd">Is</span> <span class="kwrd">Nothing</span>) <span class="kwrd">Then</span></pre>
<pre class="alt">                    <span class="rem">'write the results to the memory stream</span></pre>
<pre>                    memStream.Write(clipBoardStream.Buffer, 0, _</pre>
<pre class="alt">                                    clipBoardStream.Buffer.Length)</pre>
<pre>                    <span class="rem">'increment the current byte so next time we contact </span></pre>
<pre class="alt">                    <span class="rem">' the webservice we'll pick up where we left off</span></pre>
<pre>                    currentByte = memStream.Position</pre>
<pre class="alt">                    <span class="rem">'if it is the last transaction then we need to place </span></pre>
<pre>                    <span class="rem">' this item onto the clipboard.</span></pre>
<pre class="alt">                    <span class="kwrd">If</span> clipBoardStream.IsLastTransaction <span class="kwrd">Then</span></pre>
<pre>                        <span class="rem">'handle the clipBoardStream appropriately and add </span></pre>
<pre class="alt">                        <span class="rem">' it to the dataObject for posting to the clipblard.</span></pre>
<pre>                        HandleFinalTransaction(clipBoardStream, memStream, dataObject)</pre>
<pre class="alt">                        <span class="rem">'resize the transactionGuids array as necessary and </span></pre>
<pre>                        <span class="rem">' add the current transaction so next time we contact</span></pre>
<pre class="alt">                        <span class="rem">' the web service we won't get this one again.</span></pre>
<pre>                        <span class="kwrd">If</span> (transactionGuids <span class="kwrd">Is</span> <span class="kwrd">Nothing</span>) <span class="kwrd">Then</span></pre>
<pre class="alt">                            Array.Resize(transactionGuids, 1)</pre>
<pre>                        <span class="kwrd">Else</span></pre>
<pre class="alt">                            Array.Resize(transactionGuids, (transactionGuids.Length &#43; 1))</pre>
<pre>                        <span class="kwrd">End</span> <span class="kwrd">If</span></pre>
<pre class="alt">                        transactionGuids((transactionGuids.Length - 1)) = _</pre>
<pre>                            clipBoardStream.TransactionID</pre>
<pre class="alt">                    <span class="kwrd">End</span> <span class="kwrd">If</span></pre>
<pre>                <span class="kwrd">Else</span></pre>
<pre class="alt">                    <span class="kwrd">Exit</span> <span class="kwrd">While</span></pre>
<pre>                <span class="kwrd">End</span> <span class="kwrd">If</span></pre>
<pre class="alt">&nbsp;</pre>
<pre>            <span class="kwrd">End</span> <span class="kwrd">While</span></pre>
<pre class="alt">            <span class="kwrd">If</span> (<span class="kwrd">Not</span> (clipBoardStream) <span class="kwrd">Is</span> <span class="kwrd">Nothing</span>) <span class="kwrd">Then</span></pre>
<pre>                clipBoardStream.IsLastTransaction = <span class="kwrd">False</span></pre>
<pre class="alt">                currentByte = 0</pre>
<pre>            <span class="kwrd">End</span> <span class="kwrd">If</span></pre>
<pre class="alt">&nbsp;</pre>
<pre>        <span class="kwrd">End</span> <span class="kwrd">While</span></pre>
<pre class="alt">        Clipboard.SetDataObject(dataObject, <span class="kwrd">True</span>)</pre>
</div>
<h3>Conclusion</h3>
<p>In this project we've shown how any object (string, bitmap, or file) from the Clipboard can be serialized to an array of bytes for transmission over a web service call. We've also show how to store these clipboard items on disk on the server, and then retrieve
 them later on for assembly onto the target clipboard. I've often thought that this would be an interesting idea for a hosted ASP (Application Service Provider) service, where a hosting company would host the web service of this project for customer use. Look
 out for an upcoming article on how to add ASP .NET membership class functionality providing authentication and allowing multiple users to use the same instance of the web service.
</p>
<p>For working samples of the client application and web service in both C# and VB check out the&nbsp;<a href="http://ecn.channel9.msdn.com/o9/c4fcontent/migration/1848530/clipboard.zip">DOWNLOAD</a>.
</p>
<h3>Bio</h3>
<p>As President and principal founder of Personify Design Brian oversees the operations of the design and development businesses. Brian has more than 10 years experience in the technology industry. In his current role Brian's expertise lies in developing and
 architecting end to end customer solutions involving web application technologies such as SQL Server and ASP .NET. When not writing code Brian enjoys sailing in the Puget Sound on Far Niente, a 36 foot Catalina MKII cutter.</p>
 <img src="http://m.webtrends.com/dcs1wotjh10000w0irc493s0e_6x1g/njs.gif?dcssip=channel9.msdn.com&dcsuri=http://channel9.msdn.com/Niners/c4f.Brian-Trautman/Posts/RSS&WT.dl=0&WT.entryid=Entry:RSSView:4e78f4bb8a2d43d8a82a9e7600d523f7">]]></description>
      <comments>http://channel9.msdn.com/coding4fun/articles/Windows-Clipboard-Sharing-Through-Web-Services</comments>
      <itunes:summary>



&amp;nbsp;
This application transfers the contents of a machine&#39;s clipboard to another machine through the user of ASP .NET Web Services.



Brian Trautman


Difficulty: Easy
Time Required: 
1-3 hours
Cost: Free
Software: Visual Basic or Visual C# Express Editions
Hardware: None
Download: Download








Background
Have you ever worked on multiple machines and wished you could copy the clipboard contents from one machine to the other? I&#39;ve often found myself wishing that there was a quick and easy way to move text snippets, screenshots, or even files to another machine
 with a simple copy and paste. If this sounds interesting to you then read on.  
I wanted this to work regardless of whether both machines were online at the same time, and not be halted by firewalls, NAT&#39;s, etc. so I opted for a server based rather than a peer to peer architecture. The architecture involved a client application to transfer
 the clipboard contents to the server via a web services call, a web service to cache the clipboard contents, and another client component to retrieve the clipboard contents from the server and place them on the local machine&#39;s clipboard.
 
To tackle this problem we need programmatic access to the clipboard on both the machine to copy from and also the machine we want to copy to. Luckily .NET provides a managed wrapper around the native Windows Clipboard API that gives us access to this. The
 relevant namespaces are Clipboard for C# and my.Computer.Clipboard. Since we&#39;re interested in moving clipboard objects from one machine to another we first need to determine what type of objects get placed onto the clipboard for our various actions (copy text,
 images, and files). Writing a quick code snippet with the Clipboard namespace allows us to iterate through all objects on the clipboard for various types of actions to see what we&#39;re dealing with.
 
C#

        IDataObject clipData = Clipboard.GetDataObject();
&amp;nbsp;
        //retrieve an array of strings for all the f</itunes:summary>
      <link>http://channel9.msdn.com/coding4fun/articles/Windows-Clipboard-Sharing-Through-Web-Services</link>
      <pubDate>Fri, 09 Mar 2007 21:16:00 GMT</pubDate>
      <guid isPermaLink="false">http://channel9.msdn.com/coding4fun/articles/Windows-Clipboard-Sharing-Through-Web-Services</guid>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/1848530_100.jpg" height="75" width="100"></media:thumbnail>
      <media:thumbnail url="http://ecn.channel9.msdn.com/o9/c4f/images/1848530_220.jpg" height="165" width="220"></media:thumbnail>      
      <dc:creator>Brian Trautman</dc:creator>
      <itunes:author>Brian Trautman</itunes:author>
      <slash:comments>6</slash:comments>
      <wfw:commentRss>http://channel9.msdn.com/coding4fun/articles/Windows-Clipboard-Sharing-Through-Web-Services/RSS</wfw:commentRss>
      <category>Web Services</category>
      <category>Mash Up</category>
    </item>    
</channel>
</rss>