我目前正在开发一个个人项目,其目标是自动将歌曲文件从我的谷歌驱动器下载到游戏《克隆英雄》的本地文件夹。最终目标是拥有一个具有用户友好 GUI 的程序,这样我就可以与我的朋友分享它,因为如果我们想玩在线合作游戏,我们都需要相同的歌曲文件。
我使用 .bat 文件制作了一些以前的版本,该文件使用带有 /MIR 标志的 robocopy 函数将我的驱动器复制到本地“歌曲”文件夹。这是通过与每个人共享谷歌驱动器并让他们使用“桌面版谷歌驱动器”应用程序将共享驱动器安装在计算机上来实现的,这使得robocopy可以看到谷歌驱动器内容。
但是,正如您可以想象的那样,它不是很用户友好,并且需要每个用户编辑脚本以定位他们的歌曲目录和安装的谷歌驱动器快捷方式
现在,我通过制作一个 power shell 脚本(附在下面)来运行整个程序,这使得我可以使用 Windows 窗体作为基本 GUI,从而对其进行了改进。这是我第一次使用 power shell,我对它还很陌生,但我最终确实让它工作,唯一的主要缺点是在 robocopy 运行时表单冻结,并且你无法再实时看到输出。
但是,我想与下面列出的其他一些问题一起解决这个问题,所以我来这里寻求一些方向,并(如果允许)接受对代码的任何批评,以便我可以改进它。
我想要做出的主要改进:
在单独的线程上运行 robocopy,这样它就不会挂起表单
将 robocopy 的输出实时传输到“控制台日志文本框”
小改进(如果允许):
在 robocopy 写入数据的位置添加保护,因为如果用户选择包含重要数据的文件夹,它可能会因为 /MIR 标志而被覆盖
调整表单大小
将文件路径存储在一个 .txt 文件中,而不是多个(或其他更好的方式)
将所有 GUI 创建代码放入单个函数中以提高可读性
为robocopy添加进度条
代码:
#Functions ----------------------------------------------------
#Function to add a line to the console text box
function Add-Console_Log_Line {
Param ($Message)
$console_log_text_box.AppendText("$Message`r`n")
$console_log_text_box.Refresh()
$console_log_text_box.ScrollToCaret()
}
#Function to open the folder browser
Function Select-FolderDialog($Description="Select Folder", $RootFolder="MyComputer"){
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null
$objForm = New-Object System.Windows.Forms.OpenFileDialog
$objForm.DereferenceLinks = $true
$objForm.CheckPathExists = $true
$objForm.FileName = "[Select this folder]"
$objForm.Filter = "Folders|`n"
$objForm.AddExtension = $false
$objForm.ValidateNames = $false
$objForm.CheckFileExists = $false
$Show = $objForm.ShowDialog()
If ($Show -eq "OK")
{
Return $objForm.FileName
}
Else
{
Write-Error "Operation cancelled by user."
}
}
#Main Code---------------------------------
Add-Type -assembly System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
#creates GUI Box
$Form = New-Object System.Windows.Forms.Form
$Form.Text ='Clone Hero Song Downloader'
$Form.Width = 600
$Form.Height = 400
$Form.AutoSize = $true
$Form.FormBorderStyle = 'Fixed3D'
#creates songs source label
$source_Label = New-Object System.Windows.Forms.Label
$source_Label.Text = "Songs Source:"
$source_Label.Location = New-Object System.Drawing.Point(10,15)
$source_Label.AutoSize = $true
$Form.Controls.Add($source_Label)
#creates song source file path text box
$source_TextBox = New-Object System.Windows.Forms.TextBox
$source_TextBox.Location = New-Object System.Drawing.Point(110,10)
$source_TextBox.Size = New-Object System.Drawing.Size(350,20)
$source_TextBox.ReadOnly = $True
$Form.Controls.Add($source_TextBox)
#creates songs source file path browse button
$source_Button = New-Object System.Windows.Forms.Button
$source_Button.Location = New-Object System.Drawing.Size(470,10)
$source_Button.Size = New-Object System.Drawing.Size(100,20)
$source_Button.Text = "Browse"
$Form.Controls.Add($source_Button)
#creates the songs destination label
$destination_Label = New-Object System.Windows.Forms.Label
$destination_Label.Text = "Songs Destination:"
$destination_Label.Location = New-Object System.Drawing.Point(10,55)
$destination_Label.AutoSize = $true
$Form.Controls.Add($destination_Label)
#creates the songs destination file path textbox
$destination_TextBox = New-Object System.Windows.Forms.TextBox
$destination_TextBox.Location = New-Object System.Drawing.Point(110,50)
$destination_TextBox.Size = New-Object System.Drawing.Size(350,20)
$destination_TextBox.ReadOnly = $True
$Form.Controls.Add($destination_TextBox)
#creates the songs destination file path browse button
$destination_Button = New-Object System.Windows.Forms.Button
$destination_Button.Location = New-Object System.Drawing.Size(470,50)
$destination_Button.Size = New-Object System.Drawing.Size(100,20)
$destination_Button.Text = "Browse"
$Form.Controls.Add($destination_Button)
#creates the clone hero path label
$clone_hero_path_label = New-Object System.Windows.Forms.Label
$clone_hero_path_label.Text = "Clone Hero Path:"
$clone_hero_path_label.Location = New-Object System.Drawing.Point(10,95)
$clone_hero_path_label.AutoSize = $true
$Form.Controls.Add($clone_hero_path_label)
#creates the songs destination file path textbox
$clone_hero_path_TextBox = New-Object System.Windows.Forms.TextBox
$clone_hero_path_TextBox.Location = New-Object System.Drawing.Point(110,90)
$clone_hero_path_TextBox.Size = New-Object System.Drawing.Size(350,20)
$clone_hero_path_TextBox.ReadOnly = $True
$Form.Controls.Add($clone_hero_path_TextBox)
#creates the songs destination file path browse button
$clone_hero_path_Button = New-Object System.Windows.Forms.Button
$clone_hero_path_Button.Location = New-Object System.Drawing.Size(470,90)
$clone_hero_path_Button.Size = New-Object System.Drawing.Size(100,20)
$clone_hero_path_Button.Text = "Browse"
$Form.Controls.Add($clone_hero_path_Button)
#creates the Guitar Sniffer Path label
$guitar_sniffer_path_label = New-Object System.Windows.Forms.Label
$guitar_sniffer_path_label.Text = "Guitar Sniffer Path:"
$guitar_sniffer_path_label.Location = New-Object System.Drawing.Point(10,135)
$guitar_sniffer_path_label.AutoSize = $true
$Form.Controls.Add($guitar_sniffer_path_label)
#creates the guitar sniffer file path textbox
$guitar_sniffer_path_TextBox = New-Object System.Windows.Forms.TextBox
$guitar_sniffer_path_TextBox.Location = New-Object System.Drawing.Point(110,130)
$guitar_sniffer_path_TextBox.Size = New-Object System.Drawing.Size(350,20)
$guitar_sniffer_path_TextBox.ReadOnly = $True
$Form.Controls.Add($guitar_sniffer_path_TextBox)
#creates the guitar sniffer file path browse button
$guitar_sniffer_path_Button = New-Object System.Windows.Forms.Button
$guitar_sniffer_path_Button.Location = New-Object System.Drawing.Size(470,130)
$guitar_sniffer_path_Button.Size = New-Object System.Drawing.Size(100,20)
$guitar_sniffer_path_Button.Text = "Browse"
$Form.Controls.Add($guitar_sniffer_path_Button)
#creates the download new songs checkbox
$download_new_songs_checkbox = New-Object System.Windows.Forms.Checkbox
$download_new_songs_checkbox.Location = New-Object System.Drawing.Size(10,160)
$download_new_songs_checkbox.Size = New-Object System.Drawing.Size(200,30)
$download_new_songs_checkbox.Text = "Download New Songs"
$download_new_songs_checkbox.TabIndex = 4
$download_new_songs_checkbox.Checked = $true
$Form.Controls.Add($download_new_songs_checkbox)
#creates the launch guitar sniffer checkbox
$launch_guitar_sniffer_checkbox = New-Object System.Windows.Forms.Checkbox
$launch_guitar_sniffer_checkbox.Location = New-Object System.Drawing.Size(10,190)
$launch_guitar_sniffer_checkbox.Size = New-Object System.Drawing.Size(200,30)
$launch_guitar_sniffer_checkbox.Text = "Launch Guitar Sniffer"
$launch_guitar_sniffer_checkbox.TabIndex = 4
$launch_guitar_sniffer_checkbox.Checked = $true
$Form.Controls.Add($launch_guitar_sniffer_checkbox)
#creates console log label
$console_log_label = New-Object System.Windows.Forms.Label
$console_log_label.Text = "Console log:"
$console_log_label.Location = New-Object System.Drawing.Point(220,187)
$console_log_label.AutoSize = $true
$Form.Controls.Add($console_log_label)
#creates console log text box
$console_log_text_box = New-Object System.Windows.Forms.TextBox
$console_log_text_box.Location = New-Object System.Drawing.Size(220,205)
$console_log_text_box.Size = New-Object System.Drawing.Size(350,220)
$console_log_text_box.MultiLine = $True
$console_log_text_box.ScrollBars = "Vertical"
$console_log_text_box.ReadOnly = $True
$Form.Controls.Add($console_log_text_box)
#creates LBN logo
$file = (get-item '.\LBN.png')
$img = [System.Drawing.Image]::Fromfile($file);
$pictureBox = new-object Windows.Forms.PictureBox
$pictureBox.Location = New-Object System.Drawing.Size(35,225)
$pictureBox.Size = New-Object System.Drawing.Size(150,150)
$pictureBox.Image = $img
$Form.controls.add($pictureBox)
#creates launch clone hero button
$launch_clone_hero_button = New-Object System.Windows.Forms.Button
$launch_clone_hero_button.Location = New-Object System.Drawing.Size(10,375)
$launch_clone_hero_button.Size = New-Object System.Drawing.Size(200,50)
$launch_clone_hero_button.Text = "Launch Clone Hero"
$Form.Controls.Add($launch_clone_hero_button)
#Pull in file paths
$source_folder = Get-Content .\Song_Source_Path.txt
$source_TextBox.Text = $source_folder
$destination_folder = Get-Content .\Song_Destination_Path.txt
$destination_TextBox.Text = $destination_folder
$clone_hero_path = Get-Content .\Clone_Hero_Path.txt
$clone_hero_path_TextBox.Text = $clone_hero_path
$guitar_sniffer_path = Get-Content .\Guitar_snifffer_Path.txt
$guitar_sniffer_path_TextBox.Text = $guitar_sniffer_path
#runs the file browser script for the source when the button is pressed
$source_Button.Add_Click(
{
$source_folder = Select-FolderDialog
$source_folder = $source_folder -replace ".{21}$" #remove last 21 characters of this string (gets rid of "\[Select this folder]" )
$source_TextBox.Text = $source_folder
Out-File -FilePath .\Song_Source_Path.txt -InputObject $source_folder
}
)
#runs the file browser script for the destination when the button is pressed
$destination_Button.Add_Click(
{
$destination_folder = Select-FolderDialog
$destination_folder = $destination_folder -replace ".{21}$" #remove last 21 characters of this string (gets rid of "\[Select this folder]" )
$destination_TextBox.Text = $destination_folder
Out-File -FilePath .\Song_Destination_Path.txt -InputObject $destination_folder
}
)
#runs the file browser script for the clone hero path when the button is pressed
$clone_hero_path_Button.Add_Click(
{
$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog
[void]$FileBrowser.ShowDialog()
$clone_hero_path = $FileBrowser.FileName;
If($FileBrowser.FileNames -like "*\*") {
$FileBrowser.FileName #Lists selected files (optional)
}
else {
Write-Host "Cancelled by user"
}
if (-Not ([string]::IsNullOrEmpty($clone_hero_path))) {
$clone_hero_path_TextBox.Text = $clone_hero_path
Out-File -FilePath .\Clone_Hero_Path.txt -InputObject $clone_hero_path
}
}
)
#runs the file browser script for guitar sniffer when the button is pressed
$guitar_sniffer_path_Button.Add_Click(
{
$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog
[void]$FileBrowser.ShowDialog()
$guitar_sniffer_path = $FileBrowser.FileName;
If($FileBrowser.FileNames -like "*\*") {
$FileBrowser.FileName #Lists selected files (optional)
}
else {
Write-Host "Cancelled by user"
}
$guitar_sniffer_path_TextBox.Text = $guitar_sniffer_path
Out-File -FilePath .\Guitar_snifffer_Path.txt -InputObject $guitar_sniffer_path
}
)
#Runs the download & launch when the launch button is pressed
$launch_clone_hero_button.Add_Click(
{
If ($download_new_songs_checkbox.Checked)
{
Add-Console_Log_Line -Message "Downloading new songs..."
& robocopy.exe $source_folder $destination_folder /MIR
Add-Console_Log_Line -Message "Download Finished"
}
If ($launch_guitar_sniffer_checkbox.Checked)
{
Add-Console_Log_Line -Message "Launching Guitar Sniffer..."
Start-Process -FilePath "$guitar_sniffer_path"
}
Add-Console_Log_Line -Message "Launching Clone Hero..."
Start-Sleep -Seconds 1
Start-Process -FilePath "$clone_hero_path"
Start-Sleep -Seconds 1
$form.close()
}
)
$Form.ShowDialog()
我尝试搜索有关在单独的线程中运行 robocopy 并实时捕获输出的指南或旧帖子,但我发现的所有示例要么不适用于 robocopy,要么在同一线程中运行它,这会冻结表单它正在运行,并且仅在运行完成后将 robocopy 的输出放入文本框中。
Ian Kemp 提到使用 Visual Studio 创建一个独立的 EXE,链接 Here 可能就是他正在谈论的内容。还要考虑 mklemnt0 的 Start-ThreadJob 解决方案。看看这能让你走多远。至于保护,我建议不要完全使用 /MIR 标志。我只会做 /COPY:DATSOU 或类似的事情。当你处理像这样一般的事情时,尤其是处理你朋友的数据时,/MIR 太危险了。