I know how to instantiate object in PowerShell but I am struggling to find out if its possible to instantiate an object and set one of its properties in the same command.
The following commands work of course:
$f = &{$f=[System.Windows.Forms.Form]::new();$f.Text="Aloha!";$f}
[System.Windows.Forms.Application]::Run($f)
But combining these two commands into one does not work neither in Windows PowerShell nor in PowerShell 7.
[System.Windows.Forms.Application]::Run(&{System.Windows.Forms.Form]::new();$f.Text="Aloha!";$f})
I have no idea why the parser thinks that a ) is missing.
Your problem is unrelated to your specific code and boils down to a syntax problem:
A construct starting with &, the call operator, is a command, and therefore parsed in argument mode.
As such, in order to participate in an expression such as a method call, which is parsed in expression mode, it must be enclosed in (...), the grouping operator, in addition to the always-required ( and ) instances that enclose the method parameter value(s).
Therefore (note the extra ( and ) around & { ... }):
[System.Windows.Forms.Application]::Run(
(& {
[System.Windows.Forms.Form]::new();$f.Text="Aloha!";$f
})
)
Note: The code is spread across multiple lines purely for readability; it works equally on a single line.
However, you can simplify the call by replacing (& { ... }) with $( ... ), i.e. with the subexpression operator (which is itself an expression and therefore doesn't need the extra (...) enclosure):
[System.Windows.Forms.Application]::Run(
$(
[System.Windows.Forms.Form]::new();$f.Text="Aloha!";$f
)
)
Note:
Both & { ... } (i.e. invocation of a script block in a child scope via &) and $( ... ) support enclosing multiple statements.
However, the crucial difference is that & { ... } produces streaming output, i.e. emits output objects as they're being created, whereas $(...) collects all output up front first.
Since streaming output cannot be taken advantage of in a method call, there is no advantage to using & { ... } here (which requires the additional (...) enclosure), so $( ... ) is the simpler solution.
A secondary difference is that & runs a given script block ({ ... }) in a child scope, whereas $( ... ) runs directly in the caller's scope (as does @( ... ), the array-subexpression operator).
However, you have the option to run a script block directly in the caller's scope too, by using . , the dot-sourcing operator in lieu of & (. { ... }).
Taking a step back:
PowerShell allows you to initialize objects by casting a hashtable @{ ... }) with the desired property values, provided that the target type has a parameterless public constructor.
See this answer for more information.
Since [System.Windows.Forms.Form] meets this criterion, you can simplify your call as follows; note that since such a cast is an expression, you do not need to enclose it in extra (...) in order to use it as a method argument (since the expression doesn't contain ,):
# Note the use of a cast and a hashtable with the desired property value(s).
[System.Windows.Forms.Application]::Run(
[System.Windows.Forms.Form] @{ Text = "Aloha!" }
)
As you point out, you can make the code more concise if you add a using namespace statement at the top of your script, in addition to loading the assembly either via Add-Type -AssemblyName or via using assembly.[1]
To put it all together in a self-contained manner:
Add-Type -AssemblyName System.Windows.Forms # Load the assembly
using namespace System.Windows.Forms # Allow referencing its types by name only.
[Application]::Run([Form] @{ Text = "Aloha!" })
[1] Sadly, the using assembly form is broken in PowerShell (Core) 7 as of v7.5.x - see GitHub issue #11856.
Check (and think about) [System.Windows.Forms.Application]::Run:
OverloadDefinitions ------------------- static void Run() static void Run(System.Windows.Forms.Form mainForm) static void Run(System.Windows.Forms.ApplicationContext context)
Then, the following one-liner should work, using the Grouping operator ( ):
[System.Windows.Forms.Application]::Run((& {$a=[System.Windows.Forms.Form]::new();$a.Text="Aloha!";$a}))
As an alternative, try the Subexpression operator $( ) as follows:
[System.Windows.Forms.Application]::Run($(& {$a=[System.Windows.Forms.Form]::new();$a.Text="Aloha!";$a}))
Note: don't forget to run
if ( -not ("System.Windows.Forms.Form" -as [type]) ) {
Add-Type -AssemblyName System.Windows.Forms
}
before using [System.Windows.Forms.Application] type…
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With