Dallas
Dallas

Reputation: 542

Using PowerShell, how do I call CURL.exe using CMD.exe AND display results in WinForm

I am building a dashboard (WinForm) for internal usage, written in PS, and have a section where users can query sites using various methods/tools for inspection.

All the other methods/tools (IWR, nslookup via CMD call, OpenSSL via CMD call, CURL via CMD call, etc.) have no issues displaying their query results within a Form TextBox... and results for CURL commands seem to execute, return and save results properly in a PS string variable fine when run just within PS (see demo), but when running from the Form, PS seems to want console input for some reason and never displays the stored string in the Form (I presume because it isn't populating the same when called from the Form).

To minimally reproduce the issue, here is the code...

[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()

If you hit the GET button, the results should show in the Results textbox, but instead of returning the result to the form, PS wants an IWR parameter (if you enter www.google.ca or another Uri, it will return back to the application but does nothing with the input, and doesn't update the textfield... but you can now close the Form.

Now if in your PS window you run

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

you can see that it populates $Results_txtbox.Text correctly when running from PS, and if you mock (ie - $CURL_query_txtbox.Text = "-s www.google.ca" ) first, you will see that

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

works perfectly in PS alone, but when called from the Form it doesn't return any results, and the same cmdlet Invoke-WebRequest... message shows up in the PS console looking for a parameter to be entered.

Upvotes: 2

Views: 1200

Answers (1)

mklement0
mklement0

Reputation: 438178

The error message suggests that you accidentally called the curl alias, which is Windows PowerShell's built-in alias for the Invoke-WebRequest cmdlet (note that PowerShell [Core] 7+ no longer has this alias).

Even though you meant to call your custom CURL function, the built-in alias took precedence, because aliases have higher precedence than functions - see about_Command_Precedence.
A simple solution is to give your function a different name.

Also, you don't need cmd.exe to make your curl.exe call:

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

If the target command - curl.exe -s www.google.ca - comes from a text box filled by the user and you trust that input, use Invoke-Expression, though note that, in general, it should be avoided.

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

If only the arguments come from the text box:
Invoke-Expression "curl.exe $($CURL_query_txtbox.Text) 2>&1" | % ToString

Note:

  • % ToString (short for: ForEach-Object { $_.ToString() }) is only needed in Windows PowerShell, to ensure that stderr lines - which PowerShell wraps in ErrorRecord instances - are represented as-is in a string context - this is no longer necessary in PowerShell [Core] 7+.

  • -join "`n" (or -join [Environment]::NewLine, if you want to use the platform appropriate newline [sequence], though that is usually not necessary) is used to join the array of output lines to form a single, multi-line string; while piping to Out-String works similarly, it - unfortunately - always adds a trailing newline.

  • Also note that the stream-merging redirection, 2>&1, is embedded in the string passed to Invoke-Expression.

    • One would expect the following form to work as well:

      Invoke-Expression $CURL_query_txtbox.Text 2>&1
      
    • However, as of PowerShell 7.1 it does not; this unexpected behavior is discussed in GitHub issue #14503.

Upvotes: 2

Related Questions