Jump to content

Recommended Posts

Posted
20 minutes ago, MattyD said:

And resizing the window actually works remarkably well. I guess I was expecting some flicker or something- so that was a pleasant surprise! 

image.png.88d613ccca08a111ae112f1407faddba.png

 

That's quite beautiful. You are making excellent progress so far. I haven't tested this in the last few days but I'm definitely more inspired now to try some things with it later today.

  • How is the performance of the UI loading?

The reason why I ask is because a lot of Microsoft's own examples with WinUI3 are quite sluggish. Yet, I have also seen many examples from other developers which are fast and snappy.

  • How do you handle applying DPI scaling?

I'm just not sure if the WinUI3 / WinRT stuff automatically applies DPI scaling by default or if we would specify the DPI scaling as we normally would in AutoIt.

Posted

Thanks mate, we'll have to credit whoever came up with the design at Microsoft though!

18 minutes ago, WildByDesign said:

How is the performance of the UI loading?

This is honestly fine, but we're not exactly asking much of the runtime either at this point in time!   Au3check can grind a bit when you really start pushing the #include file count. But for what I'm doing at the moment its OK. 

25 minutes ago, WildByDesign said:

How do you handle applying DPI scaling?

I haven't looked at this at all sorry!

Posted (edited)

This is the same example - but I'm playing around with adding another layer of functions on top of the interface libraries.   It should make building GUIs quite a bit simpler, and introduce some semblance of resource management.

At this stage its just a rough proof of concept, so I'm not worried about naming conventions etc, or any mistakes in these functions. They'll all probably be rewritten anyway!

So anyway, as an example: I'm now doing this to add rows and columns to the grid.

;Params - $pGrid, $fValue, $vUnit ("Auto", "Pixel" or "Star")
;$vUnit can also be the enum in its numeric form. (Microsoft.UI.Xaml.GridUnitType)

GridAddRow($pGrid, 2, "Star") 
GridAddRow($pGrid, 1, "Star")
GridAddColumn($pGrid, 3, "Star")
GridAddColumn($pGrid, 5, "Star")

And the underlying func.

Func GridAddColumn($pGrid, $fWidth = 1, $vUnit = "Star")
    _WinRT_SwitchInterface($pGrid, $sIID_IGrid)
    If @error Then Return SetError(@error, @extended, False)

    Local $tGridLen = DllStructCreate("align 4;double Value;ulong GridUnitType")
    $tGridLen.Value = $fWidth
    $tGridLen.GridUnitType = IsString($vUnit) ? $mGridUnitType[$vUnit] : $vUnit

    Local $pColDef, $pColDefs = IGrid_GetColumnDefinitions($pGrid)
    $pColDef = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Controls.ColumnDefinition")
    _WinRT_SwitchInterface($pColDef, $sIID_IColumnDefinition)
    IRowDefinition_SetHeight($pColDef, $tGridLen)
    Local $iError = @error
    If Not $iError Then IVector_Append($pColDefs, $pColDef)
    IUnknown_Release($pColDef)
    IUnknown_Release($pColDefs)
    Return SetError($iError, 0, $iError = $S_OK)
EndFunc   ;==>GridAddColumn

Most funcs that accept an object will start with a  SwitchInterface() - this serves 2 purposes:

  • in your main script you don't need to bother about setting the correct interface on the object.
  • and also if you feed the func an invalid pointer (to the wrong object for eg.), it should gracefully fail. 

Factories and other supporting objects used in these funcs are all released before returning.  This is probably not be the most efficient thing to do if you're creating a bunch of instances of something - but it does mean you're cleaning up after yourself as you go.

I'm getting way ahead of myself - but this new layer wouldn't be a comprehensive wrapper. It'd probably just some basic/common functionality to improve quality of life. I figure people can always jump down to the interface libraries if need be.   Whether this will form part of an "official" release I don't know (if I ever get my act together!) - but I think building a full project without anything would just be downright tedious... And hey, if anyone else wants write/drive some of it I won't be complaining!!!

WindowTest Grid.zip

Edited by MattyD
Posted (edited)
...
#AutoIt3Wrapper_UseX64=Y
#AutoIt3Wrapper_Au3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7
If Not @AutoItX64 And Not @Compiled Then
    ShellExecute(StringTrimRight(@AutoItExe, 4) & "_x64.exe", '"' & @ScriptFullPath & '"')
    Exit
EndIf
#include <GUIConstants.au3>
...

It asked me to GetRuntime() but I installed it yesterday so, the above is a good solution for those not in SciTE, just click-clicking the file from explorer :)  

Edited by argumentum

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

  • 2 months later...
Posted

@MattyD From your WindowTest Grid example from September 3rd, you have three sections of the weather GUI with background colored with the following:

Local $aiColours[3] = [0xFF2F5CB6, 0xFF1F3D7A, 0xFF152951]
    Local $apBorders[3], $pBrush
    For $i = 0 To 2
        $pBrush = CreateSolidBrush($aiColours[$i])
        $apBorders[$i] = CreateBorder()
        IBorder_SetBackground($apBorders[$i], $pBrush)
        IUnknown_Release($pBrush)
        PanelAddChild($pGrid, $apBorders[$i])
    Next

So what I am curious about is how would we go about changing some of those background colors after the GUI is already showing?

I'm trying to understand the WinUI 3 stuff now. I'm not even sure that each part of the GUI even has a window handle. The whole WinRT/WinUI 3 stuff kind of boggles my mind as to how to change this stuff after the fact.

Posted (edited)

sure thing. - yeah, there are no window handles per se - apart from the WinID of the main window.  Everything is done on objects. 

so that colour change would be something like:

$pBrush = CreateSolidBrush("Red")
IBorder_SetBackground($apBorders[0], $pBrush)
IUnknown_Release($pBrush)

In the Sept 3rd. example, to keep things tidy I release the border objects once they're created. So you'd need either need to change the background before releasing, or get the border object back by some other means. Via its parent for example - in this case the grid object (before it too is released!).

As for triggering the color change - In this example,  once we get into the main loop everything is event based. When we hit the following line the script won't continue until the dispatch loop is broken with _WinUI3_PostQuitMessage. 

IDispatcherQueue3_RunEventLoop($pDispatchQ)

The general idea is to create a bunch of handlers with _WinRT_CreateDelegate(). But if you'd rather work within a while loop, it is possible to create your own dispatcher with _WinUI3_GetMsg / _WinUI3_DispatchMessage.  I think there are probably examples of this early in the thread!

Edited by MattyD
typo
Posted

Hi all,

Updated libraries based on the new Version 1.8 of WindowsAppSDK are here. Until now we've been on 1.7.x

The v1.8 runtime can be installed from here: (Remembering that we require the x64 version.)

A copy of the latest bootstrapper dll is included in the zip - but  if you need to manually update it later for whatever reason, it has moved.

So you'll now find it here: https://www.nuget.org/packages/Microsoft.WindowsAppSdk.Foundation/
download package (on the right) >  extract contents > then you'll find it under .\runtimes\win-x64\native

  • 1 month later...
Posted (edited)

Just having a bit more of a play with this.

I've found we can avoid the creating that custom window proc... this is good, as it seems breaking the dispatcher loop is done in part by posting a WM_QUIT message. I hadn't noticed it at first, but I think AutoIt was seeing this and exiting automatically once it found that message.  So now our script goes through all the way to the end.

Also the demo shows how we can constrain the max/min window size, and automatically grow and shrink the xaml content with the window.. (we were previously doing the latter via that window proc)

Example

Edited by MattyD
Posted (edited)

ah ok thanks, I've updated _WinUI3_Startup() to target v1.8 of the WindowsApp SDK.. I think its not throwing a soft error f there's a version mismatch with the runtime... I'll see if I can fix that for next time...

Can you try doing a _WinUI3_Startup(1, 7) to target v1.7 and see if that works?  (assuming that you still have it installed) . Otherwise v1.8 can be installed from here...

1 hour ago, argumentum said:

 #AutoIt3Wrapper_UseX64=y

And yep I forgot this at the top, it should definitely be there  too.

Edited by MattyD
Posted
...
Func _WinRT_Startup()
    If Not @AutoItX64 Then Return SetError(1, 0, 1)
...
Func _WinUI3_Startup($iMajorVers = Default, $iMinorVers = Default, $sVersionTag = Default, $iMinVers = Default, $iOptions = Default)
    If Not @AutoItX64 Then Return SetError(1, 0, False)
...

#include "WinUIBase.au3"
If Not @AutoItX64 Then Exit ConsoleWrite(@CRLF & '@@(' & @ScriptLineNumber & ') : Needs to run x64.' & @CRLF)
_WinRT_Startup()
If @error Then Exit ConsoleWrite(@CRLF & '@@(' & @ScriptLineNumber & ') : Error=' & @error & @CRLF)
_WinUI3_Startup()
If @error Then Exit ConsoleWrite(@CRLF & '@@(' & @ScriptLineNumber & ') : Error=' & @error & @CRLF)
...

..something to let the user know that is not running x64. And since the default install runs x86 for compatibility, when click-click from Explorer, the "#AutoIt3Wrapper_UseX64=y" is not going to do it's thing.

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

  • 2 weeks later...
Posted

I'm currently looking into the Microsoft.UI.Xaml.Application class - and its giving me plethora of extra things to try and understand and implement...

Currently, my focus is on IApplicationOverrides::OnLaunched. In the example WinUI3 apps online, there's generally a user defined "OnLaunched" function where you do a bunch of initialisation. Ideally we should be replicating this behavior. So how do we achieve that? 

We are used to dealing with delegates, which would ordinarily fire when an object is ready for us. But this uses a totally different mechanism - method overrides. We need to create our own object that implements a custom IApplicationOverrides::OnLaunched method, and get the system to call this instead of the normal routine. From what I've read, this black magic is possible because of object composition.

So revisiting composable objects, the factories look like this:

Fn : value = CreateInstance(In baseInterface, Out innerInterface)
                  P0 : value                type: Windows.UI.Xaml.Application 
                  P1 : baseInterface        type: Object 
                  P2 : innerInterface       type: Object*

This is essentially a method to combine two objects together -

  • so we provide the "base" object with our customised "IApplicationOverrides" interface, 
  • the factory creates the "inner" application object
  • and the "combined" object, (the one we use) is what's returned.

IApplicationOverrides is derived from IInspectable, so our base object's v-table will need to contain:

  1. IUnknown::QueryInterface
  2. IUnknown::AddRef
  3. IUnknown::Release
  4. IInspectable::GetIIDs
  5. IInspectable::GetRuntimeClassName
  6. IInspectable::GetTrustLevel
  7.  IApplicationOverrides::OnLaunched

We already have a home-brewed IUnknown implementation for our delegates, so I guess I'll knock up something for the rest and see how we fare. 

I'm a little wary because our custom IUnknown/IInspectable routines will only work properly for our home-grown objects. We store internal data like the refcount in a struct underneath the v-table ptr - but I have no idea how a proper COM object handles this. 

Let's say our implementation of "QueryInterface" is called with the Inner object's ptr for some reason. If the requested IID is supported, then we should AddRef() to increment the reference count... But we crash instead because the refcount is not physically where we expect it to be...

Anyway I'll leave it there for now...

Posted

OK, I think its kindof working.  The composed obj comes back on our makeshift interface.

But there's a problem. While we're on IApplicationOverrides,  the IUnkown/IInspectable calls invoke our custom functions. But our creation doesn't know about the Inner object, so we get trapped on IApplicationOverrides. 

Basically, (I think) calling QI with  $sIID_IApplicationOverrides on the composed object correctly puts it on our makeshift interface - but once we're there, our custom QI func can't take us back to, say,  $sIID_IApplication. Sooo - we need to somehow forward any IUnknown/IInspectable calls we receive back to the inner object.. OK, so whats the best way to do this?  Well, in our IUnknown/IInspectable implementations we essentially need to do something like:

Func __Overrides_AddRef($pThis)
    If $pThis = ($pSomeOuterOj) Then $pThis = ($pAssociatedInnerObj)
    Return IUnknown_AddRef($pThis)
EndFunc

Ideally I'd like to store the $pInner address internally in our object - but I'll need to think about the best way to implement that.  I think I'll just create a global map between the pInner an pOuter objects for now. Its probably not the best approach long term, but it'll do for testing purposes.

Posted (edited)

We are getting close - our overrides interface is working!

You can see that when our QI function is asked for IApplicationOverrides it points to our object, otherwise it points to the inner object.

There is an issue with cleanup though. Once we release all our references to the Application object, the system will eventually want to clean it up.  It doesn't know how to deal with our outer object though, so the script just crashes at that point.
It Looks like the ref count was running into the negatives on cleanup. So we just need to AddRef once to increment 1 on creation.

Other than that, we just need to sort out how we're storing/accessing that inner object pointer.
Think this will more or less do. Modified example...

;Using WinRT v1.6 Libraries
#AutoIt3Wrapper_UseX64=Y

#include "..\Include\WinRT.au3"
#include "..\Include\WinRT_WinUI3.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Application.au3"

Global $__g_hQueryInterfaceStub, $__g_hAddRefStub, $__g_hReleaseStub
Global $__g_hGetIIDsStub, $__g_hGetRuntimeClassNameStub, $__g_hGetTrustLevelStub

Global $__g_aOverrides[1][4]

_WinRT_Startup()
_WinUI3_Startup()
If @error Then Exit MsgBox(0, "Error", _WinAPI_GetErrorMessage(@error))

_Main()
_WinUI3_Shutdown()
_WinRT_Shutdown()
ConsoleWrite("Finished." & @CRLF)

Func _Main()
    Local $pApp_Fact = _WinRT_GetActivationFactory("Microsoft.UI.Xaml.Application", $sIID_IApplicationFactory)

    Local $hOnLaunched = DllCallbackRegister("OnLaunched", "none", "ptr;ptr")
    ;IID_IApplicationOverrides only has 1 func, but other overrides interfaces may have more. So $ahAppOverridesFuncs param is an array.
    Local $ahAppOverridesFuncs[1] = [$hOnLaunched]

    ;Factory must be on the correct interface for this func to work.
    Local $pApp = CreateOverrides($pApp_Fact, $ahAppOverridesFuncs, $sIID_IApplicationOverrides)

    ;Check IInspectable works as expected.
    ConsoleWrite("Switch To IApplication" & @CRLF)
    _WinRT_SwitchInterface($pApp, $sIID_IApplication)
    _WinRT_DisplayInterfaces($pApp)

    ConsoleWrite("Switch To IApplicationOverrides" & @CRLF)
    _WinRT_SwitchInterface($pApp, $sIID_IApplicationOverrides)
    _WinRT_DisplayInterfaces($pApp)

    ;Test out override
    ConsoleWrite("Test On Launched func" & @CRLF)
    IApplicationOverrides_OnLaunched($pApp, 0)

    ;Start App.
    ConsoleWrite("Startup App" & @CRLF)
    Local $pStartupDgt = _WinRT_CreateDelegate("AppStart")

    _WinRT_SwitchInterface($pApp_Fact, $sIID_IApplicationStatics)
    IApplicationStatics_Start($pApp_Fact, $pStartupDgt) ;Starts internal dispatch loop.
    ConsoleWrite("Exiting..." & @CRLF)

    IUnknown_Release($pApp_Fact)
    IUnknown_Release($pApp)
    DestroyOverrides($pApp)
EndFunc

Func AppStart($pThis, $pSender, $pArgs)
    ConsoleWrite("App Startup Delegate Fired." & @CRLF)
EndFunc

Func OnLaunched($pThis, $pArgs)
    ConsoleWrite("On Launched Fired." & @CRLF & @CRLF)
    If $pArgs Then
        ConsoleWrite("We really launched! so let's now exit." & @CRLF)
        _WinRT_SwitchInterface($pThis, $sIID_IApplication)
        IApplication_Exit($pThis)
    EndIf
EndFunc

Func CreateOverrides($pFactory, $ahFunctions, $sIID_IOverrides)

    If Not $__g_hQueryInterfaceStub Then
        $__g_hQueryInterfaceStub = DllCallbackRegister("__QueryInterfaceStub", "long", "ptr;ptr;ptr")
        $__g_hAddRefStub = DllCallbackRegister("__AddRefStub", "long", "ptr")
        $__g_hReleaseStub = DllCallbackRegister("__ReleaseStub", "long", "ptr")
        $__g_hGetIIDsStub = DllCallbackRegister("__GetIIDsStub", "ulong", "ptr;ptr;ptr")
        $__g_hGetRuntimeClassNameStub = DllCallbackRegister("__GetRuntimeClassNameStub", "ulong", "ptr;ptr")
        $__g_hGetTrustLevelStub = DllCallbackRegister("__GetTrustLevelStub", "ulong", "ptr;ptr")
    EndIf

    Local $iOverridesId = UBound($__g_aOverrides)
    ReDim $__g_aOverrides[$iOverridesId + 1][4]
    $__g_aOverrides[0][0] += 1

    Local $tVTab = DllStructCreate(StringFormat("ptr pFunc[%d];", UBound($ahFunctions) + 6))
    $tVTab.pFunc(1) = DllCallbackGetPtr($__g_hQueryInterfaceStub)
    $tVTab.pFunc(2) = DllCallbackGetPtr($__g_hAddRefStub)
    $tVTab.pFunc(3) = DllCallbackGetPtr($__g_hReleaseStub)
    $tVTab.pFunc(4) = DllCallbackGetPtr($__g_hGetIIDsStub)
    $tVTab.pFunc(5) = DllCallbackGetPtr($__g_hGetRuntimeClassNameStub)
    $tVTab.pFunc(6) = DllCallbackGetPtr($__g_hGetTrustLevelStub)
    For $i = 0 To UBound($ahFunctions) - 1
        $tVTab.pFunc((7 + $i)) = DllCallbackGetPtr($ahFunctions[$i])
    Next

    Local $tagOverridesObj = "align 4;ptr pVTab;ptr pInner;byte IID_IOverrides[16]"
    Local $tOverrides = DllStructCreate($tagOverridesObj)
    $tOverrides.pVTab = DllStructGetPtr($tVTab)
    _WinAPI_GUIDFromStringEx($sIID_IOverrides, DllStructGetPtr($tOverrides, "IID_IOverrides"))
    Local $pOverrides = DllStructGetPtr($tOverrides)

    Local $pInner
    Local $pComposed = __CreateInstance($pFactory, $pOverrides, $pInner)
    $tOverrides.pInner = $pInner
    IUnknown_AddRef($pComposed)

    $__g_aOverrides[$iOverridesId][0] = $pComposed
    $__g_aOverrides[$iOverridesId][1] = $tOverrides
    $__g_aOverrides[$iOverridesId][2] = $tVTab
    $__g_aOverrides[$iOverridesId][3] = $ahFunctions

    Return $pComposed
EndFunc

Func DestroyOverrides($pOverrides)
    If Not $pOverrides Or Not IsPtr($pOverrides) Then Return SetError($ERROR_INVALID_PARAMETER, 0, False)
    For $i = 0 To UBound($__g_aOverrides) - 1
        If $__g_aOverrides[$i][0] = $pOverrides Then ExitLoop
    Next
    If $i = UBound($__g_aOverrides) Then Return SetError($ERROR_INVALID_PARAMETER, 0, False)

    Local $iOverridesId = $i
    Local $ahFunctions = $__g_aOverrides[$i][3]
    For $i = 0 To UBound($ahFunctions) - 1
        DllCallbackFree($ahFunctions[$i])
    Next

    For $j = 0 To UBound($__g_aOverrides, 2) - 1
        $__g_aOverrides[$iOverridesId][$j] = 0
    Next
    $__g_aOverrides[0][0] -= 1

    If Not $__g_aOverrides[0][0] Then
        DllCallbackFree($__g_hQueryInterfaceStub)
        DllCallbackFree($__g_hAddRefStub)
        DllCallbackFree($__g_hReleaseStub)
        DllCallbackFree($__g_hGetIIDsStub)
        DllCallbackFree($__g_hGetRuntimeClassNameStub)
        DllCallbackFree($__g_hGetTrustLevelStub)
        $__g_hQueryInterfaceStub = 0
        $__g_hAddRefStub = 0
        $__g_hReleaseStub = 0
        $__g_hGetIIDsStub = 0
        $__g_hGetRuntimeClassNameStub = 0
        $__g_hGetTrustLevelStub = 0
    EndIf
EndFunc


Func __QueryInterfaceStub($pThis, $pIID, $ppObj)
    Local $pFunc, $aCall, $hResult
    Local $tThis = DllStructCreate("align 4;ptr pVTab;ptr pInner;byte IID_IOverrides[16]", $pThis)

    If _WinAPI_StringFromGUID($pIID) = _WinAPI_StringFromGUID(DllStructGetPtr($tThis, "IID_IOverrides")) Then
        DllStructSetData(DllStructCreate("ptr", $ppObj), 1, $pThis)
        __AddRefStub($pThis)
        $hResult = $S_OK
    Else
        $pThis = $tThis.pInner
        $pFunc = __WinRT_GetFuncAddress($pThis, 1)
        If Not @error Then $aCall = DllCallAddress("ulong", $pFunc, "ptr", $pThis, "ptr", $pIID, "ptr", $ppObj)
        $hResult =  @error ? $E_FAIL : $aCall[0]
    EndIf
    Return $hResult
EndFunc

Func __AddRefStub($pThis)
    Local $pFunc, $aCall, $iRefCount = 0
    Local $tThis = DllStructCreate("align 4;ptr pVTab;ptr pInner", $pThis)
    $pThis = $tThis.pInner
    $pFunc = __WinRT_GetFuncAddress($pThis, 2)
    If Not @error Then $aCall = DllCallAddress("uint", $pFunc, "ptr", $pThis)
    If Not @error Then $iRefCount = $aCall[0]
    Return $iRefCount
EndFunc

Func __ReleaseStub($pThis)
    Local $pFunc, $aCall, $iRefCount = 0
    Local $tThis = DllStructCreate("align 4;ptr pVTab;ptr pInner", $pThis)
    $pThis = $tThis.pInner
    $pFunc = __WinRT_GetFuncAddress($pThis, 3)
    If Not @error Then $aCall = DllCallAddress("uint", $pFunc, "ptr", $pThis)
    If Not @error Then $iRefCount = $aCall[0]
;~  ConsoleWrite("refcnt=" & $iRefCount & @CRLF)

    Return $iRefCount
EndFunc

Func __GetIIDsStub($pThis, $pIidCount, $ppIIDs)
    Local $pFunc, $aCall
    Local $tThis = DllStructCreate("align 4;ptr pVTab;ptr pInner", $pThis)
    $pThis = $tThis.pInner
    $pFunc = __WinRT_GetFuncAddress($pThis, 4)
    If Not @error Then $aCall = DllCallAddress("ulong", $pFunc, "ptr", $pThis, "ptr", $pIidCount, "ptr", $ppIIDs)
    Return @error ? $E_FAIL : $aCall[0]
EndFunc

Func __GetRuntimeClassNameStub($pThis, $phClassName)
    Local $pFunc, $aCall
    Local $tThis = DllStructCreate("align 4;ptr pVTab;ptr pInner", $pThis)
    $pThis = $tThis.pInner
    $pFunc = __WinRT_GetFuncAddress($pThis, 5)
    If Not @error Then $aCall = DllCallAddress("ulong", $pFunc, "ptr", $pThis, "ptr", $phClassName)
    Return @error ? $E_FAIL : $aCall[0]
EndFunc

Func __GetTrustLevelStub($pThis, $piTrustLevel)
    Local $pFunc, $aCall
    Local $tThis = DllStructCreate("align 4;ptr pVTab;ptr pInner", $pThis)
    $pThis = $tThis.pInner
    $pFunc = __WinRT_GetFuncAddress($pThis, 6)
    If Not @error Then $aCall = DllCallAddress("ulong", $pFunc, "ptr", $pThis, "ptr", $piTrustLevel)
    Return @error ? $E_FAIL : $aCall[0]
EndFunc

Func __CreateInstance($pThis, $pBaseInterface, ByRef $pInnerInterface)
    Local $vFailVal = Ptr(0)
    Local $pFunc = __WinRT_GetFuncAddress($pThis, 7)
    If @error Then Return SetError(@error, @extended, $vFailVal)
    If $pBaseInterface And IsInt($pBaseInterface) Then $pBaseInterface = Ptr($pBaseInterface)
    If $pBaseInterface And (Not IsPtr($pBaseInterface)) Then Return SetError($ERROR_INVALID_PARAMETER, 0, $vFailVal)
    Local $aCall = DllCallAddress("long", $pFunc, "ptr", $pThis, "ptr", $pBaseInterface, "ptr*", 0, "ptr*", 0)
    If @error Then Return SetError(__WinRT_GetDllError(), 0, $vFailVal)
    $pInnerInterface = $aCall[3]
    Return SetError($aCall[0], 0, $aCall[4])
EndFunc
Edited by MattyD
Posted

Ok, now we are getting somewhere..

We're now using the window class, which houses the object that we were using before.. the appwindow. So now the dispatcher automatically exits on "OnLastWindowClose". Huzzah!

Everything else seems to pretty much auto-initialise too - so there's less to worry about with presenters, dispatchers, contentbridges, desktopxamlsources etc.  We just need to set the content property of the Window object.

And the app now automatically adjusts for dark mode too!

#AutoIt3Wrapper_UseX64=Y

#include "..\Include\WinRT.au3"
#include "..\Include\WinRT_WinUI3.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Application.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.LaunchActivatedEventArgs.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Window.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Hosting.DesktopWindowXamlSource.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Media.SolidColorBrush.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Controls.Stackpanel.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Controls.Button.au3"
#include "..\Include\Interfaces\Windows.Foundation.Collections.IVector.au3"
#include "..\Include\Classes\Windows.Foundation.PropertyValue.au3"
#include "..\Include\Classes\Microsoft.UI.Colors.au3"

Global $__g_hQueryInterfaceStub, $__g_hAddRefStub, $__g_hReleaseStub
Global $__g_hGetIIDsStub, $__g_hGetRuntimeClassNameStub, $__g_hGetTrustLevelStub

Global $__g_aOverrides[1][4]

_WinRT_Startup()
_WinUI3_Startup()
If @error Then Exit MsgBox(0, "Error", _WinAPI_GetErrorMessage(@error))

_Main()
_WinUI3_Shutdown()
_WinRT_Shutdown()
ConsoleWrite("Finished." & @CRLF)

Func _Main()

    Local $pApp_Fact = _WinRT_GetActivationFactory("Microsoft.UI.Xaml.Application", $sIID_IApplicationFactory)

    Local $hOnLaunched = DllCallbackRegister("OnLaunched", "none", "ptr;ptr")
    ;IID_IApplicationFactory only has 1 func, but other overrides interfaces may have more. So $ahAppOverridesFuncs param is an array.
    Local $ahAppOverridesFuncs[1] = [$hOnLaunched]

    ;Factory must be on the correct interface for this func to work.
    Local $pApp = CreateOverrides($pApp_Fact, $ahAppOverridesFuncs, $sIID_IApplicationOverrides)

    ;Start App.
    ConsoleWrite("Startup App" & @CRLF)
    Local $pStartupDgt = _WinRT_CreateDelegate("AppStart")

    _WinRT_SwitchInterface($pApp_Fact, $sIID_IApplicationStatics)
    IApplicationStatics_Start($pApp_Fact, $pStartupDgt) ;Starts internal dispatch loop.
    ConsoleWrite("Exiting..." & @CRLF)

    IUnknown_Release($pApp_Fact)
    DestroyOverrides($pApp)
EndFunc

Func AppStart($pThis, $pSender, $pArgs)
    ConsoleWrite("App Startup Delegate Fired." & @CRLF)
EndFunc

Func OnLaunched($pThis, $pEventArgs)
    ConsoleWrite("On Launched Fired." & @CRLF & @CRLF)
    If $pEventArgs Then

        Local $sArgs = ILaunchActivatedEventArgs_GetArguments($pEventArgs)
        ConsoleWrite("LauncArgs=" & $sArgs & @CRLF)

        ;Access shutdown mode porp. should be OnLastWindowClose by default.
        _WinRT_SwitchInterface($pThis, $sIID_IApplication3)
        Local $iShutdownMode = IApplication3_GetDispatcherShutdownMode($pThis)
        ConsoleWrite(StringFormat("ShutdownMode = %s", _WinRT_GetEnum($mDispatcherShutdownMode, $iShutdownMode)) & @CRLF)

        Local $pWindow = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Window")

        ;Create Stackpanel and set as root content
        Local $pStackPanel = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Controls.StackPanel")
        _WinRT_SwitchInterface($pStackPanel, $sIID_IStackPanel)
        _WinRT_SwitchInterface($pWindow, $sIID_IWindow)
        IWindow_SetContent($pWindow, $pStackPanel)

        Local $pButton = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Controls.Button")

        ;Set the button content (text)
        _WinRT_SwitchInterface($pButton, $sIID_IContentControl)
        Local $pProp_Fact = _WinRT_GetActivationFactory("Windows.Foundation.PropertyValue", $sIID_IPropertyValueStatics)
        Local $pProp = IPropertyValueStatics_CreateString($pProp_Fact, "Push Me")
        IContentControl_SetContent($pButton, $pProp)
        IUnknown_Release($pProp)

        ;Add the button to the grid
        _WinRT_SwitchInterface($pStackPanel, $sIID_IPanel)
        Local $pChildren = IPanel_GetChildren($pStackPanel)
        IVector_Append($pChildren, $pButton)
        IUnknown_Release($pChildren)

        ;Set Border
        Local $pColor_Fact = _WinRT_GetActivationFactory("Microsoft.UI.Colors", $sIID_IColorsStatics)
        Local $tColor = IColorsStatics_GetBlue($pColor_Fact)
        IUnknown_Release($pColor_Fact)
        Local $pBrush_Fact = _WinRT_GetActivationFactory("Microsoft.UI.Xaml.Media.SolidColorBrush", $sIID_ISolidColorBrushFactory)
        Local $pBrush = ISolidColorBrushFactory_CreateInstanceWithColor($pBrush_Fact, $tColor)
        IUnknown_Release($pBrush_Fact)
        _WinRT_SwitchInterface($pStackPanel, $sIID_IStackPanel)
        IStackPanel_SetBorderBrush($pStackPanel, $pBrush)
        IUnknown_Release($pBrush)

        Local $tThickness = DllStructCreate("double Left; double Top; double Right; double Bottom")
        $tThickness.Left = 10
        $tThickness.Top = 10
        $tThickness.Right = 10
        $tThickness.Bottom = 10
        IStackPanel_SetBorderThickness($pStackPanel, $tThickness)
        IUnknown_Release($pStackPanel)

        _WinRT_SwitchInterface($pWindow, $sIID_IWindow)
        IWindow_Activate($pWindow)
    EndIf
EndFunc

Func CreateOverrides($pFactory, $ahFunctions, $sIID_IOverrides)

    If Not $__g_hQueryInterfaceStub Then
        $__g_hQueryInterfaceStub = DllCallbackRegister("__QueryInterfaceStub", "long", "ptr;ptr;ptr")
        $__g_hAddRefStub = DllCallbackRegister("__AddRefStub", "long", "ptr")
        $__g_hReleaseStub = DllCallbackRegister("__ReleaseStub", "long", "ptr")
        $__g_hGetIIDsStub = DllCallbackRegister("__GetIIDsStub", "ulong", "ptr;ptr;ptr")
        $__g_hGetRuntimeClassNameStub = DllCallbackRegister("__GetRuntimeClassNameStub", "ulong", "ptr;ptr")
        $__g_hGetTrustLevelStub = DllCallbackRegister("__GetTrustLevelStub", "ulong", "ptr;ptr")
    EndIf

    Local $iOverridesId = UBound($__g_aOverrides)
    ReDim $__g_aOverrides[$iOverridesId + 1][4]
    $__g_aOverrides[0][0] += 1

    Local $tVTab = DllStructCreate(StringFormat("ptr pFunc[%d];", UBound($ahFunctions) + 6))
    $tVTab.pFunc(1) = DllCallbackGetPtr($__g_hQueryInterfaceStub)
    $tVTab.pFunc(2) = DllCallbackGetPtr($__g_hAddRefStub)
    $tVTab.pFunc(3) = DllCallbackGetPtr($__g_hReleaseStub)
    $tVTab.pFunc(4) = DllCallbackGetPtr($__g_hGetIIDsStub)
    $tVTab.pFunc(5) = DllCallbackGetPtr($__g_hGetRuntimeClassNameStub)
    $tVTab.pFunc(6) = DllCallbackGetPtr($__g_hGetTrustLevelStub)
    For $i = 0 To UBound($ahFunctions) - 1
        $tVTab.pFunc((7 + $i)) = DllCallbackGetPtr($ahFunctions[$i])
    Next

    Local $tagOverridesObj = "align 4;ptr pVTab;ptr pInner;byte IID_IOverrides[16]"
    Local $tOverrides = DllStructCreate($tagOverridesObj)
    $tOverrides.pVTab = DllStructGetPtr($tVTab)
    _WinAPI_GUIDFromStringEx($sIID_IOverrides, DllStructGetPtr($tOverrides, "IID_IOverrides"))
    Local $pOverrides = DllStructGetPtr($tOverrides)

    Local $pInner
    Local $pComposed = __CreateInstance($pFactory, $pOverrides, $pInner)
    $tOverrides.pInner = $pInner
    IUnknown_AddRef($pComposed)

    $__g_aOverrides[$iOverridesId][0] = $pComposed
    $__g_aOverrides[$iOverridesId][1] = $tOverrides
    $__g_aOverrides[$iOverridesId][2] = $tVTab
    $__g_aOverrides[$iOverridesId][3] = $ahFunctions

    Return $pComposed
EndFunc

Func DestroyOverrides($pOverrides)
    If Not $pOverrides Or Not IsPtr($pOverrides) Then Return SetError($ERROR_INVALID_PARAMETER, 0, False)
    For $i = 0 To UBound($__g_aOverrides) - 1
        If $__g_aOverrides[$i][0] = $pOverrides Then ExitLoop
    Next
    If $i = UBound($__g_aOverrides) Then Return SetError($ERROR_INVALID_PARAMETER, 0, False)

    Local $iOverridesId = $i
    Local $ahFunctions = $__g_aOverrides[$i][3]
    For $i = 0 To UBound($ahFunctions) - 1
        DllCallbackFree($ahFunctions[$i])
    Next

    For $j = 0 To UBound($__g_aOverrides, 2) - 1
        $__g_aOverrides[$iOverridesId][$j] = 0
    Next
    $__g_aOverrides[0][0] -= 1

    If Not $__g_aOverrides[0][0] Then
        DllCallbackFree($__g_hQueryInterfaceStub)
        DllCallbackFree($__g_hAddRefStub)
        DllCallbackFree($__g_hReleaseStub)
        DllCallbackFree($__g_hGetIIDsStub)
        DllCallbackFree($__g_hGetRuntimeClassNameStub)
        DllCallbackFree($__g_hGetTrustLevelStub)
        $__g_hQueryInterfaceStub = 0
        $__g_hAddRefStub = 0
        $__g_hReleaseStub = 0
        $__g_hGetIIDsStub = 0
        $__g_hGetRuntimeClassNameStub = 0
        $__g_hGetTrustLevelStub = 0
    EndIf
EndFunc

Func __QueryInterfaceStub($pThis, $pIID, $ppObj)
    Local $pFunc, $aCall, $hResult
    Local $tThis = DllStructCreate("align 4;ptr pVTab;ptr pInner;byte IID_IOverrides[16]", $pThis)

    If _WinAPI_StringFromGUID($pIID) = _WinAPI_StringFromGUID(DllStructGetPtr($tThis, "IID_IOverrides")) Then
        DllStructSetData(DllStructCreate("ptr", $ppObj), 1, $pThis)
        __AddRefStub($pThis)
        $hResult = $S_OK
    Else
        $pThis = $tThis.pInner
        $pFunc = __WinRT_GetFuncAddress($pThis, 1)
        If Not @error Then $aCall = DllCallAddress("ulong", $pFunc, "ptr", $pThis, "ptr", $pIID, "ptr", $ppObj)
        $hResult =  @error ? $E_FAIL : $aCall[0]
    EndIf
    Return $hResult
EndFunc

Func __AddRefStub($pThis)
    Local $pFunc, $aCall, $iRefCount = 0
    Local $tThis = DllStructCreate("align 4;ptr pVTab;ptr pInner", $pThis)
    $pThis = $tThis.pInner
    $pFunc = __WinRT_GetFuncAddress($pThis, 2)
    If Not @error Then $aCall = DllCallAddress("uint", $pFunc, "ptr", $pThis)
    If Not @error Then $iRefCount = $aCall[0]
    Return $iRefCount
EndFunc

Func __ReleaseStub($pThis)
    Local $pFunc, $aCall, $iRefCount = 0
    Local $tThis = DllStructCreate("align 4;ptr pVTab;ptr pInner", $pThis)
    $pThis = $tThis.pInner
    $pFunc = __WinRT_GetFuncAddress($pThis, 3)
    If Not @error Then $aCall = DllCallAddress("uint", $pFunc, "ptr", $pThis)
    If Not @error Then $iRefCount = $aCall[0]
;~  ConsoleWrite("refcnt=" & $iRefCount & @CRLF)

    Return $iRefCount
EndFunc

Func __GetIIDsStub($pThis, $pIidCount, $ppIIDs)
    Local $pFunc, $aCall
    Local $tThis = DllStructCreate("align 4;ptr pVTab;ptr pInner", $pThis)
    $pThis = $tThis.pInner
    $pFunc = __WinRT_GetFuncAddress($pThis, 4)
    If Not @error Then $aCall = DllCallAddress("ulong", $pFunc, "ptr", $pThis, "ptr", $pIidCount, "ptr", $ppIIDs)
    Return @error ? $E_FAIL : $aCall[0]
EndFunc

Func __GetRuntimeClassNameStub($pThis, $phClassName)
    Local $pFunc, $aCall
    Local $tThis = DllStructCreate("align 4;ptr pVTab;ptr pInner", $pThis)
    $pThis = $tThis.pInner
    $pFunc = __WinRT_GetFuncAddress($pThis, 5)
    If Not @error Then $aCall = DllCallAddress("ulong", $pFunc, "ptr", $pThis, "ptr", $phClassName)
    Return @error ? $E_FAIL : $aCall[0]
EndFunc

Func __GetTrustLevelStub($pThis, $piTrustLevel)
    Local $pFunc, $aCall
    Local $tThis = DllStructCreate("align 4;ptr pVTab;ptr pInner", $pThis)
    $pThis = $tThis.pInner
    $pFunc = __WinRT_GetFuncAddress($pThis, 6)
    If Not @error Then $aCall = DllCallAddress("ulong", $pFunc, "ptr", $pThis, "ptr", $piTrustLevel)
    Return @error ? $E_FAIL : $aCall[0]
EndFunc

Func __CreateInstance($pThis, $pBaseInterface, ByRef $pInnerInterface)
    Local $vFailVal = Ptr(0)
    Local $pFunc = __WinRT_GetFuncAddress($pThis, 7)
    If @error Then Return SetError(@error, @extended, $vFailVal)
    If $pBaseInterface And IsInt($pBaseInterface) Then $pBaseInterface = Ptr($pBaseInterface)
    If $pBaseInterface And (Not IsPtr($pBaseInterface)) Then Return SetError($ERROR_INVALID_PARAMETER, 0, $vFailVal)
    Local $aCall = DllCallAddress("long", $pFunc, "ptr", $pThis, "ptr", $pBaseInterface, "ptr*", 0, "ptr*", 0)
    If @error Then Return SetError(__WinRT_GetDllError(), 0, $vFailVal)
    $pInnerInterface = $aCall[3]
    Return SetError($aCall[0], 0, $aCall[4])
EndFunc
Posted (edited)

Apologies, I'll jump out of my bubble for a tick!

So I've been working off v1.6 of the WinRT libraries which is available here...
https://sourceforge.net/projects/autoit-winrt-libraries/files/1.6/WinRT v1.6.zip/download

You'll probably want to paste the code from my last post into the "examples" folder. Wherever you put it, just make sure the folder contains the bootstrapper dll "Microsoft.WindowsAppRuntime.Bootstrap.dll".

And by default we're targeting the WindowsAppSDK runtime linked below... (which is the latest stable at the time of posting)
https://aka.ms/windowsappsdk/1.8/1.8.251106002/windowsappruntimeinstall-x64.exe 

Edit: Or if you want to save time, here's a more "portable" version of the same example :) 
https://sourceforge.net/projects/autoit-winrt-libraries/files/WinUI3/WinAppSDK_1.8/AppOnLaunchExample.zip/download

Before/after dark mode is switched on.

image.png.3d1555cb62433039a620d53e5e2ab16e.png

Edited by MattyD
Posted (edited)
5 hours ago, MattyD said:

Everything else seems to pretty much auto-initialise too - so there's less to worry about with presenters, dispatchers, contentbridges, desktopxamlsources etc.  We just need to set the content property of the Window object.

And the app now automatically adjusts for dark mode too!

These are all really great developments on the WinUI 3 side of things. I'm really happy to hear that the your making solid progress and gaining better understanding of it all. Great work! :)

2 hours ago, MattyD said:

So I've been working off v1.6 of the WinRT libraries which is available here...

Personally, I have always kept a directory since your WinRT v1.6 release and drop/create anything in the Examples folder to test and learn. It's a good setup that you've got going on there. Just like that ListView XAML example. Dropped it in there to test and worked great.

2 hours ago, MattyD said:

Before/after dark mode is switched on.

That is great to see for the WinUI 3 stuff. I just tested your latest example with changing themes and it worked perfectly.

Obviously this is all still very early in this journey. But thinking ahead for a moment...

What happens in the case of version mismatches between the bootstrapper DLL and the Windows App SDK that a user has on their machine?

MS has gotten very confusing in recent years. I've never fully understood where (or when) end users get updates to the Windows App SDK platform. I've never seen it come through Windows Update and I've never really seen it anywhere.

I understand that we need to use the bootstrapper DLL since we are not using packaged MSIX. I feel like it's kind of unfortunate that MS doesn't store that bootstrapper DLL anywhere on a user's machine so that we could load it that way which would also be a matching version of what the user has installed.

EDIT: So I don't really have a sense as to whether or not the majority of Win11 users would automatically have the latest Windows App SDK platform and so on.

Edited by WildByDesign

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...