About a month ago I decided to try my hand at making a WinForms application in PowerShell. To my pleasant surprise I found it easier than doing the same in C#. All was well until I tried to inherit from WinForms Forms.
This:
using namespace System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms
class MainForm : Form {
}
Produces this error:
Unable to find type [Form].
I'm still realitively new to PowerShell, so at the time I had no clue what was going on. After considerable searching I finally had to slowed myself down and begin reading entire answers/comments even if they didn't seem to relate to the problem. It was the note at the bottom of an answer given by mklement0 that finally began shedding some light on the situation for me.
Apparently in an effort to prevent accidental code execution, assemblies are not loaded during parsing, but later during the script's execution, but classes, and any references a class makes to external assemblies, are fully parsed. Thus creating a catch-22 parsing issue that can only be resolved by loading assemblies prior to running a PowerShell script. (Or at least that is my current understanding - follow the links in mklement0's note to get the details.)
Now, by chance, the code I was working on was almost the perfect workaround, or solution, to the problem. All I had to do was change this CMD file from this:
<# :
@ECHO OFF
SET f0=%~f0
PowerShell -NoProfile -Command .([scriptblock]::Create((get-content -raw $Env:f0)))
GOTO :EOF
#>
using namespace System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms
class MainForm : Form {
}
To this :
<# :
@ECHO OFF
SET f0=%~f0
PowerShell -NoProfile -Command Add-Type -AssemblyName System.Windows.Forms;.([scriptblock]::Create((get-content -raw $Env:f0)))
GOTO :EOF
#>
using namespace System.Windows.Forms
class MainForm : Form {
}
And with that small change I was back in business, and with VSCode I can tell it the CMD script is really a PowerShell script and that makes almost everything functional - other than VSCode complaining about things related to catch-22 parsing issue.
But this also helped me realize that in a pure PowerShell script this would work:
using namespace System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms
.([scriptblock]::Create(@'
class MainForm : Form {
}
'@))
And it does work, only all the code in the string @'
'@
is now treated by VSCode as a string. So trying to create a class inside a string is about the same as using Windows notepad, which is doable, but not pleasant.
So, how are people working around this? Is the solutions I've presented here about as good as it gets?
PowerShell does NOT appear to have a Preload-Assembly -ScriptBlock {}
command that parses and executes the script block prior to parsing and executing the rest of the script. Are there plans for such a solution in the future? Does such a command by another name already exist and I've just failed come across it?
Perhaps this question will not provide me any answers beyond what I already have. But maybe this will help others reduce the time they spend trying to figure out why they can't inherit a WinForms Form - along with giving them the options I found.
On the other, if someone has better answers - please share!