web-dev-qa-db-fra.com

OpenClipboard à échoué lors de la copie des données à partir de WPF DataGrid

J'ai une application WPF utilisant Datagrid. L'application fonctionnait correctement jusqu'à l'installation de Visual Studio 2012 et de l'aperçu Blend + SketchFlow. Maintenant, quand j'essaie de copier les données de la grille dans le presse-papiers avec Ctrl + C (quelle que soit l'application), je reçois l'exception suivante:

System.Runtime.InteropServices.COMException (0x800401D0): OpenClipboard Failed (Exception from HRESULT: 0x800401D0 (CLIPBRD_E_CANT_OPEN))
   at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
   at System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(Int32 errorCode, IntPtr errorInfo)
   at System.Windows.Clipboard.Flush()
   at System.Windows.Clipboard.CriticalSetDataObject(Object data, Boolean copy)
   at System.Windows.Controls.DataGrid.OnExecutedCopy(ExecutedRoutedEventArgs args)
   at System.Windows.Controls.DataGrid.OnExecutedCopy(Object target, ExecutedRoutedEventArgs args)
   at System.Windows.Input.CommandBinding.OnExecuted(Object sender, ExecutedRoutedEventArgs e)
   at System.Windows.Input.CommandManager.ExecuteCommandBinding(Object sender, ExecutedRoutedEventArgs e, CommandBinding commandBinding)
   at System.Windows.Input.CommandManager.FindCommandBinding(CommandBindingCollection commandBindings, Object sender, RoutedEventArgs e, ICommand command, Boolean execute)
   at System.Windows.Input.CommandManager.FindCommandBinding(Object sender, RoutedEventArgs e, ICommand command, Boolean execute)
   at System.Windows.Input.CommandManager.OnExecuted(Object sender, ExecutedRoutedEventArgs e)
   at System.Windows.UIElement.OnExecutedThunk(Object sender, ExecutedRoutedEventArgs e)
   at System.Windows.Input.ExecutedRoutedEventArgs.InvokeEventHandler(Delegate genericHandler, Object target)
   at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   at System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
   at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)
   at System.Windows.Input.RoutedCommand.ExecuteImpl(Object parameter, IInputElement target, Boolean userInitiated)
   at System.Windows.Input.RoutedCommand.ExecuteCore(Object parameter, IInputElement target, Boolean userInitiated)
   at System.Windows.Input.CommandManager.TranslateInput(IInputElement targetElement, InputEventArgs inputEventArgs)
   at System.Windows.UIElement.OnKeyDownThunk(Object sender, KeyEventArgs e)
   at System.Windows.Input.KeyEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
   at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   at System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
   at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)
   at System.Windows.Input.InputManager.ProcessStagingArea()
   at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
   at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
   at System.Windows.Interop.HwndKeyboardInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawKeyboardActions actions, Int32 scanCode, Boolean isExtendedKey, Boolean isSystemKey, Int32 virtualKey)
   at System.Windows.Interop.HwndKeyboardInputProvider.ProcessKeyAction(MSG& msg, Boolean& handled)
   at System.Windows.Interop.HwndSource.CriticalTranslateAccelerator(MSG& msg, ModifierKeys modifiers)
   at System.Windows.Interop.HwndSource.OnPreprocessMessage(Object param)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at System.Windows.Threading.Dispatcher.Invoke(DispatcherPriority priority, Delegate method, Object arg)
   at System.Windows.Interop.HwndSource.OnPreprocessMessageThunk(MSG& msg, Boolean& handled)
   at System.Windows.Interop.HwndSource.WeakEventPreprocessMessage.OnPreprocessMessage(MSG& msg, Boolean& handled)
   at System.Windows.Interop.ComponentDispatcherThread.RaiseThreadMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.Run()
   at System.Windows.Application.RunDispatcher(Object ignore)
   at System.Windows.Application.RunInternal(Window window)
   at System.Windows.Application.Run(Window window)
   at System.Windows.Application.Run()

C'est vraiment énervant.

J'ai vu quelques références à ce problème ici et à divers endroits sur le Web, sans réelle solution.

Je peux vérifier que le Presse-papiers est verrouillé lorsque cette exception est déclenchée dans Visual Studio, car je ne pouvais pas copier-coller le message (je devais l'écrire dans un fichier). En outre, le Presse-papiers n'était pas verrouillé avant le début du processus de copie.

Comment résoudre ce problème?

63
Arsen Zahray

Nous utilisons .NET 4.0. Nous avons eu le même problème, mais après avoir déconnecté le système, le code fonctionnait normalement pendant un certain temps.

Enfin nous avons trouvé l'alternative.

Si vous voulez copier une chaîne dans le presse-papiers,

string data = "Copy This"

Jusqu'à présent, j'utilisais la méthode suivante

Clipboard.SetText(data);

Il échouait encore et encore. Ensuite, j'ai examiné d'autres méthodes disponibles pour définir du texte dans le presse-papiers dans Clipboard Class et j'ai essayé les solutions suivantes:

Clipboard.SetDataObject(data);

Et ça a marché :). Je n'ai plus jamais eu le problème.

79
kushdilip

C'est un bogue dans le gestionnaire de presse-papiers WPF. Vous devez gérer l'exception non gérée dans l'événement Application.DispatcherUnhandledException.

Ajoutez cet attribut à l'élément Application dans votre App.xaml

DispatcherUnhandledException="Application_DispatcherUnhandledException"

Ajoutez ce code à votre fichier App.xaml.cs

void Application_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
    var comException = e.Exception as System.Runtime.InteropServices.COMException;

    if (comException != null && comException.ErrorCode == -2147221040)
         e.Handled = true;
}
68
Alex Wiese

Moi aussi, je rencontre un problème dans une application dans laquelle je copie des informations dans le presse-papiers lorsque les utilisateurs consultent un ListBox. Les informations copiées sont liées à l'élément sélectionné et leur permettent de les coller (informations mentionnées) dans d'autres applications pour plus de commodité. Parfois, j'obtiens CLIPBRD_E_CANT_OPEN sur les systèmes de certains utilisateurs, mais pas sur d'autres.

Bien que je n’ai toujours pas pu résoudre le conflit, j’ai pu créer du code pour trouver l’application causant le conflit. J'aimerais au moins partager ce code dans l'espoir que cela aide quelqu'un. J'ajouterai l'instruction using, les attributs et la méthode que j'ai créés pour trouver l'objet Process du coupable. A partir de l'élément Process, vous pouvez obtenir le nom du processus, le PID, le titre de la fenêtre principale (s'il en a un) et d'autres données potentiellement utiles. Voici les lignes de code que j'ai ajoutées sans le code qui l'appelle. (NOTE: Au-dessous de l'extrait de code, j'ai encore une friandise à partager):

using System.Diagnostics;               // For Process class
using System.Runtime.InteropServices;   // For DllImport's

...

[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern IntPtr GetOpenClipboardWindow();

[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

...

    ///-----------------------------------------------------------------------------
    /// <summary>
    /// Gets the Process that's holding the clipboard
    /// </summary>
    /// <returns>A Process object holding the clipboard, or null</returns>
    ///-----------------------------------------------------------------------------
    public Process ProcessHoldingClipboard()
    {
        Process theProc = null;

        IntPtr hwnd = GetOpenClipboardWindow();

        if (hwnd != IntPtr.Zero)
        {
            uint processId;
            uint threadId = GetWindowThreadProcessId(hwnd, out processId);

            Process[] procs = Process.GetProcesses();
            foreach (Process proc in procs)
            {
                IntPtr handle = proc.MainWindowHandle;

                if (handle == hwnd)
                {
                    theProc = proc;
                }
                else if (processId == proc.Id)
                {
                    theProc = proc;
                }
            }
        }

        return theProc;
    }

OTHER NOTE: Une autre chose qui simplifiait un peu mon code était de convertir de System.Windows.Clipboard à System.Windows.Forms.Clipboard (voir System.Windows.Forms.Clipboard Class ), car cette dernière dispose d'une méthode SetDataObject () à 4 paramètres, qui inclut un nombre de tentatives et un délai de nouvelle tentative en millisecondes. Cela a au moins supprimé certaines tentatives bruit de mon code. 

Votre kilométrage peut varier ... De plus, il peut y avoir des effets secondaires sur lesquels je ne suis pas encore tombé par hasard. Si quelqu'un le sait, veuillez les commenter. En tout état de cause, j'espère que cela s'avérera utile à quelqu'un.

6
John

J'ai également eu ce problème dans WPF 4.0 et 4.5 depuis que j'ai installé TeraCopy (Windows 7, 64 bits). Chaque Clipboard.SetText () a échoué avec une exception System.Runtime.InteropServices.COMException.

Ma première solution consistait à désinstaller TeraCopy - cela fonctionnait, mais j'adore cette application. J'ai donc dû chercher une autre solution pour résoudre ce problème. La solution était de remplacer

Clipboard.SetText("my string");

avec

Clipboard.SetDataObject("my string");
5
pr0gg3r

J'ai eu le même problème avec RichTextBox. Le code suivant s'est écrasé de manière aléatoire:

TextRange tr = new TextRange(rich.Document.ContentStart, rich.Document.ContentEnd);
System.Windows.Clipboard.SetDataObject(tr.Text);

Il semble préférable d’utiliser System.Windows.Controls.RichTextBox.Copy

2
AVEbrahimi

J'ai eu un problème pour récupérer les données XAML du presse-papiers avec .NET 4.6.1.

Message d'erreur:

Echec d'OpenClipboard (exception de HRESULT: 0x800401D0 (CLIPBRD_E_CANT_OPEN)))

Je l'ai résolu comme suit:

int counter = 0;
object xamlClipData = null;

while (xamlClipData == null)
{
    try
    {
        if (counter > 10)
        {
            System.Windows.MessageBox.Show("No access to clipboard xaml data.");
            break;
        }

        counter++;

        if (System.Windows.Clipboard.GetDataObject().GetDataPresent(DataFormats.Xaml))
        {
            xamlClipData = System.Windows.Clipboard.GetData(DataFormats.Xaml);
        }
    }
    catch { }
}
2
Pollitzer

J'ai finalement trouvé une solution pour utiliser le mode de copie par défaut implémenté par DataGrid.

Les réponses précédentes n'ont pas fonctionné pour moi:

  • Using Clipboard.SetDataObject (data); insteed de Clipboard.SetText (data) -> Cette solution n’était pas ce à quoi je m'attendais, je ne voulais pas implémenter moi-même la fonctionnalité de copie.
  • Traitement DispatcherUnhandledException: Je ne sais pas pourquoi, mais cela n’a pas fonctionné pour moi. La méthode attachée à cet événement n'a pas été appelée.

J'ai enfin trouvé un nouveau moyen de gérer ce problème. Il vous suffit d'effacer le presse-papiers avant d'appuyer sur "Ctrl + C".

J'ai donc créé un nouveau style dans les ressources de fichier MainWindows.xaml:

<Window.Resources>
    <Style TargetType="DataGrid">
        <EventSetter Event="PreviewKeyDown" Handler="DataGrid_PreviewKeyDown"/>
    </Style>
</Window.Resources>

Ce style est conçu pour gérer le "previewKeyDown" dans toutes les datagrids de mon application. La méthode appelée est la suivante:

private void DataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.C && (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
    {
        System.Windows.Forms.Clipboard.Clear();
    }
}

Après cela, le problème a été résolu.

1
zlink17

J'ai eu le même problème en copiant des cellules Excel dans le presse-papiers et en obtenant des données du presse-papiers sous forme de chaîne HTML.

Vous pouvez utiliser (while-try-catch) comme dans le code ci-dessous.

Excel.Application exap = new Microsoft.Office.Interop.Excel.Application();
Excel.Workbook wb = exap.Workbooks.Open(
                      sourceFileNameTextBox.Text,
                      Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                      Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                      Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                      Type.Missing, Type.Missing);
Excel.Sheets sh = wb.Worksheets;

bool clip = false;

// Copy Excel cells to clipboard
while (!clip)
{
    try
    {
        ws.Cells.get_Range(cells[0], cells[1]).Copy(Type.Missing);
        clip = true;
    }
    catch
    {
        clip = false;
    }
}

string b = "";

// Get Excel cells data from the clipboard as HTML

clip = false;
while(!clip)
{
    try
    {
        b = Clipboard.GetData(DataFormats.Html) as string;
        clip = true;
    }
    catch
    {
        clip = false;
    }
}

De plus, vous pouvez avoir un compteur dans while si la boucle est plus de 10 fois ou plus, une exception se produit. Je teste que son compteur maximum est un et dans un seul travail du presse-papier en boucle.

1
Majid gharaei

Ajout de ma réponse à la question mentionnée SO pour référence -

Trouvé ceci par Andrew Smith à http://blogs.infragistics.com/forums/t/35379.aspx -

Techniquement, un seul processus peut ouvrir le presse-papiers, donc si un autre processus a-t-il ouvert les demandes suivantes échouera jusqu'au libère d'abord le presse-papiers. C'était en quelque sorte traité dans WinForms Presse-papiers classe où il faudrait réessayer le jeu avec un délai entre chacun essaie mais la classe du presse-papier WPF ne le fait pas, donc si elle échoue le le premier spectacle l'exception se produit. Même alors, nous devrions probablement attraper l'exception et déclencher l'erreur d'opération du presse-papiers si elle échoue toujours.

La même chose est expliquée et certains moyens de le réparer sont mentionnés sur ce blog italien -

WPF DataGrid Clipboard, bogue (?) & Travail

Google Traduction

Suivre le fil de discussion MSDN suggère que cela pourrait être un problème spécifique à une machine. Pouvez-vous reproduire cela sur d’autres machines? -

Exception CLIPBRD_E_CANT_OPEN lors de la copie dans le Presse-papiers à partir d'un Grille de données

Mettre à jour:

Le lien de blog semble être en panne, mais une version en cache est accessible via ce lien -

WPF DataGrid Clipboard, bogue (?) & Travail (traduction en cache)

1
akjoshi

Il existe une signature d'événement/de méthode DataGrid dans ce but précis, CopyingRowClipboardContent (expéditeur d'objet, DataGridRowClipboardEventArgs) et est plus fiable que Clipboard.SetDataObject (data) ou Clipboard.SetText (data).

Voici comment l'utiliser.

Définissez "FullRow" dans le mode SelectionUnit pour dataGrid appelé myDataGrid

<DataGrid x:Name="myDataGrid" SelectionUnit="FullRow"></DataGrid>

Nous avons une méthode, myDataGrid_CopyingRowClipboardContent, qui est appelée pour each row dans le contrôle de données afin de copier son contenu dans le Presse-papiers. Par exemple, pour une grille de données à sept lignes, il est appelé sept fois.

public int clipboardcalledcnt { get; set; } // CopyingRowClipboardContent invoked count
private void myDataGrid_CopyingRowClipboardContent(object sender, DataGridRowClipboardEventArgs e)
{
    PathInfo cellpath = new PathInfo(); // A custom class to hold path information
    string path = string.Empty;

    DataGrid dgdataPaths = (DataGrid)sender;
    int rowcnt = dgdataPaths.SelectedItems.Count;

    cellpath = (PathInfo)e.Item;

    path = "Row #" + clipboardcalledcnt + " Len=" + cellpath.Length.ToString() + ", path=" + cellpath.Path;

    e.ClipboardRowContent.Clear();

    if (clipboardcalledcnt == 0) // Add header to clipboard paste
        e.ClipboardRowContent.Add(new DataGridClipboardCellContent("", null, "--- Clipboard Paste ---\t\t\n")); // \t cell divider, repeat (number of cells - 1)

    clipboardcalledcnt++;
    e.ClipboardRowContent.Add(new DataGridClipboardCellContent(path, null, path));

    if (clipboardcalledcnt == rowcnt)
        clipboardcalledcnt = 0;
}
0
Markus