Moi!
Teen "virtuaalikonetta", joka tulkkaa binäärimuotoista "virtuaalikiintolevyä". Siinä on aika tehokas virheiden käsittely, mutta siinä on parantamisen varaa. Kun virhe jää kiinni Try-Catchissa, se hyppää virheenkäsittelijään joka tekee crashdumpin. Crashdumppiin tallentuu virtuaalikoneen muisti ja prosessorin tila. Muistin viimeisessä alkiossa on virheen tiedot sekä StackTrace. Siihen pitäisi saada myös virheen syy.
Miten tämä onnistuu vai onnistuuko millään?
Määrittele "virheen syy". Useinhan jos tietokoneessa ohjelma kaatuu, niin virheen syy on huonossa ohjelmoinnissa. Tämän tai muun varsinaisen syyn saaminen selville ei yleensä ole koneellisesti mahdollista. Eli minkälaista vastausta haetaan kysymykseen "mikä oli virheen syy"?
argumentexception?
Käyt ensin läpi mahdolliset syyt, ja määrität virheelle oman tekstisi.
Grez kirjoitti:
Määrittele "virheen syy".
Ainakin tuliko virhe virtuaalisessa tominnassa vai ihan oikeassa toiminnassa (eli vaikka koodissa bugi). Täytyypä määritellä oma exception-luokka...
groovyb kirjoitti:
Käyt ensin läpi mahdolliset syyt, ja määrität virheelle oman tekstisi.
Niin kai.
Nyt tein oman exceptionin VCError. Tällä hetkellä crashdumppi näyttää tätä:
VCdmp@mem2790# ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||¿1±black;¿2±gray| VCErr: Number 0 Description General error: Object reference not set to an instance of an object. Infodata Object reference not set to an instance of an object.<fmt>System.NullReferenceException: Object reference not set to an instance of an object. at VComputer.VComputer.Screen.keydown(Object sender, KeyEventArgs e) in C:\Users\Pauli\Documents\Visual Studio 2010\Projects\VB\VC\VC\VComputer.vb:line 581 at System.Windows.Forms.Control.OnKeyDown(KeyEventArgs e) at System.Windows.Forms.Control.ProcessKeyEventArgs(Message& m) at System.Windows.Forms.Control.ProcessKeyMessage(Message& m) at System.Windows.Forms.Control.WndProc(Message& m) at System.Windows.Forms.ScrollableControl.WndProc(Message& m) at System.Windows.Forms.Form.WndProc(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m) at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam) at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg) at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData) at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context) at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context) at System.Windows.Forms.Application.DoEvents() at VComputer.VComputer.Screen.ScrBIOS.Controller.Update() in C:\Users\Pauli\Documents\Visual Studio 2010\Projects\VB\VC\VC\VComputer.vb:line 631 @reg#0|0|0||
Eli pseudona:
dumppiheaderi, muistimerkintä, dumpin muistiosuuden koko ja muistin sisältöä (viimeiset ekalla rivillä on näyttömuistista eli näytönkontrollointikäskyt)
vika muistialue on errori:
numero 0
selitys virheelle
infodata eli additionaaliset tiedot
callstackki
prosessoritietojen headeri ja prossun rekisterit
Call Stackista näkee mitä kone on tehnyt. Se oli käynnistetty uudestaan ja sen jälkeen kaikki oli tyhjätty. Kun näyttöä päivitetään (viimeinen rivi callstackissa), kutsuttiin Application.DoEvents() -metodia ja se suoritti monimutkaisen toimintasarjan .NET-Frameworkissä. Lopulta päädyttiin näppäimistökäsittelijään, ja siellä yritettiin napsia keycode taullukkoon. Koska koneen uudelleenkäynnistyksessä kaikkiin muuttujiin on sijoitettu nothing, tulee exception.
Eli teitkö näin?
Try //jotain Catch Ex As Exception Dim sError = "VCErr: \r\nNumber 0 \r\nDescription General error: " + ex.ToString() Throw New ArgumentException(sError) End Try
Nyt kun lähdetään loggeria tekemään (tai mitä tahansa virheenkäsittelijää/tallentajaa), Olisi aika näppärää saada tietoon mikä metodi/funktio virheen laukaisee (jotta saadaan tietoon missä vaiheessa itse virhe tapahtuu, jotta ei tarvitse sourcea tutkia joka kerta)
Esim tuo yllä oleva esimerkkisi ei kerro mikä virheen triggaa, joten virheen selvitys olisi aika hankalaa.
Catch ex as System.NullReferenceException //Logger.Log(MyErrorCodes Errorcode, DateTime ErrorTime, string TriggeredBy, string SourceClass, string CallStack) MyLogger.Log(ErrorCodeEnum.NullReference,DateTime.Now().ToString(),"MyGroovybClass","MyFunction()",ex.ToString())
Log.txt, eventlog, tekstiä ruudulla yms. mihin sitten logger tai virheenkäsittelijä kirjoittaakaan:
VcError: Code 0
Explanation: Null reference Exception Occurred
Source: MyFunction()
Trigged by: MyGroovybClass
TimeStamp: 13.14.2012 22:08
CallStack: Lorem Ipsum
Tästä seuraava porras on lokitustyyppien määrittäminen (esim Information,Warning, Error) ja lokitusasteiden käyttäminen ( Basic | Virheet tallennetaan, Advanced | Virheet ja varoitukset tallennetaan , All | Virheet, varoitukset ja metodi/funktiokutsut tallennetaan ), jotka otetaan käyttöön vaikka sovellusargumentteina.
MyProgram.exe -EnableLog All
13.4.2012 22:08 Information: Program started
13.4.2012 22:08 Information: Checking program settings
13.4.2012 22:08 Information: Entering CheckSettings()
13.4.2012 22:08 Warning: Resolution not set in Settings.ini
13.4.2012 22:08 Error:
VcError: Code 0
Explanation: Null reference Exception Occurred
Source: CheckSettings()
Trigged by: MySettings.Resolution
TimeStamp: 13.14.2012 22:08
CallStack: Lorem Ipsum
13.4.2012 22:08 Information: Exiting CheckSettings()
13.4.2012 22:08 Information: Exiting Program
MyProgram.exe -EnableLog Advanced
13.4.2012 22:08 Warning: Resolution not set in Settings.ini
13.4.2012 22:08 Error:
VcError: Code 0
Explanation: Null reference Exception Occurred
Source: CheckSettings()
Trigged by: MySettings.Resolution
TimeStamp: 13.14.2012 22:08
CallStack: Lorem Ipsum
MyProgram.exe -EnableLog Basic
13.4.2012 22:08 Error:
VcError: Code 0
Explanation: Null reference Exception Occurred
Source: CheckSettings()
Trigged by: MySettings.Resolution
TimeStamp: 13.14.2012 22:08
CallStack: Lorem Ipsum
En aivan noin. Mutta tässä on jotakin koodia:
Private Shared Sub POST() 'biosin post-test joka käynnistetään omaan säikeeseensä On Error GoTo errHandler 'virheenkäsittely tällä kertaa näin 'tässä välissä on koodi joka lataa laitteisiin oikeat tiedot filuista Memory.PutObject(1024, "POST") : If Not Memory.GetObject(1024) = "POST" Then Console.Beep(750, 250) : ErrorHandler(New VCError(1, "1<fmt>")) 'muistitesti Memory.PutObject(0, "init") 'muistiin prosessorin init-käsky joka onkin ainut ei-binäärikäsky Processor.Run(0) : If Processor.Initialized = False Then Console.Beep(750, 250) : Console.Beep(750, 250) : ErrorHandler(New VCError(2, "1<fmt>")) 'käsketään prosessorin suorittaa init-käsky If DiskCount = 0 Then Console.Beep(750, 2500) : Console.Beep(750, 250) : Console.Beep(750, 250) : ErrorHandler(New VCError(3, "1<fmt>")) 'jos kiintolevyjä 0, tulee virhe Memory.PutObject(1, Nothing) 'vlearataan muisti jossa post-testin delegaatti oli Data.Booting = True 'bootataan Do : Thread.Sleep(2500) : Loop Until Data.Booting = False 'odotetaan muita säikeitä (kello tms.) Memory.PutObject(1024, "¿1±black;¿2±gray") '1024 on näyttömuisti If Not Data.bdFound Then Memory.PutObject(0, " ") 'jos bootdiskiä ei löydy, haltataan kone Processor.Run(0) 'suoritetaan boottisektori/halt-käsky Exit Sub 'poistutaan errHandler: 'jos sattuu jokin muu virhe ErrorHandler(New VCError(0, "'" & Err().Description & "' with code " & Err().Number & "<fmt>" & Err().Description)) 'lähetetään se errorHandleriin End Sub
Toinen esimerkki:
Public Shared Sub Update() 'Screen.ScrBIOS.Controller.Update() Try g = GetGraphics() 'haetaan graphicsit If Not Processor.Initialized Then 'jos on haltattu _frm.BackgroundImageLayout = ImageLayout.Tile _frm.BackgroundImage = My.Resources.vclogo 'logo näyttöön End If Application.DoEvents() If TypeOf Memory.GetObject(1024) Is Bitmap Then GoTo begindraw If Not cursor Then 'kursori g.DrawString("_", New Font("Courier", 36, FontStyle.Bold), New SolidBrush(clsClr), New Point(scrPos.X + 5, scrPos.Y + 5)) End If begindraw: 'piirto If TypeOf Memory.GetObject(1024) Is Bitmap Then 'bitmappi g.DrawImage(CType(Memory.GetObject(1024), Bitmap), New Point(0, 0)) 'piirretään _frm.Refresh() 'kaksi vähän turhaa kutstua _frm.Update() Else If dt Then g.Clear(clsClr) : scrPos = New Point(5, 5) Dim lines As String = Memory.GetObject(1024).ToString() For Each cmd As String In Split(lines, ";") 'käskyt If cmd = "¿0±" Then scrPos = New Point(5, scrPos.Y + 25) : Continue For 'uusi rivi Select Case Left(cmd, 3) Case "¿1±" 'cls scrPos = New Point(5, 5) clsClr = Color.FromName(Right(cmd, cmd.Length - 3)) g.Clear(clsClr) 'clear screen Case "¿2±" 'clr clr = Color.FromName(Right(cmd, cmd.Length - 3)) 'värin vaihto Case "¿3±" 'print g.DrawString(Right(cmd, cmd.Length - 3), New Font("Courier", 24), New SolidBrush(clr), scrPos) 'printataan scrPos = New Point(scrPos.X + cmd.Length * 12 + 12, scrPos.Y) Case "¿4±" 'dt dt = Not dt 'joku ihme-drawto-käsky Case Else BIOS.ErrorHandler(New VCError(9, cmd & "<fmt>" & cmd)) 'virhe End Select Next End If If TypeOf Memory.GetObject(1024) Is Bitmap Then GoTo enddraw If cursor Then g.DrawString("_", New Font("Courier", 36, FontStyle.Bold), New SolidBrush(clr), New Point(scrPos.X + 3, scrPos.Y)) 'kursori End If enddraw: cursor = Not cursor _frm.Refresh() 'päivitys Catch ex As Exception 'virhe BIOS.ErrorHandler(New VCError(0, ex.Message & "<fmt>" & ex.ToString())) 'virheenkäsittely End Try End Sub
Errorhandleri:
Public Shared Sub ErrorHandler(VCerr As VCError) 'täällä on ehkä liikaa try-catcheja... Try Memory.PutObject(1025, " ") 'halt varmuuden vuoksi, ettei crashdumpin teko hidastu Processor.Run(1025) Catch End Try Try : Memory.PutObject(1025, VCerr.Message) : Catch : End Try 'errorin viesti Try Thread.Sleep(250) Data.RunClock = False 'määritetään kello pois päältä Catch Finally Try Screen.GetForm().BackgroundImage = My.Resources.error_image 'errori-image Screen.GetForm().BackgroundImageLayout = ImageLayout.Stretch 'venytetty kuva Catch : End Try Try CreateCrashdump(Application.StartupPath & "\crashdump.mem") 'crashdumppi Process.Start(Application.ExecutablePath, "report") 'virheraportti ExitProgram = True 'poistutaan (pääsäie hoitaa homman) Thread.CurrentThread.Suspend() 'pysähdytään Catch : End Try End Try
VCError -luokka:
Public Class VCError Inherits Exception 'exceptioni 'virheet on listattu järjestyksessä: Public ReadOnly ERRs() As String = {"General error: {0}", "Step {0}: POSTErr - MemoryTest", "Step {0}: POSTErr - ProcessorTest", _ "Step {0}: POSTErr - DiskTest", "Step {0}: POSTErr - DiskRWTest", "Step {0}: POSTErr - ScreenTest", _ "ProcessorErr: '{0}' is not valid for the calculate command", "ProcessorErr: {0} is not a valid command", "ScreenErr: {0} is not a valid command"} Dim _num As Integer, _id As String 'numero ja infodata ("id") Protected msg As String 'viesti on suojattu Public Overloads ReadOnly Property Message As String Get Return msg End Get End Property Public Sub New(num As Integer, infodata As String) 'konstruktori MyBase.New() msg = "\n\nVCErr:\nNumber " & num & "\nDescription " & String.Format(ERRs(num), infodata.Split("<fmt>")(0) & "\nInfodata " & infodata & "\n" & StackTrace) 'muodostetaan viesti msg = Replace(msg, "\n", Global.Microsoft.VisualBasic.vbCrLf) 'korvataan \n:t rivinvaihdoilla _num = num _id = infodata End Sub End Class
En kaikkea koodia tietenkään laita (niitähän on yli 700 riviä), toivottavasti tuosta ymmärtää oleellisimman :)
Löysin yhdestä kirjasta, että virheiden käsittelyn voi tehdä delegaatin ja AppDomain.CurrentDomain.ErrorHandler
tai jonkun muun vastaavan avulla.
Aihe on jo aika vanha, joten et voi enää vastata siihen.