2013-04-17 83 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/