2013-04-17 44 views
0

我一直在使用Powershell一段时间,它不是理想的编程环境,但却被我的程序困住了。跑马灯进度条在Powershell中冻结

我的程序是一个带有选取框进度条和搜索作业的GUI。

我的程序执行的操作:使用Powershell运行脚本后,如果模式为MTA,它将在STA模式下重新启动Powershell。之后,它会要求提供一个文件夹位置。输入文件夹位置后,它将开始搜索作业,并将搜索文件的位置。每个文件都将被存储到一个数组中。该数组将打印到将保存在桌面上的tempfile.txt中。同时,该作业正在搜索GUI将使用滚动进度条显示表单的文件。

我的程序需要做什么:在作业完成搜索和存储文件后,它必须关闭表单。

我已经尝试过使用$ formSearchingFiles.Close()命令,但我注意到乔布斯无法关闭他们的“父”线程,所以这项工作将无法关闭窗体。

我也尝试使用Wait-Job cmdlet解决问题,但然后“选框进度”栏会冻结,否则表单根本不会显示。

我已经看过很多互联网的解决方案,但我找不到适合这个问题的。我正在考虑多处理,但我不知道这是否可能在PowerShell 2.0中(我限制在2.0或更低)。

我也不知道Search-Job是否可以通知主线程完成任务,以便主线程可以继续执行程序,而不冻结进度条。

我希望我已经解释了有关程序和我的问题。

# Get the path of the script 
$scriptPath = ((Split-Path $script:MyInvocation.MyCommand.Path) + "\") 
$scriptName = $MyInvocation.MyCommand.Name 
$script = $scriptPath + $scriptName 

# Check if powershell is running in STA(Single Threaded Apartment) or MTA(Multi Threaded Apartment) mode. 
# If it is running in MTA mode then restart Powershell in STA mode. 
if ([threading.thread]::CurrentThread.GetApartmentState() -eq "MTA") 
{ 
    Write-Host Restarting Powershell in STA mode 
    & $env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe -sta "& {&'$script'}" 
} 
else 
{ 
    $folderPath = $currentFolderLocation.Text 
    $tempFile = $currentStagingLocation.Text 
    $tempFile += "\fileArray.txt" 

    function OnApplicationLoad {  
     return $true #return true for success or false for failure 
    } 

    function OnApplicationExit { 
     $script:ExitCode = 0 #Set the exit code for the Packager 
    } 

    function Call-Searching_pff { 
     [void][reflection.assembly]::Load("mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") 
     [void][reflection.assembly]::Load("System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") 
     [void][reflection.assembly]::Load("System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a") 
     [void][reflection.assembly]::Load("System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") 
     [void][reflection.assembly]::Load("System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") 
     [void][reflection.assembly]::Load("System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") 
     [void][reflection.assembly]::Load("System.DirectoryServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a") 
     [void][reflection.assembly]::Load("System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") 
     [void][reflection.assembly]::Load("System.ServiceProcess, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a") 

     [System.Windows.Forms.Application]::EnableVisualStyles() 
     $formSearchingFiles = New-Object 'System.Windows.Forms.Form' 
     $label = New-Object 'System.Windows.Forms.Label' 
     $progressbar = New-Object 'System.Windows.Forms.ProgressBar' 
     $InitialFormWindowState = New-Object 'System.Windows.Forms.FormWindowState' 

     $FormEvent_Load={ 

     $folderPath = &read-host "Enter path" 
     $tempFile = (([Environment]::GetFolderPath("Desktop")) + "\tempfile.txt") 

      $SearchJob = Start-Job -scriptblock { 
       param ($folderPath, $tempFile) 
       $fileArray = @() 
       # Get all files and folders under the specified path 
       $items = Get-ChildItem -Path $folderPath -Recurse 
       foreach ($item in $items) 
       { 
        # Check if the item is a file or a folder 
        if (!($item.PSIsContainer)) 
        { 
         # Extract path of file with path of entered folder 
         $extractedPath = $item.FullName 
         $extractedPath = $extractedPath.Replace($folderPath, "") 
         $fileArray += $extractedPath 
        } 
       } 
       # Save array in temporary file 
       $fileArray | out-file $tempFile 
       $formSearchingFiles.Close() #Does not work inside job :(

      } -ArgumentList @($folderPath, $tempFile)  
     } 

     $Form_StateCorrection_Load= 
     { 
      #Correct the initial state of the form to prevent the .Net maximized form issue 
      $formSearchingFiles.WindowState = $InitialFormWindowState 
     } 

     $Form_Cleanup_FormClosed= 
     { 
      #Remove all event handlers from the controls 
      try 
      { 
       $formSearchingFiles.remove_Load($FormEvent_Load) 
       $formSearchingFiles.remove_Load($Form_StateCorrection_Load) 
       $formSearchingFiles.remove_FormClosed($Form_Cleanup_FormClosed) 
      } 
      catch [Exception]{ } 
     } 

     # formSearchingFiles 
     $formSearchingFiles.Controls.Add($label) 
     $formSearchingFiles.Controls.Add($progressbar) 
     $formSearchingFiles.ClientSize = '394, 122' 
     $formSearchingFiles.FormBorderStyle = 'FixedDialog' 
     $formSearchingFiles.MaximizeBox = $False 
     $formSearchingFiles.Name = "formSearchingFiles" 
     $formSearchingFiles.StartPosition = 'CenterScreen' 
     $formSearchingFiles.Text = "Compatibility Checker" 
     $formSearchingFiles.add_Load($FormEvent_Load) 

     # label 
     $label.Location = '12, 27' 
     $label.Name = "label" 
     $label.Size = '368, 26' 
     $label.TabIndex = 1 
     $label.Text = "Searching for files, please wait.." 
     $label.TextAlign = 'MiddleCenter' 

     # progressbar 
     $progressbar.Location = '12, 68' 
     $progressbar.MarqueeAnimationSpeed = 40 
     $progressbar.Name = "progressbar" 
     $progressbar.Size = '370, 30' 
     $progressbar.Style = 'Marquee' 
     $progressbar.TabIndex = 0 

     #Save the initial state of the form 
     $InitialFormWindowState = $formSearchingFiles.WindowState 
     #Init the OnLoad event to correct the initial state of the form 
     $formSearchingFiles.add_Load($Form_StateCorrection_Load) 
     #Clean up the control events 
     $formSearchingFiles.add_FormClosed($Form_Cleanup_FormClosed) 
     #Show the Form 
     return $formSearchingFiles.ShowDialog() 
    } #End Function 

    #Call OnApplicationLoad to initialize 
    if((OnApplicationLoad) -eq $true) 
    { 
     #Call the form 
     Call-Searching_pff | Out-Null 
     #Perform cleanup 
     OnApplicationExit 
    } 
} 
+0

为什么要使用一个WinForms进度条什么特别的原因?你见过write-progress cmdlet吗?它可以在控制台中为你呈现一个进度条,而不必担心所有这些STA垃圾。 – x0n

+0

这个程序只是我的完整程序的一部分,我使用Windows窗体来制作一个漂亮的GUI,它比控制台中的文本更加用户友好。我刚刚发布了这篇文章,因为只有在这篇文章中我遇到了一个问题。 – LittleOne

+0

我可能有解决方案。通过使用同步哈希表作为2个线程之间的“通信链接”。还没有测试过。 – LittleOne

回答

2

我找到了解决我自己问题的方案。解决方案:同步哈希表作为线程之间的“通信链接”

创建哈希表后,您可以向其中添加变量和对象。所有线程(您允许访问散列)都可以读取/写入这些变量和对象。

创建同步。哈希表:

$syncHash = [hashtable]::Synchronized(@{}) 
#Where $syncHash is the name of your hash table 

添加变量和对象哈希表:

$syncHash.ProgressBar = $progressBar 
#Create new variable ProgressBar in hash table and assign $progressBar to it 

创建新的线程,并允许使用的哈希表:

$processRunspace =[runspacefactory]::CreateRunspace() 
$processRunspace.ApartmentState = "STA" 
$processRunspace.ThreadOptions = "ReuseThread"   
$processRunspace.Open() 
$processRunspace.SessionStateProxy.SetVariable("syncHash",$syncHash) 

$psCmd = [PowerShell]::Create().AddScript({ 
    #Your Thread Code Here 
}) 
$psCmd.Runspace = $processRunspace 
$data = $psCmd.BeginInvoke() 

从新线程更改$ progressBar的值:

$syncHash.ProgressBar.Value = 1 

感谢:http://learn-powershell.net/2012/10/14/powershell-and-wpf-writing-data-to-a-ui-from-a-different-runspace/