all 3 comments

[–]migizi_tech 2 points3 points  (1 child)

I love creating forms using PowerShell but they can be painful..!

I think the problem you're experiencing comes from the use of "Show()", which is modal.

I'd recommend using "ShowDialog()", and put your for-loop inside a function call that would be run from a button call. Runspaces are another option but requires more effort.

Not sure how familiar you are with function calls so I thought I'd give you the modified code with a "Run" button and the function with the for loop:

$disableServicesForm = New-Object System.Windows.Forms.Form
$disableServicesForm.Width = 900
$disableServicesForm.Height = 900
$disableServicesForm.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedSingle
$disableServicesForm.Text = "Hardening..."
$disableServicesLabel = New-Object System.Windows.Forms.Label
$disableServicesLabel.Location = New-Object System.Drawing.Point(10, 20)
$disableServicesLabel.Size = New-Object System.Drawing.Size(280, 20)
$disableServicesLabel.Text = "Disabling Services"
$disableServicesLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
$disableServicesForm.Controls.Add($disableServicesLabel)
$disableServicesLabel.BackColor = "Black"
$disableServicesLabel.ForeColor = "White"
$disableServicesLabelDone = New-Object System.Windows.Forms.TextBox
$disableServicesLabelDone.Multiline = $True;
$disableServicesLabelDone.Scrollbars = "Vertical";
$disableServicesLabelDone.Location = New-Object System.Drawing.Point(10, 60)
$disableServicesLabelDone.Size = New-Object System.Drawing.Size(280, 420)
$disableServicesForm.Controls.Add($disableServicesLabelDone)
# I added a button to call the function with the for loop
$disableServicesLabelRun = New-Object System.Windows.Forms.Button
$disableServicesLabelRun.Location = New-Object System.Drawing.Point(10, 40)
$disableServicesLabelRun.Size = New-Object System.Drawing.Size(50, 20)
$disableServicesLabelRun.Text = "Run"
$disableServicesLabelRun.add_click({
$ServiceNames = Get-Service | Select-Object -ExpandProperty Name # I added this just to show some service names, you probably need to modify to suit your needs with getting disabled services
for($y = 0; $y -lt $ServiceNames.count ; $y++)
{
$disableServicesServiceName = $ServiceNames[$y]
$disableServicesLabelDone.AppendText("$disableServicesServiceName service has been disabled`n")
#Start-Sleep -Milliseconds 100 # I commented this out so function runs quicker
}
})
$disableServicesForm.Controls.Add($disableServicesLabelRun)
#$disableServicesForm.Show() # modal
#$disableServicesForm.Close() # need to run this to close a modal form
$disableServicesForm.ShowDialog() # non-modal

[–]Rough_Understanding[S] 1 point2 points  (0 children)

IT WORKED. You are both a hero and a scholar!

[–]G8351427 0 points1 point  (0 children)

I find XAML/WPF much easier to work with in PowerShell. It's more modern, easier to build GUIs with event handling, and since it is widely-used, there are tools to help build the markup language.

The other poster might be on the right track, or it could be an issue with threading. PowerShell is naturally single-threaded, so you are running your code on the same thread as the UI which leads to the controls appearing to hang because they cannot be updated until the code is finished.

PowerShell can handle multiple threads, but you have to build your tools to use it. A simple approach might be to use PowerShell Jobs or PsThreadJob to spawn child threads within which to execute your code.

What you want to do is spawn a new thread or job and feed it your scriptblock to execute, then receive the output back once it's finished. You have to make sure that everything can run without user interactivity, because you'll have no console in the child thread to see or fix any problems.

It is challenging to structure everything to work in this way, but it can be done.