web-dev-qa-db-ja.com

WPF DataGridから貼り付けデータをコピーするとOpenClipboardが失敗しました

Datagridを使用したWPFアプリケーションがあります。 Visual Studio 2012およびBlend + SketchFlowプレビューをインストールするまで、アプリケーションは正常に機能しました。さて、グリッドからクリップボードにデータをコピーしようとすると Ctrl + C (どのアプリケーションでも)、次の例外が発生しています:

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()

これは本当に迷惑です。

私はこの問題への参照をいくつか見ました here およびWeb上のさまざまな場所で、実際の解決策はありません。

Visual Studioでこの例外が発生すると、クリップボードがロックされていることを確認できます。メッセージをコピーして貼り付けることができなかったためです(ファイルに書き込む必要がありました)。また、コピープロセスが開始される前にクリップボードはロックされませんでした。

この問題を解決するには?

65
Arsen Zahray

.NET 4.0を使用しています。同じ問題がありましたが、システムからログオフした後、しばらくの間コードが正常に機能していました。

最後に、代替案を見つけました。

文字列をクリップボードにコピーする場合は、

string data = "Copy This"

今まで私は次の方法を使用していました

Clipboard.SetText(data);

何度も失敗していました。次に、Clipboard Classのクリップボードにテキストを設定するために利用できる他のメソッドを見て、次のことを試しました。

Clipboard.SetDataObject(data);

そして、それは働いた:)。私は二度と問題を抱えていません。

85
kushdilip

これは、WPFクリップボードハンドラーのバグです。 Application.DispatcherUnhandledExceptionイベントで未処理の例外を処理する必要があります。

この属性をApp.xamlのApplication要素に追加します

DispatcherUnhandledException="Application_DispatcherUnhandledException"

このコードを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;
}
70
Alex Wiese

私も、ユーザーがListBoxを熟読するときに情報をクリップボードにコピーするアプリケーションで問題を抱えています。コピーされた情報は、選択されたアイテムに関連しており、便宜上、他のアプリケーションにその情報(前述の情報)を貼り付けることができます。時々、一部のユーザーのシステムでCLIPBRD_E_CANT_OPENを取得しますが、他のシステムでは取得しません。

fixの競合はまだできませんが、競合の原因となっているアプリケーションを見つけるためのコードをいくつか作成できました。少なくともこのコードを誰かに役立つことを期待して共有したいと思います。 usingステートメント、属性、および作成したメソッドを追加して、犯人のProcessオブジェクトを見つけます。 Processアイテムから、プロセスの名前、PID、メインウィンドウタイトル(ある場合)、およびその他の潜在的に有用なデータを取得できます。これを呼び出すコードなしで追加したコード行を次に示します。 (注:コードスニペットの下に、共有するもう1つの情報があります):

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;
    }

その他の注記:コードを少し簡略化したもう1つの変更点は、System.Windows.Clipboardを使用して変換することでした。 System.Windows.Forms.ClipboardSystem.Windows.Forms.Clipboard Class を参照)後者には4つのパラメーターがあるためSetDataObject ()再試行回数と再試行遅延をミリ秒単位で含むメソッド。これにより、少なくともコードから再試行noiseの一部が削除されました。

あなたの走行距離は変わるかもしれません...そして、私はまだつまずいたことのないこれに副作用があるかもしれませんので、誰かがそれらを知っているならばコメントしてください。いずれにせよ、これが誰かに役立つことを願っています。

6
John

TeraCopy(Windows 7、64ビット)をインストールしたため、WPF 4.0および4.5でもこの問題が発生しました。すべてのClipboard.SetText()は、System.Runtime.InteropServices.COMExceptionで失敗しました。

私の最初の解決策はTeraCopyをアンインストールすることでした-それは機能しましたが、私はこのアプリケーションが大好きなので、その問題を解決するために別の解決策を探す必要がありました。解決策は交換することでした

Clipboard.SetText("my string");

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

RichTextBoxでも同じ問題が発生しました。次のコードはランダムにクラッシュしました:

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

System.Windows.Controls.RichTextBox.Copyを使用することが望ましいようです

2
AVEbrahimi

.NET 4.6.1でクリップボードからXAMLデータを取得する際に問題が発生しました。

エラーメッセージ:

OpenClipboardの失敗(HRESULTからの例外:0x800401D0(CLIPBRD_E_CANT_OPEN)))

私は次のように解決しました:

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

最後に、DataGridによって実装されたデフォルトのコピーモードを使用するソリューションを見つけました。

以前の答えは私にはうまくいきませんでした:

  • Clipboard.SetDataObject(data);を使用Clipboard.SetText(data)のinsteed->このソリューションは期待したものではなく、コピー機能を自分で実装したくありませんでした。
  • DispatcherUnhandledExceptionの処理:理由はわかりませんが、うまくいきませんでした。このイベントにアタッチされたメソッドは呼び出されませんでした。

この問題を処理する新しい方法をようやく見つけました。 「Ctrl + C」を押す前に、クリップボードをクリアするだけです。

そのため、MainWindows.xamlファイルリソースで新しいスタイルを作成しました。

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

このスタイルは、アプリケーションのすべてのデータグリッドで「previewKeyDown」を処理するために作成されています。呼び出されるメソッドは次のとおりです。

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

その後、問題は解決しました。

1
zlink17

Excelのセルをクリップボードにコピーし、クリップボードからデータをHTML文字列として取得する際にも同じ問題がありました。

以下のコードのように(while-try-catch)を使用できます。

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;
    }
}

また、ループが10回以上ある場合、whileにカウンターを含めることができ、例外が発生します。私はその最大カウンターが1つで、一度にループクリップボードの作業をテストします。

1
Majid gharaei

言及されたSO参照用の質問-

Andrew Smithの http://blogs.infragistics.com/forums/t/35379.aspx からこれを見つけました。

技術的には、1つのプロセスのみがクリップボードを開くことができます。そのため、別のプロセスが開いた場合、最初のクリップボードが解放されるまで後続の要求は失敗します。これはWinForms Clipboardクラスで処理され、各試行の間に遅延を設定してセットを再試行しますが、WPFクリップボードクラスはこれを行わないため、最初のショーで失敗すると例外が発生します。それでも、おそらく例外をキャッチし、まだ失敗する場合はクリップボード操作エラーを発生させる必要があります。

同じことがイタリアのブログで説明されており、それを修正するいくつかの方法が言及されています-

WPF DataGrid Clipboard BUG(?)&Work

Google翻訳

MSDNフォーラムのスレッドをたどると、これはマシン固有の問題である可能性がありますが、他のマシンで再現できますか? -

DataGridからクリップボードにコピーする際のCLIPBRD_E_CANT_OPEN例外

更新:

ブログのリンクはダウンしているようですが、キャッシュされたバージョンにはこのリンクからアクセスできます-

WPF DataGrid Clipboard BUG(?)&Work(Cached translation)

1
akjoshi

この正確な目的のためのDataGridイベント/メソッドシグネチャCopyingRowClipboardContentobject senderDataGridRowClipboardEventArgs e)があり、Clipboard.SetDataObject(data)またはClipboard.SetText(data)よりも信頼性があります。

使用方法は次のとおりです。

MyDataGridというdataGridのSelectionUnitモードで「FullRow」を設定します

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

myDataGrid_CopyingRowClipboardContentというメソッドがあります。このメソッドは、dataGridのeach行に対して呼び出され、その内容をクリップボードにコピーします。たとえば、7行のデータグリッドの場合、これは7回呼び出されます。

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