GUIで誰でも使えるRobocopyをPowerShellで実装する

使ったもの、バージョン等
powershell 5.1
powershell ISE

　部署異動で、個人のフォルダ移行作業に限った用途を想定しています。
　素直にRichCopy使えたら、それが一番と思っているのですが、インストール禁止、自作exe禁止の環境で仕事をしているのでこの条件でスクリプトを書きました。

　一緒に配布するバッチファイルはこちらです↓
https://qiita.com/harryyuni/items/c7095d0e472d50fdc35e

　GUIの仕様はこんな感じ
　ドラッグアンドドロップでパスを指定します。共有フォルダ（ネットワークフォルダ）使用可能です
![raw-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/622636/58247390-2dff-ae89-1e8b-034198654dbe.png)

　/Lテスト実行を実行し、ドライブの空き容量をチェックします
<img width="900" alt="2020-05-13 (1).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/622636/26898603-9c9b-0338-467a-125f1bbd3d82.png">


 /Lテスト実行時に作成したログの行数と本番実行中のログの行数を比較してプログレスバーを表示します。
Write-Progressは窓に表示される仕様なので、窓を開かないスクリプト実行のためにWindowsフォームで実装しています。
<img width="900" alt="2020-05-13 (3).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/622636/48b6b265-343d-6f87-c1d3-de36167b5064.png">


スクリプトです。

```powershell:powershell5.1

#Set-Locationにタイムラグがあるので最初にデスクトップに移動しておく
#(ログをカレントディレクトリに保存する準備)
$wShell= New-Object -ComObject WScript.Shell
[String]$dtpPath=$wShell.SpecialFolders("desktop")
Set-Location $dtpPath

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.drawing
        $Form1 = New-Object  System.Windows.Forms.Form
        $Label1 = New-Object System.Windows.Forms.Label
        $Label2 = New-Object System.Windows.Forms.Label
        $Label3 = New-Object System.Windows.Forms.Label
        $Label4 = New-Object System.Windows.Forms.Label
        $Label5 = New-Object System.Windows.Forms.Label
        $Label6 = New-Object System.Windows.Forms.Label
        $Label7 = New-Object System.Windows.Forms.Label
        $Label8 = New-Object System.Windows.Forms.Label
        $Label9 = New-Object System.Windows.Forms.Label
        $Label10 = New-Object System.Windows.Forms.Label
        $Label11 = New-Object System.Windows.Forms.Label
        $Label12 = New-Object System.Windows.Forms.Label
        #button
        $ButtonAddRestrictStr = New-Object System.Windows.Forms.Button
        $ButtonAddExceptStr = New-Object System.Windows.Forms.Button
        #textbox
        $TxtBoxCopyFromFolder = New-Object System.Windows.Forms.TextBox
        $TxtBoxCopyToFolder = New-Object System.Windows.Forms.TextBox
        $TxtBoxRestrictStr = New-Object System.Windows.Forms.TextBox
        $TxtBoxRestrictContents = New-Object System.Windows.Forms.TextBox
        $TxtBoxExceptContents = New-Object System.Windows.Forms.TextBox
        $TxtBoxExceptStr = New-Object System.Windows.Forms.TextBox
        #checkbox
        $CheckRestrictExcelFile = New-Object System.Windows.Forms.CheckBox
        $CheckRestrictWordFile = New-Object System.Windows.Forms.CheckBox
        $CheckRestrictTaroFile = New-Object System.Windows.Forms.CheckBox
        $CheckRestrictPDFFile = New-Object System.Windows.Forms.CheckBox
        $CheckExceptPDFFile = New-Object System.Windows.Forms.CheckBox        
        $CheckExceptTaroFile = New-Object System.Windows.Forms.CheckBox
        $CheckExceptWordFile = New-Object System.Windows.Forms.CheckBox
        $CheckExceptExcelFile = New-Object System.Windows.Forms.CheckBox
        # DatetimePicker
        $DatePickLastModified = New-Object System.Windows.Forms.DatetimePicker
#font
        $font9=New-Object System.Drawing.Font("BIZ UDゴシック", 9.0,[System.Drawing.FontStyle]::Regular,[ System.Drawing.GraphicsUnit]::Point,128)
        $font6=New-Object System.Drawing.Font("BIZ UDゴシック", 6.0,[System.Drawing.FontStyle]::Regular,[ System.Drawing.GraphicsUnit]::Point ,128)
        #
        #Label1
        $Label1.AutoSize = $True
        $Label1.Font = $font6
        $Label1.Location = New-Object System.Drawing.Point(50, 15)
        $Label1.Name = "Label1"
        $Label1.Size = New-Object System.Drawing.Size(80, 18)
        $Label1.Text = "コピー元フォルダ  ドラッグ＆ドロップ"
        #
        #Label2
        $Label2.AutoSize = $True
        $Label2.Font = $font6
        $Label2.Location = New-Object System.Drawing.Point(50, 57)
        $Label2.Name = "Label2"
        $Label2.Size = New-Object System.Drawing.Size(80, 18)
        $Label2.Text = "コピー先フォルダ  ドラッグ＆ドロップ"
        #
        #Label3
        $Label3.AutoSize = $True
        $Label3.Font = $font9
        $Label3.Location = New-Object System.Drawing.Point(3, 180)
        $Label3.Name = "Label3"
        $Label3.Size = New-Object System.Drawing.Size(242, 18)
        $Label3.Text = "指定したファイルのみコピー"
        #
        #Label4
        $Label4.AutoSize = $True
        $Label4.Font = $font9
        $Label4.Location = New-Object System.Drawing.Point(3, 328)
        $Label4.Name = "Label4"
        $Label4.Size = New-Object System.Drawing.Size(260, 18)
        $Label4.Text = "一部ファイルを除外してコピー"
        #
        #Label5
        $Label5.AutoSize = $True
        $Label5.Font = $font9
        $Label5.Location = New-Object System.Drawing.Point(12, 271)
        $Label5.Name = "Label5"
        $Label5.Size = New-Object System.Drawing.Size(80, 18)
        $Label5.Text = "指定一覧"
        #
        #Label6
        $Label6.AutoSize = $True
        $Label6.Font = $font9
        $Label6.Location = New-Object System.Drawing.Point(608, 240)
        $Label6.Name = "Label6"
        $Label6.Size = New-Object System.Drawing.Size(62, 18)
        $Label6.Text = "を含む"
        #
        #Label7
        $Label7.AutoSize = $True
        $Label7.Font = $font9
        $Label7.Location = New-Object System.Drawing.Point(447, 214)
        $Label7.Name = "Label7"
        $Label7.Size = New-Object System.Drawing.Size(116, 18)
        $Label7.Text = "ファイル名に"
        #
        #Label8
        $Label8.AutoSize = $True
        $Label8.Font = $font9
        $Label8.Location = New-Object System.Drawing.Point(447, 360)
        $Label8.Name = "Label8"
        $Label8.Size = New-Object System.Drawing.Size(116, 18)
        $Label8.Text = "ファイル名に"
        #
        #Label9
        $Label9.AutoSize = $True
        $Label9.Font = $font9
        $Label9.Location = New-Object System.Drawing.Point(608, 386)
        $Label9.Name = "Label9"
        $Label9.Size = New-Object System.Drawing.Size(62, 18)
        $Label9.TabIndex = 27
        $Label9.Text = "を含む"
        #
        #Label10
        $Label10.AutoSize = $True
        $Label10.Font = $font9
        $Label10.Location = New-Object System.Drawing.Point(12, 417)
        $Label10.Name = "Label10"
        $Label10.Size = New-Object System.Drawing.Size(80, 18)
        $Label10.TabIndex = 26
        $Label10.Text = "除外一覧"
        #Label11
        $Label11.AutoSize = $True
        $Label11.Font = $font9
        $Label11.Location = New-Object System.Drawing.Point(170, 110)
        $Label11.Name = "Label11"
        $Label11.Size = New-Object System.Drawing.Size(100, 18)
        $Label11.Text = "最終更新日が"
        #Label12
        $Label12.AutoSize = $True
        $Label12.Font = $font9
        $Label12.Location = New-Object System.Drawing.Point(400, 110)
        $Label12.Name = "Label12"
        $Label12.Size = New-Object System.Drawing.Size(150, 18)
        $Label12.Text = "より前のファイルを除く"
        #
#フォルダ入力テキストボックス
        $TxtBoxCopyFromFolder.Location = New-Object System.Drawing.Point(97, 30)
        $TxtBoxCopyFromFolder.Name = "copyFromFolder"
        $TxtBoxCopyFromFolder.Size = New-Object System.Drawing.Size(571, 25)
        $TxtBoxCopyFromFolder.TabIndex = 0
        $TxtBoxCopyFromFolder.AllowDrop=$True
        #
        $TxtBoxCopyToFolder.Location = New-Object System.Drawing.Point(98, 71)
        $TxtBoxCopyToFolder.Name = "copyToFolder"
        $TxtBoxCopyToFolder.Size = New-Object System.Drawing.Size(571, 25)
        $TxtBoxCopyToFolder.TabIndex = 1
        $TxtBoxCopyToFolder.AllowDrop=$True
        #
#最終更新日
        $DatePickLastModified.AutoSize = $True
        $DatePickLastModified.Font = $font9
        $DatePickLastModified.location = "260, 107"
        $DatePickLastModified.Size = New-Object System.Drawing.Size(120, 33)
        $DatePickLastModified.Format = "Long"
        $DatePickLastModified.ShowUpDown = $True
        $DatePickLastModified.Value=(Get-Date).AddYears(-2)
        $DatePickLastModified.TabIndex=2
#指定項目
        #
        #CheckRestrictExcelFile
        $CheckRestrictExcelFile.AutoSize = $True
        $CheckRestrictExcelFile.Font = $font9
        $CheckRestrictExcelFile.Location = New-Object System.Drawing.Point(21, 213)
        $CheckRestrictExcelFile.Name = "CheckRestrictExcelFile"
        $CheckRestrictExcelFile.Size = New-Object System.Drawing.Size(241, 22)
        $CheckRestrictExcelFile.TabIndex = 3
        $CheckRestrictExcelFile.Text = "Excel(.xls .xlsx .xlsm)"
        #
        #CheckRestrictPDFFile
        $CheckRestrictPDFFile.AutoSize = $True
        $CheckRestrictPDFFile.Font = $font9
        $CheckRestrictPDFFile.Location = New-Object System.Drawing.Point(289, 214)
        $CheckRestrictPDFFile.Name = "CheckRestrictPDFFile"
        $CheckRestrictPDFFile.Size = New-Object System.Drawing.Size(115, 22)
        $CheckRestrictPDFFile.TabIndex = 4
        $CheckRestrictPDFFile.Text = "PDF(.pdf)"
        #
        #CheckRestrictWordFile
        $CheckRestrictWordFile.AutoSize = $True
        $CheckRestrictWordFile.Font = $font9
        $CheckRestrictWordFile.Location = New-Object System.Drawing.Point(21, 241)
        $CheckRestrictWordFile.Name = "CheckRestrictWordFile"
        $CheckRestrictWordFile.Size = New-Object System.Drawing.Size(232, 22)
        $CheckRestrictWordFile.TabIndex = 5
        $CheckRestrictWordFile.Text = "Word(.doc .docx .docm)"
        #
        #CheckRestrictTaroFile
        $CheckRestrictTaroFile.AutoSize = $True
        $CheckRestrictTaroFile.Font = $font9
        $CheckRestrictTaroFile.Location = New-Object System.Drawing.Point(289, 241)
        $CheckRestrictTaroFile.Name = "CheckRestrictTaroFile"
        $CheckRestrictTaroFile.Size = New-Object System.Drawing.Size(142, 22)
        $CheckRestrictTaroFile.TabIndex = 6
        $CheckRestrictTaroFile.Text = "一太郎(.jtd)"
        #
        #RestrictStr
        $TxtBoxRestrictStr.Location = New-Object System.Drawing.Point(446, 237)
        $TxtBoxRestrictStr.Name = "RestrictStr"
        $TxtBoxRestrictStr.Size = New-Object System.Drawing.Size(162, 25)
        $TxtBoxRestrictStr.TabIndex = 7
        #
        #ButtonAddRestrictStr
        $ButtonAddRestrictStr.Location = New-Object System.Drawing.Point(660, 230)
        $ButtonAddRestrictStr.Name = "ButtonAddRestrictStr"
        $ButtonAddRestrictStr.Size = New-Object System.Drawing.Size(70, 28)
        $ButtonAddRestrictStr.TabIndex = 8
        $ButtonAddRestrictStr.Text = "追加"
        #
        #RestrictContents
        $TxtBoxRestrictContents.Location = New-Object System.Drawing.Point(99, 268)
        $TxtBoxRestrictContents.Name = "RestrictContents"
        $TxtBoxRestrictContents.Size = New-Object System.Drawing.Size(691, 25)
        $TxtBoxRestrictContents.TabIndex = 9
#除外項目
        #
        #CheckExceptExcelFile
        $CheckExceptExcelFile.AutoSize = $True
        $CheckExceptExcelFile.Font = $font9
        $CheckExceptExcelFile.Location = New-Object System.Drawing.Point(21, 359)
        $CheckExceptExcelFile.Name = "CheckExceptExcelFile"
        $CheckExceptExcelFile.Size = New-Object System.Drawing.Size(241, 22)
        $CheckExceptExcelFile.TabIndex = 10
        $CheckExceptExcelFile.Text = "Excel(.xlsx .xlsm .xls)"
        #
        #CheckExceptPDFFile
        $CheckExceptPDFFile.AutoSize = $True
        $CheckExceptPDFFile.Font = $font9
        $CheckExceptPDFFile.Location = New-Object System.Drawing.Point(289, 360)
        $CheckExceptPDFFile.Name = "CheckExceptPDFFile"
        $CheckExceptPDFFile.Size = New-Object System.Drawing.Size(115, 22)
        $CheckExceptPDFFile.TabIndex = 11
        $CheckExceptPDFFile.Text = "PDF(.pdf)"
        #
        #CheckExceptWordFile
        $CheckExceptWordFile.AutoSize = $True
        $CheckExceptWordFile.Font = $font9
        $CheckExceptWordFile.Location = New-Object System.Drawing.Point(21, 387)
        $CheckExceptWordFile.Name = "CheckExceptWordFile"
        $CheckExceptWordFile.Size = New-Object System.Drawing.Size(232, 22)
        $CheckExceptWordFile.TabIndex = 12
        $CheckExceptWordFile.Text = "Word(.docx .docm .doc)"
        #
        #CheckExceptTaroFile
        $CheckExceptTaroFile.AutoSize = $True
        $CheckExceptTaroFile.Font = $font9
        $CheckExceptTaroFile.Location = New-Object System.Drawing.Point(289, 387)
        $CheckExceptTaroFile.Name = "CheckExceptTaroFile"
        $CheckExceptTaroFile.Size = New-Object System.Drawing.Size(142, 22)
        $CheckExceptTaroFile.TabIndex = 13
        $CheckExceptTaroFile.Text = "一太郎(.jtd)"
        #
        #ExceptStr
        $TxtBoxExceptStr.Location = New-Object System.Drawing.Point(446, 383)
        $TxtBoxExceptStr.Name = "ExceptStr"
        $TxtBoxExceptStr.Size = New-Object System.Drawing.Size(162, 25)
        $TxtBoxExceptStr.TabIndex = 14
        #
        #ButtonAddExceptStr
        $ButtonAddExceptStr.Location = New-Object System.Drawing.Point(660, 377)
        $ButtonAddExceptStr.Name = "ButtonAddExceptStr"
        $ButtonAddExceptStr.Size = New-Object System.Drawing.Size(70, 28)
        $ButtonAddExceptStr.TabIndex = 15
        $ButtonAddExceptStr.Text = "追加"
        #
        #ExceptContents
        $TxtBoxExceptContents.Location = New-Object System.Drawing.Point(99, 414)
        $TxtBoxExceptContents.Name = "ExceptContents"
        $TxtBoxExceptContents.Size = New-Object System.Drawing.Size(691, 25)
        $TxtBoxExceptContents.TabIndex = 16
        #
#フォームのボタン
        $OKButton = New-Object System.Windows.Forms.Button
        $OKButton.Location = "500,450"
        $OKButton.Size = "75,30"
        $OKButton.Text = "OK"
        $OKButton.TabIndex=17
        #
        $CancelButton = New-Object System.Windows.Forms.Button
        $CancelButton.Location = "600,450"
        $CancelButton.Size = "75,30"
        $CancelButton.Text = "Cancel"
        $CancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
        $CancelButton.TabIndex=18
#外枠
        #
        #Form1
        $Form1.AutoScaleDimensions = New-Object System.Drawing.SizeF(10.0, 18.0)
        $Form1.AutoScaleMode = [System.Windows.Forms.AutoScaleMode]::Font
        $Form1.ClientSize = New-Object System.Drawing.Size(800, 500)

        $Form1.SuspendLayout()
        $Form1.Controls.Add($OKButton)
        $Form1.Controls.Add($CancelButton)
        $Form1.Controls.Add($CheckExceptPDFFile)
        $Form1.Controls.Add($Label1)
        $Form1.Controls.Add($Label2)        
        $Form1.Controls.Add($Label3)
        $Form1.Controls.Add($Label4)        
        $Form1.Controls.Add($Label5)
        $Form1.Controls.Add($Label6)
        $Form1.Controls.Add($Label7)
        $Form1.Controls.Add($Label8)
        $Form1.Controls.Add($Label9)
        $Form1.Controls.Add($Label10)
        $Form1.Controls.Add($Label11)
        $Form1.Controls.Add($Label12)

        $Form1.Controls.Add($TxtBoxExceptContents)
        $Form1.Controls.Add($ButtonAddExceptStr)
        $Form1.Controls.Add($TxtBoxExceptStr)
        $Form1.Controls.Add($CheckExceptTaroFile)
        $Form1.Controls.Add($CheckExceptWordFile)
        $Form1.Controls.Add($CheckExceptExcelFile)
        $Form1.Controls.Add($CheckRestrictPDFFile)
        $Form1.Controls.Add($TxtBoxRestrictContents)
        $Form1.Controls.Add($ButtonAddRestrictStr)
        $Form1.Controls.Add($TxtBoxRestrictStr)
        $Form1.Controls.Add($CheckRestrictTaroFile)
        $Form1.Controls.Add($CheckRestrictWordFile)
        $Form1.Controls.Add($CheckRestrictExcelFile)
        $Form1.Controls.Add($TxtBoxCopyToFolder)
        $Form1.Controls.Add($TxtBoxCopyFromFolder)
        $Form1.Controls.Add($DatePickLastModified)
        $Form1.Name = "Form1"
        $Form1.Text = "フォルダごとファイルをコピー:　robocopy"
        $Form1.AcceptButton = $OKButton
        $Form1.CancelButton = $CancelButton
        $Form1.TopLevel=$True
        $Form1.ResumeLayout($False)
        $Form1.PerformLayout
#ドラッグアンドドロップ
        function DROPSHORTCUT([String]$dropFolder){if($dropFolder.Contains(".lnk")){return($wShell.CreateShortcut($dropFolder).TargetPath)
                                                                              }else{return $dropFolder}}
        $TxtBoxCopyFromFolder.Add_DragEnter({$_.effect="Copy"})
        $TxtBoxCopyFromFolder.Add_DragDrop({if($_.Data.GetData("FileDrop").Count -ne 1){[void][System.Windows.Forms.MessageBox]::Show("複数のフォルダは選択できません")
                                                                                  }else{$TxtBoxCopyFromFolder.Text = DROPSHORTCUT -dropFolder $_.Data.GetData("FileDrop")}})
        $TxtBoxCopyToFolder.Add_DragEnter({$_.effect="Copy"})
        $TxtBoxCopyToFolder.Add_DragDrop({if($_.Data.GetData("FileDrop").Count -ne 1){[void][System.Windows.Forms.MessageBox]::Show("複数のフォルダは選択できません")
                                                                                }else{$TxtBoxCopyToFolder.Text = DROPSHORTCUT -dropFolder $_.Data.GetData("FileDrop")}})     
#チェックボックス
        function CHECKFUNC($chkbox,$addStr,$contents){if($chkbox -eq $true){foreach($s in $addStr){$contents=$contents+ $s}
                                                                      }else{foreach($s in $addStr){$contents=$contents.replace($s ,"")}}
                                                      return($contents)}
        $CheckRestrictExcelFile.Add_CheckedChanged({$TxtBoxRestrictContents.Text=CHECKFUNC -chkbox $CheckRestrictExcelFile.Checked -addStr "`*.xlsx ","`*.xlsm ","`*.xls " -contents $TxtBoxRestrictContents.Text})
        $CheckRestrictWordFile.Add_CheckedChanged({$TxtBoxRestrictContents.Text=CHECKFUNC -chkbox $CheckRestrictWordFile.Checked -addStr "`*.docx ","`*.docm ","`*.doc " -contents $TxtBoxRestrictContents.Text})
        $CheckRestrictTaroFile.Add_CheckedChanged({$TxtBoxRestrictContents.Text=CHECKFUNC -chkbox $CheckRestrictTaroFile.Checked -addStr "`*.jtd " -contents $TxtBoxRestrictContents.Text})
        $CheckRestrictPDFFile.Add_CheckedChanged({$TxtBoxRestrictContents.Text=CHECKFUNC -chkbox $CheckRestrictPDFFile.Checked -addStr "`*.pdf " -contents $TxtBoxRestrictContents.Text})

        $CheckExceptExcelFile.Add_CheckedChanged({$TxtBoxExceptContents.Text=CHECKFUNC -chkbox $CheckExceptExcelFile.Checked -addStr "`*.xlsx ","`*.xlsm ","`*.xls " -contents $TxtBoxExceptContents.Text})
        $CheckExceptWordFile.Add_CheckedChanged({$TxtBoxExceptContents.Text=CHECKFUNC -chkbox $CheckExceptWordFile.Checked -addStr "`*.docx ","`*.docm ","`*.doc " -contents $TxtBoxExceptContents.Text})
        $CheckExceptTaroFile.Add_CheckedChanged({$TxtBoxExceptContents.Text=CHECKFUNC -chkbox $CheckExceptTaroFile.Checked -addStr "`*.jtd " -contents $TxtBoxExceptContents.Text})
        $CheckExceptPDFFile.Add_CheckedChanged({$TxtBoxExceptContents.Text=CHECKFUNC -chkbox $CheckExceptPDFFile.Checked -addStr "`*.pdf " -contents $TxtBoxExceptContents.Text})
#指定，除外ファイル名の追加ボタン
        $ButtonAddRestrictStr.Add_Click({if($TxtBoxRestrictStr.Text -ne ""){
                                                if($TxtBoxRestrictStr.Text.Contains(" ") -or $TxtBoxRestrictStr.Text.Contains("　")){
                                                                    [void][System.Windows.Forms.MessageBox]::Show("スペースを含む文字列は指定できません")
                                                                    }else{$TxtBoxRestrictContents.Text=$TxtBoxRestrictContents.Text + "*"+$TxtBoxRestrictStr.Text+"* "
                                                                          $TxtBoxRestrictStr.Text=""}
                                                                          }})                                                            
        $ButtonAddExceptStr.Add_Click({if($TxtBoxExceptStr.Text -ne ""){
                                                if($TxtBoxExceptStr.Text.Contains(" ") -or $TxtBoxExceptStr.Text.Contains("　")){
                                                                    [void][System.Windows.Forms.MessageBox]::Show("スペースを含む文字列は除外できません")
                                                                    }else{$TxtBoxExceptContents.Text= $TxtBoxExceptContents.Text + "*"+$TxtBoxExceptStr.Text+"* "
                                                                          $TxtBoxExceptStr.Text=""}
                                                                          }})        
#OKボタン
        $OKButton.Add_Click({
            #テキストボックス内のテキストを安定して文字列として扱うため変数に格納
            [String]$CopyFromFolder=$TxtBoxCopyFromFolder.Text
            [String]$CopyToFolder=$TxtBoxCopyToFolder.Text
            [String]$ToFolderName=$CopyToFolder.Substring($CopyToFolder.LastIndexOf("\")+1)
            [String]$FromFolderName=$CopyFromFolder.Substring($CopyFromFolder.LastIndexOf("\")+1)
            [String]$toFolderChildName=$FromFolderName +"_Robocopy"
            [String]$toFolderChildPath=$CopyToFolder +"`\"+ $toFolderChildName
            #共有フォルダ内でのsplit-pathやtest-pathの挙動が不安定なのでFileSystemObjectを使用
            $fso= New-Object -ComObject Scripting.FileSystemObject
#フォルダパスのチェック
        if([String]::IsNullOrEmpty($CopyToFolder) -or [String]::IsNullOrEmpty($CopyFromFolder)){
                                [void][System.Windows.Forms.MessageBox]::Show("コピー元フォルダ又はコピー先フォルダが入力されていません")
        }elseif($CopyToFolder.Contains(" ") -or $CopyToFolder.Contains("　") -or $CopyFromFolder.Contains(" ") -or $CopyFromFolder.Contains("　")){
                                [void][System.Windows.Forms.MessageBox]::Show("パスにスペースを含むフォルダは指定できません")
#再帰的なフォルダコピーの禁止
        }elseif($CopyToFolder.Contains($CopyFromFolder)){
                                [void][System.Windows.Forms.MessageBox]::Show(
                                "　コピー先フォルダがコピー元フォルダ内にあります。`n　入れ子構造で無限にコピーが繰り返されるので、コピー先フォルダを変更してください。")
#フォルダの存在チェック
        }elseif($fso.FolderExists($CopyFromFolder) -ne $true){[void][System.Windows.Forms.MessageBox]::Show("コピー元フォルダが見つかりません")
        }elseif($fso.FolderExists($CopyToFolder) -ne $true){[void][System.Windows.Forms.MessageBox]::Show("コピー先フォルダが見つかりません")
#すでにRobocopyフォルダがあるとき上書き確認
        }elseif($fso.FolderExists($toFolderChildPath) -eq $true){
                                $ansMsg=[System.Windows.Forms.MessageBox]::Show(
                                "上書き保存しますか？`n`nコピー元フォルダ「"+($FromFolderName) +"」をRobocopyしたフォルダが`nコピー先フォルダ「"+
                                $ToFolderName +"」に存在します`n
                                `n－－上書き保存について－－`n・コピー元が古い場合、そのファイルを除外(/xo)`n　(コピー先の新しい同名ファイルを残します)
                                `n・コピー先にのみ存在するファイルやフォルダを除外(/xx)`n　(コピー先にのみ存在するファイル・フォルダを残します)",
                                "フォルダの上書き保存","OKCancel","Question","button1")
                                if($ansMsg -eq "OK"){$Form1.DialogResult="OK"}
        }else{$Form1.DialogResult="OK"}
        })
#容量条件満たすまでここに戻ってくる
[Boolean]$incomplete=$True
[Boolean]$Form2Unload=$True
    while($incomplete){
#フォーム表示
        $result = $Form1.ShowDialog()
#フォームOK選択
            if ( $result -ne "OK" ){exit}        
        #テキストボックス内のテキストを安定して文字列として扱うため変数に格納
        [String]$CopyFromFolder=$TxtBoxCopyFromFolder.Text
        [String]$CopyToFolder=$TxtBoxCopyToFolder.Text
        [String]$RestrictContents=$TxtBoxRestrictContents.Text
        [String]$ExceptContents=$TxtBoxExceptContents.Text
        [String]$LastModifiedDate=$DatePickLastModified.Value.ToString("yyyyMMdd")
        [String]$ToFolderName=$CopyToFolder.Substring($CopyToFolder.LastIndexOf("\")+1)
        [String]$FromFolderName=$CopyFromFolder.Substring($CopyFromFolder.LastIndexOf("\")+1)
        [String]$toFolderChildName= $FromFolderName +"_Robocopy"
        [String]$toFolderChildPath= $CopyToFolder +"`\"+ $toFolderChildName
        #共有フォルダ内でのsplit-pathやtest-path,new-itemの挙動が不安定なのでFileSystemObjectを使用
        $fso= New-Object -ComObject Scripting.FileSystemObject           
        $doMessage=[System.Windows.Forms.MessageBox]::Show("コピー元フォルダ「"+ $FromFolderName +"」をコピーして`nコピー先フォルダ「"+
                                                          $ToFolderName+"」内に保存します。`n よろしいですか？`n`n[条件]`nファイルの最終更新日："+ $LastModifiedDate +
                                                          "`n指定文字列・拡張子："+$RestrictContents + "`n除外文字列・拡張子："+$ExceptContents,
                                                          "実行確認","OKCancel","Information","button1")
            if($doMessage -eq "OK"){
#ロボコピー(容量確認のためのテスト実行)
                # /R:1ファイルコピーに失敗したら１回再試行 /W:3再試行は３秒後 
                #/XJDフォルダ接合ポイント,特殊フォルダ除外（無限ループ防止） /XJFファイル接合ポイント除外  /L　テスト試行（サイズ確認用）
                $testRobocopy= Start-Process -FilePath "robocopy.exe" -ArgumentList "$CopyFromFolder $toFolderChildPath $RestrictContents /s /xo /xx /maxage:$LastModifiedDate /XJD /XJF /R:1 /W:3 /L /xf $ExceptContents" -WorkingDirectory $dtpPath -PassThru -WindowStyle Hidden -RedirectStandardOutput ".\robocopy.log" 
                [void]$wShell.popup("ドライブの容量確認をしています。しばらくお待ちください。",1,"実行確認中")
                $wShell > $null               
                Wait-Process -Name Robocopy -ErrorAction SilentlyContinue
                #本番実行のプログレスバー(本番実行のログの行数/テスト実行のログの行数)のためにログの行数を取得
                [String]$logPath=$dtpPath + "\" + "robocopy.log"
                $logContents=Get-Content -Path $logPath
                [int]$fullLog=$logContents.Length
                [String]$byteLine=$logContents| Select-Object -last 1 -Skip 3
                [String]$totalbyteStr=($byteLine.Substring($byteLine.IndexOf(":")+11,10)).trim()
                [String]$totalbyte=$totalbyteStr.replace(" ","")+"b"
                [Double]$totalbyteSize=1*$totalbyte
                $toDrive=$fso.GetDrive($fso.GetDriveName($toFolderChildPath))
                $toDriveSpace=$toDrive.AvailableSpace
                function BYTEROUND($space,$byte){$formatByte=[Math]::Round($space/$byte,2,[MidpointRounding]::AwayFromZero)
                                                 return([String]$formatByte)}
                        if($toDriveSpace -ge 1GB){[String]$toDriveSpaceRound=(BYTEROUND -space $toDriveSpace -byte 1GB) +"gb"
                   }elseif($toDriveSpace -ge 1MB){[String]$toDriveSpaceRound=(BYTEROUND -space $toDriveSpace -byte 1MB) +"mb"
                                            }else{[String]$toDriveSpaceRound=(BYTEROUND -space $toDriveSpace -byte 1KB) +"kb"}
                               if($totalbyteStr -eq "0"){
                                                         $EmptyMsg=[System.Windows.Forms.MessageBox]::Show(
                                                        "コピー予定ファイルの推定容量が0バイトです。コピーを実行しますか？`n詳細はデスクトップに保存されたログ「robocopy.log」を御確認ください。",
                                                        "実行確認","OKCancel","Question","button1")
                                                            if($EmptyMsg -eq "OK"){$incomplete=$false}
               }elseif($totalbyteSize -le $toDriveSpace){
                                                         [void][System.Windows.Forms.MessageBox]::Show("実行可能です`nコピーに必要な容量"+$totalbyte + "`n(ドライブの空き容量"+$toDriveSpaceRound +")")
                                                         $incomplete=$false
                                                   }else{
                                                         [void][System.Windows.Forms.MessageBox]::Show("容量が足りません。条件を変更してください`nコピーに必要な容量"+ $totalbyte +
                                                         "`n(ドライブの空き容量"+$toDriveSpaceRound +")`n`n詳細はデスクトップに保存されたログ「robocopy.log」を御確認ください。")
                                                        }
                                     }
                        }#ここまで容量条件満たすまで繰り返し
    $fso > $null
    $Form1.Close()
    $Form1.Dispose()
#ロボコピー(本番実行)                      
    $Robocopy= Start-Process -FilePath "robocopy.exe" -ArgumentList "$CopyFromFolder $toFolderChildPath $RestrictContents /s /xo /xx /maxage:$LastModifiedDate /XJD /XJF /R:1 /W:3 /xf $ExceptContents" -WorkingDirectory $dtpPath -PassThru -WindowStyle Hidden -RedirectStandardOutput ".\robocopy.log"
#プログレスバーのユーザーフォーム
    #Form2
    $Form2 = New-Object System.Windows.Forms.Form
    $Label13 = New-Object System.Windows.Forms.Label
    $ProgressBar = New-Object System.Windows.Forms.ProgressBar
    $font9=New-Object System.Drawing.Font("BIZ UDゴシック", 9.0,[System.Drawing.FontStyle]::Regular,[ System.Drawing.GraphicsUnit]::Point,128)
    #Label13
    $Label13.AutoSize = $True
    $Label13.Font = $font9
    $Label13.Location = New-Object System.Drawing.Point(10, 10)
    $Label13.Name = "Label13"
    $Label13.Size = New-Object System.Drawing.Size(80, 18)
    $Label13.Text = "しばらくお待ちください"
    # プログレスバー
    $ProgressBar.AutoSize=$True
    $ProgressBar.Location = "40,45"
    $ProgressBar.Size = "500,30"
    $ProgressBar.Maximum = "100"
    $ProgressBar.Minimum = "0"
    $ProgressBar.Style = "Continuous"
    #Form2
    $Form2.AutoScaleDimensions = New-Object System.Drawing.SizeF(10.0, 18.0)
    $Form2.AutoScaleMode = [System.Windows.Forms.AutoScaleMode]::Font
    $Form2.ClientSize = New-Object System.Drawing.Size(600, 150)
    $Form2.SuspendLayout()
    $Form2.Controls.Add($ProgressBar)
    $Form2.Controls.Add($Label13)
    $Form2.Name = "Form2"
    $Form2.Text = "robocopy進行状況"
    $Form2.TopLevel=$True
    $Form2.ResumeLayout($False)
    $Form2.PerformLayout                           
    #本番実行のログは最終的にテスト実行のログより4行多くなる(末尾の改行2,バイト/秒の行1,MB/分の行１)が誤差の範囲なので
    #本番実行のログの行数(実行%除く)/テスト実行のログの行数でバー作成
    [int]$stay=0
    [int]$ProgressBar.Value=0
    $Form2.Add_Shown({while($ProgressBar.Value -lt 100){if([String]::IsNullOrEmpty($logPath)-ne $true){
                                        $pNow=(Select-String -Path $logPath -NotMatch -Pattern "`%").Count/ $fullLog*100
                                            if($pNow -gt 100){$pNow=100}
                                                #1秒間行数が変化なしで,プロセスも存在しない(エラーになる)なら，プログレスバーを最大にしてループを抜ける
                                                if($ProgressBar.Value -eq $pNow){
                                                                    $stay+=1
                                                                    if($stay -ge 100){try{Get-Process -Name Robocopy -ErrorAction Stop
                                                                                }catch [Microsoft.PowerShell.Commands.ProcessCommandException]{
                                                                                        $ProgressBar.Value=100
                                                                                        $Form2.Close()
                                                                                        $Form2.Dispose()
                                                                                        break
                                                                                        }
                                                                                    }
                                                                }
                                        $ProgressBar.Value=$pNow
                                        Start-Sleep -Milliseconds 10
                                            if($ProgressBar.Value -ge 100){$Form2.Close()
                                                                           $Form2.Dispose()}
                                        }
                       }})
    $Form2.ShowDialoWait-Process -Name Robocopy -ErrorAction SilentlyContinue
[System.Windows.Forms.MessageBox]::Show("　フォルダのコピーが完了しました。`n　詳細はデスクトップに保存されたログ「robocopy.log」を御確認ください。")
```

　安定した動作と引き換えにスペースを含むフォルダを指定できないという欠陥を残しました。
　特にパスの指定やパスチェック、ドライブの空き容量確認で苦しんだ結果、filesystemobjectで実装するというだいぶ泥臭いコードになっています。


```powershell:
#Set-Locationにタイムラグがあるので最初にデスクトップに移動しておく
#(ログをカレントディレクトリに保存する準備)
$wShell= New-Object -ComObject WScript.Shell
[String]$dtpPath=$wShell.SpecialFolders("desktop")
Set-Location $dtpPath
```
　powershellは変数に入れたフォルダのパスをコマンドレットの引数に指定するとエラーが出やすいです。変数の中身がwrite-hostでは正しく見えても、いざ引数として渡すと、なぜかカレントディレクトリと２重のパスになったり、ｃドライブが見つけられなかったりエラーが生じます。
 いっそパスを変数に入れず、カレントディレクトリを移動して、カレントディレクトリとの相対パスでパスを指定すると、安定してフォルダを指定できます。共有フォルダを使わない限りですが。
　ただ、Set-Locationも直後に関係する処理を行うとエラーが出ることがあるので、ちょっと早く移動しておくと安全です。

```powershell:
#ドラッグアンドドロップ
        function DROPSHORTCUT([String]$dropFolder){if($dropFolder.Contains(".lnk")){return($wShell.CreateShortcut($dropFolder).TargetPath)
                                                                              }else{return $dropFolder}}
        $TxtBoxCopyFromFolder.Add_DragEnter({$_.effect="Copy"})
        $TxtBoxCopyFromFolder.Add_DragDrop({if($_.Data.GetData("FileDrop").Count -ne 1){[void][System.Windows.Forms.MessageBox]::Show("複数のフォルダは選択できません")
                                                                                  }else{$TxtBoxCopyFromFolder.Text = DROPSHORTCUT -dropFolder $_.Data.GetData("FileDrop")}})

```
　PowerShellでフォルダの指定をする上で、もう一つの障害がドライブの割り当てをしていない共有フォルダへのアクセスです。フォルダ選択ダイアログで、デフォルトでは共有フォルダが選択できません。
　不思議なことに、Shell.applicationを使用しても同じです。vbsでは共有フォルダ選択できるのに・・・。
　そこで、ダイアログはあきらめてテキストボックスへのドラッグ＆ドロップでフォルダ選択をする仕様です。ただ、ショートカットはそのままではショートカットの置き場所しか取得できないのでWScript.ShellのCreateShortcutで本当のパスを引いてきます。

```powershell:
            #テキストボックス内のテキストを安定して文字列として扱うため変数に格納
            [String]$CopyFromFolder=$TxtBoxCopyFromFolder.Text
            [String]$CopyToFolder=$TxtBoxCopyToFolder.Text
```
これが結構大事で、テキストボックスの中身$○○○○.Textをそのまま使いまわすとエラーの温床になります。文字列の型強制をした変数に入れてからパスの編集や存在チェックをした方がエラーが起きません。
          
```powershell:
[String]$ToFolderName=$CopyToFolder.Substring($CopyToFolder.LastIndexOf("\")+1)
            [String]$FromFolderName=$CopyFromFolder.Substring($CopyFromFolder.LastIndexOf("\")+1)
            [String]$toFolderChildName=$FromFolderName +"_Robocopy"
            [String]$toFolderChildPath=$CopyToFolder +"`\"+ $toFolderChildName

```
Robocopyの最も恐れるべきミスは、コピー先フォルダにデータ入りのフォルダを選択してデータを削除してしまうことだと思います。
子フォルダを用意してコピーするようにすると安全です。
          
```powershell:
            #共有フォルダ内でのsplit-pathやtest-pathの挙動が不安定なのでFileSystemObjectを使用
            $fso= New-Object -ComObject Scripting.FileSystemObject
#フォルダパスのチェック
        if([String]::IsNullOrEmpty($CopyToFolder) -or [String]::IsNullOrEmpty($CopyFromFolder)){
                                [void][System.Windows.Forms.MessageBox]::Show("コピー元フォルダ又はコピー先フォルダが入力されていません")
        }elseif($CopyToFolder.Contains(" ") -or $CopyToFolder.Contains("　") -or $CopyFromFolder.Contains(" ") -or $CopyFromFolder.Contains("　")){
                                [void][System.Windows.Forms.MessageBox]::Show("パスにスペースを含むフォルダは指定できません")
#再帰的なフォルダコピーの禁止
        }elseif($CopyToFolder.Contains($CopyFromFolder)){
                                [void][System.Windows.Forms.MessageBox]::Show(
                                "　コピー先フォルダがコピー元フォルダ内にあります。`n　入れ子構造で無限にコピーが繰り返されるので、コピー先フォルダを変更してください。")
#フォルダの存在チェック
        }elseif($fso.FolderExists($CopyFromFolder) -ne $true){[void][System.Windows.Forms.MessageBox]::Show("コピー元フォルダが見つかりません")
        }
```
共有フォルダ内でのtest-pathやnew-itemは挙動が不安定です。ファイルシステムオブジェクトを使用すれば、共有フォルダ内のフォルダ作成や存在チェックが難なくできるのでおすすめです。PowerShell使ってるのにWScript.ShellにFileSystemObjectですか・・という感は否めませんが。

```powershell
#ロボコピー(容量確認のためのテスト実行)
                # /R:1ファイルコピーに失敗したら１回再試行 /W:3再試行は３秒後 
                #/XJDフォルダ接合ポイント,特殊フォルダ除外（無限ループ防止） /XJFファイル接合ポイント除外  /L　テスト試行（サイズ確認用）
                $testRobocopy= Start-Process -FilePath "robocopy.exe" -ArgumentList "$CopyFromFolder $toFolderChildPath $RestrictContents /s /xo /xx /maxage:$LastModifiedDate /XJD /XJF /R:1 /W:3 /L /xf $ExceptContents" -WorkingDirectory $dtpPath -PassThru -WindowStyle Hidden -RedirectStandardOutput ".\robocopy.log" 
```
Start-Processの-ArgumentListは、全てを""ダブルクォーテーションで囲むとエラーが出にくいです。
しかし、それぞれのパラメータを""ダブルクォーテーションで囲めなくなるので、囲む必要のあるスペースなどをパスに含めることができなくなってしまいました。


```powershell:
[Double]$totalbyteSize=1*$totalbyte
```
PowerShellでstringを数値に変換したいときは、型強制や型変換ではうまくいかないことが多いですが、「１＊○○」と１を掛けるとうまく変換できます。
このとき、１は左側に来るようにする必要があるようです。

```powershell:
$toDrive=$fso.GetDrive($fso.GetDriveName($toFolderChildPath)
$toDriveSpace=$toDrive.AvailableSpace
```
ドライブの取得、容量確認もおすすめはGet-PSDriveではなくFileSystemObjectです。
Get-PSDriveは変数に入ったパスを分解したドライブを捕まえるのが一筋縄にいきません。ｃドライブを見つけられないときもあります。単に僕の技術不足なのですが・・。
FileSystemObjectなら、ローカルドライブを割り当てていないネットワークドライブのチェックも楽々です。

```powershell:
$fso > $null
    $Form1.Close()
    $Form1.Dispose()
```
FileSystemObjectはともかく、フォームを解放するとRobocopyの実行速度が速くなる実感があります。測ってないですが。

```powershell:
#本番実行のログは最終的にテスト実行のログより4行多くなる(末尾の改行2,バイト/秒の行1,MB/分の行１)が誤差の範囲なので
    #本番実行のログの行数(実行%除く)/テスト実行のログの行数でバー作成
    [int]$stay=0
    [int]$ProgressBar.Value=0
    $Form2.Add_Shown({while($ProgressBar.Value -lt 100){if([String]::IsNullOrEmpty($logPath)-ne $true){
                                        $pNow=(Select-String -Path $logPath -NotMatch -Pattern "`%").Count/ $fullLog*100
                                            if($pNow -gt 100){$pNow=100}
                                                #1秒間行数が変化なしで,プロセスも存在しない(エラーになる)なら，プログレスバーを最大にしてループを抜ける
                                                if($ProgressBar.Value -eq $pNow){
                                                                    $stay+=1
                                                                    if($stay -ge 100){try{Get-Process -Name Robocopy -ErrorAction Stop
                                                                                }catch [Microsoft.PowerShell.Commands.ProcessCommandException]{
                                                                                        $ProgressBar.Value=100
                                                                                        $Form2.Close()
                                                                                        $Form2.Dispose()
                                                                                        break
                                                                                        }
                                                                                    }
                                                                }
                                        $ProgressBar.Value=$pNow
                                        Start-Sleep -Milliseconds 10
                                            if($ProgressBar.Value -ge 100){$Form2.Close()
                                                                           $Form2.Dispose()}
                                        }
                       }})
```
　プログレスバーをどう起動するかという問題がありますが、Form.Shownイベントを使用すればフォームの表示とともに自動でプログレスバーを動かすことができます。

　本番実行のログは、実行%を除くと最終的にテスト実行のログより4行多くなります(末尾の改行2,バイト/秒の行1,MB/分の行１)が、誤差の範囲なので本番実行のログの行数/テスト実行のログの行数でバーを作成しています。
　プログレスバーの数値の上限を１００に設定しているので、１００を超えるとエラーになります。１００を超えたら、バーに入れる前に１００に変更します。

　バーの実行中にボタンをクリックしてバーを閉じることは、並列処理が必要になり、難しいです。僕はあきらめてしまいましたが、バーが途中で止まった場合を想定して、Get-ProcessでRobocopyが既に見つからなければフォームを閉じるようにしています。
　再試行に３秒を設定しているのに１秒でチェックに入るの？とも思えますが、パスの行数チェックやプログレスバーの再描画で結構時間を食うので、割と間が確保されます。sleepはなくてもいいくらいです。

　思いのほか長くなってしまいましたが、以上です。技術がなく、あきらめた点も多いです。解決策をご存じの方は、御教授いただけるとありがたいです。