c# - WPF DataGrid에서 데이터를 붙여 넣을 때 OpenClipboard가 실패했습니다.




copy-paste (9)

.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 { }
}

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

이것은 정말로 성가신 일입니다.

이 문제에 대한 언급과 웹상의 다양한 위치에 대한 실제 해결책이 없습니다.

Visual Studio에서이 예외가 발생할 때 클립 보드가 잠겨 있는지 확인할 수 있습니다. 메시지에 붙여 넣기를 복사 할 수 없으므로 (파일에 써야 함). 또한 복사 프로세스가 시작되기 전에 클립 보드가 잠겨 있지 않았습니다.

이 문제를 해결하는 방법?


나는 또한 사용자가 ListBox를 숙독하면서 클립 보드에 정보를 복사하는 응용 프로그램에서 문제가 발생했습니다. 복사 된 정보는 선택한 항목과 관련이 있으며 편의를 위해 다른 응용 프로그램에 붙여 넣을 수 있습니다. 일부 사용자의 시스템에서는 CLIPBRD_E_CANT_OPEN을 (를) 얻을 수 있지만 다른 시스템에서는 그렇지 않습니다.

필자는 여전히 논쟁을 해결할 수 없었지만, 응용 프로그램이 충돌을 일으킬 수있는 코드를 만들 수있었습니다. 적어도 누군가에게 도움이되기를 희망하면서이 코드를 공유하고 싶습니다. 범인의 Process 개체를 찾기 위해 만든 using 문, 특성 및 메서드를 추가합니다. 프로세스 항목에서 프로세스의 이름, PID, 주 창 제목 (있는 경우) 및 기타 잠재적으로 유용한 데이터를 얻을 수 있습니다. 다음은 코드를 호출하는 코드없이 코드 행을 추가 한 것입니다. ( 참고 : 코드 스 니펫 아래에 공유 할 한 곡 더 있습니다.)

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

다른 참고 사항 : 내 코드를 단순화 한 것으로 바꾼 다른 한 가지는 System.Windows.Forms.Clipboard ( System.Windows.Forms.Clipboard 클래스 참조)로 변환하는 것이 었습니다. 재시도 횟수 및 재시도 지연을 밀리 초 단위로 포함하는 SetDataObject () 메서드 매개 변수. 적어도 내 코드에서 재시도 노이즈 를 제거했습니다.

귀하의 마일리지는 다를 수 있습니다 ... 플러스 거기에 내가 아직 따라하지 않은 부작용이있을 수 있습니다, 그래서 만약 그들 중 누구도 의견을 주시기 바랍니다. 어쨌든 이것은 이것이 누군가에게 유용함을 증명하기를 바랍니다.


TeraCopy (Windows 7, 64-bit)를 설치 한 이후 WPF 4.0 및 4.5 에서도이 문제가 발생했습니다. 모든 Clipboard.SetText ()는 System.Runtime.InteropServices.COMException으로 실패했습니다.

내 첫 번째 솔루션은 TeraCopy를 제거하는 것이었지만 효과적 이었지만이 애플리케이션을 좋아하기 때문에이 문제를 해결하기 위해 다른 솔루션을 찾아야했습니다. 해결책은

Clipboard.SetText("my string");

Clipboard.SetDataObject("my string");

이 목적에 맞는 CopyingRowClipboardContent (개체 보낸 사람, DataGridRowClipboardEventArgs e)에 대한 DataGrid 이벤트 / 메서드 서명이 있으며 Clipboard.SetDataObject (data) 또는 Clipboard.SetText (data)보다 더 신뢰할 수 있습니다.

사용법은 다음과 같습니다.

myDataGrid라는 dataGrid의 SelectionUnit 모드에서 "FullRow"를 설정합니다.

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

DataGrid의 행에 대해 해당 내용을 클립 보드에 복사하기 위해 호출되는 myDataGrid_CopyingRowClipboardContent 메서드가 있습니다. 예를 들어, 7 개의 행이있는 DataGrid의 경우이를 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;
}

RichTextBox에서도 같은 문제가 발생했습니다. 다음 코드는 무작위로 추락했습니다.

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

System.Windows.Controls.RichTextBox.Copy 를 사용하는 것이 좋습니다.


우리는 .NET 4.0을 사용하고 있습니다. 우리에게는 같은 문제가 있었지만 시스템에서 로그 오프 한 후 얼마 지나지 않아 코드가 정상적으로 작동했습니다.

마지막으로 대안을 찾았습니다.

문자열을 클립 보드에 복사하려면,

string data = "Copy This"

지금까지 다음과 같은 방법을 사용하고있었습니다.

Clipboard.SetText(data);

그것은 여러 번 실패하고있었습니다. 그런 다음 클립 보드 클래스 에서 클립 보드의 텍스트를 설정할 수있는 다른 방법을 살펴본 후 다음을 시도했습니다.

Clipboard.SetDataObject(data);

그리고 그것은 작동했습니다 :). 나는 결코 다시 문제가 없었다.


언급 된에서 내 대답을 추가 그래서 참조를 위해 질문 -

Andrew Smith ( http://blogs.infragistics.com/forums/t/35379.aspx 에서이 사실을 발견했습니다.

기술적으로 오직 하나의 프로세스 만 클립 보드를 열 수 있으므로 다른 프로세스가 열리면 첫 번째 클립 보드가 해제 될 때까지 후속 요청이 실패합니다. 이것은 일종의 WinForms Clipboard 클래스에서 각 try 사이에 지연을 사용하여 집합을 다시 시도하지만 WPF 클립 보드 클래스는이 작업을 수행하지 않으므로 첫 번째 쇼가 실패하면 예외가 발생합니다. 그래도 우리는 예외를 잡아서 여전히 실패 할 경우 클립 보드 작업 오류를 발생시켜야합니다.

똑같은 것이 설명되어 있고 그것을 고치기위한 몇 가지 방법이이 이탈리아 블로그에 언급되어 있습니다 -

WPF DataGrid 클립 보드 BUG (?) 및 Work (캐시 된 번역) ( Orignal Link )

다음 MSDN 포럼 스레드가이 특정 컴퓨터 특정 문제가 될 수 있음을 제안합니다 -

DataGrid에서 클립 보드로 복사 할 때 CLIPBRD_E_CANT_OPEN 예외


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

아래 함수 GetCharFromKey (Key key)가 트릭을 수행합니다.

일련의 win32 호출을 사용하여 누른 키를 디코딩합니다.

  1. WPF 키에서 가상 키 가져 오기

  2. 가상 키에서 스캔 코드를 얻는다.

  3. 유니 코드 문자 가져 오기

오래된 게시물 은 좀 더 자세하게 설명합니다.

      public enum MapType : uint
      {
         MAPVK_VK_TO_VSC = 0x0,
         MAPVK_VSC_TO_VK = 0x1,
         MAPVK_VK_TO_CHAR = 0x2,
         MAPVK_VSC_TO_VK_EX = 0x3,
      }

      [DllImport("user32.dll")]
      public static extern int ToUnicode(
          uint wVirtKey,
          uint wScanCode,
          byte[] lpKeyState,
          [Out, MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 4)] 
            StringBuilder pwszBuff,
          int cchBuff,
          uint wFlags);

      [DllImport("user32.dll")]
      public static extern bool GetKeyboardState(byte[] lpKeyState);

      [DllImport("user32.dll")]
      public static extern uint MapVirtualKey(uint uCode, MapType uMapType);

      public static char GetCharFromKey(Key key)
      {
         char ch = ' ';

         int virtualKey = KeyInterop.VirtualKeyFromKey(key);
         byte[] keyboardState = new byte[256];
         GetKeyboardState(keyboardState);

         uint scanCode = MapVirtualKey((uint)virtualKey, MapType.MAPVK_VK_TO_VSC);
         StringBuilder stringBuilder = new StringBuilder(2);

         int result = ToUnicode((uint)virtualKey, scanCode, keyboardState, stringBuilder, stringBuilder.Capacity, 0);
         switch (result)
         {
            case -1: 
               break;
            case 0: 
               break;
            case 1:
               {
                  ch = stringBuilder[0];
                  break;
               }
            default:
               {
                  ch = stringBuilder[0];
                  break;
               }
         }
         return ch;
      }




c# wpf datagrid clipboard copy-paste