programing

WPF DataGrid에서 붙여넣기 데이터를 복사할 때 OpenClipboard가 실패했습니다.

codeshow 2023. 4. 17. 22:10
반응형

WPF DataGrid에서 붙여넣기 데이터를 복사할 때 OpenClipboard가 실패했습니다.

데이터 그리드를 사용하는 WPF 애플리케이션이 있습니다.Visual Studio 2012와 Blend+Sketch Flow 미리보기를 설치하기 전까지는 응용 프로그램이 정상적으로 작동했습니다.이제 +(어느 애플리케이션에서나)를 사용하여 그리드의 데이터를 클립보드에 복사하려고 하면 다음과 같은 예외가 발생합니다.

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에서 이 예외가 발생하면 메시지를 복사하여 파일에 붙여넣을 수 없기 때문에 클립보드가 잠겨 있는지 확인할 수 있습니다.또한 복사 프로세스가 시작되기 전에 클립보드가 잠기지 않았습니다.

이 문제를 어떻게 해결하나요?

사용하고 있습니다.NET 4.0.같은 문제가 있었습니다만, 시스템 로그오프 후, 한동안 코드는 정상적으로 동작하고 있었습니다.

마침내 우리는 대안을 찾았다.

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

string data = "Copy This"

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

Clipboard.SetText(data);

그것은 계속해서 실패하고 있었다.그런 다음 클립보드 클래스에서 클립보드의 텍스트를 설정하는 데 사용할 수 있는 다른 방법을 살펴보고 다음을 시도했습니다.

Clipboard.SetDataObject(data);

효과가 있었습니다. :)다시는 그런 일이 없었지

WPF 클립보드핸들러의 버그입니다응용 프로그램에서 처리되지 않은 예외를 처리해야 합니다.Dispatcher Unhandled Exception 이벤트입니다.

이 Atribute를 에 추가합니다.ApplicationApp.xaml의 요소

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

저도 사용자가 ListBox를 읽을 때 클립보드에 정보를 복사하는 응용 프로그램에서 문제가 발생했습니다.복사되는 정보는 선택한 항목과 관련이 있으며, 편의를 위해 다른 응용 프로그램에 붙여넣을 수 있습니다.일부 사용자 시스템에서 CLIPBRD_E_CANT_OPEN이 표시되는 경우가 있지만 다른 사용자 시스템에서는 표시되지 않습니다.

경합을 수정하지 못했지만 경합을 일으키는 애플리케이션을 찾기 위한 코드를 만들 수 있었습니다.적어도 누군가에게 도움이 되길 바라면서 이 코드를 공유하고 싶습니다.범인의 Process 객체를 찾기 위해 작성한 사용문, 속성 및 메서드를 추가합니다.프로세스 항목에서 프로세스의 이름, PID, 기본 창 제목(있는 경우) 및 기타 유용한 데이터를 얻을 수 있습니다.코드 없이 추가한 코드 행은 다음과 같습니다(메모: 코드 조각 아래에 공유할 tidbit이 하나 더 있습니다).

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

기타 메모: 코드를 조금 간략하게 한 다른 변경 사항은 시스템 사용에서 변환하는 것입니다.윈도클립보드를 시스템에 연결합니다.윈도Forms. 클립보드(시스템 참조).윈도Forms.Clipboard Class)는 리트라이 횟수와 리트라이 지연(밀리초 단위)을 포함한4 파라미터 SetDataObject() 메서드가 있기 때문입니다.이것으로 적어도 코드에서 재시도 노이즈의 일부가 제거되었습니다.

주행거리가 다를 수 있습니다.또, 아직 깨닫지 못한 부작용이 있을지도 모르기 때문에, 아는 사람이 있으면 코멘트를 주세요.어쨌든, 나는 이것이 누군가에게 도움이 되기를 바란다.

TeraCopy(Windows 7, 64비트)를 인스톨 했을 때부터, WPF 4.0 및 4.5에서도 이 문제가 발생했습니다.모든 클립보드SetText()가 시스템에서 실패했습니다.런타임상호 운용 서비스COMException.

첫 번째 솔루션은 Tera Copy를 제거하는 것이었습니다.이 어플리케이션이 마음에 들었기 때문에 이 문제를 해결하기 위해 다른 솔루션을 찾아야 했습니다.해결방법은,

Clipboard.SetText("my string");

와 함께

Clipboard.SetDataObject("my string");

RichTextBox에서도 같은 문제가 있었습니다.다음 코드가 랜덤으로 크래시되었습니다.

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

시스템을 사용하는 것이 더 좋을 것 같습니다.윈도컨트롤리치 텍스트 박스카피

를 사용하여 클립보드에서 XAML 데이터를 가져오는 데 문제가 발생했습니다.NET 4.6.1.

오류 메시지:

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에 의해 구현된 기본 복사 모드를 사용할 수 있는 솔루션을 찾았습니다.

이전 답변은 효과가 없었습니다.

  • 클립보드 사용.SetDataObject(데이터) 클립보드의 인스톨.SetText(데이터) --> 이 솔루션은 기대했던 솔루션과는 달랐습니다.복사기능을 실장하고 싶지 않았습니다.
  • Dispatcher Unhandled Exception 처리 : 이유는 모르겠지만 동작하지 않았습니다.이 이벤트에 연결된 메서드가 호출되지 않았습니다.

나는 마침내 이 문제를 해결할 새로운 방법을 찾았다.Ctrl + C를 누르기 전에 클립보드를 지우기만 하면 됩니다.

그래서 Main Windows.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();
    }
}

그 후, 문제는 해결되었다.

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

'에도요.while루프가 10회 이상일 경우 예외가 발생합니다.나는 그것의 최대 카운터를 1로 테스트하고 1회 반복 클립보드로 동작합니다.

코드 app.xaml

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

코드 파일 app.xaml.cs

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;

namespace WpfApp1
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        private void DataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.C && (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
            {
                System.Windows.Forms.Clipboard.Clear();
            }
        }
    }
}

나는 이 코드를 처리했다.

을 위한 이벤트CopyingRowClipboardContent )object sender,DataGridRowClipboardEventArgs.Clipboard.SetDataObject(data) ★★★★★★★★★★★★★★★★★」Clipboard.SetText(data).

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

선택 항목에서 "FullRow"를 설정합니다.myDataGrid라는 dataGrid의 단위 모드

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

이 ★★★★★★★★★★★★★★★★★★★★★★★.myDataGrid_CopyingRowClipboardContentdataGrid의 각 행에 대해 해당 내용을 클립보드에 복사하도록 호출됩니다.예를 들어, 행이 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;
}

WPF 데이터그램을 Excel(CSV)로 내보내기 위한 확장 메서드를 씁니다.

"MyDatagrid"가 데이터그램의 이름일 경우, 하나의 회선 코드를 사용하여 사용자 제어로 호출합니다.

MyDatagrid.ExportToExcel(this);

이 메서드를 확장 스태틱클래스에 추가합니다.

#region DataGrid Extentions

public static void ExportToExcel(this DataGrid dg, UserControl owner, string filename = "")
{
    try
    {
        dg.SelectionMode = DataGridSelectionMode.Extended;
        dg.SelectAllCells();

        Clipboard.Clear();
        ApplicationCommands.Copy.Execute(null, dg);

        var saveFileDialog = new SaveFileDialog
        {
            FileName = filename != "" ? filename : "gpmfca-exportedDocument",
            DefaultExt = ".csv", 
            Filter = "Common Seprated Documents (.csv)|*.csv"
        };

        if (saveFileDialog.ShowDialog() == true)
        {
            var clip2 = Clipboard.GetText();
            File.WriteAllText(saveFileDialog.FileName, clip2.Replace('\t', ','), Encoding.UTF8);
            Process.Start(saveFileDialog.FileName);
        }    
   
        dg.UnselectAllCells();
        dg.SelectionMode = DataGridSelectionMode.Single;
    }
    catch (Exception ex)
    {
        owner.ShowMessageBox(ex.Message);
        Clipboard.Clear();
    }
}
#endregion

마지막으로 잊지 않고 하다

using Microsoft.Win32;

확장 클래스 및 세트

ClipboardCopyMode="IncludeHeader"

네 데이터그램을 위해.

언급URL : https://stackoverflow.com/questions/12769264/openclipboard-failed-when-copy-pasting-data-from-wpf-datagrid

반응형