首页 > 解决方案 > 使用 PowerShell,如何使用 CMD.exe 调用 CURL.exe 并在 WinForm 中显示结果

问题描述

我正在构建一个供内部使用的仪表板(WinForm),用 PS 编写,并且有一个部分,用户可以使用各种方法/工具查询站点以进行检查。

所有其他方法/工具(IWR、通过 CMD 调用的 nslookup、通过 CMD 调用的 OpenSSL、通过 CMD 调用的 CURL 等)在表单文本框中显示它们的查询结果都没有问题......并且 CURL 命令的结果似乎可以执行,仅在 PS 中运行时,可以将结果正确返回并保存在 PS 字符串变量中(参见演示),但是从表单运行时,PS 似乎出于某种原因需要控制台输入,并且从不在表单中显示存储的字符串(我假设是因为从表单调用时填充的内容不同)。

为了尽量减少问题的重现,这里是代码......

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")  
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 
[void] [System.Windows.Forms.Application]::EnableVisualStyles() 
$Form = New-Object system.Windows.Forms.Form 
$Form.Size = New-Object System.Drawing.Size(600,880) 
$form.MaximizeBox = $false 
$Form.StartPosition = "CenterScreen" 
$Form.FormBorderStyle = 'Fixed3D' 
$Form.Text = "Query Dashboard" 
$Font = New-Object System.Drawing.Font("Arial",9,[System.Drawing.FontStyle]::Bold) 
$form.Font = $Font 

#####RESULTSBOX and OUTPUT BUTTONS####
$Results_txtbox = New-Object System.Windows.Forms.RichTextBox
$Results_txtbox.Multiline = $true
$Results_txtbox.ScrollBars = "Vertical"
$Results_txtbox.Location = New-Object System.Drawing.Size(10,490)
$Results_txtbox.Size = New-Object System.Drawing.Size(485,270)
$Form.Controls.Add($Results_txtbox) 

##########CURL#############
#divider
$CURLLinediv = New-Object System.Windows.Forms.Label 
$CURLLinediv.Text = ""
$CURLLinediv.BorderStyle = "Fixed3D"
$CURLLinediv.AutoSize = $false
$CURLLinediv.height = 2 
$CURLLinediv.width = 550 
$CURLLinediv.Location = New-Object System.Drawing.Size(20,340)
$Form.Controls.Add($CURLLinediv) 

$LabelCURL = New-Object System.Windows.Forms.Label 
$LabelCURL.Text = "CURL" 
$LabelCURL.AutoSize = $true 
$LabelCURL.Location = New-Object System.Drawing.Size(30,342) 
$Form.Controls.Add($LabelCURL) 


$CURL_query_txtbox = New-Object System.Windows.Forms.TextBox
$CURL_query_txtbox.Location = New-Object System.Drawing.Size(20,360)
$CURL_query_txtbox.Size = New-Object System.Drawing.Size(300,20)
$CURL_query_txtbox.Text = "-s www.google.ca"
$CURL_query_txtbox.add_MouseHover($CURLquery_Help)
$Form.Controls.Add($CURL_query_txtbox) 
$CURL_query_tip = New-Object System.Windows.Forms.ToolTip
$CURLquery_Help ={ $tip = "Enter entire CURL CMD here starting after curl"
     $CURL_query_tip.SetToolTip($this,$tip)
}

$CURL_button = New-Object System.Windows.Forms.Button 
$CURL_button.Location = New-Object System.Drawing.Size(325,355) 
$CURL_button.Size = New-Object System.Drawing.Size(40,22) 
$CURL_button.Text = "GET" 
$CURL_button.Add_Click({CURL}) 
$Form.Controls.Add($CURL_button) 

function CURL 
{ 
#MOCK
#$CURL_query_txtbox.Text = "-s www.google.ca"
###$Results_txtbox.Text = (curl.exe $($CURL_query_txtbox.Text) 2>&1 | % ToString) -join "`n" 
###$Results_txtbox.Text = (curl.exe -s www.google.ca 2>&1 | % ToString) -join "`n" 
$Results_txtbox.Text = Invoke-Expression "curl.exe $($CURL_query_txtbox.Text) 2>&1"
} 

$result = $Form.ShowDialog()

如果您点击 GET 按钮,结果应该显示在结果文本框中,但 PS 不需要将结果返回到表单,而是需要一个 IWR 参数(如果您输入www.google.ca或其他 Uri,它将返回到应用程序,但对输入不执行任何操作,也不更新文本字段......但您现在可以关闭表单。

现在,如果在您的 PS 窗口中运行

$Results_txtbox.Text = (curl.exe -s www.google.ca 2>&1 | % ToString) -join "`n"

你可以看到它$Results_txtbox.Text在从 PS 运行时正确填充,如果你$CURL_query_txtbox.Text = "-s www.google.ca"先模拟(即 - ),你会看到

$Results_txtbox.Text = Invoke-Expression "curl.exe $($CURL_query_txtbox.Text) 2>&1"

单独在 PS 中完美运行,但是从 Form 调用时它不会返回任何结果,并且cmdlet Invoke-WebRequest... 消息显示在 PS 控制台中,寻找要输入的参数。

标签: winformspowershellcurlcmd

解决方案


错误消息表明您不小心调用了curl alias,这是Windows PowerShell的 cmdlet 的内置别名Invoke-WebRequest(请注意 PowerShell [Core] 7+ 不再具有此别名)。

即使您打算调用自定义CURL 函数,内置别名也优先,因为别名比函数具有更高的优先级 - 请参阅about_Command_Precedence
一个简单的解决方案是给你的函数一个不同的名字。

此外,您无需cmd.exe拨打curl.exe电话:

$Results_txtbox.Text = (curl.exe -s www.google.ca 2>&1 | % ToString) -join "`n"

如果目标命令 - curl.exe -s www.google.ca- 来自用户填写的文本框,并且您信任该输入,请使用Invoke-Expression,但请注意,通常应避免使用

$Results_txtbox.Text = (
  Invoke-Expression "$($CURL_query_txtbox.Text) 2>&1" | % ToString
) -join "`n"

如果只有参数来自文本框:
Invoke-Expression "curl.exe $($CURL_query_txtbox.Text) 2>&1" | % ToString

笔记:

  • % ToString(缩写:)ForEach-Object { $_.ToString() }仅在Windows PowerShell中需要,以确保stderrErrorRecord行 - PowerShell 在实例中包装- 在字符串上下文中按原样表示 - 这在PowerShell [Core] 7+中不再需要。

  • -join "`n"(或者-join [Environment]::NewLine,如果您想使用平台适当的换行符 [sequence],尽管这通常不是必需的)用于连接输出行数组以形成单个多行字符串;虽然管道的Out-String工作方式类似,但不幸的是,它总是添加一个尾随换行符

  • 另请注意,流合并重定向2>&1嵌入传递给的字符串中Invoke-Expression

    • 人们会期望以下形式也能正常工作:

      Invoke-Expression $CURL_query_txtbox.Text 2>&1
      
    • 但是,从 PowerShell 7.1 开始,它没有;这种意外行为在GitHub 问题 #14503中进行了讨论。


推荐阅读