2015-09-11 12 views
10

Sto utilizzando la chiamata API Segnalazione errori Windows RegisterApplicationRestart per registrare un'applicazione che deve essere riavviata automaticamente da WER, quando l'app si arresta in modo anomalo o il PC viene riavviato.Riavviare un programma arrestato con RegisterApplicationRestart senza prompt utente

Tuttavia, quando l'app si arresta in modo anomalo, viene visualizzata la finestra di dialogo WER predefinita ("xyz ha smesso di rispondere"/"Desidera inviare ulteriori informazioni sul problema") e solo dopo aver chiuso questa finestra di dialogo il programma si riavvia.

C'è un modo per sopprimere questa finestra di dialogo?

Se chiamo SetErrorMode(SEM_NOGPFAULTERRORBOX), la finestra di dialogo è soppressa, come previsto, ma il riavvio stesso non funziona più.

Se suppongo globalmente la finestra di dialogo modificando la chiave di registro HKEY_CURRENT_USER\Software\ Microsoft\Windows\Windows Error Reporting\DontShowUI, ottengo lo stesso risultato: la finestra di dialogo è soppressa, ma l'app non si riavvia neanche.

Sono a conoscenza di soluzioni alternative come un secondo programma watchdog, ma mi piacerebbe davvero risolverlo nel modo più semplice possibile con gli strumenti dell'API di segnalazione errori di Windows.

+1

Il documento ufficiale per RegisterApplicationRestart indica chiaramente: "l'applicazione non viene riavviata automaticamente senza il consenso dell'utente" –

risposta

8

È possibile utilizzare invece RegisterApplicationRecoveryCallback e riavviare il processo. Non elimina la finestra di dialogo Segnalazione errori, ma può riavviare l'applicazione senza l'interazione dell'utente.

using System; 
using System.Diagnostics; 
using System.Runtime.InteropServices; 
using System.Reflection; 
using System.Threading; 

namespace Test 
{ 
    class Program 
    { 
     public delegate int RecoveryDelegate(IntPtr parameter); 

     [DllImport("kernel32.dll")] 
     private static extern int RegisterApplicationRecoveryCallback(
       RecoveryDelegate recoveryCallback, 
       IntPtr parameter, 
       uint pingInterval, 
       uint flags); 

     [DllImport("kernel32.dll")] 
     private static extern void ApplicationRecoveryFinished(bool success); 

     private static void RegisterForRecovery() 
     { 
      var callback = new RecoveryDelegate(p=> 
      { 
       Process.Start(Assembly.GetEntryAssembly().Location); 
       ApplicationRecoveryFinished(true); 
       return 0; 
      }); 

      var interval = 100U; 
      var flags = 0U; 

      RegisterApplicationRecoveryCallback(callback,IntPtr.Zero,interval,flags); 
     } 

     static void Main(string[] args) 
     { 
      RegisterForRecovery(); 

      for (var i = 3; i > 0; i--) 
      { 
       Console.SetCursorPosition(0, Console.CursorTop); 
       Console.Write("Crash in {0}", i); 
       Thread.Sleep(1000); 
      } 
      Environment.FailFast("Crash."); 
     } 
    } 
} 

Impostando CodiceErrore a SEM_NOGPFAULTERRORBOX stiamo cambiando il comportamento delle eccezioni di filtraggio e costringerlo a passare l'eccezione (EXCEPTION_CONTINUE_SEARCH) invece di popping Errore finestra di segnalazione (EXCEPTION_EXECUTE_HANDLER).

Forse un modo corretto (che in realtà impedisce la visualizzazione di Error Report Dialog nella maggior parte dei casi) sarebbe utilizzare SetUnhandledExceptionFilter e fare il ripristino lì che in. Net equivale approssimativamente all'utilizzo di AppDomain.CurrentDomain.UnhandledException. se necessario per rilevare le eccezioni Win32, è necessario abilitare LegacyCorruptedStatePolicy aggiungendo le seguenti righe alla configurazione dell'app.

<configuration> 
    <runtime> 
     <legacyCorruptedStateExceptionsPolicy enabled="true" /> 
    </runtime> 
</configuration> 

tuttavia non prenderà tutti (es. Environment.FastFail o alcune violazioni di accesso), quindi vorrei suggerire di utilizzare entrambi.

using System; 
using System.Diagnostics; 
using System.Runtime.InteropServices; 
using System.Reflection; 
using System.Threading; 

namespace Test 
{ 
    class Program 
    { 
     public delegate int RecoveryDelegate(IntPtr parameter); 

     [DllImport("kernel32.dll")] 
     private static extern int RegisterApplicationRecoveryCallback(
       RecoveryDelegate recoveryCallback, 
       IntPtr parameter, 
       uint pingInterval, 
       uint flags); 

     [DllImport("kernel32.dll")] 
     private static extern void ApplicationRecoveryFinished(bool success); 

     private static void RegisterForRecovery() 
     { 
      var callback = new RecoveryDelegate(p=> 
      { 
       Recover(); 
       ApplicationRecoveryFinished(true); 
       return 0; 
      }); 

      var interval = 100U; 
      var flags = 0U; 

      RegisterApplicationRecoveryCallback(callback,IntPtr.Zero,interval,flags); 
     } 

     private static void Recover() 
     { 
      //do the recovery and cleanup 
      Process.Start(Assembly.GetEntryAssembly().Location); 
     } 

     private static unsafe void Crash1() 
     { 
      var p = (int*)0; 
      p[0] = 0; 
     } 

     private static unsafe void Crash2() 
     { 
      var v = 1; 
      var p =&v; 
      p -= ulong.MaxValue; 
      p[0] = 0; 
     } 

     static void Main(string[] args) 
     { 
      AppDomain.CurrentDomain.UnhandledException += 
       new UnhandledExceptionEventHandler((s, e) => 
       { 
        Recover(); 
        Environment.Exit(1); 
       }); 

      RegisterForRecovery(); 

      for (var i = 3; i > 0; i--) 
      { 
       Console.SetCursorPosition(0, Console.CursorTop); 
       Console.Write("Crash in {0}", i); 
       Thread.Sleep(1000); 
      } 

      //different type of crash 
      throw new Exception("Crash."); 
      //Environment.FailFast("Crash."); 
      //Crash1(); 
      //Crash2(); 
     } 
    } 
} 
Problemi correlati