winforms 使用System.Net.WebClient上传文件时,如果我在powershell脚本中使用System.Windows.Forms,则WebClient停止工作

disbfnqx  于 2023-11-21  发布在  .NET
关注(0)|答案(1)|浏览(129)

我想下载文件从互联网上的形式和显示下载进度在ProgressBar.要做到这一点,我订阅加载事件,并做异步加载.一切都在工作.这里是简化的代码(删除所有不必要的):

Add-Type -assembly System.Windows.Forms

$isDownloaded = $False
$webMain = New-Object System.Net.WebClient

Register-ObjectEvent -InputObject $webMain -EventName 'DownloadFileCompleted' -SourceIdentifier WebMainDownloadFileCompleted -Action {    
    $Global:isDownloaded = $True
}

Register-ObjectEvent -InputObject $webMain -EventName 'DownloadProgressChanged' -SourceIdentifier WebMainDownloadProgressChanged -Action {
    $Global:Data = $event
}

function DownloadFile($Link, $Path, $Name) {

    write-host "begin"

    $Global:webMain.DownloadFileAsync($Link, $Path)
    
    While (!$isDownloaded) {
        $percent = $Global:Data.SourceArgs.ProgressPercentage
        If ($percent -ne $null) {
            write-host $percent
        }
        Wait-Event -Timeout 1
    }

    write-host "end"
}

DownloadFile 'https://www.7-zip.org/a/7z2301-x64.exe' 'D:\7Zip.exe' '7Zip'

字符串
一切正常。现在我把它添加到代码的任何地方
第一个月

$Button1 = New-Object System.Windows.Forms.Button
并且脚本不工作。DownloadProgressChanged和DownloadFileCompleted事件根本不会发生。
问:为什么只是创建一个窗体或按钮会干扰脚本?
如果没有System.Windows.Forms.Form,代码可以正常工作,但我最终需要创建一个Form并在其上呈现加载。
DownloadFileAsync工作-文件已下载,但使用任何New-Object System.Windows.Forms.* 时,Register-ObjectEvent中的事件本身不会发生(但没有它们也能正常工作)。

h7appiyu

h7appiyu1#

正如在评论中所述,PowerShell不是一个很好的编程语言,问题是.ShowDialog()阻止线程,不允许您的事件正常执行。解决方案是在一个单独的运行空间中注册事件,下面是如何完成的最小示例(尽可能少)。我添加了一些指针注解来帮助您理解逻辑,尽管代码显然并不容易,如前所述,PowerShell不是为此设计的语言,C#会给你一个更容易的时间给予。

Demo:


的数据

产品代码:

Add-Type -Assembly System.Windows.Forms

[System.Windows.Forms.Application]::EnableVisualStyles()

$form = [System.Windows.Forms.Form]@{
    Size            = '500, 150'
    FormBorderStyle = 'Fixed3d'
}
$btn = [System.Windows.Forms.Button]@{
    Name     = 'MyButton'
    Text     = 'Click Me!'
    Size     = '90, 30'
    Location = '370, 70'
    Anchor   = 'Bottom, Right'
}
$btn.Add_Click({
    # disable the button here to allow a single download at a time
    $this.Enabled = $false
    # hardcoded link here for demo
    $downloader.DownloadFileAsync('https://www.7-zip.org/a/7z2301-x64.exe', "$pwd\7Zip.exe")
})
$progress = [System.Windows.Forms.ProgressBar]@{
    Name     = 'MyProgressBar'
    Size     = '460, 40'
    Location = '10, 10'
}
$form.Controls.AddRange(($btn, $progress))

# create a WebClient instance
$downloader = [System.Net.WebClient]::new()
# create a new runspace where the Download Events will execute
# this new runspace will have the PSHost hooked so that things like
# `Write-Host`, `Out-Host`, `Write-Warning`, etc. goes straight to the console
# its easier for troubleshooting but not mandatory
$rs = [runspacefactory]::CreateRunspace($Host)
$rs.Open()
# add the `$form` instance to the runspace scope
$rs.SessionStateProxy.PSVariable.Set([psvariable]::new('form', $form))

# the code that will initiate the events in the new runspace
$ps = [powershell]::Create().AddScript({
    $registerObjectEventSplat = @{
        InputObject      = $args[0] # $args[0] = $downloader
        EventName        = 'DownloadProgressChanged'
        SourceIdentifier = 'WebMainDownloadProgressChanged'
        Action           = {
            # lock the form before doing anything, otherwise there could be a race condition with
            # `DownloadFileCompleted` event
            [System.Threading.Monitor]::Enter($form)
            $progress = $form.Controls.Find('MyProgressBar', $false)[0]
            # for demo, in addition to increment the progress bar,
            # show the percentage to the console
            $eventArgs.ProgressPercentage | Out-Host
            # increment the progress bar
            $progress.Value = $eventArgs.ProgressPercentage
            # release the form
            [System.Threading.Monitor]::Exit($form)
        }
    }
    Register-ObjectEvent @registerObjectEventSplat
    $registerObjectEventSplat['EventName'] = 'DownloadFileCompleted'
    $registerObjectEventSplat['SourceIdentifier'] = 'WebMainDownloadFileCompleted'
    $registerObjectEventSplat['Action'] = {
        [System.Threading.Monitor]::Enter($form)
        # when the download is completed, enable the button
        $form.Controls.Find('MyButton', $false)[0].Enabled = $true
        # and show this to the console
        Write-Host 'Download Completed!'
        [System.Threading.Monitor]::Exit($form)
    }
    Register-ObjectEvent @registerObjectEventSplat
}).AddArgument($downloader)
$ps.Runspace = $rs
$task = $ps.BeginInvoke()
$form.ShowDialog()

字符串

相关问题