2017年10月9日

【Access】ログファイルをローテーションする


ログファイルにどんどん追記していくと際限なくファイルサイズが増大していってしまいます。そこでログファイルがある一定のファイルサイズを超えたらローテーションして古いファイルを削除したりして増大するのを防ぎます。

標準モジュール

今回、標準モジュールに下記のようなプロシージャを作成しました。
ログファイルの最大サイズ、ログファイル名、ログファイルの拡張子、あとローテーション世代数を定数で設定しています。

ローテートは、最大サイズを超えたらファイル名に数字をプラスしていきます。そして、ローテーション世代数を超えたファイルは削除するようにしています。

error.log

error1.log

error2.log

error3.log

削除

'定数
Private Const MAX_FILE_SIZE As Long = 4096            'ログファイルサイズ(KB)
Private Const LOG_FILE_NAME As String = "error"       'ログファイル名
Private Const LOG_FILE_EXT  As String = "log"         '拡張子名
Private Const ROTATE_NUM As Integer = 3               'ログローテーション世代数

'ログローテート
Public Sub LogRotate(ByVal fileName As String)
On Error GoTo Err_Trap

    Dim fso        As FileSystemObject
    Dim i          As Integer
    Dim r          As Integer
    Dim folderPath As String
    Dim srcFile    As String
    Dim destFile   As String
    Dim oldestFile As String
    Dim checkFile  As String
    Dim fileSize   As Long

    'FileSystemObjectオブジェクトを作成する
    Set fso = CreateObject("Scripting.FileSystemObject")
   
    'ファイルが存在するかチェック
    If fso.FileExists(fileName) = False Then
        Exit Sub
    End If

    'ファイルサイズを取得する
    fileSize = fso.GetFile(fileName).Size

    'ファイルサイズをチェック
    If fileSize < MAX_FILE_SIZE * 1024 Then
        '最大サイズを超えてなければ処理を抜ける
        Exit Sub
    End If

    'フォルダパスを取得
    folderPath = fso.GetParentFolderName(fileName)

    '最古ファイルパス
    oldestFile = folderPath & "\" & LOG_FILE_NAME & ROTATE_NUM & "." & LOG_FILE_EXT

    '最古ファイルが存在すれば削除
    If fso.FileExists(oldestFile) Then
        Call fso.DeleteFile(oldestFile)
        r = ROTATE_NUM - 1
    Else
        'ローテーション世代数を調べる
        For r = (ROTATE_NUM - 1) To 0 Step -1
            If r = 0 Then
                checkFile = folderPath & "\" & LOG_FILE_NAME & "." & LOG_FILE_EXT
            Else
                checkFile = folderPath & "\" & LOG_FILE_NAME & r & "." & LOG_FILE_EXT
            End If

            If fso.FileExists(checkFile) Then
                Exit For
            End If
        Next
    End If

    'ファイル名をローテーションする
    For i = r To 0 Step -1
        If i = 0 Then
            srcFile = folderPath & "\" & LOG_FILE_NAME & "." & LOG_FILE_EXT
        Else
            srcFile = folderPath & "\" & LOG_FILE_NAME & i & "." & LOG_FILE_EXT
        End If

        destFile = folderPath & "\" & LOG_FILE_NAME & i + 1 & "." & LOG_FILE_EXT

        If fso.FileExists(srcFile) Then
            'ファイル名変更
            Call fso.MoveFile(srcFile, destFile)
        End If
    Next

Exit_Trap:

    Set fso = Nothing

Exit Sub
Err_Trap:
    MsgBox Err.Number & Space(2) & Err.Description, vbCritical, "エラー"
    Resume Exit_Trap
End Sub

実際のローテートする部分だけです。
このプロシージャをログファイル作成時に呼び出してあげればローテートしてくれます。

2017年10月8日

Embedlyを使ってブログカードを作ってみた

以前からほかのブログとかを見てると、埋め込みでサムネイル付きのリンクが貼り付けてあって、こういうの自分もやってみたい!って思っていたのですが、調べてみると「はてなカード」と言って、はてなブログ用のサービスらしいんです。

このブログはBloggerですので、残念ながら「はてなカード」は使えないんですよね。

そこで他に何かいい方法はないかと探してみたところ、今日、Embedlyというサービスを見つけました。

試しにブログカードを作ってみましたので、その使い方をちょっとご紹介したいと思います。

2017年10月7日

【Access】エラーログを出力する


システムでエラーが発生したときにファイルにエラーログを出力する方法です。

参照設定

まず、今回ファイルの作成やフォルダの作成にFileSystemObjectオブジェクトを使ので参照設定で「Microsoft Scripting Runtime」を選択してください。


標準モジュール

次に標準モジュールに下記のように記述します。
'Win32API
Private Declare Sub GetLocalTime Lib "kernel32" (lpSystem As SYSTEMTIME)

'構造体
Private Type SYSTEMTIME
    wYear As Integer
    wMonth As Integer
    wDayOfWeek  As Integer
    wDay  As Integer
    wHour As Integer
    wMinute As Integer
    wSecond As Integer
    wMilliseconds As Integer
End Type

'エラーログ作成
Public Sub MakeErrorLog(ByRef errObj As ErrObject, _
                        ByVal procName As String)

    Dim errNumber        As Long
    Dim errDescription   As String
    Dim messageLog       As String
    
    'エラー情報を保管
    errNumber = errObj.Number
    errDescription = errObj.Description
    
    'エラーを消去
    errObj.Clear
    
    messageLog = "【エラー番号】" & errNumber & _
              " 【詳細】" & errDescription & _
              " 【発生場所】" & procName
    
    '書き込み
    Call WriteLog(messageLog)
    
    'メッセージボックス表示
    MsgBox errNumber & Space(2) & errDescription, vbCritical, "エラー"

End Sub

'ログ出力
Private Sub WriteLog(ByVal message As String)
On Error GoTo ErrorTrap

    Dim ts, fso         As FileSystemObject
    Dim projectPath     As String
    Dim logFolder       As String
    Dim logFile         As String
    Dim logFileName     As String
    Dim sysLocalTime    As SYSTEMTIME
    
    
    '現在時刻を取得
    GetLocalTime sysLocalTime
    
    'ログファイル名
    logFileName = "error.log"
    
    'プロジェクトパスを取得
    projectPath = CurrentProject.Path
    
    'ログ格納フォルダ
    logFolder = projectPath & "\Log"
    
    'FileSystemObjectオブジェクトを作成する
    Set fso = CreateObject("Scripting.FileSystemObject")
   
    'ログ格納フォルダの確認(なければ作成)
    If fso.FolderExists(logFolder) = False Then
        Call fso.CreateFolder(logFolder)
    End If
    
    'ログファイルフルパス名
    logFile = logFolder & "\" & logFileName
    
    '追記モードでファイルオープン
    Set ts = fso.OpenTextFile(logFile, ForAppending, True)

    '書き込み
    Call ts.WriteLine("[" & sysLocalTime.wYear & "/" & _
                        Format(sysLocalTime.wMonth, "00") & "/" & _
                        Format(sysLocalTime.wDay, "00") & " " & _
                        Format(sysLocalTime.wHour, "00") & ":" & _
                        Format(sysLocalTime.wMinute, "00") & ":" & _
                        Format(sysLocalTime.wSecond, "00") & "." & _
                        Format(sysLocalTime.wMilliseconds, "000") & "] " & message)
                      
    'ファイルクローズ
    ts.Close
    
    Set ts = Nothing
    Set fso = Nothing
    
Exit Sub
ErrorTrap:
    MsgBox Err.Number & Space(2) & Err.Description, vbCritical, "エラー"
End Sub
今回、MakeErrorLogの引数にErrオブジェクトを渡してエラー番号とエラーの詳細をログに出力するようにしています。また、プロシージャ名を渡してどこでエラーが発生したかもログに残すようにしています。

実際のファイルに出力する部分は、FileSystemObjectオブジェクトを使って行っています。ファイルは追記モードで開き、発生時刻はWin32APIを使いミリ秒まで出すようにしました。

2017年10月6日

【Access】マウスポインターの形状を変更する


通常、Accessでマウスカーソルを砂時計に変更するにはDoCmd.Hourglassを使いますが、ScreenオブジェクトのMousePointerプロパティを使っても変更することが出来ます。


MousePointer プロパティの設定値

設定値 内容
0既定値
1標準の選択 (矢印)
3テキスト選択 (I字型ポインター)
7上下に拡大/縮小
9左右に拡大/縮小
11待ち状態 (砂時計)

1 矢印

Application.Screen.MousePointer = 1




3 I字型ポインター

Application.Screen.MousePointer = 3




7 上下に拡大/縮小

Application.Screen.MousePointer = 7




9 左右に拡大/縮小

Application.Screen.MousePointer = 9




11 待ち状態 (砂時計)

Application.Screen.MousePointer = 11









2017年10月5日

【Access】エラー発生時の終了処理


そのプロシージャを終わらせるにあたって実行しなければいけない処理というのがある場合があります。たとえば、データベースへの接続オブジェクトのクローズ処理だったり、戻り値の設定をすることだったり。ただ、エラーが発生してしまい途中でエラー処理に飛ばされてしまうことがあります。

そのような場合、エラー処理を行ったあとにResumeステートメントで終了処理に飛ばしてあげます。

下記の例では、マウスカーソルを砂時計に変更し、エラー発生時でも必ずマウスカーソルを元に戻すようにしています。
Public Sub ExitTest()
On Error GoTo Err_Trap

    'マウスを砂時計に切り替える
    DoCmd.Hourglass True
    
    Dim a As Long
    
    a = 30 / 0

    MsgBox "a = " & a

Exit_Trap:
    
    '終了処理

    'マウスを元に戻す
    DoCmd.Hourglass False

Exit Sub
Err_Trap:
    MsgBox "エラー番号:" & Err.Number & vbCrLf & vbCrLf & Err.Description, vbExclamation, "エラー"
    Resume Exit_Trap
End Sub
ポイントは、Err_Trapラベル後のメッセージボックスを表示したあとにResumeステートメントでExit_Trapラベルに処理を飛ばしているところです。
Resumeステートメントでラベルを指定すると、そのラベルに処理を移動できるようになっています。






2017年10月4日

【PowerShell】ファイルの拡張子を一括で変更するスクリプト


ファイルの拡張子を一括で変更するスクリプトを作ってみました。

「Change-FileExtension.ps1」
#引数(該当フォルダ, 変更前拡張子, 変更後拡張子)
param($targetDir, $oldExt, $newExt)

#正規表現の指定
$matStr = '.' + $oldExt
$oldStr = '\.'+ $oldExt + '$'
$newStr = '.' + $newExt

#該当するファイルの拡張子を置換
Get-ChildItem -Path $targetDir | Where-Object {$_.Extension -eq $matStr} | Rename-Item -NewName { $_.Name -replace $oldStr, $newStr }

実行例

たとえばフォルダ(C:\work\test)にこのようなファイルがあるとします。



このファイルの拡張子を「log」から「txt」に一括で変更するには次のよう引数を指定します。
引数は左から「該当フォルダ」「変更前拡張子」「変更後拡張子」。
PS C:\work\access> .\Change-FileExtension.ps1 C:\work\test log txt


実行するとこのように拡張子が一括で変更されます。







2017年10月3日

【Access】エラー処理


システム開発をする上で重要になってくるのがエラー処理です。エラー処理を正しく行っていないとエラーによりAccess自体を強制終了しなければいけないといったことになってしまうことがあります。

Access VBAのエラー処理は2つの方法があり、一つはエラーが起きても無視して処理を続行させる方法と、もう一つはエラーが起きたら特定の行に処理を移す方法があります。

エラーが起きても無視して処理を続行させる方法

まずは何もエラー処理してない場合です。下記のコードは0除算エラーが発生します。
Public Sub ErrorResumeTest()

    Dim a As Long
    
    a = 30 / 0
    
    MsgBox "a = " & a

End Sub

実行するとこのように「0で除算しました。」というエラーが出てプログラムが停止してしまいます。



次はこのコードに下記のように「On Error Resume Next」ステートメントを追加してみます。
Public Sub ErrorResumeTest()
On Error Resume Next

    Dim a As Long
    
    a = 30 / 0
    
    MsgBox "a = " & a

End Sub

実行結果


「On Error Resume Next」を追加すると、実行時エラーを無視するようになり、次のステートメントに処理を移しプログラムの実行を継続させます。


エラーが起きたら特定の行に処理を移す方法

次にエラーが起きた場合に特定の行に処理を移す方法です。下記のコードでは、「On Error GoTo」ステートメントを使ってエラーが起きた際に処理を移動させています。
Public Sub ErrorGotoTest()
On Error GoTo Err_Trap

    Dim a As Long
    
    a = 30 / 0

    MsgBox "a = " & a

Exit Sub
Err_Trap:
    MsgBox "エラー番号:" & Err.Number & vbCrLf & vbCrLf & Err.Description, vbExclamation, "エラー"
End Sub
GoToで指定した「Err_Trap」というラベルに処理を飛ばすようにしています。これによりプログラムを停止することなくエラーメッセージを表示させたりすることが出来るようになります。

また、注意してほしいのは、「Err_Trap」ラベルの前には「Exit Sub」などの終了処理を入れてください。そうしないとエラーが発生してないときもエラーメッセージが表示されてしまいます。


実行結果