Compartilhamento da área de transferência do Windows com o Web Services (Parte 1)

Este aplicativo transfere o conteúdo da área de transferência de um computador para outro usando o ASP .NET Web Services

Por | @oficinadanet Programação
Histórico

Já aconteceu de você estar trabalhando em vários computadores ao mesmo tempo e precisar copiar o conteúdo da área de transferência de um computador em outro? Eu sempre quis que existisse uma forma fácil e rápida de mover trechos de textos, capturas de telas e até mesmo arquivos para outro computador com um simples copiar e colar. Se soar interessante para você, continue lendo este artigo.

Eu queria que funcionasse independentemente de ambos os computadores estarem ligados ao mesmo tempo, e não queria ser interrompido por firewalls, NATs etc. Por isso, optei por uma arquitetura baseada em servidor, em vez de ponto-a-ponto. A arquitetura inclui um aplicativo cliente, para transferir o conteúdo da área de transferência ao servidor por meio de uma chamada ao Web Services, um Web service, para armazenar esse conteúdo em cache, e outro componente cliente para recuperar o conteúdo e disponibilizá-lo na área de transferência do computador local.

Para resolver esse problema, precisamos de acesso via programação às áreas de transferência de ambos os computadores, tanto o de origem quanto o de destino. Felizmente, o .NET oferece um invólucro gerenciado da API nativa da área de transferência do Windows que nos permite esse acesso. Os namespaces relevantes são Clipboard for C# e my.Computer.Clipboard. Já que estamos interessados em mover objetos da área de transferência de um computador para outro, primeiro precisamos determinar que tipos de objetos serão colocados na área de transferência em cada uma das ações (copiar texto, imagens e arquivos). Um pequeno trecho de código com o namespace Clipboard nos permitirá iterar em cada um dos objetos da área de transferência em busca dos vários tipos de ações, para verificar com o que estamos lidando.

C#
IDataObject clipData = Clipboard.GetDataObject();

//retrieve an array of strings for all the formats available on the clipboard.
string[] formats = clipData.GetFormats();

//iterate through the list of formats on the clipboard
foreach (string format in formats)
{
      //add each objeoct to an arraylist so we can inspect the object types
      object dataObject = clipData.GetData(format);
              
}


VB
Dim clipData As IDataObject = Clipboard.GetDataObject

Dim formats() As String = clipData.GetFormats

'iterate through the list of formats on the clipboard
For Each format As String In formats
    'add each objeoct to an arraylist so we can inspect the object types
    Dim dataObject As Object = clipData.GetData(format)
Next


A captura de tela abaixo mostra o resultado de se copiar texto do Word para a área de transferência. Cada formato na matriz de seqüências de caracteres é uma representação dos dados da área de transferência. Como ainda não se sabe qual será o aplicativo de destino (onde os dados serão colados), a área de transferência guarda os mesmos dados em vários formatos. O objetivo desse projeto é replicar todos esses formatos na área de transferência do computador de destino.

Compartilhamento da área de transferência do Windows com o Web Services (Parte 1)

Após a inspeção na área de transferência em busca de objetos tipo texto, imagem e arquivo, ficou fácil determinar os principais tipos com os quais devemos nos preocupar. São eles: "System.IO.MemoryStream", “System.IO.FileStream”, "System.Drawing.Bitmap", e "System.String". Como todas essas informações serão transferidas ao servidor por meio de Web Services, torna-se simples serializar todos os objetos em bytes para a transmissão. Esse procedimento é necessário por uma série de razões, incluindo o fato de que objetos complexos, como MemoryStreams, simplesmente não podem ser serializados e enviados através da chamada ao Web Service, como é possível com objetos Strings. Além disso, alguns objetos são maiores do que o permitido pela chamada ao Web Service. Com isso, seria necessário dividi-los em pedaços menores para a transmissão e, depois, esses objetos precisariam ser montados novamente no servidor, na ordem correta. Além disso, quando o cliente solicitar os itens da área de transferência, será preciso desmontar cada objeto, enviar por meio de um Web Service, retornar o resultado ao cliente e, então, remontá-lo.

O primeiro item a ser criado é uma função de base que lide com a quebra desses fluxos muito grandes em matrizes de bytes mais gerenciáveis, para possibilitar a transmissão ao Web Service. A função abaixo executa essa tarefa enviando blocos de MemoryStream com tamanhos limitados pela constante “byteCount”. Quando o limite é alcançado, o buffer é enviado pela chamada Web Service para armazenamento e montagem no servidor. Se houver 0 bytes a serem enviados, ou quantidade de bytes inferior à constante “byteCount”, enviaremos os elementos restantes do buffer e usaremos o sinalizador “isFinalTransaction” para mostrar ao Web service que este objeto específico está concluído.

C#
private void UploadStreamBlock(string format, string objectType, MemoryStream memStream)
        {
            //each time we enter this function we have a new transaction beginning.  A transaction represents a comlete
            //object on the clipboard and we'll use this on the server side to know how to put the stream back together
            string transactionGuid = System.Guid.NewGuid().ToString();
            memStream.Position = 0;

            byte[] buffer = new byte[byteCount];
            bool isFinalTransaction = false;

            //while the current stream position plus our byte count is less than the length of the stream continue sending as much
            //as we can.
            while ((memStream.Position + byteCount) <= memStream.Length)
            {
                //if we happen to be on the last byte of the stream set the final transaction flag to true so the server
                //will know that this is the last bit of this transaction to expect.
                if (memStream.Position + byteCount == memStream.Length)
                {
                    isFinalTransaction = true;
                }
                //read the stream into our buffer for transmission over the web service.
                memStream.Read(buffer, 0, byteCount);
                ws.InsertMessageStream(buffer, format, objectType, transactionGuid, isFinalTransaction, clipBoardGUID);
            }

            long remainingBytes = memStream.Length - memStream.Position;
            //if we still have remaining bytes left figure out how many and transmit the last bit of this ojbect over the
            //web service.
            if ((int)remainingBytes > 0)
            {
                byte[] remainingBuffer = new byte[(int)remainingBytes];

                memStream.Read(remainingBuffer, 0, (int)remainingBytes);
                ws.InsertMessageStream(remainingBuffer, format, objectType, transactionGuid, true, clipBoardGUID);
            }

}


VB
Private Sub UploadStreamBlock(ByVal format As String, ByVal objectType As String, ByVal memStream As MemoryStream)
        'each time we enter this function we have a new transaction beginning.  A transaction represents a comlete
        'object on the clipboard and we'll use this on the server side to know how to put the stream back together
        Dim transactionGuid As String = System.Guid.NewGuid.ToString
        memStream.Position = 0
        Dim buffer() As Byte = New Byte((byteCount) - 1) {}
        Dim isFinalTransaction As Boolean = False
        'while the current stream position plus our byte count is less than the length of the stream continue sending as much
        'as we can.

        While ((memStream.Position + byteCount) _
                    <= memStream.Length)
            'if we happen to be on the last byte of the stream set the final transaction flag to true so the server
            'will know that this is the last bit of this transaction to expect.
            If ((memStream.Position + byteCount) _
                        = memStream.Length) Then
                isFinalTransaction = True
            End If
            'ream the stream into our buffer for transmission over the web service.
            memStream.Read(buffer, 0, byteCount)
            clipService.InsertMessageStream(buffer, format, objectType, transactionGuid, isFinalTransaction, clipBoardGUID)

        End While
        Dim remainingBytes As Long = (memStream.Length - memStream.Position)
        'if we still have remaining bytes left figure out how many and transmit the last bit of this ojbect over the
        'web service.
        If (CType(remainingBytes, Integer) > 0) Then
            Dim remainingBuffer() As Byte = New Byte((CType(remainingBytes, Integer)) - 1) {}
            memStream.Read(remainingBuffer, 0, CType(remainingBytes, Integer))
            clipService.InsertMessageStream(remainingBuffer, format, objectType, transactionGuid, True, clipBoardGUID)
        End If
    End Sub

O lado do servidor do Web Service precisa recriar a área de transferência a partir de várias matrizes de bytes. Por isso, é importante que todos os objetos, tipos e formatos sejam preservados para que a área de transferência funcione de forma adequada no computador de destino. Nós usamos o clipBoardGuid para determinar se estamos lidando com uma área de transferência nova ou adicionando objetos a uma instância existente. Usamos o sinalizador isFinalTransaction para saber se a matriz de bytes deveria ser parte de uma transação existente, ou se era a primeira em uma nova transação. Todos os itens da área de transferência são salvos em disco, para uma recuperação posterior por qualquer cliente que os solicite. O código para esse procedimento está abaixo.

target="_blank">Continua...
Fonte: Microsoft MSDN

Mais sobre:
Share Tweet
Recomendado
Comentários
Carregar comentários
Destaquesver tudo