goo blog サービス終了のお知らせ 

私的備忘録

C#とかVBAとか。自分の備忘用なので丁寧には書いてないのであしからず。

長さチェック

2022-03-21 | 日記

 

# ファイル名およびフォルダ名の長さをチェックする
# 長さが255バイト以上だとアウト

 


# Get-ChildItem -Recurse だとフォルダが巨大な場合に長時間 無応答となるため
# あえてループ処理とする

 


Write-Host `nEnterキーを押してください。処理を開始します。`n -ForegroundColor Yellow
Read-Host
Write-Host -NoNewline 配下の全フォルダを検索しています

 


$global:オブジェクト総数 = 0
$global:違反ファイル数 = 0
$global:結果記録 = ""

 

function ファイルフォルダカウント( $対象フォルダ )
{​​​​​
    $配下オブジェクト = Get-ChildItem( $対象フォルダ ) -Force

 

    $global:オブジェクト総数 += ( ( $配下オブジェクト | Measure-Object ).Count )
    Write-Host -NoNewline "."

 

    foreach( $要素 in $配下オブジェクト )
    {​​​​​
        フォルダファイル名バイト数チェック $要素
        if ( $要素.PSIsContainer -eq $true )
        {​​​​​
            ファイルフォルダカウント $要素.FullName
        }​​​​​
    }​​​​​
}​​​​​

 


function フォルダファイル名バイト数チェック( $対象オブジェクト )
{​​​​​
    $バイト数 = [System.Text.Encoding]::GetEncoding("utf-8").GetByteCount( $対象オブジェクト.Name )
    if ( $バイト数 -gt 255 )
    {​​​​​
        $global:違反ファイル数 += 1

 

        if ( $対象オブジェクト.PSIsContainer -eq $true )
        {​​​​​
            $対象種類 = "フォルダ"
            $対象場所 = $対象オブジェクト.Parent.FullName
            $対象名前 = $対象オブジェクト.Name
        }​​​​​
        else
        {​​​​​
            $対象種類 = "ファイル"
            $対象場所 = $対象オブジェクト.Directory.FullName
            $対象名前 = $対象オブジェクト.Name
        }​​​​​
       
       $global:結果記録 += "■■■ 名前が長すぎる $対象種類 を発見 ■■■`n"
       $global:結果記録 += "■場所:`n  $対象場所 `n"
       $global:結果記録 += "■${​​​​​対象種類}​​​​​名:`n  $対象名前 `n"
       $global:結果記録 += "■${​​​​​対象種類}​​​​​名のバイト数: $バイト数 `n`n`n"
    }​​​​​
}​​​​​

 

 


ファイルフォルダカウント $PSScriptRoot
Write-Host `n処理が完了しました。`n`n

 

$global:結果記録 += "`n`n全部で $オブジェクト総数 のファイル/フォルダをチェックしました。`n"

 


# エラーログを確認
if ( $Error.Count -gt 0 )
{​​​​​
    $global:結果記録 += "`n`n★★★ また、下記のようなエラーが出ています。 ★★★`n`n"
    $global:結果記録 += "アクセス権の関係でチェックできなかった可能性があります。`n"
    $global:結果記録 += "別途、確認してください。`n`n"
    foreach( $要素 in $Error )
    {​​​​​
        $global:結果記録 += $要素.ToString() + "`n`n"
    }​​​​​
}​​​​​

 


Write-Host $global:結果記録

 

if ( $global:違反ファイル数 -eq 0 )
{​​​​​
    Write-Host 名前が長すぎるファイル/フォルダはありませんでした。`n`n -ForegroundColor Green
}​​​​​
else
{​​​​​
    $結果出力ファイル名 = Get-Date -Format "yyyyMMddHHmmss"
    Write-Output $global:結果記録 | Out-File "ファイル名長さチェック結果_$結果出力ファイル名.txt"
    Write-Host `n処理結果を、「 ファイル名長さチェック結果${​​​​​結果出力ファイル名}​​​​​.txt 」というファイルとして保存しました。ご確認ください。`n`n -ForegroundColor Red
}​​​​​

 
Read-Host 終了してこの画面を閉じます。Enterキーを押してください。


ieWaitしまくり

2021-10-09 | 日記

Option Explicit


Sub ieWaitAll()
    Debug.Print "waiting..."
    Dim colSh As Object
    Dim win As Object
    Dim strTemp As String
    Dim objIE As Object
    
    Set colSh = CreateObject("Shell.Application")
    
    Dim i As Long
    For i = 1 To 100
        For Each win In colSh.Windows
            On Error Resume Next
            strTemp = win.document.Title
            On Error GoTo 0
            Set objIE = win
            ieWait objIE
        Next
    Next i
    Debug.Print "complete!"
End Sub


Sub ieWait(ie As InternetExplorer)
    Do While ie.Busy Or ie.readyState
        DoEvents
    Loop
End Sub


Sub test()
    Dim i As Long
    Do While True
        Debug.Print i
        ieWaitAll
        DoEvents
        i = i + 1
    Loop

End Sub

 

Sub test2()
    Dim i As Long
    For i = 1 To 3
        Debug.Print i
    Next i
End Sub


フォームを使ってダイアログ的にデータを取得する場合のベストプラクティスについて

2021-02-27 | 日記

◆ダイアログのようなものを作りたいときに、 InputBox では機能的に不足していて、ユーザーフォームでやらざるを得ないときにどうしたら良いか
◆フォームを使ってダイアログ的にデータを取得する場合のベストプラクティスについて

⇒次の4種類の方法がある。
(0)フォーム側で Me.Hide したのち、呼び出し側で TextBox.Value などで値を取り出す
(1)標準モジュール(呼び出し側)にグローバル変数を宣言する
(2)フォーム側にPublicで変数を宣言する
(3)フォーム側にプロパティを定義する

ただし、(0)の方法は、値の検証(不正な値のときにユーザーに再入力を求める等)が必要な場合などには不向きであるため、意外と実用性がない。

従って(1)~(3)から状況に応じて選択。

厳密にカプセル化するのであれば、(3)となるが、記述量が多くなるため、簡易な開発では、(2)で充分と思われる。

ちなみに、(2)と(3)について具体的な手順としては、
(2)フォーム側にPublicで変数を宣言する
①フォーム側にPublicで変数を定義
②必要に応じて値の検証をしたのち、変数に代入
③フォーム側で、 Me.Hide して、呼び出し側に遷移
④呼び出し側で、フォームの変数にアクセスし、その後 Unload UserForm1 で閉じる

(3)フォーム側にプロパティを定義する
①フォーム側にPrivateで変数を定義
②プロパティとしてLetとGetを定義
③必要に応じて値の検証をしたのちプロパティにセット
④フォーム側で、 Me.Hide して、呼び出し側に遷移
⑤呼び出し側で、プロパティにアクセスし、その後 Unload UserForm1 で閉じる


(↓以下、結論に至るまでの過程)
・フォームを開くには、
UserForm1.Show
・フォームを閉じるには、そのフォーム内で
Unload Me

である。
問題は、ダイアログを呼び出す側が .Show をすると、(特別にモーダレスにでもしない限り、)フォームが閉じられるまでは呼び出し側のプロシージャは停止しており、フォームが閉じられると .Show の次の行から実行再開される、という構造で、つまり「戻り値」のような仕組みが無い、ことである。

フォーム側で Unload Me する前に、呼び出し側へデータを渡したいわけだが、そのベストプラクティスがよくわからない。

思いつくのは、
・グローバル変数を使う
・セルを使う
だが、これらはあまりオブジェクト指向的でないように思える。
ExcelVBAに特化して考えた場合は、グローバル変数でいいのだろうとは思うが。

ん、なんか正解っぽいものを見つけた気がする。次の通り。
・フォーム側にプロパティを設置する(というか、フォーム内のグローバル領域に、 Private で変数宣言すれば良い?)
・フォームからメインに戻るときに、 Unload.Me ではなく、
Me.Hide
する。(これでも、制御はメインに戻るらしい。)
・メイン側で、フォームの変数にアクセスしてから、 Unload で閉じる。

実際に試してみた結果、上のやり方でできることがわかった。
また、プロパティを使わずに、単に TextBox1.Value で取得できてしまうこともわかった。例えば Unload する前に、
myText2 = UserForm1.TextBox2.Value
とやれば、値を取り出すことができる。

おそらく、推測するに、フォームを閉じる前に値の検証が必要な場合は、プロパティを使うほうが良いのだろう。
逆に、フォームを閉じてしまってから、データを検証すれば良い場合は、プロパティを使わなくても良いような気がする。(ただし、 Variant で受けて検証する、などは必要かもしれないが。)

ちなみに、フォーム内のグローバル領域で Public に変数を宣言すると、その変数は呼び出し側からも見えて使えるようだ。これでもいいのかもしれない。(これだと、カプセル化もなにも関係なくなるし、それならそもそも呼び出し側のグローバル変数でいいじゃないか、ということになってしまうかも。)

というわけで、
①しっかりカプセル化するならフォームのプロパティを使う
②そこまで厳密じゃなくていいならフォーム内に Public 変数を宣言

という感じで良いのではないだろうか。
ちなみに②は、たしかに中途半端ではあるかもしれないが、標準モジュール内にグローバル変数を書くのよりは気持ち悪くない感じがするので、グローバル変数よりマシと思う。

 


InputBoxで入力を受け付ける(テンプレ)

2021-02-27 | 日記

◆InputBoxで入力を受け付ける(内容チェック機能付き)
・下記のサンプルでは「正の整数」を受け付ける
※小数点以下を入力したとしても、強制的に四捨五入されるようなので注意


Private Function ユーザ入力受付(Prompt As String, _

                                Optional Title As String = "")

    Dim myFlag As Boolean
    myFlag = False
    Dim buf As Variant
    Do
        buf = Application.InputBox(Prompt, Title)
        Select Case True
            Case buf = False
                '// キャンセルの場合はマクロ全体を終了
                '// (0が入力された場合も該当する)
                MsgBox "キャンセルして終了します。"
                End
            Case buf = ""
                MsgBox "何も入力されていません。"
            Case IsNumeric(buf) = False
                MsgBox "数値を入力してください。"
            Case buf < 0
                MsgBox "0より小さい数は入力できません。"
            Case Else
                myFlag = True
        End Select
    Loop While myFlag = False
    ユーザ入力受付 = buf
End Function

 


スクロール、セルの画面表示、移動

2021-02-27 | 日記

◆◆◆スクロール、セルの画面表示、移動


◆そもそも
・セルを .Activate すると、画面の中央あたりにそのセルが表示される。
・ Find を使った場合も同様の画面表示がなされる。

 

◆特定のセルを画面左上に表示させる
Application.GoTo Cells(100,100), True
・第2引数に True を指定すると、画面左上に表示となる。

 

◆指定の行、列を画面左上に表示させる(数値で指定)
With ActiveWindow
    .ScrollRow = 100
    .ScrollColumn = 100
End With
・数値のところを ActiveCell.Row や ActiveCell.Column とすれば、アクティブセルを画面左上にすることもできる。

 

◆現在表示されている(見えている)セルを知る
Window オブジェクトの、 .VisibleRange プロパティを使う。
・このプロパティは Range であるため、 .Address や .Rows .Columns などを持っている。
・たとえば下記のような情報を知ることができる。
ActiveWindow.VisibleRange.Row   ⇒画面の上端にある行の番号
ActiveWindow.VisibleRange.Rows.Count    ⇒画面に表示している行数(全部で何行分が表示されているのか)
ActiveWindow.VisibleRange.Address ⇒画面に表示されているセル範囲(「$A$21:$N$38」といった結果を得ることができる)

 


◆任意のセル単位、ページ単位でスクロールさせる
Pane または Window オブジェクトに対しては、
.LargeScroll()
.SmallScroll()
というメソッドが使える。
.LargeScroll がページ単位、 .SmallScroll がセル単位である。
引数は4つ取り、 Down, Up, ToRight, ToLeft である。それぞれ数値で指定する。
・このときアクティブセルは移動しない。