雪国の技術的な夏

プログラミング的な話や特定のツール設定の話など。 ※ただし変態に限る。

ファイル名&更新日付を撮影日付(更新日付)で変換


・ファイル名を更新日付で変換.vbs

Option Explicit

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'ファイル名を更新日付で変換
'  arg1 : (必須)コピー元パス(ファイル or フォルダ)
'  arg2 :       コピー先パス(〃)
'
'  コピー元からコピー先へファイルをコピーし、
'  撮影日付(更新日付)でリネームする。
'  ※フォルダ指定の場合は指定直下のファイルのみが対象
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'履歴
'   1.0.0       新規作成
'   1.0.1       画像形式で撮影日時があればそちらを優先させる
'   1.0.2       撮影日時がうまく取れていなさそうならファイル自体の更新日付とする、一番最初に例外出る問題修正、引数指定が相対パスにもそこそこ(暫定)対応
'   1.0.3       (2018/11/23)エクスプローラのドラッグに対応
'   1.0.4       (2023/10/29)ffmpegを用いてiPhoneからの撮影日を判定材料にできるよう対応
'                           物理コピーとリネームのモードを切り分けできるよう対応
'                           同名リネーム時に何もしないよう対応
'                           更新日付もファイル名に合わせれるよう対応
'                           ブラッシュアップ
'   1.0.5       (2023/11/01)heicも対象にするよう対応、ffmpegのcreation_timeにタイムゾーンを反映するよう対応
'   1.0.6       (2023/11/29)処理を画像と動画に分離(画像でffmpegしても得るものがないため)、ffmpegのタイムゾーン判定基準(簡易)が間違ってたの修正、PNG解析して"eXIf"からも作成日付をとれるよう対応
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'<メモ>
'・CreateObject("Wia.ImageFile") ExifDTOrig を使用する方法
'  →ファイル先頭の解析した値って感じではなく、持ってる値取ってきただけって感じ
'
'
'
'
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Const VERSION = "1.0.6"

'コピーフラグ
'  1:物理コピー
'  2:リネーム
Const COPY_FLAG = 2

'更新日付更新するか(変更する一番古い日付と同じ)
'  True :変更する
'  False:しない
Const EDIT_DATE_FLAG = True





'時差(分)
Dim m_TimeZoneDate



'メイン処理
Call Main
WScript.Quit


'OpenTextFile-IOMode
Const ForReading = 1
Const ForWriting = 2
Const ForAppending = 8

'OpenTextFile-Format
Const TristateFalse = 0
Const TristateTrue = -1
Const TristateUseDefault = -2


'//////////////////////////////////////////////////////////////////////////////
'//
'//  説明
'//     メイン処理
'//
'//  引数
'//     なし
'//
'//  戻り値
'//     なし
'//
'//////////////////////////////////////////////////////////////////////////////
Sub Main()
    Dim oFsoF       '<FileSystemObject>
    Dim sSrcDir     'コピー元フォルダパス(末尾「\」なし)
    Dim sDesDir     'コピー先フォルダパス(末尾「\」なし)
    Dim lNum        '数(コマンドライン引数の数)
    Dim iCnt1       'ループ用
    Dim sTmp        '作業用
    Dim oFsoS       '<ShellFolderItem> In <Shell.Application.Namespace().Items>
    Dim oWs         '<WScript.Shell>
    Dim bFfmpegVer  '"ffmpeg"が使えるかどうか(True:使える)
    Dim oExe        '<WScript.Shell> <Exec>
    Dim oSa         '<Shell.Application>
    Dim sMsg        '処理結果格納変数




    sMsg = ""
    
    
    '■引数取得/チェック■
    '引数からSrc/Des を取得する
    lNum = GetArg(sSrcDir, sDesDir)
    
    '引数判定[1]
    Select Case lNum
    Case 1
        'ドラッグなどのケース、出力先は固定値とする。
        sDesDir = sSrcDir & "_Des"
    Case 2
        'bat起動などを想定(2フォルダドラッグは検知できないので無視)
    Case Else
        WScript.echo "[中断]" & lNum & "件:指定が不正!" & vbCrLf _
                   & "・[1]コピー元" & vbCrLf _
                   & "・[1]コピー元, [2]コピー先" & vbCrLf _
                   & "を指定してください。"
        Exit Sub
    End Select
    
    
    Set oFsoF = CreateObject("Scripting.FileSystemObject")
    
    
    '引数判定[2]
    If Not oFsoF.FolderExists(sSrcDir) Then
        '指定のフォルダが存在しない場合は中断
        WScript.echo "[中断]コピー元「" & sSrcDir & "」フォルダが存在しない"
        Set oFsoF = Nothing
        Exit Sub
    End If
    
    
    '■実処理1:出力先の調整■
    If COPY_FLAG = 1 Then
        '物理的にDesを複製
        'コピー先を事前に削除する(強制)
        If oFsoF.FolderExists(sDesDir) Then
            Call oFsoF.DeleteFolder(sDesDir, True)
        End If
        '一度コピー先へコピー
        Call oFsoF.CopyFolder(sSrcDir, sDesDir, True)
        
    ElseIf COPY_FLAG = 2 Then
        'Src側をリネーム
        sDesDir = sSrcDir
    End If
    
    
    '■実処理1:日付切替■
    'コピー先のファイルを一式取得   ※TODO:配下フォルダまでは見ない
    Set oSa = CreateObject("Shell.Application")
    Set oWs = CreateObject("WScript.Shell")
    
    
    
    'ffmpeg確認
    bFfmpegVer = GetFfmpegVer(oWs)

    'タイムゾーンの時差取得
    m_TimeZoneDate = GetTimeZoneDate()

    'コピー先フォルダをループし、ファイル名を更新日付などに変更する
    For Each oFsoS In oSa.Namespace(sDesDir).Items
        Dim sTmpNameTail    'ファイル名(拡張子付き)
        Dim sTmpFullName    'ファイル名フルパス(拡張子付き)
        Dim sDateTime       '実際の日付(更新日付/撮影日付)
        Dim sTmpTail        '拡張子
        Dim sTmpTailLow     '拡張子(判定用の小文字)
        
        
        With oFsoS
            sTmpTail = oFsoF.GetextensionName(.Name)
            sTmpTailLow = LCase(sTmpTail)
            '拡張子で行う処理を変更
            Select Case sTmpTailLow
            Case "png"
                '画像系統は 「撮影日付」「更新日付」「Apple撮影日付」から古いものを優先とする ※WhenTakenはタイムゾーンもプラスする
                sDateTime = .ModifyDate
                If .ExtendedProperty("WhenTaken") <> "" Then
                    'EXIF?から撮影日を取得
                    sTmp = DateAdd("n", m_TimeZoneDate, .ExtendedProperty("WhenTaken"))
                    If sTmp < sDateTime Then
                        sDateTime = sTmp
                    End If
                End If
                sTmp = GetExifCreateDateInPNG(.Path)
                If sTmp <> "" Then
                    sTmp = CDate(sTmp)
                    If sTmp < sDateTime Then
                        sDateTime = sTmp
                    End If
                End If
                
                
            Case "jpg", "jpeg", "gif", "png", "bmp", "bitmap", "heic"
                '画像系統は 「撮影日付」「更新日付」「Apple撮影日付」から古いものを優先とする ※WhenTakenはタイムゾーンもプラスする
                sDateTime = .ModifyDate
                If .ExtendedProperty("WhenTaken") <> "" Then
                    'EXIF?から撮影日を取得
                    sTmp = DateAdd("n", m_TimeZoneDate, .ExtendedProperty("WhenTaken"))
                    If sTmp < sDateTime Then
                        sDateTime = sTmp
                    End If
                End If
            Case "mov", "avi", "mp4", "mp3", "flv"
                '画像系統は 「撮影日付」「更新日付」「Apple撮影日付」から古いものを優先とする ※WhenTakenはタイムゾーンもプラスする
                sDateTime = .ModifyDate
                If .ExtendedProperty("WhenTaken") <> "" Then
                    'EXIF?から撮影日を取得
                    sTmp = DateAdd("n", m_TimeZoneDate, .ExtendedProperty("WhenTaken"))
                    If sTmp < sDateTime Then
                        sDateTime = sTmp
                    End If
                End If
                If (bFfmpegVer) Then
                    'ffmpegから作成日時(撮影日時)取得し判定
                    sTmp = GetFfmpegInfo(oWs, .Path)
                    If sTmp < sDateTime Then
                        sDateTime = sTmp
                    End If
                End If
            Case Else
                'その他は更新日付を優先とする
                sDateTime = .ModifyDate
            End Select
            
            '変換後のファイル名取得
            sTmpNameTail = GetDateNum(sDateTime) & "-" & GetTimeNum(sDateTime) & "." & sTmpTail
            sTmpFullName = sDesDir & "\" & sTmpNameTail
            
            '同名ファイルとなった場合の対処(あったら連番を振っていく※元々のはそのまま)
            For iCnt1 = 2 To 999
                If oFsoF.FileExists(sTmpFullName) Then
                    If (.Name = sTmpNameTail) Then
                        'リネーム前後で変化がない場合は現状維持(更新日付は見る)
                        Call EdtModifyDate(oFsoS, sDateTime)
                        Exit For
                    Else
                        sTmpNameTail = GetDateNum(sDateTime) & "-" & GetTimeNum(sDateTime) & "_" & Right("00" & iCnt1, 3) & "." & sTmpTail
                        sTmpFullName = sDesDir & "\" & sTmpNameTail
                    End If
                Else
                    'ファイル名(/更新日付)変更
                    .Name = sTmpNameTail
                    Call EdtModifyDate(oFsoS, sDateTime)
                    Exit For
                End If
            Next
            If (iCnt1 = 1000) Then
                'リネームできなければそのまま終わっちゃう。
                sMsg = sMsg & "[" & .Name & "]リネームできず" & vbCrLf
            End If
'            WScript.echo .name  & " = " & sTmpNameTail & vbcrlf & _
'                         "" & ( oSa.Namespace(sDesDir).ParseName(.name).ExtendedProperty("size")) & vbcrlf & _
'                         (.name = sTmpNameTail)
        End With
    Next

    sMsg = sMsg & vbCrLf & oSa.Namespace(sDesDir).Items.Count & "ファイル処理完了"


    Set oWs = Nothing
    Set oSa = Nothing
    Set oFsoF = Nothing
    
    
    '結果を画面だけに…
    WScript.echo sMsg

End Sub


'//////////////////////////////////////////////////////////////////////////////
'//
'//  説明
'//     コマンドライン引数の情報取得
'//
'//  引数
'//     sSrcDir O   コピー元フォルダパス(末尾「\」なし)
'//     sDesDir O   コピー先フォルダパス(末尾「\」なし) ※指定がない場合は空白
'//
'//  戻り値
'//     コマンドライン引数で指定した数
'//
'//////////////////////////////////////////////////////////////////////////////
Public Function GetArg(ByRef sSrcDir, ByRef sDesDir)
    Dim oParm
    Dim oFso        '<Scripting.FileSystemObject>
    Dim lCnt1
    Dim sArgArr()

    '初期化
    Set oParm = WScript.Arguments
    GetArg = oParm.Count
    
'WScript.echo GetArg
    '判定
    If (0 < GetArg) Then
        Set oFso = CreateObject("Scripting.FileSystemObject")
        ReDim sArgArr(GetArg - 1)
        '指定のフォルダ名を設定
        With oFso
            For lCnt1 = 0 To GetArg - 1
                If .FileExists(oParm(lCnt1)) Then
                    'ファイルならそこのフォルダ名を取得
                    sArgArr(lCnt1) = .getParentFolderName(oParm(lCnt1))
'WScript.echo "[" & lCnt1 & "]  " & sArgArr(lCnt1)
'                    exit for
                ElseIf .FolderExists(oParm(lCnt1)) Then
                    'フォルダ指定ならそこのフォルダ名を取得
                    sArgArr(lCnt1) = .GetAbsolutePathName(oParm(lCnt1))
'WScript.echo "[" & lCnt1 & "]  " & sArgArr(lCnt1)
'                    exit for
                Else
                    'TODO:フォルダが既存から存在しない場合でも一応入れる、相対パス考慮で
                    sArgArr(lCnt1) = .BuildPath(.getParentFolderName(WScript.ScriptFullName), oParm(lCnt1))
                End If
            Next
        End With
        
        '引数にセット
        Select Case GetArg
        Case 1:
            'コピー元だけ
            sSrcDir = sArgArr(0)
            sDesDir = ""
        Case 2:
            'コピー元/コピー先をセット
            sSrcDir = sArgArr(0)
            sDesDir = sArgArr(1)
        Case Else:
            '指定より多い場合は最初の二つのみセット
            sSrcDir = sArgArr(0)
            sDesDir = sArgArr(1)
        End Select
        
        '後始末
        Set oFso = Nothing
        
    Else
        '引数ない場合はしゃーない
        sSrcDir = ""
        sDesDir = ""
    End If
    
    '後始末
    Set oParm = Nothing
    
    '正常終了
    Exit Function
End Function


'//////////////////////////////////////////////////////////////////////////////
'//
'//  説明
'//     (共通)実際の更新日付と変更予定の更新日時に差異がある場合は更新日付を再設定する
'//
'//  引数
'//     oFsoS        I/O <ShellFolderItem> In <Shell.Application.Namespace().Items>
'//     sDateTime    I   実際の日付(更新日付/撮影日付)
'//
'//  戻り値
'//     なし
'//
'//////////////////////////////////////////////////////////////////////////////
Public Sub EdtModifyDate(oFsoS, sDateTime)
    With oFsoS
        If EDIT_DATE_FLAG Then
            If .ModifyDate <> sDateTime Then
                .ModifyDate = sDateTime
            End If
        End If
    End With
End Sub


'//////////////////////////////////////////////////////////////////////////////
'//
'//  説明
'//     ffmpegが使えるか確認
'//
'//  引数
'//     oWs I   <WScript.Shell>
'//
'//  戻り値
'//     true  : ffmpegが使える
'//     false : ffmpegが使えない
'//
'//////////////////////////////////////////////////////////////////////////////
Public Function GetFfmpegVer(oWs)
    'Dim sLine
    Dim oRet
    
    
    If oWs Is Nothing Then
        Set oWs = CreateObject("WScript.Shell")
    End If
    
    Set oRet = oWs.Exec("cmd /c ffmpeg -version")
    With oRet
        'cmd結果待ち
        Do While .Status = 0
          WScript.Sleep 100
        Loop
        
        GetFfmpegVer = (.ExitCode = 0)
    End With
End Function


'//////////////////////////////////////////////////////////////////////////////
'//
'//  説明
'//     ffmpegの情報を取得する
'//
'//  引数
'//     oWs     I   <WScript.Shell>
'//     sPath   I   確認対象のファイルパス(フルパス)
'//
'//  戻り値
'//     ffmpegから取ってきた古い日付のDate型日付(何かしら動画ファイルの場合に取得)、
'//     失敗している場合は Now が返る
'//
'//////////////////////////////////////////////////////////////////////////////
Public Function GetFfmpegInfo(oWs, sPath)
    Dim sLine           '作業用(取得行)
'    Dim sTmp: sTmp = "" '作業用
    Dim arTmp           '作業用
    Dim oExec           'cmd結果
    Dim dOldestDate     '一番若い<Date型>
    Dim dTmpDate        '作業用<Date型>
    Dim bTimeZoneFlg    'タイムゾーン補正が必要かどうか
    
    
    'NOTE.cmdのウィンドウはチラチラ出ちゃうのは我慢する(Runで隠せるが標準出力が取れない問題あるし(結果をファイルに落とし込めば行けるが、パフォーマンス悪いので却下))
    Set oExec = oWs.Exec("cmd /c ffmpeg -i """ & sPath & """ 2>&1 | find ""creation"" /i")
    With oExec
        'cmd結果待ち
        Do While .Status = 0
          WScript.Sleep 100
        Loop
        
        dOldestDate = Now   '※調べるファイルは現在日時より古い前提(システム日付に問題がなければ…)
        
        '結果読み込んで古い日付を取得
        With .StdOut
            Do While .AtEndOfStream = False
'    creation_time                   : 2023-10-11T13:26:40.000000Z
'    com.apple.quicktime.creationdate: 2023-09-27T18:20:45+0900
'    →といった内容の書式のフォーマットを YYYY/MM/DD HH:MM:SS に変更する
                sLine = .ReadLine
                bTimeZoneFlg = (0 < InStr(sLine, "creation_time"))
                '日付部分のみ抽出
                arTmp = Split(sLine, ":", 2)
                sLine = Trim(arTmp(1))
                sLine = Mid(sLine, 1, 19)
                sLine = Replace(sLine, "-", "/")
                sLine = Replace(sLine, "T", " ")

                dTmpDate = CDate(sLine)
                If bTimeZoneFlg Then
                    dTmpDate = DateAdd("n", m_TimeZoneDate, dTmpDate)
                End If
                If (dTmpDate < dOldestDate) Then
                    dOldestDate = dTmpDate
                End If
'sTmp = sTmp & Mid(sPath, InStrRev(sPath, "\")+1, 255) & " : " & dTmpDate & vbCrLf
            Loop
        End With
    End With
    Set oExec = Nothing
'WScript.echo sTmp

    
    GetFfmpegInfo = dOldestDate
End Function




'//////////////////////////////////////////////////////////////////////////////
'//
'//  説明
'//     PNGのeXIf情報から日付取得
'//
'//  引数
'//     filepath    I   ファイルフルパス
'//
'//  戻り値
'//     失敗時:空文字
'//     成功時:日付文字列
'//
'//////////////////////////////////////////////////////////////////////////////
Public Function GetExifCreateDateInPNG(ByVal filepath)
    Dim bBuf
    Dim iIdx1
    Dim iIdx1U
    Dim iLen
    Dim iIdx3
    Dim lLen
    Dim sTmp
    Dim sMsg


    'Call UseTextAscWBufferRead(bBuf, filepath, 65536)
    Call UseTextAscWBufferRead(bBuf, filepath, 2000)

    sTmp = BinToHexStr(bBuf, 0, 8)
    
    If sTmp <> "89504E470D0A1A0A" Then
        'PNG形式じゃない場合は失敗
        GetExifCreateDateInPNG = ""
        Exit Function
    End If
    'TODO:拡張子もチェックしてていいかも?


    '中身ループさせてeXIf情報を取得する
    iIdx1 = 8	'PNG形式
    iIdx1U = UBound(bBuf)
    lLen = 0
    Do While (iIdx1 < iIdx1U)
        'サイズ取得
        iLen = CLng("&H" & BinToHexStr(bBuf, iIdx1, 4))
        iIdx1 = iIdx1 + 4
        'チャンク
        sTmp = BinToAsciiStr(bBuf, iIdx1, 4)
        iIdx1 = iIdx1 + 4
        'データ&CRC
        iIdx1 = iIdx1 + iLen + 4
        '確認用
        
        Select Case sTmp
        Case "eXIf", "IEND"
            Exit Do
        Case "IDAT"
            'NOTE.iPhoneではEXIF<IDATの定義っぽいので、これが来ても終わる。
            Exit Do
        End Select
    Loop

    If sTmp <> "eXIf" Then
        'お目当ての情報がなかったので終了
        GetExifCreateDateInPNG = ""
        Exit Function
    End If

    '日付形式を取得し変換する
    'TODO:位置は固定(フォーマットが見当たらなかったので…"eXIf"サイズは130(0x82)ぐらいかな?)
    sTmp = BinToAsciiStr(bBuf, iIdx1+92-iLen-4, 19)
    sTmp = Replace(sTmp, ":", "/", 1, 2)
    if IsDate(sTmp) Then
        GetExifCreateDateInPNG = sTmp
    Else
        GetExifCreateDateInPNG = ""
    End If

End Function




'http://moritora.seesaa.net/article/272696528.html
'//////////////////////////////////////////////////////////////////////////////
'//
'//  説明
'//     bin読込
'//
'//  引数
'//     xxx
'//
'//  戻り値
'//     なし
'//
'//////////////////////////////////////////////////////////////////////////////
Sub UseTextAscWBufferRead(ByRef a(), ByVal filepath, ByVal lReadLen)
    Dim stream
    Set stream = CreateObject("ADODB.Stream")
    With stream
        Call .Open()
        .Type = 2 '2:adTypeText)
        .Charset = "UTF-16BE"
        Call .LoadFromFile(filepath)
        ReDim a(.Size - 1)
        Dim length
        length = lReadLen
        Dim j
        j = 0
        Dim remain
        remain = .Size \ 2
        Do While (remain > 0)
            If (remain < length) Then
                length = remain
            End If
            Dim buffer
            buffer = .ReadText(length)
            Dim i, n
            For i = 1 To length
                n = AscW(Mid(buffer, i, 1))
                a(j) = (n And &HFF00&) \ &H100
                a(j + 1) = n And &HFF
                j = j + 2
            Next
            remain = remain - length
        Loop
        If ((.Size And 1) = 1) Then
            .Position = .Size
            Call .WriteText(ChrW(0))
            .Position = .Size - 3
            a(UBound(a)) = (AscW(.ReadText(1)) And &HFF00&) \ &H100
        End If
        Call .Close()
    End With
End Sub




'//////////////////////////////////////////////////////////////////////////////
'//
'//  説明
'//     年月日取得
'//
'//  引数
'//     日付(文字列可能)
'//
'//  戻り値
'//     yyyymmdd形式
'//
'//////////////////////////////////////////////////////////////////////////////
Public Function GetDateNum(sDate)
    Dim dDate
    
    '日付形式に変換
    dDate = CDate(sDate)
    GetDateNum = Right("0000" & Year(dDate), 4) _
               & Right("00" & Month(dDate), 2) _
               & Right("00" & Day(dDate), 2)
    Exit Function
End Function


'//////////////////////////////////////////////////////////////////////////////
'//
'//  説明
'//     時間取得
'//
'//  引数
'//     日付(文字列可能)
'//
'//  戻り値
'//     hhmmss形式
'//
'//////////////////////////////////////////////////////////////////////////////
Public Function GetTimeNum(sDate)
    Dim dTime
    
    '日付形式に変換
    dTime = CDate(sDate)
    GetTimeNum = Right("00" & Hour(dTime), 2) _
               & Right("00" & Minute(dTime), 2) _
               & Right("00" & Second(dTime), 2)
End Function

'//////////////////////////////////////////////////////////////////////////////
'//
'//  説明
'//     タイムゾーンの時差取得
'//
'//  引数
'//     なし
'//
'//  戻り値
'//     時差(分っぽい)
'//
'//////////////////////////////////////////////////////////////////////////////
Public Function GetTimeZoneDate()

    Dim oLocator        'WbemScripting
    Dim oService        'ConnectServer
    Dim oClassSet       'ExecQuery結果
    Dim oClass          'ループ用obj

    GetTimeZoneDate = 0

    Set oLocator = WScript.CreateObject("WbemScripting.SWbemLocator")
    Set oService = oLocator.ConnectServer
    Set oClassSet = oService.ExecQuery("Select Bias From Win32_TimeZone")

    '時差のみ取得
    For Each oClass In oClassSet
        GetTimeZoneDate = oClass.Bias
        Exit For
    Next

    '後始末
    Set oClassSet = Nothing
    Set oClass = Nothing
    Set oService = Nothing
    Set oLocator = Nothing

End Function



'//////////////////////////////////////////////////////////////////////////////
'//
'//  説明
'//     byte配列からHEX文字列取得
'//
'//  引数
'//     bBuf        I   byte配列
'//     lStartIdx   I   開始インデックス(bBuf範囲内)
'//     lSize       I   取得byteサイズ
'//
'//  戻り値
'//     0埋め16進文字列(大文字)
'//
'//////////////////////////////////////////////////////////////////////////////
Public Function BinToHexStr(bBuf(), ByVal lStartIdx, ByVal lSize)
    Dim sTmp
    Dim lIdx

    sTmp = ""
    lIdx = lStartIdx
    For lIdx = 0 To (lSize - 1)
        sTmp = sTmp & Right("00" & Hex(bBuf(lIdx + lStartIdx)), 2)
    Next

    BinToHexStr = sTmp
End Function


'//////////////////////////////////////////////////////////////////////////////
'//
'//  説明
'//     byte配列からASCII文字列取得
'//
'//  引数
'//     bBuf        I   byte配列
'//     lStartIdx   I   開始インデックス(bBuf範囲内)
'//     lSize       I   取得byteサイズ
'//
'//  戻り値
'//     ASCII文字列
'//
'//////////////////////////////////////////////////////////////////////////////
Public Function BinToAsciiStr(bBuf(), ByVal lStartIdx, ByVal lSize)
    Dim sTmp
    Dim lIdx

    sTmp = ""
    lIdx = lStartIdx
    For lIdx = 0 To (lSize - 1)
        sTmp = sTmp & Chr(bBuf(lIdx + lStartIdx))
    Next

    BinToAsciiStr = sTmp
End Function

[その他]
動作保証できません。
・操作方法を間違えれば危険なものであるため、いかなる不利益も保証しません。