Pergunta

Por que o código a seguir às vezes causa uma exceção com o conteúdo "CLIPBRD_E_CANT_OPEN":

Clipboard.SetText(str);

Isso geralmente ocorre na primeira vez que a área de transferência é usada no aplicativo e não depois disso.

Foi útil?

Solução

Na verdade, acho que esse é o culpa da API Win32.

Para definir dados na área de transferência, você deve abra primeiro.Apenas um processo pode ter a área de transferência aberta por vez.Então, quando você verificar se outro processo está com a área de transferência aberta por qualquer razão, sua tentativa de abri-lo falhará.

Acontece que os Serviços de Terminal controlam a área de transferência e, em versões mais antigas do Windows (pré-Vista), você precisa abrir a área de transferência para ver o que há dentro...o que acaba bloqueando você.A única solução é esperar até que os Serviços de Terminal fechem a área de transferência e tentar novamente.

É importante perceber que isso não é específico dos Serviços de Terminal:isso pode acontecer com qualquer coisa.Trabalhar com a área de transferência no Win32 é uma condição de corrida gigante.Mas, como por design você só deve mexer na área de transferência em resposta à entrada do usuário, isso geralmente não representa um problema.

Outras dicas

Isso é causado por um bug/recurso na área de transferência dos Serviços de Terminal (e possíveis outras coisas) e pela implementação .NET da área de transferência.Um atraso na abertura da área de transferência causa o erro, que geralmente passa em alguns milissegundos.

A solução é tentar várias vezes em um loop e dormir entre elas.

for (int i = 0; i < 10; i++)
{
    try
    {
        Clipboard.SetText(str);
        return;
    }
    catch { }
    System.Threading.Thread.Sleep(10);
} 

Eu sei que esta pergunta é antiga, mas o problema ainda existe.Conforme mencionado anteriormente, esta exceção ocorre quando a área de transferência do sistema é bloqueada por outro processo.Infelizmente, existem muitas ferramentas de recorte, programas para capturas de tela e ferramentas de cópia de arquivos que podem bloquear a área de transferência do Windows.Então você receberá a exceção toda vez que tentar usar Clipboard.SetText(str) quando tal ferramenta estiver instalada em seu PC.

Solução:

nunca use

Clipboard.SetText(str);

use em vez disso

Clipboard.SetDataObject(str);

Na verdade, pode haver outro problema em questão.A chamada da estrutura (ambos os sabores WPF e winform) para algo assim (o código é do refletor):

private static void SetDataInternal(string format, object data)
{
    bool flag;
    if (IsDataFormatAutoConvert(format))
    {
        flag = true;
    }
    else
    {
        flag = false;
    }
    IDataObject obj2 = new DataObject();
    obj2.SetData(format, data, flag);
    SetDataObject(obj2, true);
}

Observe que SetDataObject é sempre chamado com true neste caso.

Internamente, isso aciona duas chamadas para a API win32, uma para definir os dados e outra para liberá-los do seu aplicativo para que fiquem disponíveis após o fechamento do aplicativo.

Já vi vários aplicativos (alguns plugins do Chrome e um gerenciador de downloads) que escutam o evento da área de transferência.Assim que a primeira chamada for realizada, o aplicativo abrirá a área de transferência para examinar os dados, e a segunda chamada para liberação falhará.

Não encontrei uma boa solução, exceto escrever minha própria classe de área de transferência que usa API win32 direta ou chamar setDataObject diretamente com false para manter os dados após o fechamento do aplicativo.

Resolvi esse problema em meu próprio aplicativo usando funções nativas do Win32:OpenClipboard(), CloseClipboard() e SetClipboardData().

Abaixo da classe wrapper que fiz.Alguém poderia por favor revise-o e diga se está correto ou não.Principalmente quando o código gerenciado está rodando como aplicativo x64 (eu uso Any CPU nas opções do projeto). O que acontece quando eu vinculo bibliotecas x86 do aplicativo x64?

Obrigado!

Aqui está o código:

public static class ClipboardNative
{
    [DllImport("user32.dll")]
    private static extern bool OpenClipboard(IntPtr hWndNewOwner);

    [DllImport("user32.dll")]
    private static extern bool CloseClipboard();

    [DllImport("user32.dll")]
    private static extern bool SetClipboardData(uint uFormat, IntPtr data);

    private const uint CF_UNICODETEXT = 13;

    public static bool CopyTextToClipboard(string text)
    {
        if (!OpenClipboard(IntPtr.Zero)){
            return false;
        }

        var global = Marshal.StringToHGlobalUni(text);

        SetClipboardData(CF_UNICODETEXT, global);
        CloseClipboard();

        //-------------------------------------------
        // Not sure, but it looks like we do not need 
        // to free HGLOBAL because Clipboard is now 
        // responsible for the copied data. (?)
        //
        // Otherwise the second call will crash
        // the app with a Win32 exception 
        // inside OpenClipboard() function
        //-------------------------------------------
        // Marshal.FreeHGlobal(global);

        return true;
    }
}

Isso aconteceu comigo no meu aplicativo WPF.O OpenClipboard falhou (exceção de HRESULT:0x800401D0 (CLIPBRD_E_CANT_OPEN)).

eu uso

ApplicationCommands.Copy.Execute(null, myDataGrid);

a solução é limpar a área de transferência primeiro

Clipboard.Clear();
ApplicationCommands.Copy.Execute(null, myDataGrid);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top