Private Sub CommandButton1_Click() Dim dataFilePath As String Dim dataBook As Workbook Dim i As Integer '① マクロ ブックのシート上のデータを取得 dataFilePath = ThisWorkbook.Worksheets(1).Range("B1").Value '② データブックをオープン Set dataBook = OpenWorkBook(dataFilePath) If dataBook Is Nothing Then MsgBox "データ ブックが存在しないため処理中断" Exit Sub End If '③ データブックの A1 ~ A3 の値をマクロブックの A4 ~ A6 に転記 For i = 1 To 3 Call SetValue(dataBook, i) Next Call ChangeFormat '④ 書式設定を変更 Call CloseBook(dataBook) '⑤ データ ブックを閉じる '⑥ 処理完了時にコントロールの Caption を変更 CommandButton1.Caption = "転記済み" MsgBox "処理終了" End Sub
Function OpenWorkBook(filePath As String) As Workbook Dim wb As Workbook If Dir(filePath) = "" Then Exit Function End If Set wb = Workbooks.Open(filePath) Set OpenWorkBook = wb End Function
Sub SetValue(dataBook As Workbook, i As Integer) ThisWorkbook.Worksheets(1).Range("A" & 3 + i).Value = dataBook.Worksheets(1).Range("A" & i).Value End Sub
Sub ChangeFormat() With ThisWorkbook.Worksheets(1).Range("A4:A6") .Font.ColorIndex = 3 .Font.Bold = True .Borders.LineStyle = xlDouble .Borders.ColorIndex = 5 End With End Sub
Sub CloseBook(dataBook As Workbook) dataBook.Close End Sub
Private Sub CommandButton1_Click() LogWriteToBuffer "IN,Sheet1,CommandButton1_Click" Dim dataFilePath As String Dim dataBook As Workbook Dim i As Integer '① マクロ ブックのシート上のデータを取得 dataFilePath = ThisWorkbook.Worksheets(1).Range("B1").Value '② データブックをオープン Set dataBook = OpenWorkBook(dataFilePath) If dataBook Is Nothing Then MsgBox "データ ブックが存在しないため処理中断" LogWriteToBuffer "OUT,Sheet1,CommandButton1_Click" Exit Sub End If '③ データブックの A1 ~ A3 の値をマクロブックの A4 ~ A6 に転記 For i = 1 To 3 Call SetValue(dataBook, i) Next Call ChangeFormat '④ 書式設定を変更 Call CloseBook(dataBook) '⑤ データ ブックを閉じる '⑥ 処理完了時にコントロールの Caption を変更 CommandButton1.Caption = "転記済み" MsgBox "処理終了" LogWriteToBuffer "OUT,Sheet1,CommandButton1_Click"
'★以下は手動で追加 LogWrite logOutputCollection End Sub
Function OpenWorkBook(filePath As String) As Workbook LogWriteToBuffer "IN,Sheet1,OpenWorkBook" Dim wb As Workbook If Dir(filePath) = "" Then LogWriteToBuffer "OUT,Sheet1,OpenWorkBook" Exit Function End If Set wb = Workbooks.Open(filePath) Set OpenWorkBook = wb LogWriteToBuffer "OUT,Sheet1,OpenWorkBook" End Function
Sub SetValue(dataBook As Workbook, i As Integer) LogWriteToBuffer "IN,Sheet1,SetValue" ThisWorkbook.Worksheets(1).Range("A" & 3 + i).Value = dataBook.Worksheets(1).Range("A" & i).Value LogWriteToBuffer "OUT,Sheet1,SetValue" End Sub
Sub ChangeFormat() LogWriteToBuffer "IN,Sheet1,ChangeFormat" With ThisWorkbook.Worksheets(1).Range("A4:A6") .Font.ColorIndex = 3 .Font.Bold = True .Borders.LineStyle = xlDouble .Borders.ColorIndex = 5 End With LogWriteToBuffer "OUT,Sheet1,ChangeFormat" End Sub
Sub CloseBook(dataBook As Workbook) LogWriteToBuffer "IN,Sheet1,CloseBook" dataBook.Close LogWriteToBuffer "OUT,Sheet1,CloseBook" End Sub <Module1 オブジェクト> ※ 共通関数記述用に追加されます
Public logOutputCollection As Collection Public Sub LogWriteToBuffer(strMsg As String) If logOutputCollection Is Nothing Then Set logOutputCollection = New Collection End If logOutputCollection.Add getNowWithMS & "," & strMsg End Sub
Public Sub LogWrite(logOutputCollection As Collection) Dim j As Integer Dim iFileNo As Integer iFileNo = FreeFile Open "C:\temp\VBAPerf.log" For Append As #iFileNo If Not logOutputCollection Is Nothing Then For j = 1 To logOutputCollection.Count Print #iFileNo, logOutputCollection(j) Next End If Close #iFileNo End Sub
Function getNowWithMS() As String Dim dtmNowTime ' 現在時刻 Dim lngHour ' 時 Dim lngMinute ' 分 Dim lngSecond ' 秒 Dim lngMilliSecond ' ミリ秒 dtmNowTime = Timer lngMilliSecond = dtmNowTime - Fix(dtmNowTime) lngMilliSecond = Right("000" & Fix(lngMilliSecond * 1000), 3) dtmNowTime = Fix(dtmNowTime) lngSecond = Right("0" & dtmNowTime Mod 60, 2) dtmNowTime = dtmNowTime \ 60 lngMinute = Right("0" & dtmNowTime Mod 60, 2) dtmNowTime = dtmNowTime \ 60 lngHour = Right("0" & dtmNowTime, 2) getNowWithMS = lngHour & ":" & lngMinute & ":" & lngSecond & "." & lngMilliSecond End Function
Sub や Function に入った直後と出る直前に、「LogWriteToBuffer “IN またはOUT,<モジュール名>,<関数名>”」という処理が自動的に追加されているのがお分かりでしょうか。また、標準モジュールにモジュールが追加され、ログ出力関数 (LogWriteToBuffer() / LogWrite()) と、時刻をミリ秒まで取得する関数 (getNowWithMS()) が追加されます。
'コメント行は除外 If Left(strCurrentCode, 1) = "'" Or UCase(Left(strCurrentCode, 4)) = "REM " Then Exit Function End If
'If xxx Then Exit Sub のような行の先頭以外に Exit がある書き方を考慮し InStr で合致確認 FoundPos = InStr(strCurrentCode, "Exit ") '"Exit " に合致しても以下のケースは除外 'a) Exit Sub の位置が先頭以外で、前にスペースがなく "xxxExit " のように文字列がある場合 'b) 行の途中からコメントで、コメント部分に "Exit " がある場合 'c) "Exit Sub" と "Exit Function" 以外の "Exit aaa" のような場合 If FoundPos > 0 Then If (FoundPos > 1 And InStr(strCurrentCode, " Exit ") = 0) _ Or (InStr(strCurrentCode, "'") > 0 And InStr(strCurrentCode, "'") < FoundPos) _ Or (InStr(strCurrentCode, "Exit Sub") = 0 And InStr(strCurrentCode, "Exit Function") = 0) Then FoundPos = 0 'Exit Sub / Exit Function は見つからなかったものとみなす End If End If
'Exit Sub / Exit Function のいずれかがある場合、その前に関数を抜けるログ出力処理追加 If FoundPos > 0 Then 'If xxx Then Exit Sub の書き方を考慮しコード整形 If Left(strCurrentCode, 3) = "If " Then 'いったん End If を入れる (If xxx Then Exit Sub + 改行 + End If) strCurrentCode = strCurrentCode & vbCrLf & "End If" '以下のように If 文を分割してコードを置き換え 'Exit Sub の手前まで (If xxx Then ) 'ログ出力 (LogWriteToBuffer "OUT : " & CurrentVBComponent.Name & " : " & strProcName & "") 'Exit Sub 以降 (Exit Sub + 改行 + End If) CurrentVBComponent.CodeModule.ReplaceLine InProcCurrentLine, _ Left(strCurrentCode, FoundPos - 1) & vbCrLf & _ "LogWriteToBuffer ""OUT," & CurrentVBComponent.Name & "," & strProcName & """" & vbCrLf & _ Mid(strCurrentCode, FoundPos) Else 'その他通常の Exit の場合は直前に関数を抜けるログ追加 CurrentVBComponent.CodeModule.InsertLines InProcCurrentLine, "LogWriteToBuffer ""OUT," & CurrentVBComponent.Name & "," & strProcName & """" End If End If End Function
'メイン モジュール Sub AddLogToFunc() '**************************************************** 'お客様環境に合わせて書き換えてください Const szBookFile = "C:\work\testProgram.xlsm" 'ログ出力処理を追加するファイルのフルパス Const szLogFile = "C:\work\VBAPerf.log" 'ログ出力ファイルのフルパス '**************************************************** Const vbext_ct_StdModule = 1 'VBComponent Type 定数 : 標準モジュール Const vbext_pk_Proc = 0 'prockind 定数 : プロパティ プロシージャ以外のすべてのプロシージャ Dim xlBook As Workbook Dim CurrentVBComponent As Object 'VBComponent Dim TotalLine As Long Dim CurrentLine As Long Dim strProcName As String Dim strCurrentCode As String Dim ProcStartLine As Long Dim ProcEndLine As Long Dim InProcCurrentLine As Long Dim FoundPos As Long Dim strFunc As String Application.EnableEvents = False Set xlBook = Workbooks.Open(szBookFile) 'ログ出力処理追加対象ブック オープン
' 対象ブックに含まれる各モジュール内関数の最初と最後にログ出力関数呼び出しを追加 For Each CurrentVBComponent In xlBook.VBProject.VBComponents TotalLine = CurrentVBComponent.CodeModule.CountOfLines 'コード末尾から 1 行ごとに確認 For CurrentLine = TotalLine To 1 Step -1 strProcName = CurrentVBComponent.CodeModule.ProcOfLine(CurrentLine, vbext_pk_Proc) 'その行が属するプロシージャ名を取得 strCurrentCode = LTrim(CurrentVBComponent.CodeModule.Lines(CurrentLine, 1)) 'End で始まる場合 : そのプロシージャの初めと終わりに処理追加 If strProcName <> Empty And Left(strCurrentCode, 4) = "End " Then ProcStartLine = CurrentVBComponent.CodeModule.ProcBodyLine(strProcName, vbext_pk_Proc) + 1 'プロシージャの先頭行を取得 ProcEndLine = CurrentLine 'End 行の前に関数を抜けるログ出力処理追加 CurrentVBComponent.CodeModule.InsertLines ProcEndLine, "LogWriteToBuffer ""OUT," & CurrentVBComponent.Name & "," & strProcName & """" 'プロシージャ開始行の直後に関数に入るログ出力処理追加 CurrentVBComponent.CodeModule.InsertLines ProcStartLine, "LogWriteToBuffer ""IN," & CurrentVBComponent.Name & "," & strProcName & """" 'さらにこのプロシージャ内の途中で処理追加すべき箇所 (Exit Sub / Exit Function) をチェック '(OUT ログ追加により CurrentLine は End Sub の 1 行前を指す) For InProcCurrentLine = CurrentLine To ProcStartLine Step -1 CheckExit CurrentVBComponent, strProcName, InProcCurrentLine Next 'このプロシージャの処理は終わっているためプロシージャ先頭までスキップ CurrentLine = ProcStartLine - 1 End If Next Next