The Goal
Creating and rendering a simple WinUI3 GUI in PowerShell 7.5 which is based on .NET 9. Nothing complicated, just a window and a button in it, such as this XAML
<?xml version="1.0" encoding="utf-8"?>
<Window
x:Class="App1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button x:Name="myButton" Click="myButton_Click">Click Me</Button>
</StackPanel>
</Window>
- Using, importing or defining C# code in PowerShell is totally okay too for the solution.
- The only thing I don’t want to do is to build/compile binaries of my own such as executables or DLLs. I want to use
.cs
CSharp files uncompiled in PowerShell if there is a need for CSharp code. - Loading and using Microsoft-signed DLLs is completely okay.
What I’ve tried so far
I’ve created a fully working WinUI3 app in Visual Studio 2022 using the latest WindowsApps SDK. Then inside of the Winui3 projectApp1binx64Debugnet8.0-windows10.0.22621.0win-x64
folder I’ve tried loading all of the DLLs
in there in PowerShell. Some 200 dlls loaded and a few failed to load.
In PowerShell now I have access to the type [Microsoft.UI.Xaml.Window]
but when I try to create an instance of it
New-Object -TypeName Microsoft.UI.Xaml.Window
# Or
[Microsoft.UI.Xaml.Window]::new()
I get the following error
MethodInvocationException: Exception calling ".ctor" with "0" argument(s): "The type initializer for '_IWindowFactory' threw an exception."
It looks like there is a dependency missing for _IWindowFactory
.
This is the full error message
Exception :
Type : System.Management.Automation.MethodInvocationException
ErrorRecord :
Exception :
Type : System.Management.Automation.ParentContainsErrorRecordException
Message : Exception calling ".ctor" with "0" argument(s): "The type initializer for '_IWindowFactory' threw an exception."
HResult : -2146233087
CategoryInfo : NotSpecified: (:) [], ParentContainsErrorRecordException
FullyQualifiedErrorId : TypeInitializationException
InvocationInfo :
ScriptLineNumber : 1
OffsetInLine : 1
HistoryId : 4
Line : [Microsoft.UI.Xaml.Window]::new()
Statement : [Microsoft.UI.Xaml.Window]::new()
PositionMessage : At line:1 char:1
+ [Microsoft.UI.Xaml.Window]::new()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CommandOrigin : Internal
ScriptStackTrace : at <ScriptBlock>, <No file>: line 1
TargetSite :
Name : ConvertToMethodInvocationException
DeclaringType : [System.Management.Automation.ExceptionHandlingOps]
MemberType : Method
Module : System.Management.Automation.dll
Message : Exception calling ".ctor" with "0" argument(s): "The type initializer for '_IWindowFactory' threw an exception."
Data : System.Collections.ListDictionaryInternal
InnerException :
Type : System.TypeInitializationException
TypeName : _IWindowFactory
TargetSite :
Name : get_Instance
DeclaringType : [Microsoft.UI.Xaml.Window+_IWindowFactory]
MemberType : Method
Module : Microsoft.WinUI.dll
Message : The type initializer for '_IWindowFactory' threw an exception.
InnerException :
Type : System.TypeInitializationException
TypeName : WinRT.ActivationFactory`1
TargetSite :
Name : As
DeclaringType : [WinRT.ActivationFactory`1[T]]
MemberType : Method
Module : Microsoft.WinUI.dll
Message : The type initializer for 'WinRT.ActivationFactory`1' threw an exception.
InnerException :
Type : System.Runtime.InteropServices.COMException
ErrorCode : -2147221164
TargetSite :
Name : ThrowExceptionForHR
DeclaringType : [System.Runtime.InteropServices.Marshal]
MemberType : Method
Module : System.Private.CoreLib.dll
Message : Class not registered (0x80040154 (REGDB_E_CLASSNOTREG))
Source : System.Private.CoreLib
HResult : -2147221164
StackTrace :
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(Int32 errorCode)
at WinRT.BaseActivationFactory..ctor(String typeNamespace, String typeFullName)
at WinRT.ActivationFactory`1..ctor()
at WinRT.ActivationFactory`1..cctor()
Source : Microsoft.WinUI
HResult : -2146233036
StackTrace :
at WinRT.ActivationFactory`1.As(Guid iid)
at Microsoft.UI.Xaml.Window._IWindowFactory..ctor()
at Microsoft.UI.Xaml.Window._IWindowFactory..cctor()
Source : Microsoft.WinUI
HResult : -2146233036
StackTrace :
at Microsoft.UI.Xaml.Window._IWindowFactory.get_Instance()
at Microsoft.UI.Xaml.Window..ctor()
at CallSite.Target(Closure, CallSite, Type)
Source : System.Management.Automation
HResult : -2146233087
StackTrace :
at System.Management.Automation.ExceptionHandlingOps.ConvertToMethodInvocationException(Exception exception, Type typeToThrow, String methodName, Int32 numArgs, MemberInfo memberInfo)
at CallSite.Target(Closure, CallSite, Type)
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at System.Management.Automation.Interpreter.DynamicInstruction`2.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
CategoryInfo : NotSpecified: (:) [], MethodInvocationException
FullyQualifiedErrorId : TypeInitializationException
InvocationInfo :
ScriptLineNumber : 1
OffsetInLine : 1
HistoryId : 4
Line : [Microsoft.UI.Xaml.Window]::new()
Statement : [Microsoft.UI.Xaml.Window]::new()
PositionMessage : At line:1 char:1
+ [Microsoft.UI.Xaml.Window]::new()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CommandOrigin : Internal
ScriptStackTrace : at <ScriptBlock>, <No file>: line 1
Other people have tried this too and had similar results. Another issue related to this problem asking for some guidance from Microsoft.
I don’t know how Visual Studio does this that makes it all so easy and automated, but I believe I need to do the same tasks manually in PowerShell.
2
Answers
Starting a WinUI3 app is quite involved, and supporting full XAML is another difficulty as it also requires compiled XAML files (.xbf), resource files (.pri), etc. These can be built with SDK tools but here I will just demonstrate how to start a WinUI .NET 8 application based on PowerShell 7.4 with or without simple XAML (no automatic binding), without the need for Visual Studio, with zero compilation at deployment time.
First create a directory and put in there:
.ps1
file,libnet6.0-windows10.0.18362.0
directory (as of today, it’s not targeting .NET 8),WinRT.Runtime.dll
from the Microsoft.Windows.CsWinRT nugetMicrosoft.Windows.SDK.NET.dll
from the Microsoft.Windows.SDK.NET.Ref nugetMicrosoft.WindowsAppRuntime.Bootstrap.dll
from WinAppSDK Runtime (you can find it in a place likeC:Program FilesWindowsAppsMicrosoft.WindowsAppRuntime.1.5_5001.95.533.0_x64__8wekyb3d8bbwe
as of today). Note: obviously, the Windows App Runtime must be installed on the PC for all this to work.This is how your folder should look (you can delete the .xml files they are for SDK documentation):
Now here is the content of
BasicWinUI.ps1
(as you can see it’s 95% C#)I start it with a .bat like this in the
BasicWinUI
folder:And here is what you should get (XAML free):
Now if you want simple XAML support, you can load a XAML file dynamically and bind to UI objects manually. For example, if you create a .XAML file like this and name it
BasicWinUIWindow.xaml
:Place it into the same folder and use a .ps1 like this for example:
Now this is what you’ll see:
PS: the .ps1 code will return
true
if you press OK andfalse
if you press Cancel.Simon provided a fantastic answer. I tried to convert his answer to an all powershell solution. The only part I couldn’t figure out was why this line can’t be translated one for one.
$this.Resources.MergedDictionaries.Add([Microsoft.UI.Xaml.Controls.XamlControlsResources]::new())
This builds the application in another runspace while leaving room to add to the DataContext and to call Window.Activate() when needed by dispatcherqueue.