プログラミングとかブログ

Unity/C#/SRPGStudio/RPGツクールMVの情報とかその他気になったことを調べて書きます。

DataGridTextColumnでコピー時エラーへの対処

xamlでDataGridTextColumnを作って、セルを1回クリックして選択状態にし、Ctrl+Cでコピーすると、例外が発生して落ちました。
f:id:shirakamisauto:20150806113530g:plain
詳細は以下のような例外でした。

System.Runtime.InteropServices.COMException はハンドルされませんでした。
  HResult=-2147221040
  Message=OpenClipboard に失敗しました (HRESULT からの例外:0x800401D0 (CLIPBRD_E_CANT_OPEN))
  Source=mscorlib
  ErrorCode=-2147221040
  StackTrace:
       場所 System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
       場所 System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(Int32 errorCode, IntPtr errorInfo)
       場所 System.Windows.Clipboard.Flush()
       場所 System.Windows.Clipboard.CriticalSetDataObject(Object data, Boolean copy)
       場所 System.Windows.Controls.DataGrid.OnExecutedCopy(ExecutedRoutedEventArgs args)
       場所 System.Windows.Controls.DataGrid.OnExecutedCopy(Object target, ExecutedRoutedEventArgs args)
       場所 System.Windows.Input.CommandBinding.OnExecuted(Object sender, ExecutedRoutedEventArgs e)
       場所 System.Windows.Input.CommandManager.ExecuteCommandBinding(Object sender, ExecutedRoutedEventArgs e, CommandBinding commandBinding)
       場所 System.Windows.Input.CommandManager.FindCommandBinding(Object sender, RoutedEventArgs e, ICommand command, Boolean execute)
       場所 System.Windows.Input.CommandManager.OnExecuted(Object sender, ExecutedRoutedEventArgs e)
       場所 System.Windows.UIElement.OnExecutedThunk(Object sender, ExecutedRoutedEventArgs e)
       場所 System.Windows.Input.ExecutedRoutedEventArgs.InvokeEventHandler(Delegate genericHandler, Object target)
       場所 System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
       場所 System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
       場所 System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
       場所 System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
       場所 System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
       場所 System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)
       場所 System.Windows.Input.RoutedCommand.ExecuteImpl(Object parameter, IInputElement target, Boolean userInitiated)
       場所 System.Windows.Input.RoutedCommand.ExecuteCore(Object parameter, IInputElement target, Boolean userInitiated)
       場所 System.Windows.Input.CommandManager.TranslateInput(IInputElement targetElement, InputEventArgs inputEventArgs)
       場所 System.Windows.UIElement.OnKeyDownThunk(Object sender, KeyEventArgs e)
       場所 System.Windows.Input.KeyEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
       場所 System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
       場所 System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
       場所 System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
       場所 System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
       場所 System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
       場所 System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)
       場所 System.Windows.Input.InputManager.ProcessStagingArea()
       場所 System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
       場所 System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
       場所 System.Windows.Interop.HwndKeyboardInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawKeyboardActions actions, Int32 scanCode, Boolean isExtendedKey, Boolean isSystemKey, Int32 virtualKey)
       場所 System.Windows.Interop.HwndKeyboardInputProvider.ProcessKeyAction(MSG& msg, Boolean& handled)
       場所 System.Windows.Interop.HwndSource.CriticalTranslateAccelerator(MSG& msg, ModifierKeys modifiers)
       場所 System.Windows.Interop.HwndSource.OnPreprocessMessage(Object param)
       場所 System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
       場所 MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
以下略

原因はインストールしていたCLCLというコピー履歴を保持するソフトと思われます。
おそらくクリップボードへのコピーが競合して、DataGridTextColumnでのコピー時にClipboard.Flush()が失敗したことが原因ではないでしょうか。
DataGridを扱うときにはコピー履歴をとるようなソフトは使わないほうがいいんでしょうね。

ただ、併用したい場合もあるでしょうから、とりあえず以下のサイトを参考に例外が起きないようにします。stackoverflow.com

まず、以下のようにApp.xamlにApplication_DispatcherUnhandledExceptionイベントを設定します。
これで未定義の例外をキャッチできるようにします。

<Application x:Class="AnimeCheckerByXaml.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml" 
             DispatcherUnhandledException="Application_DispatcherUnhandledException">
    <Application.Resources>
         
    </Application.Resources>
</Application>

次にApp.xaml.csでこのイベントを記述し、DispatcherUnhandledExceptionEventArgsの例外をクリップボードエラーのエラーコードを指定することで決め打ちで処理しています。

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