Uso de SafeDispatcher para controles hospedados personalizados en Unified Service Desk

Unified Service Desk es una aplicación basada en Windows Presentation Foundation (WPF) en la que todas las operaciones de Unified Service Desk se ejecutan en el hilo Distribuidor WPF principal. La clase Distribuidor WPF proporciona servicios para administrar la cola de los elementos de trabajo para un hilo.

Puede ampliar Unified Service Desk creando controles personalizados y hospedándolos en Unified Service Desk. Sin embargo, si un control hospedado personalizado contiene código defectuoso o ejecuta operaciones usando nuevos hilos sin administrar adecuadamente excepciones durante la ejecución de código, puede provocar problemas de estabilidad en Unified Service Desk, y puede que incluso producir que la aplicación cliente se bloquee o deje de responder. Las excepciones no controladas en controles personalizados de terceros hacen que resulte difícil identificar y solucionar problemas al equipo del producto/de soporte técnico pues quizá no tenga acceso a la información de por qué un error/excepción apareció en Unified Service Desk y al código exacto que produjo el error.

Presentamos SafeDispatcher, que proporciona un mecanismo eficaz e informativo de control de excepciones para controles hospedados personalizados en Unified Service Desk proporcionando registro predefinido para excepciones no controladas con información detallada sobre el origen y la causa de la excepción, y permitiéndole que configure o sobrescriba el control de excepciones de SafeDispatcher para realizar otros pasos. Esto también impide que el cliente de Unified Service Desk deje de responder debido a excepciones no controladas en el código del control hospedado personalizado.

¿Qué es SafeDispatcher?

SafeDispatcher se basa en las mismas líneas que Distribuidor WPF y proporciona un control de excepciones resiliente e informativo para controles hospedados personalizados en Unified Service Desk. Se expone como propiedad protegida, SafeDispatcher, en la clase DynamicsBaseHostedControl, lo que hace que SafeDispatcher esté disponible automáticamente para todos los controles hospedados personalizados de Unified Service Desk que se derivan de la claseDynamicsBaseHostedControl.

Nota

No use la clase SafeDispatcher en el código para trabajar con SafeDispatcher. En su lugar, debe usar la propiedad SafeDispatcher en la instancia de control hospedado personalizado que se deriva de la clase DynamicsBaseHostedControl para usar SafeDispatcher.

Al igual que Distribuidor WPF, SafeDispatcher proporciona métodos como BeginInvoke, Invoke e InvokeAsync para ejecutar operaciones de forma sincrónica o asincrónica en SafeDispatcher con un parámetro booleano adicional, runOnMainUiThread, que controla si se ejecutará SafeDispatcher en el hilo de la interfaz de usuario o no.

SafeDispatcher proporciona los siguientes beneficios:

  • Hilo protegido del distribuidor de interfaz de usuario: los programadores pueden ejecutar todas las operaciones dependientes de la interfaz de usuario en SafeDispatcher estableciendo el parámetro runOnMainUiThread como "true" en el método de invocación para ejecutar SafeDispatcher en el hilo de la interfaz de usuario. Cualquier excepción no controlada que se presente en el distribuidor principal de interfaz de usuario se controlará de forma segura a nivel del control hospedado en lugar de aparecer en el controlador global de Evento de DispatcherUnhandledException.

  • Hilo protegido del distribuidor ajeno a la interfaz de usuario: Los programadores pueden ejecutar todo el código independiente de la interfaz de usuario en SafeDispatcher. estableciendo el parámetro runOnMainUiThread como "false" en el método de invocación para ejecutar SafeDispatcher en el hilo ajeno a la interfaz de usuario. Cualquier excepción no controlada que se presente en el distribuidor principal ajeno a la interfaz de usuario se controlará de forma segura a nivel del control hospedado en lugar de aparecer en el controlador global de Evento de DispatcherUnhandledException.

  • Información detallada sobre origen y causa de excepciones: el controlador de excepciones de SafeDispatcher aparece cuando se activa una excepción no controlada a nivel de DynamicsBaseHostedControl por acción del hilo de la interfaz de usuario o del hilo ajeno a la interfaz de usuario, lo que permite que Unified Service Desk capture información fundamental a nivel de control hospedado, como un nombre del control hospedado, un tipo de control hospedado o un nombre de método, y la realización de un seguimiento de pila completa para identificar la ubicación y la causa exactas de la excepción.

  • Configure o reemplace el controlador de excepciones de SafeDispatcher: Los desarrolladores pueden aprovechar el comportamiento predefinido del controlador de excepciones de SafeDispatcher para mostrar al usuario información sobre la excepción no controlada o reemplazar el comportamiento según su requisito de negocios, como configurar registros adicionales, cerrar controles basados en sesión o salir del cliente de Unified Service Desk.

¿Cómo usar SafeDispatcher?

La propiedad SafeDispatcher está disponible para todas las instancias de control hospedado personalizado de Unified Service Desk que se deriven de la clase DynamicsBaseHostedControl. Una instancia de SafeDispatcher estará disponible para ejecutarse en el hilo de interfaz de usuario cuando se inicialice el control hospedado personalizado. Sin embargo, una instancia de SafeDispatcher sólo estará disponible para ejecutarse en un hilo ajeno a la interfaz de usuario al ejecutar el método de invocar por primera vez.

  • Invoque de forma sincrónica una función específica de la interfaz de usuario mediante SafeDispatcher

    SafeDispatcher.Invoke(() =>
                {
                    ProcessData();
                }, DispatcherPriority.Normal, CancellationToken.None, true);
    

    O

    SafeDispatcher.Invoke(() =>
                {
                    ProcessData();
                }, DispatcherPriority.Normal, CancellationToken.None);
    

    Nota

    Para la función específica de la interfaz de usuario, debe establecer el parámetro opcional runOnMainUiThread como “true”. Si no especifica un valor para este parámetro, se pasa "true" de forma predeterminada. Por tanto, cualquiera de las definiciones de método anteriores funciona correctamente.

  • Invoque de forma asincrónica una función específica de la interfaz de usuario mediante SafeDispatcher. Puede usar el método BeginInvoke o InvokeAsync.

    SafeDispatcher.BeginInvoke(new Action(() =>
                {
                   ProcessData();
                }));
    
    

    o

    SafeDispatcher.InvokeAsync(new Action(() =>
                {
                   ProcessData();
                }));
    
    

Personalizar el controlador de excepciones de SafeDispatcher

Con la introducción de SafeDispatcher, todas las excepciones no controladas de Unified Service Desk activarán SafeDispatcherUnhandledException Event en lugar del evento DispatcherUnhandledException global. El Método SafeDispatcherUnhandledExceptionHandler proporciona un controlador de excepciones predefinido para que SafeDispatcher muestre información al usuario de Unified Service Desk con los siguientes datos: control del origen en el que se produjo la excepción e información detallada sobre la excepción.

También puede reemplazar el control de excepciones predefinido para SafeDispatcher para que realice otra operación, como indicar al usuario que cierre un control hospedado no dinámico basado en sesión.

El código de ejemplo siguiente demuestra cómo puede reemplazar el controlador de excepciones de SafeDispatcher predefinido para mostrar un cuadro de mensajes para pedir al usuario que cierre el control hospedado personalizado cuando aparece una excepción:

protected override void SafeDispatcherUnhandledExceptionHandler(object sender, SafeDispatcherUnhandledExceptionEventArgs ex)
{
    string error = String.Format(CultureInfo.InvariantCulture,
        "Error in hosted control  Application:{0} - Exception : {1} \r\nInnerException\r\n {2}", this.ApplicationName, ex.Exception, ex.InnerException);
    DynamicsLogger.Logger.Log(error, TraceEventType.Error);
    if (MessageBox.Show("Exception occurred in hosted control - " + this.ApplicationName + ".Do you wish to close it ?", "Question", MessageBoxButton.YesNo,
        MessageBoxImage.Warning) == MessageBoxResult.Yes)
    {
        SafeDispatcher.BeginInvoke(() => { this.desktopAccess.CloseDynamicApplication(this.ApplicationName); });
    }
}

Migrar del distribuidor WPF a SafeDispatcher en controles hospedados personalizados existentes

Dado que el contrato entre el distribuidor WPF y SafeDispatcher es casi idéntico, el esfuerzo de cambiar del distribuidor WPF a SafeDispatcher es mínimo. Para migrar cualquier instancia de control hospedado derivada de la clase DynamicsBaseHostedControl, reemplace todas las instancias de "Dispatcher" por "SafeDispatcher".

Por ejemplo, suponga el siguiente código:

Dispatcher.Invoke((System.Action)delegate()
{
    DynamicsLogger.Logger.Log("Raising SetupHotKey's", TraceEventType.Verbose);
    SetupHotkeys();
    CRMGlobalManager.AppWithFocusChanged += CRMGlobalManager_AppWithFocusChanged;
    FireEvent("DesktopReady");
    InitializeFocusSelection();
});

Reemplace Dispatcher en el código anterior con SafeDispatcher; el resto del código permanece igual:

SafeDispatcher.Invoke((System.Action)delegate()
{
    DynamicsLogger.Logger.Log("Raising SetupHotKey's", TraceEventType.Verbose);
    SetupHotkeys();
    CRMGlobalManager.AppWithFocusChanged += CRMGlobalManager_AppWithFocusChanged;
    FireEvent("DesktopReady");
    InitializeFocusSelection();
});

Aspectos a considerar al usar SafeDispatcher

SafeDispatcher ofrece un modelo multiprocesos que es muy beneficioso para distribuir funciones de forma sincrónica o asincrónica a hilos de interfaz de usuario o ajenos a la interfaz de usuario. Las operaciones que se deben ejecutar en hilos que están disponibles con tolerancia a errores se deben ejecutar en SafeDispatcher. Sin embargo, la ejecución de varios hilos se debe implementar muy cuidadosamente para evitar el interbloqueo entre hilos. Un ejemplo sería distribuir desde un hilo ajeno a la interfaz de usuario al distribuidor WPF principal de forma sincrónica. Veamos este ejemplo:

Thread thread = new Thread(() =>
{
    Dispatcher.Invoke(ProcessData);
});
thread.SetApartmentState(ApartmentState.STA);
thread.Priority = ThreadPriority.Highest;
thread.IsBackground = true;
thread.Start();
thread.Join();

El método thread.Join() está haciendo que el hilo principal se bloquee esperando la finalización del hilo del contenedor uniproceso (STA), pero el hilo del STA está esperando a que el hilo principal termine la ejecución de ProcessData. Esto pone a la aplicación en una situación de interbloqueo.

De forma similar, supongamos el siguiente ejemplo:

// Invoke on STA thread
SafeDispatcher.Invoke(() =>
{
    // Invoke back on main dispatcher
    SafeDispatcher.Invoke(() =>
    {
        ProcessData();
    });
}, false);

El método SafeDispatcherUnhandledExceptionHandler será llamado si se produce una excepción en el distribuidor WPF o en el hilo de a la interfaz de usuario de STA y se activará en el hilo correspondiente en el que se produjo la excepción. Debe asegurarse de no colocar la combinación anterior en este controlador, es decir, si la excepción se produjo en un hilo ajeno a la interfaz de usuario, no distribuir de forma sincrónica al distribuidor de interfaz de usuario principal.

protected override void SafeDispatcherUnhandledExceptionHandler(object sender, SafeDispatcherUnhandledExceptionEventArgs ex)
{
    Dispatcher.Invoke(LogException);            // Incorrect
    SafeDispatcher.Invoke(LogException);        // Incorrect
    SafeDispatcher.BeginInvoke(LogException);   // Correct
    SafeDispatcher.InvokeAsync(LogException);   // Correct
}

Vea también

Crear el control hospedado Unified Service Desk personalizadoAmpliar Unified Service DeskConfigurar registro de diagnóstico del cliente en Unified Service Desk