Articoli con tag Clipboard
WPF DataGrid Clipboard BUG(?)&Workaround
Il mese scorso mi sono imbattuto in questo strano problema: La mia applicazione crashava su un particolare computer (che, per curiosità, aveva WinXP SP3 ma non credo sia questo il problema). L’utente sostiene che l’applicazione crasha anche stando a guardarla… molto strano.
0) Scoperta
L’eccezione non gestita, ricavata sia dal log applicativo (catturata nell’evento Application.DispatcherUnhandledException) sia da Event Viewer, era la seguente:
OpenClipboard Failed (Exception from HRESULT: 0x800401D0 (CLIPBRD_E_CANT_OPEN))
Dallo stack trace si evinceva che l’eccezione veniva lanciata dalla DataGrid di WPF4. Bene… andiamo in dettaglio.
1) Teoria della clipboard (fonte: Clipboard on MSDN Library)
Come è ovvio, la clipboard è un elemento di sistema a cui possono accedere più applicazioni contemporaneamente. A livello di API, ogni applicazione esegue, in genere, i seguenti passaggi:
– OpenClipboard()
– clear/read/set data
– CloseClipboard()
OpenClipboard() ritorna true se l’applicativo riesce a avere possesso della clipboard. In questo momento solamente lui è in grado di accedervi. Nessun’altra applicazione può accedervi (sia in scrittura che in lettura) prima che essa venga rilasciata con CloseClipboard().
2) Riproduzione del problema:
Create due applicazioni. La prima, un semplice Console Application, che esegue il seguente codice:
[DllImport("user32.dll")]
extern static bool OpenClipboard(IntPtr hWnd);
if (OpenClipboard(IntPtr.Zero))//lock clipboard
Thread.Sleep(60000);//così ho tempo per lanciare l’altra applicazione.
La seconda, WPF application, con un textbox e una DataGrid popolata e con elementi ClipboardContentBinding. Ora lanciate (non da VS) la console application, debuggate l’app WPF.
Premete CTRL+C sulla textbox. Blocco di pochi istanti ma niente. In questo caso il fatto che la clipboard sia lockata è correttamente gestita dalla textbox.
Premete CTRL+C selezionando qualcosa nella griglia… Bam! Ecco l’eccezione.
3) Soluzione:
Le strade sono due. La prima è scrivere una classe che deriva da DataGrid e, nell’override del metodo OnExecutedCopy() gestisce l’eccezione (credo si possa fare, anche se non ne sono troppo sicuro). Il secondo è gestire l’eccezione direttamente in Application.DispatcherUnhandledException. Poiché la DataGrid l’avevo usata in più punti nell’applicativo, non sapevo se questo era un caso isolato o se da altre parti sorgesse lo stesso problema e #soprattutto# dato i tempi stretti, ho optato per la seconda con questo codice
var comEx = e.Exception as System.Runtime.InteropServices.COMException; if (comEx != null) { switch (comEx.ErrorCode) { case -2147221040: //OpenClipboard Failed (Exception from HRESULT: 0x800401D0 (CLIPBRD_E_CANT_OPEN)) try { //Try get info about the application which lock the clipboard IntPtr handle = GetOpenClipboardWindow(); if (handle != IntPtr.Zero) { int capacity = GetWindowTextLength(new HandleRef(this, handle)) * 2; StringBuilder stringBuilder = new StringBuilder(capacity); GetWindowText(new HandleRef(this, handle), stringBuilder, stringBuilder.Capacity); Trace.TraceWarning(string.Format("CLIPBOARD LOCKED BY {0}!", stringBuilder.ToString())); } } finally { e.Handled = true; //Handle Exception. No error to user. No crash of the application. } return; } } Trace.TraceError(e.Exception.Message);
Questo è tutto.
4) Riflessioni:
Chissà quale programma causava il problema… ve lo dirò quando mi farò dare il log (se mai l’avrò)
5) Considerazioni:
Se qualcuno volesse pingare il team di sviluppo per spiegare il problema faccia pure, oppure se volesse spiegarmi come fare contattatemi.