Как я могу поймать сбой приложения / неуместный выход? - PullRequest
0 голосов
/ 17 января 2020

У меня есть VB. NET Приложение WinForms, запущенное из исполняемого файла, хранящегося в сетевой папке. В этом приложении я определил обработчик UnhandledException в ApplicationEvents (Private Sub MyApplication_UnhandledException(sender As Object, e As UnhandledExceptionEventArgs) Handles Me.UnhandledException). В моем обработчике у меня есть метод, который записывает сведения об исключении в текстовый файл, прежде чем предложить пользователю подтвердить выход из приложения.

Однако это приложение «случайно» аварийно завершает работу и завершает работу без создания журнала или сообщения. окно отображается. Такое поведение происходит в разных исполняемых точках приложения, и я отчаянно пытаюсь найти причину. Я предполагаю , что проблема может быть вызвана либо временной потерей сетевого подключения, либо какой-либо другой проблемой, связанной с базой данных PostgreSQL, но я не могу подтвердить источник, так как нет трассировки стека или подробности сообщения перед тем, как приложение исчезнет с экрана пользователя.

Это должно быть "просто", но я в растерянности, поскольку попробовал несколько вещей, включая упаковку больших блоков кода в Try...Catch блокирует и добавляет дополнительные функции ведения журнала в мой обработчик ошибок. Я попытался изменить код в моем обработчике UnhandledException, чтобы избежать проблем с созданием нового объекта (для моего объекта ErrorHandler). Я добавил проверку в обработку ошибок для регистрации ошибки локально, если сеть недоступна. Я даже добавил простое окно сообщения к событию FormClosing моей основной формы, если пользователь не инициировал закрытие напрямую, чтобы попытаться, по крайней мере, заставить приложение сделать что-то до полного закрытия ,

Независимо от того, что я пробовал до сих пор, приложение по-прежнему принудительно завершается в течение, казалось бы, случайного времени. Пользователь будет нажимать кнопку для выполнения любого из нескольких методов, которые обычно работают нормально. Если пользователь перезапускает приложение после того, как его выгнали, и снова выполняет то же самое действие, оно работает без проблем. Что мне нужно для выполнения sh, так это некоторая форма «защиты от идиота» обработки ошибок, чтобы все, что вызывает выход из приложения, было перехвачено и зарегистрировано. Я уверен, что есть вещи, о которых я не думаю на данный момент, поэтому дайте мне знать, если понадобятся какие-либо дополнительные разъяснения.


КОД

Событие приложения Startup обработчик:

Private Sub MyApplication_Startup(sender As Object, e As StartupEventArgs) Handles Me.Startup
    Try
        Common.ApplicationStartup(ApplicationSettings.CurrentUser)
    Catch ex As Exception
        Dim StartupException As New ErrorHandler(ex)

        StartupException.LogException()
        MessageBox.Show("You do not have permission to access this resource." & vbCrLf & vbCrLf &
                    "The application will now exit.")
        System.Environment.Exit(1)
    End Try

    ' *********************************************************************
    ' ** Notify the user if the application is running in test mode.     **
    ' *********************************************************************
    If ApplicationSettings.TestMode Then
        MessageBox.Show("This application is currently running in Test Mode, and will use " &
                        "local paths for data and configuration information." & vbCrLf & vbCrLf &
                        "If you are trying to use this application with live data and see " &
                        "this message, please contact the IT HelpDesk for assistance.", "TEST MODE",
                        MessageBoxButtons.OK, MessageBoxIcon.Exclamation)

        If ApplicationSettings.CurrentUser.Department = Users.Employee.Department.IS Then
            If MessageBox.Show("Do you want to continue in Test Mode?", "TEST MODE", MessageBoxButtons.YesNo,
                               MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) = DialogResult.No Then
                ApplicationSettings.TestMode = False
            End If
        End If
    End If

    ' *********************************************************************
    ' ** Initialize any application-specific settings here.              **
    ' *********************************************************************
    Try
        'If ApplicationSettings.TestMode AndAlso ApplicationSettings.CurrentUser.Department = Users.Employee.Department.IS Then
        '    MessageBox.Show("If you have any additional parameters/settings to configure for this application, " &
        '                    "please do so before commenting out this message.",
        '                    "DEVELOPMENT WARNING", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
        'End If
    Catch ex As Exception
        Dim ExHandling As New Common.ErrorHandler(ex)

        ExHandling.LogException()

        MessageBox.Show("There was a problem with initializing the application's configuration." & vbCrLf & vbCrLf &
                    "The application will now exit.")
        System.Environment.Exit(2)
    End Try
End Sub

Метод ApplicationStartup:

Public Sub ApplicationStartup(ByRef CurrentUser As Users.Employee)
    ' *********************************************************************
    ' ** Default the TestMode variable to False.  If the check for       **
    ' ** whether or not the application is running from the IDE fails,   **
    ' ** the application should assume that it's running live.           **
    ' *********************************************************************
    ApplicationSettings.TestMode = False

    ' *********************************************************************
    ' ** Perform a check of whether or not the application is running    **
    ' ** from the IDE or the Debug folder.                               **
    ' *********************************************************************
    SetTestMode()

    ' *********************************************************************
    ' ** Retrieve any parameters sent to the executable from the command **
    ' ** line and determine if the application is running from the task  **
    ' ** scheduler.                                                      **
    ' *********************************************************************
    ApplicationSettings.ScheduledTask = False
    ApplicationSettings.RuntimeParameters = System.Environment.GetCommandLineArgs().ToList

    If Not ApplicationSettings.RuntimeParameters Is Nothing AndAlso ApplicationSettings.RuntimeParameters.Count > 0 Then
        For Each Parameter As String In ApplicationSettings.RuntimeParameters
            If Parameter.ToUpper.Contains("SCHEDTASK") Then
                ApplicationSettings.ScheduledTask = True
                Exit For
            End If
        Next
    End If

    ' *********************************************************************
    ' ** Set up the CurrentUser object by querying Active Directory and  **
    ' ** the PostgreSQL database for details.                            **
    ' *********************************************************************
    Try
        If CurrentUser.ADUserName Is Nothing OrElse String.IsNullOrEmpty(CurrentUser.ADUserName) Then
            CurrentUser = New Users.Employee(Environment.UserName)
        End If
    Catch UserEx As Exception
        Dim ExHandler As New ErrorHandler(UserEx)

        ExHandler.LogException()
        Throw UserEx
    End Try

    If CurrentUser Is Nothing Then
        Throw New Exception("Username " & Environment.UserName & " was not found in Active Directory.")
    ElseIf CurrentUser.Enabled = False Then
        Throw New Exception("Username " & Environment.UserName & " is not a currently active employee.")
    End If

    ' *********************************************************************
    ' ** Default the DBCommandTimeout variable to 30.                    **
    ' *********************************************************************
    ApplicationSettings.DBCommandTimeout = 30
End Sub

Private Sub SetTestMode()
    ' *********************************************************************
    ' ** Use the Debug.Assert to call the InTestMode function, which     **
    ' ** will set the TestMode variable to True.  Debug.Assert will only **
    ' ** execute if the program is running from a debugging version of   **
    ' ** the code (in Design-Time, or from the Debug folder).  When the  **
    ' ** code is running from a compiled executable, the Debug.Assert    **
    ' ** statement will be ignored.                                      **
    ' *********************************************************************
    Debug.Assert(InTestMode)
End Sub

Private Function InTestMode() As Boolean
    ' *********************************************************************
    ' ** Set the global TestMode variable to True.  This function is     **
    ' ** only called in debug mode using the Debug.Assert method in the  **
    ' ** SetTestMode Sub.  It will not be called if the application is   **
    ' ** running from a compiled executable.                             **
    ' *********************************************************************
    Common.ApplicationSettings.TestMode = True
    Return True
End Function

Обработчик события UnhandledException:

Private Sub MyApplication_UnhandledException(sender As Object, e As UnhandledExceptionEventArgs) Handles Me.UnhandledException
    Dim Response As DialogResult = DialogResult.Yes

    Response = MessageBox.Show("An unknown error occurred in the application." & vbCrLf & vbCrLf &
                               "Do you want to exit the application?", "UNHANDLED EXCEPTION",
                               MessageBoxButtons.YesNo, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1)

    Dim UnhandledError As New ErrorHandler(e.Exception)

    UnhandledError.LogException()

    If Response = DialogResult.Yes Then
        e.ExitApplication = True
    Else
        e.ExitApplication = False
    End If
End Sub

Событие FormClosing главной формы:

Private Sub frmMain_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
    If Not e.CloseReason = CloseReason.UserClosing Then
        MessageBox.Show("The application has encountered some sort of problem and is closing.")
    End If
End Sub

Дайте мне знать, если вы хотите / хотите увидеть больше кода. Как я уже сказал, ошибка возникает в казалось бы случайных точках выполнения приложения и несовместима между одной попыткой или другой.

Ответы [ 2 ]

1 голос
/ 17 января 2020

Вот идея. Похоже, что это может потребовать переписать, как ваше приложение запускается. Но попробуйте добавить еще одну «форму запуска» с помощью Application.Run à la C# void Main() { Application.Run(Form); }. Вы можете обернуть это в Try-Catch и обработать необработанные исключения.

Добавьте форму с именем StartupForm и сделайте так, чтобы точка входа вашего приложения

enter image description here

И код StartupForm

Imports System.Threading

Public Class StartupForm
    Protected Sub StartupForm_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
        Dim applicationThread = New Thread(AddressOf Main)
        applicationThread.SetApartmentState(ApartmentState.STA)
        applicationThread.Start()
        Dispose()
    End Sub
    Protected Shared Sub Main()
        Using myForm As New MainForm()
            AddHandler AppDomain.CurrentDomain.UnhandledException, AddressOf UnhandledExceptionHandler
            AddHandler Application.ThreadException, AddressOf UIThreadExceptionHandler
            Application.EnableVisualStyles()
            Try
                Application.Run(myForm)
            Catch ex As Exception
                UnhandledExceptionHandler(myForm, New UnhandledExceptionEventArgs(ex, True))
            End Try
        End Using
    End Sub
End Class

Module ExceptionHandlers
    Public Sub UIThreadExceptionHandler(ByVal sender As Object, ByVal args As ThreadExceptionEventArgs)
        MessageBox.Show(args.Exception.Message, NameOf(UIThreadExceptionHandler))
    End Sub
    Public Sub UnhandledExceptionHandler(ByVal sender As Object, ByVal args As UnhandledExceptionEventArgs)
        MessageBox.Show(args.ExceptionObject.Message, NameOf(UnhandledExceptionHandler))
    End Sub
End Module

StartupForm запустит новый поток, прежде чем он утилизирует себя. Новый поток запускает поток пользовательского интерфейса с вашей MainForm (как бы он ни назывался - это ваша текущая форма запуска).

Чтобы проверить это, вы можете вызвать исключение из нажатия кнопки

Public Class MainForm
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Throw New Exception("Unhandled, from button on Form")
    End Sub
End Class

и видим, что он обрабатывается в начальном Try-Catch

enter image description here

Мы добавляем дополнительные обработчики на случай, если Try-Catch не поймает все

AddHandler AppDomain.CurrentDomain.UnhandledException, AddressOf UnhandledExceptionHandler
AddHandler Application.ThreadException, AddressOf UIThreadExceptionHandler

но я обнаружил, что все управляемые * исключения были перехвачены.

* Конечно, мы не будем ловить исключения из неуправляемых источников

0 голосов
/ 22 января 2020

Я бы сделал файл журнала, который представляет собой TXT-файл на хосте приложения (в папке отладки) приложения, чтобы вы, как разработчик, могли проверять его время от времени или отправлять вам эти файлы журналов удаленно, что время от времени является сбором данных. Для просмотра ошибок кода на стороне клиента и общего обзора того, что происходит сбой, используя стековую строку, вы можете просмотреть строку кода с ошибкой и подробное описание ошибки, а также

руководство по использованию средства записи потока https://www.c-sharpcorner.com/article/csharp-streamwriter-example/ так что вы бы хотели, чтобы это было так для каждого блока кода, который подвержен ошибкам

    Try
    'Here your code is written for example dim a as integer=12
    Catch ex as exception
' Here stream writer takes your ex.Message & ex.Stack Trace  and adds it to the log file
'After that show message box saying
MessageBox.Show("Oops something crashed please try again ")
'That way you are handling both the user and developer
  End Try

'Для пользователя вы не хотите, чтобы приложение взламывало sh его для вас, как для разработчика, вам нужен журнал «файл для отслеживания ошибок и предотвращения их для будущих итераций вашей программы», также при сборке с каждой версией убедитесь, что вы также убедитесь, что в вашем файле журнала есть время c даты, когда произошла ошибка и укажите пользователя, который также сделал это, если имеется

...