Excel VBA Sample 7


Option Explicit
'■簡易バイナリリーダー
Sub putBinStr() ' As Byte()
    'テキストファイル test.txt の中身
    'モグ[改行]モコ[改行]マコ
    '--------------------------------
    Dim myPath As String
    Dim io As Integer
    Dim buf() As Byte
    
    
    Application.DefaultFilePath = ThisWorkbook.Path
    myPath = "test.txt"
    
    io = FreeFile()
    
    '--------------------------------
    'For Binary で Openすると
    '規定のコードページ(文字コード)
    'が得られる。日本語環境だと Shift_JIS
    'が得られる という意味だと思う。
    '--------------------------------
    
    Open myPath For Binary As #io
    
    '--------------------------------
    'LOF関数はファイルのバイト長を返す
    '--------------------------------
    
    ReDim buf(1 To LOF(io))
    
    '--------------------------------
    'Getの第二引数は何バイト目から取得するかの指定
    '2バイト目から取得したいなら
    'バリアント型で 2 と指定する。
    '--------------------------------
    
    '--------------------------------
    'バイト型の配列で Get すると
    'ループの必要が無い。
    'つまり高速に動作する
    '--------------------------------
    Get #io, , buf
    
    Dim v As Variant
    Dim strHex As String
    Dim strDec As String
    Dim cnt As Long
    
    For Each v In buf
        cnt = cnt + 1
        
        If cnt Mod 2 Then
            strHex = "&h" & Hex(v)
            strDec = Format$(v, "@@@")
        Else
            strDec = strDec & " " & Format$(v, "@@@")
            strHex = strHex & Hex(v)
            Debug.Print strDec, strHex, Chr(strHex)
            strHex = ""
            strDec = ""
        End If
    Next
                
    'テキストから取得したBinaryデータは
    'vbUnicode指定で2バイト文字に変換できる
    Debug.Print "-----------------------"
    Debug.Print StrConv(buf, vbUnicode)
    Debug.Print "-----------------------"
    
    Close io
    
'131 130       &h8382        モ
'131  79       &h834F        グ
' 13  10       &hDA          レ
'131 130       &h8382        モ
'131  82       &h8352        コ
' 13  10       &hDA          レ
'131 125       &h837D        マ
'131  82       &h8352        コ
'-----------------------
'モグ
'モコ
'マコ
'-----------------------
    
End Sub


■バイナリリーダー2
Option Explicit
Sub BinaryReader()

    Dim myPath As String
    Dim io As Integer
    Dim buf() As Byte
    
    
    Application.DefaultFilePath = ThisWorkbook.Path
    myPath = "test.dat"
    
    io = FreeFile()
     
    Open myPath For Binary As #io
    
    ReDim buf(1 To LOF(io))

    Get #io, , buf
    Close #1
    
    Dim v As Variant
    Dim strHex As String
    Dim strDec As String
    Dim cnt As Long
    
    For Each v In buf
        cnt = cnt + 1
        '0以外はTRUE
        If cnt Mod 2 Then
            strHex = "&h" & Format$(Hex(v), "00")
            strDec = Format$(Format$(v, "000"), "@@@")

        Else
            strDec = _
                strDec & " " & _
                Format$(Format$(v, "000"), "@@@")
            strHex = _
                strHex & _
                Format$(Hex(v), "00")
            Sheet1.[a1:d1].Value = _
                Array("№", "Dec", "Hex", "Shif_JIS")
            Sheet1.Cells(cnt / 2 + 1, 1).Resize(, 4).Value = _
                Array("№" & cnt - 2 & " - №" & _
                cnt - 1, strDec, strHex, Chr(strHex))
            strHex = ""
            strDec = ""
            
        End If
    Next
End Sub


'■kanabun さんの Binary処理とDOSコマンドで
'再帰処理しないでサブディレクトリ検索までする
'高速コード⇒ただしUNICODE文字を含むファイルバスを返せない
'UNICODE対応版は一つ↓に掲載した。
Private Function GetFiles(Filename As String) As String()
    Dim tmpPath As String
    Dim sCmd As String
    Dim ko As Long
    Dim ff() As String
   
    tmpPath = Environ$("Temp") & "\Dir.tmp" '作業用ファイル名
    'Dirコマンド----------------------------------------------------------------
    '  /B          ファイル名のみを表示します (見出しや要約が付きません)。
    '  /S          指定されたディレクトリおよびそのサブディレクトリのすべての
    '              ファイルを表示します。
    '---------------------------------------------------------------------------
    sCmd = "DIR """ & Filename & """ /b/s > """ & tmpPath & """" 'Dirコマンド
    
'    Dim WSO As New WshShell 'Windows Script Host Object Modelに参照設定
'    WSO.Run ("DIR C:\*.xls /b/s",
    With CreateObject("WScript.Shell")
    
'Runコマンド第二引数:WindowStyle--------------------------------------------------------------------------------------
'1   ウィンドウをアクティブにして表示します。ウィンドウが最小化または最大化されている場合は、元のサイズと位置に戻ります。
'3   ウィンドウをアクティブにし、最大化ウィンドウとして表示します。
'7   ウィンドウを最小化し、次に上位となるウィンドウをアクティブにします。
'Runコマンド第二引数:WindowStyle--------------------------------------------------------------------------------------

'Runコマンドの第三引数:bWaitOnReturn----------------------------------------------------------------------------------
'    省略可能です。スクリプト内の次のステートメントに進まずにプログラムの実行が終了するまでスクリプトを待機させるかどうか
'    を示すブール値です。bWaitOnReturn に TRUE を指定すると、プログラムの実行が終了するまでスクリプトの実行は中断され、
'    Run メソッドはアプリケーションから返される任意のエラー コードを返します。bWaitOnReturn に FALSE を指定すると、
'    プログラムが開始すると Run メソッドは即座に復帰して自動的に 0 を返します (これをエラー コードとして解釈しないでください)。
'-----------------------------------------------------------------------------------------------------------------------

'CMDスィッチ--------------------------------------------------------------------
'/C      "文字列" に指定されたコマンドを実行した後、終了します。
'/K      "文字列" に指定されたコマンドを実行しますが、終了しません。
'-------------------------------------------------------------------------------
        ko = .Run("CMD /C " & sCmd, 7, True) 'Dirコマンド実行
    End With
 
    Dim io As Integer
    Dim buf() As Byte
    Dim flg As Boolean
    io = FreeFile()
    
    Open tmpPath For Binary As io 'ファイルリスト取得
      If LOF(io) > 0 Then
        flg = True
        ReDim buf(1 To LOF(io))
        Get #io, , buf
      
      End If
    Close io
    
    Kill tmpPath
    
    If flg Then
        ff() = Split(StrConv(buf, vbUnicode), vbCrLf)
    End If
    
    GetFiles = ff()

End Function


'テストコード
Sub GetFilesTEST()
    Dim FoundFiles() As String
    Dim i As Long
    
    FoundFiles() = GetFiles("C:\Users\yamaa*.xls")
    
    'GetDim関数は配列の初期化の有無と次元数を返す魔界の仮面弁士さん作関数。
    'http://homepage3.nifty.com/homepage_yamaV102/tool/EXCEL_API/api.htm
    'に収録されている。空の配列を判定する方法はAPI使わないときつい?
    'err.number:9 インデックスがありませんの判定を拾う関数を自作しても
    '問題ないかも。
    If GetDim(FoundFiles) > 0 Then
        For i = 0 To UBound(FoundFiles)
            Debug.Print FoundFiles(i)
        Next
    Else
        Debug.Print "条件のファイルはありません"
    End If
End Sub


タイトル
: Re: Variant配列は1次元?2次元?
投稿日
: 2008/07/11(Fri) 18:35
投稿者
: 魔界の仮面弁士 
# このネタ、過去ログにあったかも。

> 返り値のVariant型配列が1次元なのか
> 2次元なのかを判別するにはどうすれば
> よいのでしょうか?

案1) UBound(v, 2) がエラーを返すかどうかで判定する。
案2) SafeArrayGetDim API を使って受け取る。


Private Sub Command1_Click()
    Dim a As Variant, b As Variant
    ReDim a(2, 2)
    Dim c() As Variant, d() As Variant
    ReDim c(2, 2, 2)
    Dim e(1) As Variant, f(2, 3) As Variant
    
    Debug.Print "a:"; GetDim(a)    ' 2 … 次元数 2 の配列を格納した Variant 型
    Debug.Print "b:"; GetDim(b)    '-1 … 配列では無い Variant 型
    Debug.Print "c:"; GetDim(c)    ' 3 … 次元数 3 の動的配列
    Debug.Print "d:"; GetDim(d)    ' 0 … 初期化されていない動的配列
    Debug.Print "e:"; GetDim(e)    ' 1 … 次元数 1 の静的配列
    Debug.Print "f:"; GetDim(f)    ' 2 … 次元数 2 の静的配列
End Sub



===== 標準モジュール =====
Option Explicit

Private Declare Function SafeArrayGetDim Lib "oleaut32.dll" (ByVal psa As Long) As Long
Private Declare Sub GetMem4 Lib "msvbvm60" (ByVal ptr As Long, ByRef ret As Long)

Public Function GetDim(ByVal sa As Variant) As Long
    If IsArray(sa) Then
        Dim p As Long
        GetMem4 VarPtr(sa) + 8, p
        If p = 0 Then
            GetDim = 0  '初期化されていない配列
        Else
            GetDim = SafeArrayGetDim(p) '次元数
        End If
    Else
        GetDim = -1     '配列では無い
    End If
End Function


'再帰処理なしの階層探索ファイルサーチUNICODE仕様
Private Function GetFiles(FolderName As String) As String()
    Dim tmpPath As String
    Dim sCmd As String
    Dim ko As Long
    Dim ff() As String
    
    Dim ts As TextStream
    Dim tf As File
    
    '作業用ファイル名
    tmpPath = Environ$("Temp") & "\" & gFso.GetTempName
    
    Set ts = gFso.CreateTextFile(Filename:=tmpPath, Unicode:=True)
    ts.Close
    Set tf = gFso.GetFile(tmpPath)
    sCmd = "DIR """ & FolderName & """ /b/s >> """ & tmpPath & """"
    
    Dim WSO As Object 'New WshShell
    Set WSO = VBA.CreateObject("WScript.Shell")
    
    '7:ウィンドウを最小化ウィンドウとして表示します。
    'アクティブなウィンドウは切り替わりません。
    Const cnsWinStyle As Integer = 7
    With WSO
        '第三引数は bWaitOnReturn
        '実行終了まで次のステップに進むのを待つか否か
        ko = .Run("CMD /U/C " & sCmd, cnsWinStyle, True)
    End With
    
    Dim io As Integer
    Dim buf() As Byte
    Dim flg As Boolean
    io = FreeFile()
    
    Open tmpPath For Binary As io
    If LOF(io) > 0 Then
        flg = True
        'LOFで可変長文字列のバイト数を取得
        '文字列データはバイト型の配列である
        ReDim buf(1 To LOF(io))
        'Getステートメントは第三引数のサイズ分のバイト数を読み込む
        Get #io, , buf
    End If
    Close io
    
    Kill tmpPath
    
    'BOMの削除.1文字2バイト、バイト配列なので先頭2バイトを削除
    Dim buf2() As Byte
    Dim i As Long
    ReDim buf2(1 To UBound(buf) - 2)
    
    For i = 1 To UBound(buf2)
        buf2(i) = buf(i + 2)
    Next
    
    If flg Then
        'ff() = Split(StrConv(buf, vbNarrow), vbCrLf)
        ff() = Split(buf2, vbCrLf)
    End If
    
    GetFiles = ff()
End Function

'■ActiveCell と Caller の違い
Function ActiveCell_TEST()
    '自動再計算
    Application.Volatile
    ActiveCell_TEST = ActiveCell.Offset(0, -1).Value
End Function

Function Caller_TEST()
    '自動再計算
    Application.Volatile
    Caller_TEST = Application.Caller.Offset(0, -1).Value
End Function



'■Printステートメントを使ったCSV出力
'自ブック階層にテキスト出力する
Sub OutPutTEXT(ParamArray myAry() As Variant)

    Dim outFile As String
    
    outFile = ThisWorkbook.Path & "\output.csv"
    
    Open outFile For Append As #1
    Print #1, Join(myAry, ",")
    Close #1
'こうやって使う-----------------------------------------------
'    Call OutPutTEXT("Index", "ID", "Caption")
    
'    For Each ctrl In ctrl19.Controls
        'Index が 3 の時に ctrl.Caption を取得しようとすると
        'フリーズする
'        OutPutTEXT _
'            ctrl.Index, _
'            ctrl.ID, _
'            ctrl.Caption
'    Next
'/こうやって使う----------------------------------------------    
End Sub


'■インプットボックスのキャンセルボタンが押された事を判定する
Sub JudgeCancel()
    Dim ans As String
    ans = InputBox("")
    'StrPtrは vbNullString を判別する非表示のメンバーの関数。
    'データの存在するメモリアドレスを返し、Nullの時は 0 を返す
    'StrPtr を使うとInputBoxでキャンセルが押された事を判定できる。
    'Print StrPtr("aaa")
    '126023032⇒不定
    '?StrPtr(vbNullString)
    '0
    If StrPtr(ans) = 0 Then
        MsgBox "キャンセルされました"
    Else
        MsgBox ans & " が入力されました"
    End If
End Sub


'■アプリケーションを起動するとか、ディレクトリを開くとか
Option Explicit
Enum e_Open
    enmDir
    enmfile
End Enum
'長いファイル名で指定する
Sub appRun()
    Dim objWShell
    Dim strPath As String
    strPath = ThisWorkbook.Path & "\test.txt"

    Const vbMaximizeFocus = 3 '最大化、かつ最前面のウィンドウ
    Set objWShell = CreateObject("WScript.Shell")
    
    If Dir(strPath) = "" Then
        MsgBox strPath & " が見つかりません"
        Exit Sub
    End If
    
    objWShell.Run _
        "rundll32.exe url.dll,FileProtocolHandler " & _
        strPath, vbMaximizedFocus, True
    '"rundll32.exe url.dll.FileProtocolHandler "
    'を付ける事で通常のファイルパスで起動できる。
    '付けないと「短いファイル名」で指定する必要がある。
    '短いファイル名は FSO の ShortName で取得できる。
    'この Run メソッドはアプリケーションも起動するし、
    '単にディレクトリを指定してフォルダを開くのに
    '使っても良い。
    
'    rtn = objWShell.Run( _
'        "rundll32.exe url.dll,FileProtocolHandler " & _
'        strPath, vbMaximizedFocus, True)
    'と書くと 0 を返すがこれはエラーコードとしては使えず
    '失敗しても成功しても 0 を返す。
    
    'なので失敗をトラップする手段はなさそう
    
    MsgBox "appRunを実行しました。"
End Sub


'短いファイル名を返す
Function getShortName(pth As String, mode As e_Open) As String
    Dim FSO As Object
    Dim str As String
    Set FSO = CreateObject("Scripting.FileSystemObject")
    If mode = enmDir Then
        str = FSO.getFolder(pth).shortName
    Else
        str = FSO.getFile(pth).shortName
    End If
    getShortName = str
End Function


'短いファイル名で指定する
Sub appRunbyShortName()
    Dim objWShell
    Dim strPath As String
    strPath = ThisWorkbook.Path & "\test.txt"

    Const vbMaximizeFocus = 3 '最大化、かつ最前面のウィンドウ
    Set objWShell = CreateObject("WScript.Shell")
    
    If Dir(strPath) = "" Then
        MsgBox strPath & " が見つかりません"
        Exit Sub
    End If
    
    
    objWShell.Run _
        getShortName(strPath, enmfile), vbMaximizedFocus, True
        
    '"rundll32.exe url.dll.FileProtocolHandler "
    'を付ける事で通常のファイルパスで起動できる。
    '付けないと「短いファイル名」で指定する必要がある。
    '短いファイル名は FSO の ShortName で取得できる。
    'この Run メソッドはアプリケーションも起動するし、
    '単にディレクトリを指定してフォルダを開くのに
    '使っても良い。
    
'    rtn = objWShell.Run( _
'        "rundll32.exe url.dll,FileProtocolHandler " & _
'        strPath, vbMaximizedFocus, True)
    'と書くと 0 を返すがこれはエラーコードとしては使えず
    '失敗しても成功しても 0 を返す。
    
    'なので失敗をトラップする手段はなさそう
    
    MsgBox "appRunbyShortNameを実行しました。"
End Sub


''VBSで記述すると↓のようになる
''このスクリプトは実行されるスクリプトと同じディレクトリの test.txt を起動します。
'▼run.vbs▼
'このスクリプトは実行されるスクリプトと同じディレクトリの test.txt を起動します。

    Dim objWShell
    Dim strPath
    Dim strThisFilePath
    Dim FSO
    Const vbMaximizeFocus = 3 '最大化、かつ最前面のウィンドウ

    'このファイルのパスを取得する
    strThisFilePath = Wscript.ScriptFullName
    strThisFilePath = Left(strThisFilePath, InStrRev(strThisFilePath, "\"))
    strPath = strThisFilePath & "test.txt"
    
    Set objWShell = CreateObject("WScript.Shell")
    Set FSO = CreateObject("Scripting.FileSystemObject")
    
    If FSO.FileExists(strPath) = False Then
        MsgBox strPath & " が見つかりません"
       
    Else

    objWShell.Run _
        "rundll32.exe url.dll,FileProtocolHandler " & _
        strPath, vbMaximizeFocus, True
   
        MsgBox "起動プログラムを実行しました。"
    End If

'そして、↑のVBSをVBAから実行するプロシージャは以下のようになる。

Sub runVBS()
    '問題があると 0 を返す
    Dim lngAns As Long
    Dim str As String
    
    str = ThisWorkbook.Path & "\run.vbs"
    lngAns = _
        Shell("WScript.exe """ & str)
    
End Sub


'■バブルソート
Option Explicit
'バブルソート
'配列の要素数分先頭から大小比較して並び替える
'並び替えが行われなくなったらループを抜ける
Function SortBabble(ByRef ary As Variant, Optional asc As Boolean = True)
    Dim flg As Long
    Dim i As Long
    Dim tmp As Variant

    i = UBound(ary)
    flg = i

    Do While (i > 0 And flg > 0)
        Dim j As Long: j = 0
        flg = 0
        Do While (i > j)
            If (ary(j) > ary(j + 1)) = asc Then
                tmp = ary(j + 1)
                ary(j + 1) = ary(j)
                ary(j) = tmp
                flg = 1
            End If
            j = j + 1
        Loop
        i = i - 1
    Loop
End Function


Sub sampleSort()
    Dim ary As Variant
    ary = Array(10, 1, 30, 2, 4, 5)
    Dim i As Long
    For i = 0 To UBound(ary)
        Debug.Print ary(i)
    Next
    
    '10
    '1
    '30
    '2
    '4
    '5

    Call SortBabble(ary, True)
    
    For i = 0 To UBound(ary)
        Debug.Print ary(i)
    Next
    
    '1
    '2
    '4
    '5
    '10
    '30
    
    Call SortBabble(ary, False)
    
    For i = 0 To UBound(ary)
        Debug.Print ary(i)
    Next
    
    '30
    '10
    '5
    '4
    '2
    '1
    
End Sub


■'クリックソート
Option Explicit
'クリックソート
'ary:配列
'Lbnd:配列添え字の最小値
'Ubnd:配列添え字の最大値
Sub SortQuick( _
    ary As Variant, Lbnd As Long, Ubnd As Long, _
    Optional asc As Boolean = True, _
    Optional IsNumber As Boolean = True)
    
    Dim midVal As Variant, A As Long, Z As Long
    Dim tmp As Variant
    midVal = ary(Lbnd) 'あてずっぽうの中央値.根拠なし。
    A = Lbnd: Z = Ubnd
    If asc Then
        Do
            While _
                IIf(IsNumber, CDbl(ary(A)), CStr(ary(A))) < _
                IIf(IsNumber, CDbl(midVal), CStr(midVal))
                A = A + 1: Wend
            While _
                IIf(IsNumber, CDbl(ary(Z)), CStr(ary(Z))) > _
                IIf(IsNumber, CDbl(midVal), CStr(midVal))
                Z = Z - 1: Wend
            If A >= Z Then Exit Do
            tmp = ary(A): ary(A) = ary(Z): ary(Z) = tmp
            A = A + 1: Z = Z - 1
        Loop
        If Lbnd < A - 1 Then Call SortQuick(ary, Lbnd, A - 1, asc, IsNumber)
        If Z + 1 < Ubnd Then Call SortQuick(ary, Z + 1, Ubnd, asc, IsNumber)
    Else
        Do
            While _
                IIf(IsNumber, CDbl(ary(A)), CStr(ary(A))) > _
                IIf(IsNumber, CDbl(midVal), CStr(midVal))
                A = A + 1: Wend
            While _
                IIf(IsNumber, CDbl(ary(Z)), CStr(ary(Z))) < _
                IIf(IsNumber, CDbl(midVal), CStr(midVal))
                Z = Z - 1: Wend
            If A >= Z Then Exit Do
            tmp = ary(A): ary(A) = ary(Z): ary(Z) = tmp
            A = A + 1: Z = Z - 1
        Loop
        If Lbnd < A - 1 Then Call SortQuick(ary, Lbnd, A - 1, asc, IsNumber)
        If Z + 1 < Ubnd Then Call SortQuick(ary, Z + 1, Ubnd, asc, IsNumber)
    End If
End Sub

Sub testQ_Sort()
    Dim ary As Variant
    Dim i As Long
    '文字列の並び替えはコード書き換え必要
    'ary = Array("abc", "aaa", "a00", "a0000", "zz", "AAA")
    ary = Array(30, 1, 5, 20, 8, -1)
    Debug.Print "---- 並び替え前------"
    For i = LBound(ary) To UBound(ary)
        Debug.Print ary(i)
    Next
    
    Call SortQuick(ary, LBound(ary), UBound(ary), True, True)
    Debug.Print "---- 並び替え後昇順------"
    For i = LBound(ary) To UBound(ary)
        Debug.Print ary(i)
    Next

    Call SortQuick(ary, LBound(ary), UBound(ary), False, True)
    Debug.Print "---- 並び替え後降順------"
    For i = LBound(ary) To UBound(ary)
        Debug.Print ary(i)
    Next
    
    '数値を文字列として並び替える
    Call SortQuick(ary, LBound(ary), UBound(ary), True, False)
    Debug.Print "---- 並び替え後昇順------"
    For i = LBound(ary) To UBound(ary)
        Debug.Print ary(i)
    Next
    
    Call SortQuick(ary, LBound(ary), UBound(ary), False, False)
    Debug.Print "---- 並び替え後降順------"
    For i = LBound(ary) To UBound(ary)
        Debug.Print ary(i)
    Next
End Sub


'■OSのビット数を取得する
Sub OSのビット数を取得する()
'Set OsSet = CreateObject("WbemScripting.SWbemLocator")
'参照設定Microsoft WMI Scripting V1.2 Library
Dim OsSet As SWbemObjectSet
Dim OS As SWbemObject
Dim Locator As SWbemLocator
Dim Service As Object
Dim MesStr As String

Set Locator = New WbemScripting.SWbemLocator
Set Service = Locator.ConnectServer
Set OsSet = Service.ExecQuery _
("Select * From Win32_OperatingSystem")

For Each OS In OsSet

MesStr = "OS動作Bit数:" & CStr(OS.OSArchitecture) & vbCrLf

Next

MsgBox "OSの32Bit版、64Bit版判別結果です。" & _
vbCrLf & vbCrLf & MesStr

Set OsSet = Nothing
Set OS = Nothing
Set Service = Nothing
Set Locator = Nothing
End Sub

'↑VBScriptで書くと↓こう


Option Explicit

Dim OsSet 
Dim OS
Dim Locator
Dim Service
Dim MesStr

Set Locator = Wscript.CreateObject("WbemScripting.SWbemLocator")
Set Service = Locator.ConnectServer
Set OsSet = Service.ExecQuery _
("Select * From Win32_OperatingSystem")

For Each OS In OsSet

MesStr = "OS動作Bit数:" & CStr(OS.OSArchitecture) & vbCrLf

Next

MsgBox "OSの32Bit版、64Bit版判別結果です。" & _
vbCrLf & vbCrLf & MesStr

Set OsSet = Nothing
Set OS = Nothing
Set Service = Nothing
Set Locator = Nothing

'-------------------------------------------------


Option Explicit
'■列を渡しユニークなカンマ区切り文字列作成
Function getUniqueAry(clm As Range) As String
    On Error GoTo err_handle
    
    If clm.Columns.Count > 1 Then
        Err.Raise 514, "getUniqueAry", "複数列を渡せません"
    End If
    Application.ScreenUpdating = False

    Dim ws As Worksheet
    
    Set ws = Worksheets.Add
    clm.AdvancedFilter _
        xlFilterCopy, _
        clm, _
        ws.Cells(1), True
    Application.DisplayAlerts = False

    getUniqueAry = _
        Join(cAry(ws.Cells(1).CurrentRegion), ",")
    
end_handle:
    On Error Resume Next
    ws.Delete
    Application.DisplayAlerts = True
    Application.ScreenUpdating = True
    Exit Function

err_handle:

    MsgBox _
        Err.Number & " : " & _
        Err.Description, vbExclamation, _
        ThisWorkbook.Name
        Resume end_handle
        
End Function

'レンジを渡して文字列の配列を返す
Private Function cAry(rng As Range) As String()
    Dim c As Range
    Dim i As Long: i = 0
    Dim str() As String
    ReDim str(rng.Cells.Count - 1)
    For Each c In rng
        str(i) = CStr(c.Value)
        i = i + 1
    Next
    cAry = str
End Function


Sub getUniqueAr_Test()
    Dim rng As Range
    Dim time1 As Single
    Set rng = _
            Range(Cells(2, 1), _
            Cells(Cells(2, 1) _
            .CurrentRegion.Rows.Count, 1))
    time1 = Timer()
    
    Debug.Print getUniqueAry(rng)
    Debug.Print Timer() - time1
End Sub
'-------------------------------------------------


'■レジストリShell Foldersから特殊フォルダーのパスを取得する
'WSHのSpecialFolderには無いフォルダーが取得できる
Function getSpecialFolder() As String

    Dim objWshShell
    Set objWshShell = CreateObject("WScript.Shell")
    
    '読み込むエントリを定数として宣言。
    Const REG_CASHE = _
        "HKEY_CURRENT_USER\Software\Microsoft\" & _
        "Windows\CurrentVersion\Explorer\Shell Folders\Cache"
    
    getSpecialFolder = objWshShell.RegRead(REG_CASHE)
    Debug.Print getSpecialFolder
    Set objWshShell = Nothing
    
    'C:\Users\yama\AppData\Local\Microsoft\Windows\Temporary Internet Files
End Function


'ここにはないフォルダでも環境変数であるパスなら
'Environ("WINDIR")、Environ("TMP")で取得する。

'--------------------------------------------------------------------


Option Explicit
'■指定したウィンドウだけを並べて表示させるクラス
'clsWinArrng

Private WithEvents mLeftorTopWb As Workbook
Private WithEvents mRightorBottomWb As Workbook

Public Enum Style
    Vertical
    Horizontal
End Enum

Private Const cnsSukima As Single = 1

Public Sub ArrengeWindows(ByVal LeftOrTopWb As Workbook, _
                          ByVal RightOrBottomWb As Workbook, _
                          arrStyle As Style)
    Dim winLeft As Window
    Dim winRight As Window
    Dim winTop As Window
    Dim winBottom As Window
    Dim win As Window
    Dim fullWidth As Single
    Dim fullHeight As Single
    
    Set mLeftorTopWb = LeftOrTopWb
    Set mRightorBottomWb = RightOrBottomWb
    
    Application.ScreenUpdating = False
    
    Select Case arrStyle
        
        Case Vertical
            Set winLeft = LeftOrTopWb.Windows(1)
            Set winRight = RightOrBottomWb.Windows(1)
            
            winLeft.WindowState = xlMaximized
            fullWidth = winLeft.Width
            
            Windows.Arrange xlArrangeStyleVertical
            
            With winLeft
                .Width = fullWidth / 2 - cnsSukima * 4
                .Left = cnsSukima
            End With
            
            With winRight
                .Width = winLeft.Width
                .Left = winLeft.Width + cnsSukima * 2
            End With
            
            winRight.Activate
            winLeft.Activate
            
        Case Horizontal
            Set winTop = LeftOrTopWb.Windows(1)
            Set winBottom = RightOrBottomWb.Windows(1)
            
            winTop.WindowState = xlMaximized
            fullHeight = winTop.Height
            
            Windows.Arrange xlArrangeStyleHorizontal
                                                
            With winTop
                .Height = fullHeight / 2 - cnsSukima * 11.5
                .Top = cnsSukima
            End With
            
            With winBottom
                .Height = winTop.Height
                .Top = winTop.Height + cnsSukima * 2
            End With
            
            winBottom.Activate
            winTop.Activate
        
    End Select
    
end_handle:
    Application.ScreenUpdating = True
End Sub

Private Sub mLeftorTopWb_BeforeClose(Cancel As Boolean)
    
    Call WndwsMaxmize
End Sub

Private Sub mRightorBottomWb_BeforeClose(Cancel As Boolean)

    Call WndwsMaxmize
End Sub

Private Sub WndwsMaxmize()
    Application.ScreenUpdating = False
    Dim win As Window
    For Each win In Application.Windows
        With win
            .WindowState = xlNormal
            .Width = Application.Width
            .Height = Application.Height
            .Left = cnsSukima
            .Top = cnsSukima
            .WindowState = xlMaximized
        End With
    Next
    Application.ScreenUpdating = True
End Sub

Option Explicit

Private instWinNarabe As clsWinArrng
Sub test横並べ()
    
    Dim tgtwb As Workbook

    Set instWinNarabe = New clsWinArrng
    Set tgtwb = Workbooks.Add
    tgtwb.Windows(1).Caption = "こっちが左だよ~"
    
    Call instWinNarabe.ArrengeWindows(tgtwb, ThisWorkbook, Vertical)
End Sub

Sub test縦並べ()
    
    Dim tgtwb As Workbook
    Set instWinNarabe = New clsWinArrng
    Set tgtwb = Workbooks.Add
    tgtwb.Windows(1).Caption = "こっちが上だよ~"
    
    Call instWinNarabe.ArrengeWindows(tgtwb, ThisWorkbook, Horizontal)
End Sub

'--------------------------------------------------------------------


Option Explicit
'■コマンドバーに付属するコマンドをリストアップする
Sub controlsListUp()
    Dim bar As CommandBar
    Dim cntrl As CommandBarControl
    Dim ws As Worksheet
    
    Set ws = ThisWorkbook.Worksheets.Add
    
    For Each bar In Excel.CommandBars
        For Each cntrl In bar.Controls
            Call subCntrolslistUp(ws, bar, cntrl)
        Next
    Next
End Sub

'コマンドバーのコントロール情報を抽出する
Sub subCntrolslistUp( _
    ws As Worksheet, _
    bar As CommandBar, _
    cntrl As CommandBarControl)
    
    Static level As Long
    Dim itmCnt As Long
    Dim subCntrls As CommandBarControls
    
    Dim tgtRng As Range
    Dim vntDataAry As Variant
    Dim vntTitleAry As Variant
    Dim rng As Range
    Dim offsetCnt As Long
    
    '一層目か二層目以降かの切り分け
    If level Then
        '二層目以降
        With ws
            itmCnt = 4
            '書き込みレンジ
            Set tgtRng = _
            .Cells(ws.Rows.Count, _
                (level + 1) * _
                itmCnt - itmCnt + 1 + _
                IIf(level > 0, 3, 0)) _
            .End(xlUp) _
            .Resize(, itmCnt).Offset(1)
            offsetCnt = _
                ws.Cells.SpecialCells(xlCellTypeLastCell).Row - tgtRng.Row
            If offsetCnt > 0 Then
                Set tgtRng = tgtRng.Offset(offsetCnt)
            End If
            
            '書き込みデータ
            On Error Resume Next
            'Xl2007 で
            'commandbars("PivotTable Context Menu"). _
                controls("レポートにプロパティを表示(&P)"). _
                controls(3).caption
            'を取得する時トラップ出来ないエラー(-2147417848)
            'が発生した。CallByNameでCaption を取得すると
            'エラー(440)をトラップする事が出来た。。
            vntDataAry = _
                Array( _
                "'" & CallByName(cntrl, "Caption", VbGet), _
                cntrl.ID, _
                cntrl.Index, _
                GetTypeName(cntrl.Type))
            
            If Err.Number <> 0 Then
                vntDataAry = _
                    Array( _
                    "'ERR:" & Err.Number & "-" & Err.Description, _
                    cntrl.ID, _
                    cntrl.Index, _
                    GetTypeName(cntrl.Type))
            End If
            On Error GoTo 0
            
            'データのタイトル
            vntTitleAry = _
                Array( _
                "level" & level + 1 & "ControlCaption", _
                "level" & level + 1 & "ControlID", _
                "level" & level + 1 & "ControlIndex", _
                "level" & level + 1 & "ControlType")
        End With
        
    Else
        itmCnt = 7
        '一層目
        With ws
            '書き込みレンジ
            Set tgtRng = _
            .Cells(ws.Rows.Count, _
                (level + 1) * itmCnt - itmCnt + 1) _
            .End(xlUp) _
            .Resize(, itmCnt).Offset(1)
            
            '書き込みデータ
            vntDataAry = _
                Array( _
                bar.Name, _
                bar.NameLocal, _
                bar.Index, _
                "'" & cntrl.Caption, _
                cntrl.ID, _
                cntrl.Index, _
                GetTypeName(cntrl.Type))
            
            'データのタイトル
            vntTitleAry = _
                Array( _
                 "BarName", _
                 "BarLocalName", _
                 "BarIndex", _
                 "ControlCaption", _
                 "ControlID", _
                 "ControlIndex", _
                 "ControlType")
        End With
    End If
    
    With tgtRng
        .Value = vntDataAry
        
        If level Then
            Set rng = ws.Range(ws.Cells(.Row, 1), _
                .Cells(1).Offset(, -1))
            With rng
                If WorksheetFunction.CountA(rng) = 0 Then
                .Value = .Offset(-1).Value
                End If
            End With
        End If
        
        '既にタイトル行を設定していた場合はスル―
        With .Offset(-(.Row - 1))
            If WorksheetFunction.CountA(.Cells) = 0 Then
                .Value = vntTitleAry
            End If
        End With
        
        '再帰処理
        On Error Resume Next
            Set subCntrls = cntrl.Controls
        On Error GoTo 0
        
        If Not subCntrls Is Nothing Then
        'Stop
            level = level + 1
            For Each cntrl In subCntrls
                Call subCntrolslistUp(ws, bar, cntrl)
            Next
            'サブコントロールがこれ以上見つからなければ
            '上の階層に戻る
            level = level - 1
        End If
    End With
End Sub

'コントロールタイプの数値を渡し、値を返す
Private Function GetTypeName(ByRef n As MsoControlType) As String
    Select Case n
        Case 22
            GetTypeName = "msoControlActiveX"
        Case 26
            GetTypeName = "msoControlAutoCompleteCombo"
        Case 1
            GetTypeName = "msoControlButton"
        Case 5
            GetTypeName = "msoControlButtonDropdown"
        Case 12
            GetTypeName = "msoControlButtonPopup"
        Case 4
            GetTypeName = "msoControlComboBox"
        Case 0
            GetTypeName = "msoControlCustom"
        Case 3
            GetTypeName = "msoControlDropdown"
        Case 2
            GetTypeName = "msoControlEdit"
        Case 16
            GetTypeName = "msoControlExpandingGrid"
        Case 19
            GetTypeName = "msoControlGauge"
        Case 8
            GetTypeName = "msoControlGenericDropdown"
        Case 20
            GetTypeName = "msoControlGraphicCombo"
        Case 9
            GetTypeName = "msoControlGraphicDropdown"
        Case 11
            GetTypeName = "msoControlGraphicPopup"
        Case 18
            GetTypeName = "msoControlGrid"
        Case 15
            GetTypeName = "msoControlLabel"
        Case 24
            GetTypeName = "msoControlLabelEx"
        Case 7
            GetTypeName = "msoControlOCXDropdown"
        Case 21
            GetTypeName = "msoControlPane"
        Case 10
            GetTypeName = "msoControlPopup"
        Case 23
            GetTypeName = "msoControlSpinner"
        Case 14
            GetTypeName = "msoControlSplitButtonMRUPopup"
        Case 13
            GetTypeName = "msoControlSplitButtonPopup"
        Case 6
            GetTypeName = "msoControlSplitDropdown"
        Case 17
            GetTypeName = "msoControlSplitExpandingGrid"
        Case 25
            GetTypeName = "msoControlWorkPane"
        Case Else
            GetTypeName = CStr(n)
    End Select

End Function

'--------------------------------------------------------------------

'--------------------------------------------------------------------
'TEST
'Thisworkbookモジュール
Option Explicit
Private mBtnProtect As Office.CommandBarPopup
'Private mBtnProtect As Office.CommandBarButton

'自宅2003 ではブックを別名で保存し、一緒に立ちあげても
'オブジェクトが必要です なんて言ってこない。
'2007はリボンにそもそも「保護」が無く、いきなりシートの保護とかあるし
'これコマンドバーボタンじゃないしで話にならない

Private Sub Workbook_Open()
    Call btnSet
End Sub

Private Sub Workbook_Activate()
    Call btnConfig(False)
End Sub

Private Sub Workbook_Deactivate()
    Call btnConfig(True)
End Sub

Private Sub btnSet()
    '保護
    Set mBtnProtect = Application.CommandBars.FindControl(, 30029)
    
    '自宅2003だとボタンの無効は無視された
    'シートの保護
    'Set mBtnProtect = Application.CommandBars.FindControl(, 893)
End Sub

Private Sub btnConfig(enable As Boolean)
    mBtnProtect.Enabled = enable
    MsgBox mBtnProtect.Caption & " " & mBtnProtect.Enabled & " " & ThisWorkbook.Name
End Sub

'----------------------------------------------------------------------------------------

Sub 文字列からcp932では解釈できない文字のみ抽出する()
'a1 にはユニコード文字とcp932の混在文字列
'実行するとa2にcp932では解釈できない文字列だけが
'抽出される。
    Dim str As String, c As String
    Dim testc As String
    Dim i As Long
    str = Range("a1").Value
    
    For i = 1 To Len(str)
        testc = Chr(Asc(Mid$(str, i, 1)))
        c = ChrW(AscW(Mid$(str, i, 1)))
        If testc = "?" Then
            With Range("a2")
                .Value = .Value & c
            End With
        End If
    Next
End Sub

'データベースとしてのOpen Put Getステートメント
Option Explicit

Public Const myPath As String = _
"C:\Users\yama\Desktop\ブロクファイル\GetStatment考" & _
"\test.dat"

Type uPerson
    Age As Integer
    Name As String
End Type

Sub データの登録()
    Dim p As uPerson
    
    'サイズを0にする
    Open myPath For Output As #1
    Close #1
    Debug.Print myPath
    Debug.Print Len(p) '→p構造体:Total 6バイト
    '可能性として考えられる文字列の最大バイト長を加算して
    '1レコードあたりの単位バイト長を指定してOpen します。
    Open myPath For Random As #1 Len = Len(p) + 14 '6+14=20バイト
     
    p.Age = 17
    p.Name = "太郎Taro"
    Put #1, 1, p
    p.Age = 18
    p.Name = "山田花子Hanako"
    Put #1, 3, p
    p.Age = 20
    Put #1, 4, p '同じ文字列をRecord4番目にもPutする
    
    Close #1
    
End Sub

Sub 行番号を指定してデータを取得する()
    Dim p As uPerson
    
    Open myPath For Random As #1 Len = Len(p) + 14
    
    Get #1, 1, p
    
    '余分なスペースなしにデータを取得できる。
    'VBAで使い道あると思えないが(collectionがあるので)
    'VBSだった使うかも。
    Debug.Print p.Age
    Debug.Print Replace(p.Name, " ", "×"), Len(p.Name)
    Get #1, 4, p
    Debug.Print p.Age
    Debug.Print Replace(p.Name, " ", "×"), Len(p.Name)
    Close #1
    
End Sub

Option Explicit
Sub ワークシートにチェックボックスを配置する()

    Dim chbx As OLEObject
    Const cnsName As String = "checkbox1"
    
    Set chbx = Me.OLEObjects.Add(ClassType:="Forms.CheckBox.1")
    
    With chbx
        .Name = cnsName
        .Left = Range("b2").Left
        .Top = Range("b2").Top
        .Object.Value = True
        .Object.Caption = "列" & Range("b2").Column
    End With
End Sub

'次の使用例は、シート 1 に新しい Word OLE オブジェクトを作成します。
'VBA
'
'ActiveWorkbook.Worksheets("Sheet1").OLEObjects.Add _
' ClassType:="Word.Document"
'
'次の使用例はワークシート 1 にコマンド ボタンを追加します。
'VBA
'
'Worksheets(1).OLEObjects.Add ClassType:="Forms.CommandButton.1", _
' Link:=False, DisplayAsIcon:=False, Left:=40, Top:=40, _
' Width:=150, Height:=10

Sub test()
    Dim rng As Range, c As Range
    Dim chBoxes As New Collection
    Set rng = Range("a1:a11")
    
    For Each c In rng.Cells
        
    Next
        
End Sub

Option Explicit
'UserForm1モジュール
Private mChkBx(46) As MSForms.CheckBox

Private Sub UserForm_Initialize()
    Dim i As Integer
    
    For i = LBound(mChkBx) To UBound(mChkBx)
        Set mChkBx(i) = _
            Me.Controls.Add("Forms.CheckBox.1")
        With mChkBx(i)
            .Top = 3 + (i Mod 10) * 15
            .Left = 10 + (i \ 10) * 30
            .Caption = i
            .Name = "chk_" & i
        End With
    Next
End Sub

'文字列から使うべきでない文字を_に置き換えて
'安全な名前にして返す
Public Function cnvSafeName(testName As String) As String
    
    Dim rslt As String
    Dim chrAry As Variant
    Dim c As Variant
    Dim spaceA As String
    
    spaceA = Chr(32)
    
    rslt = StrConv(testName, vbNarrow)
    chrAry = _
        Array("""", "'", "`", "#", "%", ">", _
        "<", "!", ".", "[", "]", ";", ":", _
        "?", "^", "(", ")", "+", "\", "/", _
        "*", spaceA)
        
    For Each c In chrAry
        rslt = VBA.Replace(rslt, c, "_")
    Next
    
    cnvSafeName = rslt
    
End Function


Sub 配列の要素から一意な値を取得する()

    Dim dic As Object
    Dim ary
    Dim i As Long
    Dim v As Variant
    
    'Dictionary オブジェクトは文字と文字の連想配列を作成します
    Set dic = CreateObject("Scripting.Dictionary")
    
    '重複した値のある配列
    ary = Array("1", "1", "3", "1", "2", "6", "6", "5", "4", "7", "7", "9", "9", "8", "8", "8", "10")
    'key は数字であってもOK!
    'ary = Array(1, 2, 3, 1, 2, 3, 1, 2, 3)
    For i = LBound(ary) To UBound(ary)
        On Error Resume Next
        'keyが重複するとエラーになり受け付けない
        '構文 dic.Add key, Item
        dic.Add ary(i), "dummyItem"
    Next
    
    On Error GoTo 0
    
    v = dic.Keys 'Keysメソッドは key の配列に変換します。
    Stop
    For i = 0 To dic.Count - 1
        Debug.Print v(i)  '一意な値が返ります
    Next

        '1
        '3
        '2
        '6
        '5
        '4
        '7
        '9
        '8
        '10
End Sub




yamaV1.02β