0

私はしばらく Powershell を使用してきましたが、これは理想的なプログラミング環境ではありませんが、自分のプログラムで行き詰まってしまいました。

私のプログラムは、マーキー プログレスバーと検索ジョブを備えた GUI です。

プログラムの動作: Powershell でスクリプトを実行すると、モードが MTA の場合、Powershell が STA モードで再起動されます。その後、フォルダの場所を尋ねられます。フォルダの場所を入力すると、検索ジョブが開始され、ファイルの場所が検索されます。すべてのファイルは配列に格納されます。その配列は、デスクトップに保存される te​​mpfile.txt に出力されます。ジョブがファイルを検索している間、GUI は Marquee Progress バーを含むフォームを表示します。

プログラムで必要なこと: ファイルの検索と保存が完了したら、フォームを閉じる必要があります。

$formSearchingFiles.Close() コマンドで試してみましたが、ジョブが「親」スレッドを閉じることができないことに気付きました。そのため、このジョブはフォームを閉じることができません。

また、Wait-Job コマンドレットを使用して解決しようとしましたが、マーキー プログレス バーがフリーズするか、フォームがまったく表示されません。

私は解決策を求めてインターネットをよく見ましたが、この問題に合ったものを見つけることができません。マルチプロセッシングを考えていたのですが、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
    }
}
4

1 に答える 1

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/

于 2013-05-03T21:26:22.857 に答える