私はVB.Netに、グローバル変数(イベントログオブジェクトのリスト)を必要とするプロジェクトを持っています。これは、アプリケーションのStartUpイベントで変数を定義して初期化してから、アプリケーション全体にアクセスするだけで簡単です。残念ながら、これは子プロセスがない(グローバル変数へのアクセス権がない)場合にのみ有効です - 子プロセスからグローバル変数にアクセスする方法として頭を悩ませています)。winformsで定義されたグローバル変数が正常に動作しない
プログラムは複数のテストワーカープロセス(それぞれ異なるソースからの複数のDB接続、複数のソースからのリモートWebサービス、ネットワークチェックなど)をそれぞれの進行状況バーでチェックします。これらのテスト中にエラーが発生した場合は、エラーを記録する必要があります。 問題は、プログラムがWindowsイベントシステムにイベントを記録することができないということです。これは、管理者アカウントでは実行されないためです(Vistaの通常のユーザーアカウントでのロギングを防止するというMSの決定のおかげで、 、8,10)、プログラムは非同期であるためテキストファイルにもログできず、ファイルアクセスの競合の問題(すぐにテキストファイルにイベントを記録することはできません) /メモリ内のエラー(グローバル変数)、THENはすべての子プロセスが完了した後にログファイルにダンプします。意味をなさない?私のロギングクラスで私はAppEvent
Public Class AppEvent
Sub New()
EventTime_ = Date.Now
Level_ = EventLevel.Information
Description_ = String.Empty
Source_ = String.Empty
End Sub
Private EventTime_ As Date
Public Property EventTime() As Date
Get
Return EventTime_
End Get
Set(ByVal value As Date)
EventTime_ = value
End Set
End Property
Private Level_ As EventLevel
Public Property Level() As EventLevel
Get
Return Level_
End Get
Set(ByVal value As EventLevel)
Level_ = value
End Set
End Property
Private Description_ As String
Public Property Description() As String
Get
Return Description_
End Get
Set(ByVal value As String)
Description_ = value
End Set
End Property
Private Source_ As String
Public Property Source() As String
Get
Return Source_
End Get
Set(ByVal value As String)
Source_ = value
End Set
End Property
End Class
Public Enum EventLevel
[Information]
[Warning]
[Error]
[Critical]
[Fatal]
End Enum
というクラスを作成し、このためだけにパブリック変数を作成して(とAppEventsリストに最初のイベントを追加)
Namespace My
Partial Friend Class MyApplication
'global variable here (using this for logging asynch call errors, then dumping this into a log file when all asynch calls are complete (due to file contention of log file)
Public AppEvents As New List(Of AppEvent)
Private Sub MyApplication_Startup(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.ApplicationServices.StartupEventArgs) Handles Me.Startup
'create first event and add it to the global variable declared above
AppEvents.Add(New AppEvent With {.EventTime = Now, .Description = "Program Started", .Level = EventLevel.Information, .Source = "QBI"})
End Sub
End Class
End Namespace
次に、イベントのログ記録、書き出し、書き込みのためのいくつかの方法があります。
Public Class AppLogging
Public Shared Sub WriteEventToAppLog(evt As AppEvent)
LogDataToFile(FormatLineItem(evt.EventTime, evt.Level, evt.Description, evt.Source))
End Sub
Public Shared Sub WriteEventsToAppLog(AppEvents As List(Of AppEvent))
Dim sbEvents As New StringBuilder
If AppEvents.Count > 0 Then
For Each evt In AppEvents
sbEvents.AppendLine(FormatLineItem(evt.EventTime, evt.Level, evt.Description, evt.Source))
Next
LogDataToFile(sbEvents.ToString.TrimEnd(Environment.NewLine))
End If
End Sub
Private Shared Function FormatLineItem(eventTime As Date, eventLevel As EventLevel, eventDescr As String, eventSource As String) As String
Return String.Format("Logged On: {0} | Level: {1} | Details: {2} | Source: {3}", eventTime, System.Enum.GetName(GetType(EventLevel), eventLevel).Replace("[", "").Replace("]", ""), eventDescr, eventSource)
End Function
Private Shared Sub LogDataToFile(eventLogText As String, Optional ByVal LogFileName As String = "Error.log", Optional ByVal HeaderLine As String = "****** Application Log ******")
'log file operations
Dim LogPath As String = System.AppDomain.CurrentDomain.BaseDirectory()
If Not LogPath.EndsWith("\") Then LogPath &= "\"
LogPath &= LogFileName
Dim fm As FileMode
Try
If System.IO.File.Exists(LogPath) Then
fm = FileMode.Append
Else
fm = FileMode.Create
eventLogText = HeaderLine & Environment.NewLine & eventLogText
End If
Using fs As New FileStream(LogPath, fm, FileAccess.Write)
Using sw As New StreamWriter(fs)
sw.WriteLine(eventLogText)
End Using
End Using
My.Application.AppEvents.Clear() 'clears the global var
Catch ex As Exception
'handle this
End Try
End Sub
Public Shared Sub WriteEventToMemory(eventLevel As EventLevel, eventDescription As String, Optional eventSource As String = "")
Dim evt As New AppEvent
evt.Description = eventDescription
evt.Level = eventLevel
evt.EventTime = Now
evt.Source = eventSource
Try
My.Application.AppEvents.Add(evt)
Catch ex As Exception
Throw
End Try
End Sub
Public Shared Sub FlushEventsToLogFile()
WriteEventsToAppLog(My.Application.AppEvents)
End Sub
End Class
ここではいくつかのエモードがありますが、すべての例外ハンドラで呼び出されるメソッドはWriteEventToMemoryです(AppEventsをAppEventsリストに追加するだけです)。
のように(ローカルデータベースへの)サンプルテストルーチン/ワーカープロセスが見えます:try-catchブロックは、単に例外をキャッチし、メモリに書き込む
#Region "local db test"
Private Sub TestLocalDBWorkerProcess_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles TestLocalDBWorkerProcess.DoWork
Me.TestLocalDBStatusMessage = TestLocalDB()
End Sub
Private Sub TestLocalDBWorkerProcess_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles TestLocalDBWorkerProcess.RunWorkerCompleted
Me.prgLocalDatabase.Style = ProgressBarStyle.Blocks
Me.prgLocalDatabase.Value = 100
If Me.ForcedCancelTestLocalDB Then
Me.lbStatus.Items.Add("Local DB Test Cancelled.")
Else
If TestLocalDBStatusMessage.Length > 0 Then
Me.lblLocalDatabaseStatus.Text = "Fail"
Me.lbStatus.Items.Add(TestLocalDBStatusMessage)
SendMessage(Me.prgLocalDatabase.Handle, 1040, 2, 0) 'changes color to red
Else
Me.lblLocalDatabaseStatus.Text = "OK"
Me.lbStatus.Items.Add("Connection to local database is good.")
Me.prgLocalDatabase.ForeColor = Color.Green
End If
End If
Me.ForcedCancelTestLocalDB = False
Me.TestLocalDBProcessing = False
ProcessesFinished()
End Sub
Private Sub StartTestLocalDB()
Me.prgLocalDatabase.Value = 0
Me.prgLocalDatabase.ForeColor = Color.FromKnownColor(KnownColor.Highlight)
Me.prgLocalDatabase.Style = ProgressBarStyle.Marquee
Me.TestLocalDBProcessing = True
Me.TestLocalDBStatusMessage = String.Empty
Me.lblLocalDatabaseStatus.Text = String.Empty
TestLocalDBWorkerProcess = New System.ComponentModel.BackgroundWorker
With TestLocalDBWorkerProcess
.WorkerReportsProgress = True
.WorkerSupportsCancellation = True
.RunWorkerAsync()
End With
End Sub
Private Function TestLocalDB() As String
Dim Statusmessage As String = String.Empty
Try
If Me.TestLocalDBWorkerProcess.CancellationPending Then
Exit Try
End If
If Not QBData.DB.TestConnection(My.Settings.DBConnStr3) Then
Throw New Exception("Unable to connect to local database!")
End If
Catch ex As Exception
Statusmessage = ex.Message
AppLogging.WriteEventToMemory(EventLevel.Fatal, ex.Message, "TestLocalDB")
End Try
Return Statusmessage
End Function
#End Region
(私はちょうどWriteEventToMemory方法でそれを包んAppEventsのカウントがStartupイベントの後に(1)あったことが気付くまで、すべてがピーチになっているように見えましたが、それはAppEventsのリストに追加しています:My.Application.AppEvents.Add(evt)
カウントは(0)子プロセスのいずれかから、最後にカウントは(1)リストがエラーログファイルにダンプされたとき(追加された最初のイベントはそこにあった)。 AppEvents変数の複数のバージョンがあるように、明らかに動作しています。
****** Application Log ******
Logged On: 10/7/2016 6:01:45 PM | Level: Information | Details: Program Started | Source: QBI
のみ最初のイベントではなく、他のイベントを示している( - ファントムのようにそれらが追加され、ヌル参照例外またはいずれかの例外はありません)。 MAINスレッドのグローバル変数に追加されたイベントは、そのまま残ります(最終的にはログに記録されます)。だからこれは明らかにマルチスレッドの問題です(Windowsアプリケーションでこれまで試みたことはありません)。 救済方法に関するアイデアはありますか?
が厥コードの多く。 'MyApplication_Startup'以外では、どこにリストに何を追加しているのかわかりません。 [MCVE] – Plutonix
を参照してください。WriteEventToMemoryメソッドは、AppEventをAppEventsリストに追加する唯一の場所です。 私は本当にそこにあるものよりも小さなスレッド化された例を作ることはできません。(私がそれを削除しない限り) – MC9000
私はちょうど解決策に突き当たりました - グローバル変数は動作してもスレッドセーフではありません。 http://stackoverflow.com/questions/7563825/global-variable-in-asp-net#7564075 – MC9000