WildByDesign Posted September 1 Posted September 1 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! 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.
MattyD Posted September 1 Author Posted September 1 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! WildByDesign 1
MattyD Posted September 3 Author Posted September 3 (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 September 3 by MattyD argumentum and WildByDesign 2
argumentum Posted September 3 Posted September 3 (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 September 3 by argumentum MattyD and WildByDesign 1 1 Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
WildByDesign Posted November 11 Posted November 11 @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.
MattyD Posted November 11 Author Posted November 11 (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 November 11 by MattyD typo WildByDesign 1
MattyD Posted November 13 Author Posted November 13 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 argumentum and WildByDesign 2
MattyD Posted December 19 Author Posted December 19 (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 December 19 by MattyD
argumentum Posted December 19 Posted December 19 33 minutes ago, MattyD said: Just having a bit more of a play with this. (9,0) [0x00000000] The operation completed successfully. > !>23:39:47 AutoIt3 ended. rc:-1073741819 🤷♂️ On 23H2, #AutoIt3Wrapper_UseX64=y MattyD 1 Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
MattyD Posted December 19 Author Posted December 19 (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 December 19 by MattyD argumentum 1
argumentum Posted December 19 Posted December 19 8 hours ago, MattyD said: try doing a _WinUI3_Startup(1, 7) to target v1.7 and see if that works? (10,0) [0x00000000] The operation completed successfully. > +>09:57:56 AutoIt3 ended. rc:0 That did it. Thanks Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
argumentum Posted December 19 Posted December 19 ... 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. MattyD 1 Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
MattyD Posted December 19 Author Posted December 19 Good idea around checking for x64 in the init funcs. I've updated things on my end, so they should throw ERROR_EXE_MACHINE_TYPE_MISMATCH in future releases. argumentum and WildByDesign 2
MattyD Posted Sunday at 03:59 AM Author Posted Sunday at 03:59 AM 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: IUnknown::QueryInterface IUnknown::AddRef IUnknown::Release IInspectable::GetIIDs IInspectable::GetRuntimeClassName IInspectable::GetTrustLevel 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... argumentum and WildByDesign 2
MattyD Posted Sunday at 01:24 PM Author Posted Sunday at 01:24 PM 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. WildByDesign 1
MattyD Posted Sunday at 11:55 PM Author Posted Sunday at 11:55 PM (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... expandcollapse popup;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 yesterday at 12:55 AM by MattyD argumentum and WildByDesign 2
MattyD Posted yesterday at 06:03 AM Author Posted yesterday at 06:03 AM 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! expandcollapse popup#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 argumentum and WildByDesign 2
argumentum Posted yesterday at 06:15 AM Posted yesterday at 06:15 AM 11 minutes ago, MattyD said: #include "..\Include\Classes\Microsoft.UI.Xaml.Application.au3" impatiently waiting for the whole package Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
MattyD Posted yesterday at 09:11 AM Author Posted yesterday at 09:11 AM (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. Edited yesterday at 10:00 AM by MattyD WildByDesign and argumentum 2
WildByDesign Posted yesterday at 11:36 AM Posted yesterday at 11:36 AM (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 yesterday at 11:39 AM by WildByDesign
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now