ラベル 開発日記 の投稿を表示しています。 すべての投稿を表示
ラベル 開発日記 の投稿を表示しています。 すべての投稿を表示

2017年5月8日月曜日

SAS Log Utility 1.9.1.3

不具合修正中です。潜在的なバグのパターンを定義する機能に不具合あるので直します。あと、SAS9.4TS1M4に合わせて、キーワードを修正しないといけない。

2016年8月3日水曜日

SAS Metadata API

調べ物のメモです。
メターデータのAPIを使うと、ユーザのDescriptionやメールアドレスを設定できます。

rc = metadata_setattr("omsobj:Person?@Name='ogawan'","Desc", "hogehoge");


2016年7月13日水曜日

次に作りたいツール, Metadata Serverに登録されているユーザにグループを設定/解除するスクリプト

決定版のスクリプトがないので、自分で作ることにします。
欲しい機能を書きとめておきます。

  • ユーザを登録する機能
  • ユーザを削除する機能
  • 全ユーザとそのグループとロールの一覧を表示する機能
  • 全グループの一覧を表示する機能
  • ユーザに対して1つ以上のグループを設定する機能
  • ユーザに対して1つ以上のロールを設定する機能
  • 変更前にバックアップ用のデータをファイルに保存する機能
  • UNIXのシェルから実行できること
  • エラーのログファイルを指定できること
  • 指定されたファイルを読み込んで、まとめて複数のユーザを登録できること
  • 指定されたファイルを読み込んで、まとめて複数のユーザを削除できること
GUIを組みたいが、APIとしてはJavaかC#を使う必要がある。
C#はDelphiを選ぶ前に試しで画面組んだが、もう5年以上も触っていない。
UNIXのログインしてスクリプトたたかずに、PCクライアントからツールを起動して、登録と確認できたら良いです。

まずは、スクリプトを目標にします。

2016年3月27日日曜日

VBScriptでタスクスケジューラの複数のタスクをXMLに出力、削除、作成

EGで登録したスケジュールのタスクを、他のユーザへ定期的に引き渡すためにVBScriptを組みました。特定のタスクをXMLファイルに出力、削除、登録します。

SCHTASKSを使っていますが、これ1つの操作で1タスクしか定義できません。じゃあPowerShellで書こうかと思ったのですが、お客様先の環境だと使えないことが多いためVBScriptで書きました。
  • tasks_list.vbs でXMLファイルを作成
  • tasks_delete.vbs でタスクを削除
  • tasks_create.vbs でタスクを作成
キーワードを含む名前のタスクをXML形式で出力します。

'----
' Windowsのタスク スケジューラから指定された名前のタスクをXML形式で表示します。
' 標準出力にはログ、標準エラー出力にはXMLを出力します。
' 使用例: C:\Windows\System32\cscript //b c:\temp\tasks_list.vbs 1> tasks.log 2> tasks.xml
'----

Option Explicit
Dim objShell
Dim objExec 
Dim objDictionary
Dim objFso
Dim strLine
Dim strCmd
Dim Count
Dim Name
Dim QuitCode
Const MINIMIZE_WINDOW = 2
Const SCHTASKS = "C:\Windows\System32\SCHTASKS.EXE"
Const KEYWORD = "スケジュール - "

'----
' チェック関数
'----

Function CheckError(fnName)
    CheckError = False
    
    Dim strmsg
    Dim errNum
    
    If Err.Number <> 0 Then
        strmsg = "ERROE: #" & Hex(Err.Number) & " in module " & fnName & " " & Err.Description
        WScript.Stdout.WriteLine strmsg
        CheckError = True
    End If
         
End Function

'----
' オブジェクト生成
'----

Set objShell = WScript.CreateObject("WScript.Shell")
Set objDictionary = WScript.CreateObject("Scripting.Dictionary")
Set objFso = WScript.CreateObject("Scripting.FileSystemObject")

'----
' タスク一覧表示
'----

Sub Main()
    Dim ExitCode
    Dim ErrCount

    '----
    ' タスク一覧から削除対象のタスク名を取得
    '----

    Set objExec = objShell.Exec(SCHTASKS & " /query /fo:csv")
    Count = 0
    Do Until objExec.StdOut.AtEndOfStream
        strLine = objExec.StdOut.ReadLine
        If InStr(strLine, KEYWORD) <> 0 Then
            Count = Count + 1
            objDictionary.Add Count, Mid(strLine, 1, InStr(strLine, ""","))
        End If
    Loop
    If objExec.ExitCode <> 0 Then
        Do Until objExec.StdErr.AtEndOfStream
            WScript.StdOut.WriteLine objExec.StdErr.ReadLine
        Loop
        Err.Raise(51)
    End If
    Set objExec = Nothing

    '----
    ' 件数を出力
    '----

    WScript.StdOut.WriteLine "NOTE: found " & Count & " tasks to export."

    '----
    ' タスクをXML形式で表示
    '----

    For Each Name In objDictionary.Keys
        strCmd = SCHTASKS & " /query /xml /tn:" & objDictionary(Name)
        WScript.StdOut.WriteLine strCmd
        Set objExec = objShell.Exec(strCmd)
        WScript.StdErr.WriteLine("")
        Do Until objExec.StdOut.AtEndOfStream
            WScript.StdErr.WriteLine(objExec.StdOut.ReadLine)
        Loop
        WScript.StdErr.WriteLine("")

        If objExec.ExitCode <> 0 Then
            Do Until objExec.StdErr.AtEndOfStream
                WScript.StdOut.WriteLine objExec.StdErr.ReadLine
            Loop
            Err.Raise(51)
        End If
        Set objExec = Nothing
    Next

End Sub

'----
' 処理実行
'----

Sub Try()
    Call Main()
End Sub

'----
' エラー捕捉
'----

Sub Catch()
    On Error Resume Next
    Call Try()
End Sub

'----
' 処理実行
'----

QuitCode=0
WScript.StdOut.WriteLine "NOTE: script=" & WScript.ScriptFullName & " date=" & Now()
Call Catch()
If CheckError("Catch") Then
    QuitCode = 1
End If

'----
' 終了
'----

Set objShell = Nothing
Set objDictionary = Nothing
Set objFso = Nothing
WScript.StdOut.WriteLine "NOTE: script finished with exit code " & QuitCode
WScript.Quit QuitCode



特定の名前のタスクを削除します。
'----
' Windowsのタスク スケジューラから指定された名前のタスクを削除します。
' 標準出力に削除のログを出力します。
' 使用例: C:\Windows\System32\cscript //b c:\temp\tasks_delete.vbs > tasks_delete_%date:~-2,2%.log
'----

Option Explicit
Dim objShell
Dim objExec 
Dim objDictionary
Dim objFso
Dim strLine
Dim strCmd
Dim Count
Dim Name
Dim objFile
Dim QuitCode
Const SCSHTASKS = "C:\Windows\System32\SCHTASKS.EXE"

'----
' 削除するタスク名の名前
'----

Const KEYWORD = "スケジュール - "

'----
' チェック関数
'----

Function CheckError(fnName)
    CheckError = False
    
    Dim strmsg
    Dim errNum
    
    If Err.Number <> 0 Then
        strmsg = "ERROE: #" & Hex(Err.Number) & " in module " & fnName & " " & Err.Description
        WScript.Stdout.WriteLine strmsg
        CheckError = True
    End If
         
End Function

'----
' オブジェクト生成
'----

Set objShell = WScript.CreateObject("WScript.Shell")
Set objDictionary = WScript.CreateObject("Scripting.Dictionary")
Set objFso = WScript.CreateObject("Scripting.FileSystemObject")

'----
' タスク削除
'----

Sub Main()
    Dim ExitCode
    Dim ErrCount

    '----
    ' タスク一覧から削除対象のタスク名を取得
    '----

    Set objExec = objShell.Exec(SCSHTASKS & " /query /fo:csv")
    Count = 0
    Do Until objExec.StdOut.AtEndOfStream
        strLine = objExec.StdOut.ReadLine
        If InStr(strLine, KEYWORD) <> 0 Then
            Count = Count + 1
            objDictionary.Add Count, Mid(strLine, 1, InStr(strLine, ""","))
        End If
    Loop
    If objExec.ExitCode <> 0 Then
        Do Until objExec.StdErr.AtEndOfStream
            WScript.StdOut.WriteLine objExec.StdErr.ReadLine
        Loop
        Err.Raise(51)
    End If
    Set objExec = Nothing

    '----
    ' 件数を出力
    '----

    WScript.StdOut.WriteLine "NOTE: found " & Count & " tasks to delete."

    '----
    ' タスクを削除
    '----

    For Each Name In objDictionary.Keys
        strCmd = SCSHTASKS & " /delete /f /tn " & objDictionary(Name)
        WScript.StdOut.WriteLine strCmd
        Set objExec = objShell.Exec(strCmd)

        Do Until objExec.StdOut.AtEndOfStream
            WScript.StdOut.WriteLine objExec.StdOut.ReadLine
        Loop

        If objExec.ExitCode <> 0 Then
            Do Until objExec.StdErr.AtEndOfStream
                WScript.StdOut.WriteLine objExec.StdErr.ReadLine
            Loop
            Err.Raise(51)
        End If
        Set objExec = Nothing
    Next

End Sub

'----
' 処理実行
'----

Sub Try()
    Call Main()
End Sub

'----
' エラー捕捉
'----

Sub Catch()
    On Error Resume Next
    Call Try()
End Sub

'----
' 処理実行
'----

QuitCode = 0
WScript.StdOut.WriteLine "NOTE: script=" & WScript.ScriptFullName & " date=" & Now()
Call Catch()
If CheckError("Catch") Then
    QuitCode = 1
End If

'----
' 終了
'----

Set objShell = Nothing
Set objDictionary = Nothing
Set objFso = Nothing
WScript.StdOut.WriteLine "NOTE: script finished with exit code " & QuitCode
WScript.Quit QuitCode

XMLファイルからタスクを作成します。
'----
' WindowsのタスクをXMLファイルから定義します。
' 引数に tasks_list.vbs で作成したXMLファイルを指定します。
' タスクの実行ユーザは、スクリプトの実行者に置き換えられます。
' 使用例: C:\Windows\System32\cscript //b c:\temp\tasks_create.vbs tasks.xml
'----

Option Explicit
Dim objShell
Dim objExec 
Dim objDictionary
Dim objFso
Dim strLine
Dim strCmd
Dim Count
Dim Name
Dim objFile
Dim xmlFile
Dim QuitCode
Const SCSHTASKS = "C:\Windows\System32\SCHTASKS.EXE"

'----
' 実行ユーザのキーワード
'----

Const KEYWORD = "      "

'----
' チェック関数
'----

Function CheckError(fnName)
    CheckError = False
    
    Dim strmsg
    Dim errNum
    
    If Err.Number <> 0 Then
        strmsg = "ERROE: #" & Hex(Err.Number) & " in module " & fnName & " " & Err.Description
        WScript.Stdout.WriteLine strmsg
        CheckError = True
    End If
         
End Function

'----
' XMLのコメントからユーザ名を取得
'----

Function XmlUserName(Buf)
    Dim Pos1, Pos2
    Const Keyword1 = "username="
    Const Keyword2 = " -->"

    XmlUserName = "N/A"

    Pos1 = InStr(Buf, Keyword1)
    Pos2 = InStr(Buf, Keyword2)
    If Pos1 <> 0 And Pos2 <> 0 Then
        XmlUserName = Mid(Buf, Pos1 + Len(Keyword1), Pos2 - (Pos1 + Len(Keyword1)))
    End If
End Function

'----
' XMLのコメントからタスク名を取得
'----

Function XmlTaskName(Buf)
    Dim Pos1, Pos2
    Dim AryStrings
    Const Keyword1 = "<!-- end "
    Const Keyword2 = "username="

    XmlTaskName = "N/A"

    Pos1 = InStr(Buf, Keyword1)
    Pos2 = InStr(Buf, Keyword2)
    If Pos1 <> 0 And Pos2 <> 0 Then
        XmlTaskName = Trim(Mid(Buf, Pos1 + Len(Keyword1), Pos2 - (Pos1 + Len(Keyword1))))
        XmlTaskName = Mid(XmlTaskName, 2, Len(XmlTaskName) - 2)

        AryStrings = Split(XmlTaskName, "\")
        XmlTaskName = AryStrings(UBound(AryStrings))
    End If
End Function

'----
' オブジェクト生成
'----

Set objShell = WScript.CreateObject("WScript.Shell")
Set objDictionary = WScript.CreateObject("Scripting.Dictionary")
Set objFso = WScript.CreateObject("Scripting.FileSystemObject")

'----
' タスク定義
'----

Sub Main()
    Dim UserName
    Dim TaskName
    Dim FilePath

    '----
    ' テンポラリファイルのパスを設定
    '----
    FilePath = objShell.ExpandEnvironmentStrings("%TEMP%") & "\task.xml"

    '----
    ' 引数のXMLファイルを開く
    '----
    If WScript.Arguments.Count <> 1 Then
        Err.Rase(51)
    End If
    Set objFile = objFso.OpenTextFile(WScript.Arguments.Item(0))

    Count = 0
    strLine = objFile.ReadLine
    Do Until objFile.AtEndOfStream
        If InStr(strLine, "<!-- begin") <> 0 Then
            Set xmlFile = objFSO.OpenTextFile(FilePath, 2, True)
            strLine = objFile.ReadLine
            Do Until objFile.AtEndOfStream or (InStr(strLine, "<!-- end") <> 0)
                ' ユーザIDを書き換える
                If InStr(strLine, KEYWORD) = 1 Then
                    strLine = "      " & objShell.ExpandEnvironmentStrings("%USERDOMAIN%") & "\" & objShell.ExpandEnvironmentStrings("%USERNAME%") & ""
                End If
                xmlFile.WriteLine strLine
                strLine = objFile.ReadLine
            Loop
            UserName = xmlUserName(strLine)
            TaskName = xmlTaskName(strLine)
            xmlFile.Close

            '----
            ' タスク登録
            '----
            Count = Count + 1
            strCmd = SCSHTASKS & " /create /xml """ & FilePath & """ /tn:""EG\" & UserName & "." & Right("000" & Count, 3) & " " & TaskName & """"
            WScript.StdOut.WriteLine strCmd
            Set objExec = objShell.Exec(strCmd)

            Do Until objExec.StdOut.AtEndOfStream
                WScript.StdOut.WriteLine objExec.StdOut.ReadLine
            Loop
            If objExec.ExitCode <> 0 Then
                Do Until objExec.StdErr.AtEndOfStream
                    WScript.StdOut.WriteLine objExec.StdErr.ReadLine
                Loop
                Err.Raise(51)
            End If
            Set objExec = Nothing

        End If
        If objFile.AtEndOfStream = False Then
            strLine = objFile.ReadLine
        End If
    Loop

    '----
    ' 件数を出力
    '----

    WScript.StdOut.WriteLine "NOTE: create " & Count & " tasks."

    '----
    ' テンポラリのXMLファイルを削除
    '----
    objFso.DeleteFile FilePath, True

End Sub

'----
' 処理実行
'----

Sub Try()
    Call Main()
End Sub

'----
' エラー捕捉
'----

Sub Catch()
    On Error Resume Next
    Call Try()
End Sub

'----
' 処理実行
'----

QuitCode = 0
WScript.StdOut.WriteLine "NOTE: script=" & WScript.ScriptFullName & " date=" & Now()
Call Catch()
If CheckError("Catch") Then
    QuitCode = 1
End If

'----
' 終了
'----

Set objShell = Nothing
Set objDictionary = Nothing
Set objFso = Nothing
WScript.StdOut.WriteLine "NOTE: script finished with exit code " & QuitCode
WScript.Quit QuitCode




2016年2月21日日曜日

SAS Log Utility 1.8.1.1, ログファイルのエンコーディングを判定

Vectorに新しいバージョンの公開を依頼しました。今回の修正内容は以下のとおりです。

  • 不具合の修正
  • SASログファイルのエンコーディングを自動判定
  • SJIS, JIS, EUC, UTF8N, UTF8, UTF7など複数のエンコーディングに対応
  • Optionsのメニューの構成を変更
  • 日本語のマッチングパターン(keyword.ini)を訂正



ログファイルのエンコーディング判定は、ココの情報が役立ちました。これが無ければ、短時間で実装できなかったです。

ListViewのソートは、DEBUGのシンボルを定義したときと、外したときで動作が異なっていました。原因分からず。機能的には、だいぶ落ち着いてきたと思います。そろそろメジャーバージョンを上げて、処理構造の無駄を取り除きたいです。

2016年2月17日水曜日

EncodeDetect.pas

開発のメモです。
ログファイルのエンコーディング判定の関数、下にネタがあります。

EncodeDetect : EncodeDetectリファレンス

http://www.watercolor-city.net/ct_delphi/delphi_tiburon/doc_thirdparty/tp_ed_ref.htm

これを使ってコードを判定、TEncodeingを生成した後します。
StringListからEncodingを指定して、読み取ると正しく読み取れました。
試しにElapsed Timeの表示に組み込んだらあっさりと動きました。

2016年2月15日月曜日

UTF-8のログ

読み込めないSASログがあった。調べてみたらUTF-8だった。特に機能仕様には明記していなかったが、扱えるのはANSI, SJISのファイルです。

UTF-8はBOMあり、BOMなしで扱いが異なる。
別途サンプルのProjectを作って、検証から始める。

仕方ないので、メモ帳でSJISに変換してしのぎます。

実装の参考記事は こちら です。

2016年2月14日日曜日

SAS Log Utilityの概要

2009年に書いたツールの概要を加筆します。
SAS Log Utilityの概要について説明します。

概要
SAS Log Utilityは、SASプログラムが出力するログファイルを分析して、パフォーマンス診断や入出力の解析に役立つ情報を作成します。SAS Log Utilityを使うと以下のことができます。
  • SASプログラムの処理時間をDATAステップで集計して、ボトルネックとなっている処理を見つけることができます。
  • SASプログラムがどのようなデータを読み書きしているか、DATAステップ又はプロシージャ単位で捉えて、データの流れを理解することができます。
  • MPRINTのログを捉えて、マクロ単位の処理時間を集計します。
  • 複数のSASプログラムがどのようなデータを読み書きし、プログラム間でのデータの連携を可視化します。
機能
  • SASログからDATAステップ/プロシージャ単位の処理時間を抽出
  • SASログから処理時間を抽出し、データセット単位で処理に要した時間を集計
  • SASログからDATAステップ/プロシージャの入出力を抽出し、データの生成、読み取り、更新、削除の流れを表形式に出力
  • 設定ファイルの切り替えで、日本語と英語のSASログに対応
  • エラーにつながるNOTE:のメッセージをSASログから捕捉
  • INIファイルを追加、修正して日本語、英語以外のSASログを解析可能
  • SASログからMPRINTのログを抽出し、マクロ単位の処理時間を集計
  • SASログの分析結果をExcelに出力

SAS Log Utility 1.7.1.16, 不具合修正と設定ファイルの切り替え機能を追加

Vectorに新しいバージョンの公開を依頼しました。今回の修正内容は以下のとおりです。

  • 不具合の修正
  • 日本語と英語の設定ファイルを分けて、nlsのサブフォルダ下に移動
  • システムのデフォルトの言語から、設定ファイルを切り替えるように修正
  • Optionsのメニューの構成を変更

今回、日本語、英語の設定を分けたのが大きな変更点です。仕組みとしてはnlsフォルダの下に、ISO言語省略名のサブフォルダを定義して、INIファイルを作成すれば、他の言語も追加できます。が、まだ検証できていません。

修正した不具合は以下のものです。

  • FILENAMEを使わないで直接ファイルのパスを指定しているときに、入出力を捕捉できない
  • データセット名の変更が正しくCRUD表に反映されない
  • 英語のSASログで、データセットの書き込み(W)が正しく反映されない

他の言語の対応は、時間のあるときに試したいです。
開発で謎なのが、Patterns... のボタンにアイコンを表示していたのですが、いつのまにか表示されなくなりました。IDEの中だと、鉛筆のマークが出ているのですが、実行すると消えてしまいます。この検証はまた時間のあるときに行います。

2016年2月13日土曜日

Delphi 条件付コンパイルの設定

開発のメモです。
リリース、デバッグ版とは別にインターナルのバージョンを作りました。条件付コンパイルの設定が分からなかったので、設定画面を貼り付けておきます。Vectorに載せるリリース版から分岐して、仕事で使うためのバージョンを作成しました。


土曜日の作業で、英語版の設定ファイルが8割がたできました。
明日、まとめてVectorに新しいバージョンを載せたいです。



2016年2月9日火曜日

NLS下のフォルダを探して、メニューを設定する(2)

開発のメモです。
設定ボタンの配置を換えました。
設定ボタンに16x16のアイコンを設定しました。
ISO言語省略名の選択は、ラジオボタンのタイプに変更しました。
潜在的なバグを見つけ出すパターンの設定は、Potential Problemのタブの下に移動しました。
言語の設定を切り替えるたびに、設定ファイルを読み直すように実装しました。





今日は、有給休暇でした。
銀行の手続きで3時間も掛かりました。

2016年2月7日日曜日

NLS下のフォルダを探して、メニューを設定する

開発のメモ。SASに倣って、NLSのフォルダ下にある言語のサブフォルダを見て、設定を切り替えられるように作成中です。



2016年2月1日月曜日

SAS Log Utility, 多言語対応の仕組みを考える

多言語対応で必要なものは何?

設定ファイルは、ISO言語省略名ごとに保持できるようにしたい。

どこに、設定ファイルを保持するか?
!SASROOT\nls\ja の形に倣って設定ファイルを配置するか?
それとも、設定ファイルの後ろにISO言語省略名を付与するのか?

LOCALEの取得は、Windows.GetLocaleInfoでできる。

言語の切り替えは、メニューから行う。
切り替えはツールの再起動なしで、反映できるようにしたい。

言語を切り替えて、ダイアログの表示を切り替えられるか?
reinit.ReinitializeFormsは使えるのか?
reinit.LoadNewResourceModuleは使えるのか?

他に使い出のあるものは何?

マイグレーション案件、面倒なことが色々ある。
何か効率化できるネタはないか。
VAの構成って面倒、設定だけではなくエビデンスの取得もできる何かが欲しい。

移行で毎回手を焼くのが、過去データの移送と変換です。
量が多いので、並列化したり、夜間に差分検出して移送する、中断、再開の制御を入れています。

他には?
BI Serverの構成が面倒です。
SMCの管理は苦手なのです、私はね。



2016年1月30日土曜日

きっと役立つ、バグになりそうなNOTE:のチェック

SAS Log Utilityには、エラーでもなく、ワーニングでもないけど、バグになりそうなメッセージをチェックする機能があります。2013年頃投稿、2016年1月30日更新です。

  • 変数 ABC は初期化されていません
  • 以下の箇所で文字値を数値に変換しました
  • 以下の箇所で数値を文字値に変換しました
  • ステートメントに BY値を繰り返すデータセットが複数あります
  • 欠損値を含んだ計算により、以下の箇所で欠損値が生成されました
  • 切り捨てられた行があります
  • カラム999で0による割り算がありました
  • 関数XYZ(行999 カラム999の引数は無効です
  • LOST CARD.
  • NOEXECオプションが指定
  • ループが発生したため、DATAステップの実行を中止しました
  • 引用符で囲まれた文字列の後の識別子の意味は
  • 数値をプリントするには小さすぎるW.D出力形式
  • ライブラリABCは存在しません
  • クエリは元のデータに要約統計量の結果を再マージします
  • データセットABCにオブザベーションがありません
  • ファイルABC(memtype=DATA)は見つかりませんが、DELETEステートメントに存在します
  • ABCは未参照のラベル定義です
  • ABCに対して、無効なデータが行999カラム999にあります
  • ステートメントが行の終端に達したので、次の行を読み込みます
  • 要約化中にディスク処理が発生しました
  • CASE式にELSE句がありません

関数の引数無効とかは、意外と気がつきにくいです。

SAS Log Utility 1.6.1.8, 不具合修正

Vectorに登録の申請をしました。
修正したのは以下の点です。

  • NOTE:でチェックするパターンを追加
  • ステータスバーにISO言語省略名を表示
  • ログの行末に追加されている空白又はタブを削除して処理
  • ListViewのソートの不具合を修正

ステータスバーに言語省略名を表示したのは、多言語対応の準備です。
行末のタブ又は空白を削除するようにしたのは、SASログをEXCELファイルに貼って渡す人がいて、処理の区切りを正確に判定できなかったためです。これがメモリリークの原因のひとつになっていました。ちなみに、EXCELに貼り付けて渡すとダブルクォートがエスケープされて、正しくパースできないことがありますので、私はWinZipで圧縮して渡します。

ステータスバーの左に言語省略名追加


記録まで

2016年1月28日木曜日

SAS Log Inspector 1.3.1.2, リリース


機能は変えず、組み込みのマッチングパターンを24種類に増やしました。どんなパターンが定義されているかは、以下の画像をご覧ください。Vectorに公開の依頼を投げました。



SAS Log Inspectorの機能
  • 複数のログファイルからエラー、バグに繋がるNOTEを集計
  • 日本語と英語版のメッセージに対応
  • Excelファイルにログをチェックした結果をエクスポート
  • 任意のチェック項目を検査対象から外すことができる
  • Notepadを起動して、該当のログファイルを参照できる
  • マッチングパターンを画面から追加、修正できる

2016年1月17日日曜日

Windows.GetLocaleInfoでLanguage Tagを取得

ツールのI18Nの検討メモです。
設定ファイルの言語を区別するため、Language Tag(ja, JP, en-usとか)を取得する方法を探しています。ログ解析の設定を日本語、英語と切り替えられるようにするための調査です。ネット上を探すとそのまま使えるサンプルが見つからないため、DelphiForFun HomeにあったDemoのコードを参考にしてサンプルコードを作りました。



試すときは、Formの上に、ButtonとListViewを配置し、ListViewの形式をvsReport、カラムを5つ定義してください。参考にしたのは以下のURLの情報です。
Locale Constants Demo

Language Tagの定義を探していたら、以下の情報が見つかりました。昔々、VAX Notes上でお世話になった記憶が微かにあります。
国際化プログラミング

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, shellapi, Vcl.ComCtrls;

type
  TForm1 = class(TForm)
    ListView1: TListView;
    Button1: TButton;
    procedure FormActivate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

type
  TRec = Record
    Code: Cardinal;
    Name: String;
    Text: String;
  end;

const
  rSet: Array [0 .. 79] of TRec = (
    (Code: LOCALE_ILANGUAGE; Name: '言語ID'; Text: 'LOCALE_ILANGUAGE'),
    (Code: LOCALE_SLANGUAGE; Name: '言語名'; Text: 'LOCALE_SLANGUAGE'),
    (Code: LOCALE_SENGLANGUAGE; Name: '言語英語名'; Text: 'LOCALE_SENGLANGUAGE'),
    (Code: LOCALE_SABBREVLANGNAME; Name: '言語省略名'; Text: 'LOCALE_SABBREVLANGNAME'),
    (Code: LOCALE_SNATIVELANGNAME; Name: '言語固有名'; Text: 'LOCALE_SNATIVELANGNAME'),
    (Code: LOCALE_ICOUNTRY; Name: '国コード'; Text: 'LOCALE_ICOUNTRY'),
    (Code: LOCALE_SCOUNTRY; Name: '国名'; Text: 'LOCALE_SCOUNTRY'),
    (Code: LOCALE_SENGCOUNTRY; Name: '国英語名'; Text: 'LOCALE_SENGCOUNTRY'),
    (Code: LOCALE_SABBREVCTRYNAME; Name: '国省略名'; Text: 'LOCALE_SABBREVCTRYNAME'),
    (Code: LOCALE_SNATIVECTRYNAME; Name: '国固有名'; Text: 'LOCALE_SNATIVECTRYNAME'),
    (Code: LOCALE_IDEFAULTLANGUAGE; Name: 'デフォルト言語ID'; Text: 'LOCALE_IDEFAULTLANGUAGE'),
    (Code: LOCALE_IDEFAULTCOUNTRY; Name: 'デフォルト国コード'; Text: 'LOCALE_IDEFAULTCOUNTRY'),
    (Code: LOCALE_IDEFAULTCODEPAGE; Name: 'デフォルトOEMコードページ'; Text: 'LOCALE_IDEFAULTCODEPAGE'),
    (Code: LOCALE_IDEFAULTANSICODEPAGE; Name: 'デフォルトANSIコードページ'; Text: 'LOCALE_IDEFAULTANSICODEPAGE'),
    (Code: LOCALE_IDEFAULTMACCODEPAGE; Name: 'デフォルトMACコードページ'; Text: 'LOCALE_IDEFAULTMACCODEPAGE'),
    (Code: LOCALE_FONTSIGNATURE; Name: 'フォント署名'; Text: 'LOCALE_FONTSIGNATURE'),
    (Code: LOCALE_SISO639LANGNAME; Name: 'ISO言語省略名'; Text: 'LOCALE_SISO639LANGNAME'),
    (Code: LOCALE_SISO3166CTRYNAME; Name: 'ISO国省略名'; Text: 'LOCALE_SISO3166CTRYNAME'),
    (Code: LOCALE_SLIST; Name: '区切り記号'; Text: 'LOCALE_SLIST'),
    (Code: LOCALE_IMEASURE; Name: '単位'; Text: 'LOCALE_IMEASURE'),
    (Code: LOCALE_SDECIMAL; Name: '小数点の記号'; Text: 'LOCALE_SDECIMAL'),
    (Code: LOCALE_STHOUSAND; Name: '桁区切り記号'; Text: 'LOCALE_STHOUSAND'),
    (Code: LOCALE_SGROUPING; Name: '区切る桁数'; Text: 'LOCALE_SGROUPING'),
    (Code: LOCALE_IDIGITS; Name: '小数点以下の桁数'; Text: 'LOCALE_IDIGITS'),
    (Code: LOCALE_ILZERO; Name: '少数前ゼロの桁数'; Text: 'LOCALE_ILZERO'),
    (Code: LOCALE_INEGNUMBER; Name: '負の値の形式'; Text: 'LOCALE_INEGNUMBER'),
    (Code: LOCALE_SNATIVEDIGITS; Name: '0から9の表記'; Text: 'LOCALE_SNATIVEDIGITS'),
    (Code: LOCALE_SPOSITIVESIGN; Name: '正の記号'; Text: 'LOCALE_SPOSITIVESIGN'),
    (Code: LOCALE_SNEGATIVESIGN; Name: '負の記号'; Text: 'LOCALE_SNEGATIVESIGN'),
    (Code: LOCALE_IPOSSIGNPOSN; Name: '正の記号の位置'; Text: 'LOCALE_IPOSSIGNPOSN'),
    (Code: LOCALE_INEGSIGNPOSN; Name: '負の記号の位置'; Text: 'LOCALE_INEGSIGNPOSN'),
    (Code: LOCALE_SCURRENCY; Name: '通貨記号'; Text: 'LOCALE_SCURRENCY'),
    (Code: LOCALE_SINTLSYMBOL; Name: '国際通貨記号'; Text: 'LOCALE_SINTLSYMBOL'),
    (Code: LOCALE_SMONDECIMALSEP; Name: '小数点の記号'; Text: 'LOCALE_SMONDECIMALSEP'),
    (Code: LOCALE_SMONTHOUSANDSEP; Name: '桁区切り記号'; Text: 'LOCALE_SMONTHOUSANDSEP'),
    (Code: LOCALE_SMONGROUPING; Name: '区切る桁数'; Text: 'LOCALE_SMONGROUPING'),
    (Code: LOCALE_ICURRDIGITS; Name: '小数点以下の桁数'; Text: 'LOCALE_ICURRDIGITS'),
    (Code: LOCALE_IINTLCURRDIGITS; Name: '小数点以下の桁数'; Text: 'LOCALE_IINTLCURRDIGITS'),
    (Code: LOCALE_ICURRENCY; Name: '正の値の形式'; Text: 'LOCALE_ICURRENCY'),
    (Code: LOCALE_INEGCURR; Name: '負の値の形式'; Text: 'LOCALE_INEGCURR'),
    (Code: LOCALE_IPOSSYMPRECEDES; Name: '正の通貨記号の位置'; Text: 'LOCALE_IPOSSYMPRECEDES'),
    (Code: LOCALE_IPOSSEPBYSPACE; Name: '正の通貨記号の分離位置'; Text: 'LOCALE_IPOSSEPBYSPACE'),
    (Code: LOCALE_INEGSYMPRECEDES; Name: '負の通貨記号の位置'; Text: 'LOCALE_INEGSYMPRECEDES'),
    (Code: LOCALE_INEGSEPBYSPACE; Name: '負の通貨記号の分離位置'; Text: 'LOCALE_INEGSEPBYSPACE'),
    (Code: LOCALE_SDATE; Name: '区切り記号'; Text: 'LOCALE_SDATE'),
    (Code: LOCALE_SSHORTDATE; Name: '短い形式'; Text: 'LOCALE_SSHORTDATE'),
    (Code: LOCALE_SLONGDATE; Name: '長い形式'; Text: 'LOCALE_SLONGDATE'),
    (Code: LOCALE_IDATE; Name: '短い形式の年月日順'; Text: 'LOCALE_IDATE'),
    (Code: LOCALE_ILDATE; Name: '長い形式の年月日順'; Text: 'LOCALE_ILDATE'),
    (Code: LOCALE_ICENTURY; Name: '年の桁数'; Text: 'LOCALE_ICENTURY'),
    (Code: LOCALE_IDAYLZERO; Name: '日前ゼロの有無'; Text: 'LOCALE_IDAYLZERO'),
    (Code: LOCALE_IMONLZERO; Name: '月前ゼロの有無'; Text: 'LOCALE_IMONLZERO'),
    (Code: LOCALE_ICALENDARType; Name: 'カレンダの種類'; Text: 'LOCALE_ICALENDARType'),
    (Code: LOCALE_IOPTIONALCALENDAR; Name: '追加カレンダの種類'; Text: 'LOCALE_IOPTIONALCALENDAR'),
    (Code: LOCALE_IFIRSTDAYOFWEEK; Name: '週の先頭日'; Text: 'LOCALE_IFIRSTDAYOFWEEK'),
    (Code: LOCALE_IFIRSTWEEKOFYEAR; Name: '年の先頭月'; Text: 'LOCALE_IFIRSTWEEKOFYEAR'),
    (Code: LOCALE_SDAYNAME1; Name: '週の第1日名'; Text: 'LOCALE_SDAYNAME1'),
    (Code: LOCALE_SDAYNAME2; Name: '週の第2日名'; Text: 'LOCALE_SDAYNAME2'),
    (Code: LOCALE_SDAYNAME3; Name: '週の第3日名'; Text: 'LOCALE_SDAYNAME3'),
    (Code: LOCALE_SDAYNAME7; Name: '週の第7日名'; Text: 'LOCALE_SDAYNAME7'),
    (Code: LOCALE_SABBREVDAYNAME1; Name: '週の第1日省略名'; Text: 'LOCALE_SABBREVDAYNAME1'),
    (Code: LOCALE_SABBREVDAYNAME2; Name: '週の第2日省略名'; Text: 'LOCALE_SABBREVDAYNAME2'),
    (Code: LOCALE_SABBREVDAYNAME3; Name: '週の第3日省略名'; Text: 'LOCALE_SABBREVDAYNAME3'),
    (Code: LOCALE_SABBREVDAYNAME7; Name: '週の第7日省略名'; Text: 'LOCALE_SABBREVDAYNAME7'),
    (Code: LOCALE_SMONTHNAME1; Name: '年の第1月名'; Text: 'LOCALE_SMONTHNAME1'),
    (Code: LOCALE_SMONTHNAME2; Name: '年の第2月名'; Text: 'LOCALE_SMONTHNAME2'),
    (Code: LOCALE_SMONTHNAME3; Name: '年の第3月名'; Text: 'LOCALE_SMONTHNAME3'),
    (Code: LOCALE_SMONTHNAME12; Name: '年の第12月名'; Text: 'LOCALE_SMONTHNAME12'),
    (Code: LOCALE_SMONTHNAME13; Name: '年の第13月名'; Text: 'LOCALE_SMONTHNAME13'),
    (Code: LOCALE_SABBREVMONTHNAME1; Name: '年の第1月省略名'; Text: 'LOCALE_SABBREVMONTHNAME1'),
    (Code: LOCALE_SABBREVMONTHNAME2; Name: '年の第2月省略名'; Text: 'LOCALE_SABBREVMONTHNAME2'),
    (Code: LOCALE_SABBREVMONTHNAME3; Name: '年の第3月省略名'; Text: 'LOCALE_SABBREVMONTHNAME3'),
    (Code: LOCALE_SABBREVMONTHNAME12; Name: '年の第12月省略名'; Text: 'LOCALE_SABBREVMONTHNAME12'),
    (Code: LOCALE_STIME; Name: '区切り記号'; Text: 'LOCALE_STIME'),
    (Code: LOCALE_STIMEFORMAT; Name: '時間の形式'; Text: 'LOCALE_STIMEFORMAT'),
    (Code: LOCALE_ITIME; Name: '時間制(12/24)'; Text: 'LOCALE_ITIME'),
    (Code: LOCALE_ITIMEMARKPOSN; Name: '午前午後記号の位置'; Text: 'LOCALE_ITIMEMARKPOSN'),
    (Code: LOCALE_ITLZERO; Name: '時刻前ゼロの有無'; Text: 'LOCALE_ITLZERO'),
    (Code: LOCALE_S1159; Name: '午前の記号'; Text: 'LOCALE_S1159'),
    (Code: LOCALE_S2359; Name: '午後の記号'; Text: 'LOCALE_S2359')
  );

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
  Form1.Close;
end;

procedure TForm1.FormActivate(Sender: TObject);
var
  buf: array [1 .. 100] of char;
  i: integer;
  AItem: TListItem;
begin
  for i := Low(rSet) to High(rSet) do
  begin
    Windows.GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, rSet[i].Code, @buf, 100);

    AItem := ListView1.Items.Add;
    AItem.Caption := Format('%.3d', [i + 1]);
    AItem.SubItems.Add(rSet[i].Name);
    AItem.SubItems.Add(rSet[i].Text);
    AItem.SubItems.Add(Format('%d', [rSet[i].Code]));
    AItem.SubItems.Add(string(buf));
  end;

end;

end.

2016年1月4日月曜日

AdvListViewのソートを使う方法

実装を何度も忘れたのでメモします。
AdvListViewでソートを使うためには以下の設定が必要です。

  • SortShow := True を設定
  • AdvListViewの各カラムにTagの番号を設定します。
  • イベント OnColumnClick のハンドラを定義しその中で
  • SortColumnをカラムのタグから指定

procedure TForm1.AdvListView1ColumnClick(Sender: TObject; Column: TListColumn);
var
  tag: integer;
begin

  tag := Column.Tag;
  AdvListView1.SortColumn := tag;

end;
便利!

SAS Log Utility 1.6.1.5, 文字列のパターンをINIファイルで定義

年末年始の休みを使って、SAS Log Utilityを更新しました。
  • 潜在的な問題のパターンをINIファイルに定義
  • ログを解析するためのパターンをINIファイルに定義

Vectorに新バージョンの登録を依頼しました。SAS Log Utilityは仕事でもっとも良く使うツールです。これまで、SASのバージョンアップの度に、マッチングのパターンを変えてはビルドしていました。とはいえ、いつも同じバージョンのSASを使っていないので、その都度直してビルドするのは面倒です。そんな理由で、設定のフォームを作って修正できるようにしました。

追加したOptionsのメニュー


バグに繋がりそうなNOTE:のパターン


処理時間、データセット名を捕捉するためのパターン定義
マッチングのパターンですが、"Keyword Setting"では、データセット名やファイル名などのメタ文字を定義できるようにしました。たとえば"[:dsn:]"でデータセット名のパターンを定義します。これは、Nリテラルのデータセット名をすっきりと定義、参照するために実装した機能です。


定義しているパターン(Keyword)の件数は固定です。このパターンは、行の種類を特定するためと、行の中に含まれる情報を抽出するための2種類があります。情報を抽出するとは、「999 オブザベーション」とかのデータ数、データセット名、ファイル名などを取り出すことです。

それと英語のパターン定義は、外しました。将来、多言語対応で設定を切り替えられるようにするため、いったんパターンから外しました。

INIファイルの書式が、桁固定で分かりにくいが、それはゆくゆく直します。いまは使えることの方が大事なので後回しです。

2015年12月28日月曜日

EGPからログを抽出するVBScript

EGPからログを抽出するスクリプトを作りました。EGPをZIPのファイルにコピーして、そのなかからresult.logのファイルを特定し、1つのファイルに出力しています。この方法は、公式ではなくNon Supportです。EGPのファイルが壊れたときに救済手段として、どこかに紹介されていました。
DebugFlag = Trueだと、メッセージのダイアログを表示します。あと、ログファイルの並び順は意識していません。

'---
' プログラム: EGPのファイルからSASログを抽出します。
'       説明: EGPの拡張子をZIPに変更して、ZIPのフォルダからresult.logのファイルを抽出
'     作成者: mining
'
'      引数1: EGPのファイルパス
'      引数2: ログファイルのパス
'     実行例: cscript foo.vbs C:\temp\foo.egp C:\temp\foo.log
'
'---

Option Explicit
On Error Resume Next

'---
' 定数
'---

Const FOF_SILENT = &H4              ' 進捗ダイアログを表示しない
Const FOF_NOCONFIRMATION = &H10     ' 上書き確認ダイアログを表示しない
Const ForWriting = 2                ' テキストファイルのオープン
Const ForReading = 1                ' テキストファイルのオープン
Const TristateUseDefault = -2       ' Opens the file using the system default.
Const TristateTrue = -1             ' Opens the file as Unicode.
Const TristateFalse = 0             ' Opens the file as ASCII.
Const DebugFlag = True

'---
' 変数
'---

Dim objShell
Dim objFso
Dim objTs
Dim sZipFile
Dim sTempFolder
Dim sEgpFilePath
Dim sLogFilePath
Dim ErrCount
Dim WarCount
Dim sMsg

'---
' オブジェクト生成します。
'---

Set objShell = CreateObject("Shell.Application")
Set objFso = CreateObject("Scripting.FileSystemObject")

'---
' 引数をチェックします。
'--

If WScript.Arguments.Count <> 2 Then
    Call MsgBox("引数1にEGP、引数2にログファイルを指定してください。", vbOKOnly + vbExclamation, WScript.ScriptName)
    WScript.Quit(1)
End If

sEgpFilePath = WScript.Arguments.Item(0)
sLogFilePath = WScript.Arguments.Item(1)

If objFso.FileExists(sEgpFilePath) = False Then
    Call MsgBox("引数1で指定したファイルが存在しません。", vbOKOnly + vbExclamation, WScript.ScriptName)
    WScript.Quit(1)
End If

'---
'   デバッグ用のメッセージ出力
'---

Sub DebugMsg(msg)
    If DebugFlag = True Then
        Call MsgBox(msg, vbOKOnly + vbInformation, WScript.ScriptName)
    End If
End Sub


'---
'   ZIPファイルを指定したフォルダに解凍
'---

Sub Unzip(objShell, sFile, sFolder)
    Dim objFilesInZip
    Dim objFolder
    
    Set objFilesInZip = objShell.Namespace(sFile).Items
    If Err.Number <> 0 Then
        Exit Sub
    End If
    Set objFolder = objShell.Namespace(sFolder)
    If Err.Number <> 0 Then
        Exit Sub
    End If
    
    If (Not objFolder Is Nothing) Then
        objFolder.CopyHere objFilesInZip, FOF_NOCONFIRMATION + FOF_SILENT
    Else
 Err.Raise 432 ' オートメーションの操作中にファイル名またはクラス名を見つけられませんでした。
    End If

   Set objFilesInZip = Nothing
   Set objFolder = Nothing
End Sub

'---
'   フォルダを作成
'---

Sub CreateUnzipFolder(objFso, sFolder)
    objFso.CreateFolder sFolder
End Sub

'---
'   フォルダを削除
'---

Sub DeleteUnzipFolder(objFso, sFolder)
    If objFso.FolderExists(sFolder) = True Then
        objFso.DeleteFolder sFolder, True
    End If
End Sub

'---
'   テンポラリのフォルダのパスを作成
'---

Function CreateFolderPath(objFso, sFolder)
    Const TemporaryFolder = 2
    Dim objTempFolder
    
    Set objTempFolder = objFso.GetSpecialFolder(TemporaryFolder)
    CreateFolderPath = objFso.BuildPath(objTempFolder.Path, sFolder)
    Set objTempFolder = Nothing
End Function

'---
'   サブフォルダからresult.logを探して、objTsに出力
'---

Sub SearchLog(objFso, objTs, tmpFolderItems)
    Const FileName = "result.log"
    Dim objFolderItemsB
    Dim objItem
    Dim Stream
    
    For Each objItem in tmpFolderItems
    
        ' 取り出した物がファイルかフォルダかを判定
        If objItem.IsFolder Then
            ' フォルダであれば、再帰呼び出しでフォルダ階層を手繰ります。
            Set objFolderItemsB = objItem.GetFolder
            Call SearchLog(objFso, objTs, objFolderItemsB.Items())
        ElseIf objItem.Name = FileName Then
            ' ファイル名が一致したら、テキストを読み取りobjTSに出力します。
            Set Stream = CreateObject("ADODB.Stream")
            Stream.Charset = "UTF-8"
            Stream.Type = 2
            Stream.Open
            Stream.LoadFromFile(objItem.Path)
            objTs.Write(Stream.ReadText)
            Stream.Close
            Set Stream = Nothing
        End If
    
    Next
    
    Set objItem = Nothing
    Set objFolderItemsB = Nothing

End Sub

'---
'   ログファイルからERROR, WARNINGの件数をカウント
'---

Sub CountLog(objFso, sLogFile, byRef ErrCount, byRef WarCount)
    Const KeyError = "e ERROR"
    Const KeyWarning = "w WARNING"
    Dim objTs
    Dim sBuf

    On Error Goto 0

    ErrCount = 0
    WarCount = 0

    Set objTs = objFso.OpenTextFile(sLogFile, ForReading, False, TristateTrue)
    If Err.Number <> 0 Then
        Exit Sub
    End If

    Do Until objTs.AtEndOfLine = True
        sBuf = objTs.ReadLine
        If Left(sBuf, Len(KeyError)) = KeyError Then
            ErrCount = ErrCount + 1
        ElseIf Left(sBuf, Len(KeyWarning)) = KeyWarning Then
            WarCount = WarCount + 1
        End If
    Loop

    objTs.Close
    If Err.Number <> 0 Then
        Exit Sub
    End If

    Set objTs = Nothing

End Sub

'---
' エラーチェック
'---

Function CheckError(fnName)
    Checkerror = False
    
    Dim strmsg
    Dim errNum
    
    If Err.Number <> 0 Then
        strmsg = "Error #" & Hex(Err.Number) & vbCrLf & "In Function " & fnName & vbCrLf & Err.Description
        Call MsgBox(strmsg, vbOkOnly + vbCritical, WScript.ScriptName)
        Checkerror = True
    End If
         
End Function

'---
' ログファイルを開きます。
'---

Set objTs = objFso.CreateTextFile(sLogFilePath, True, True)
If CheckError("objFso.CreateTextFile") Then
    WScript.Quit(1)
End If

'---
' EGPの拡張子をZIPに変更してコピーします。
'---

sZipFile = CreateFolderPath(objFso, objFso.GetTempName & ".zip")
Call DebugMsg("EGPの拡張子をZIPに変えてコピー:" & sZipFile)
objFso.CopyFile sEgpFilePath, sZipFile
If CheckError("objFso.CopyFile") Then
    WScript.Quit(1)
End If

'---
' 解凍先のテンポラリのフォルダを作成します。
'---

sTempFolder = CreateFolderPath(objFso, objFso.GetTempName)
Call DebugMsg("テンポラリのフォルダを作成:" & sTempFolder)
Call CreateUnzipFolder(objFso, sTempFolder)
If CheckError("CreateUnzipFolder") Then
    WScript.Quit(1)
End If


'---
' ZIPファイルを解凍します。
'---

Call DebugMsg("ZIPファイルを解凍:" & sZipFile)
Call Unzip(objShell, sZipFile, sTempFolder)
If CheckError("Unzip") Then
    WScript.Quit(1)
End If


'---
' テンポラリフォルダからログファイル探してobjTSに出力します。
'---

Call DebugMsg("テンポラリのフォルダからログを収集:" & sTempFolder)
Call SearchLog(objFso, objTs, (objShell.NameSpace(sTempFolder)).Items)
If CheckError("SearchLog") Then
    WScript.Quit(1)
End If

'---
' ログファイルを閉じます。
'---

objTs.Close
If CheckError("objTs.Close") Then
    WScript.Quit(1)
End If

'---
' テンポラリのフォルダを削除します。
'---

Call DebugMsg("テンポラリのフォルダを削除:" & sTempFolder)
Call DeleteUnzipFolder(objFso, sTempFolder)
If CheckError("DeleteUnzipFolder") Then
    WScript.Quit(1)
End If

'---
' ZIPファイルを削除します。
'---

Call DebugMsg("ZIPファイルを削除:" & sZipFile)
objFso.DeleteFile sZipFile
If CheckError("objFso.DeleteFile") Then
    WScript.Quit(1)
End If

'---
' Error, Warningの件数を数えます。
'---

Call CountLog(objFso, sLogFilePath, ErrCount, WarCount)
If CheckError("objFso.DeleteFile") Then
    WScript.Quit(1)
End If
Call DebugMsg("ERROR件数:" & CStr(ErrCount) & " WARNING件数:" & CStr(WarCount))

'---
' オブジェクトを破棄します。
'---

Set objTs = Nothing
Set objFso = Nothing
Set objShell = Nothing

'---
' 終了
'---

WScript.Quit(0)