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 Smile (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.

Pubblicità

, ,

Lascia un commento