Jump to content

Recommended Posts

Posted (edited)

Any feedback on the code is appreciated.

  • It compiles and runs so from that perspective
  • no issues but still complex to understand when to use vtable or just hack by knowing the function numbers.
  • no tesseract or other OCR libraries needed, just plain AutoIt and Windows libraries (on windows 11)


So once in a while I still use AutoIt

and now found out with Powershell 5.1 the WinRt

on how to do stuff with the nice OCR gettext (thats also in Win11 Capture tool) and was wondering how to get this working in AutoIt

Jumping back and forward in threads of this forum on AutoItObject, ObjCreateInterface, Com/Com++, Embedding .NET in AutoIt, CuiAutomation I already understand WinRt is another animal to get into AutoIt.

I didnt want to hijack: 


Learning step 1

  • Understanding combase.dll and RoGetActivationFactory
  • Made a very nice script that goes from
    • Learning WinRt
    • To understand the WinRt OCR
    • To highlighting aspects of the rectangles found
  • So much easier with AI then when I wrote the IUIAutomation library (took me months, now only several hours to understand the WinRt base as extension on traditional COM)
     
; =====================================================================================
; WINRT OCR SCRIPT - THE BULLETPROOF HYBRID METHOD
; =====================================================================================
#include "ScreenCapture.au3"

#region --- GLOBALS & DLLS ---
Global $hDLLComBase = DllOpen("combase.dll")
Global $hDLLOle32 = DllOpen("ole32.dll")
Global $hDLLShcore = DllOpen("shcore.dll")
#endregion

#region --- GUIDS & BLUEPRINTS ---
Global Const $sIInspectable = "{AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90}"
Global Const $sIAsyncInfoGUID = "{00000036-0000-0000-C000-000000000046}"
Global Const $sIRandomAccessStreamGUID = "{905A0FE1-BC53-11DF-8C49-001E4FC686DA}"
Global Const $sIBitmapDecoderStaticsGUID = "{438CCB26-BCEF-4E95-BAD6-23A822E58D01}"
Global Const $sIBitmapFrameWithSoftwareBitmapGUID = "{FE287C9A-420C-4963-87AD-691436E08383}"
Global Const $sIOcrEngineStaticsGUID = "{5BFFA85A-3384-3540-9940-699120D428A8}"
Global Const $sIOcrEngineGUID = "{5A14BC41-5B76-3140-B680-8825562683AC}"

Global $tagIInspectable = _
    "GetIids hresult(ulong*;ptr*);" & _
    "GetRuntimeClassName hresult(ptr*);" & _
    "GetTrustLevel hresult(int*);"

Global $tagIAsyncInfo = $tagIInspectable & _
    "get_Id hresult(uint*);" & _
    "get_Status hresult(int*);" & _
    "get_ErrorCode hresult(long*);" & _
    "Cancel hresult();" & _
    "Close hresult();"

Global $tagIBitmapDecoderStatics = $tagIInspectable & _
    "get_BmpDecoderId hresult(ptr*);" & _
    "get_JpegDecoderId hresult(ptr*);" & _
    "get_PngDecoderId hresult(ptr*);" & _
    "get_TiffDecoderId hresult(ptr*);" & _
    "get_GifDecoderId hresult(ptr*);" & _
    "get_JpegXRDecoderId hresult(ptr*);" & _
    "get_IcoDecoderId hresult(ptr*);" & _
    "GetDecoderInformationEnumerator hresult(ptr*);" & _
    "CreateAsync hresult(ptr;ptr*);"

Global $tagIBitmapFrameWithSoftwareBitmap = $tagIInspectable & _
    "GetSoftwareBitmapAsync hresult(ptr*);"

Global $tagIOcrEngineStatics = $tagIInspectable & _
    "get_MaxImageDimension hresult(uint*);" & _
    "get_AvailableRecognizerLanguages hresult(ptr*);" & _
    "IsLanguageSupported hresult(ptr;bool*);" & _
    "TryCreateFromLanguage hresult(ptr;ptr*);" & _
    "TryCreateFromUserProfileLanguages hresult(ptr*);"

Global $tagIOcrEngine = $tagIInspectable & _
    "RecognizeAsync hresult(ptr;ptr*);" & _
    "get_RecognizerLanguage hresult(ptr*);"
#endregion

; --- OCR RESULT BLUEPRINTS ---
Global $tagIOcrResult = $tagIInspectable & _
    "get_Lines hresult(ptr*);" & _
    "get_TextAngle hresult(ptr*);" & _
    "get_Text hresult(ptr*);"

Global $tagIVectorView = $tagIInspectable & _
    "GetAt hresult(uint;ptr*);" & _
    "get_Size hresult(uint*);"

Global $tagIOcrLine = $tagIInspectable & _
    "get_Words hresult(ptr*);" & _
    "get_Text hresult(ptr*);"

Global $tagIOcrWord = $tagIInspectable & _
    "get_BoundingRect hresult(ptr);" & _
    "get_Text hresult(ptr*);"

#region --- STEP 1 & 2: INIT & CAPTURE ---
ConsoleWrite(">>> [Step 1] Initializing WinRT..." & @CRLF)
DllCall($hDLLComBase, "long", "RoInitialize", "int", 1)

Global $sTempImage = @TempDir & "\ocr_snapshot.png"
ConsoleWrite(">>> [Step 2] Capturing screen to: " & $sTempImage & @CRLF)
_ScreenCapture_Capture($sTempImage, 0, 0, 500, 500)
Sleep(300) ; Geef de hardeschijf genoeg tijd om de foto te flushen
#endregion

#region --- STEP 3: CREATE WINRT STREAM ---
ConsoleWrite(">>> [Step 3] Opening file as a WinRT Stream..." & @CRLF)
Global $pStream = 0
Global $tIID_Stream = _WinRT_CreateGUID($sIRandomAccessStreamGUID)
Global $aResStream = DllCall($hDLLShcore, "long", "CreateRandomAccessStreamOnFile", "wstr", $sTempImage, "dword", 0, "struct*", $tIID_Stream, "ptr*", 0)

If Not @error And $aResStream[0] = 0 Then
    $pStream = $aResStream[4]
    ConsoleWrite("    + Success! Stream pointer: " & $pStream & @CRLF)
Else
    ConsoleWrite("    ! Error: Failed to create Stream." & @CRLF)
    Exit
EndIf
#endregion

#region --- STEP 4: BITMAP DECODER ---
ConsoleWrite(">>> [Step 4] Creating BitmapDecoder..." & @CRLF)
Global $pDecoderStatics = _WinRT_RoGetActivationFactory("Windows.Graphics.Imaging.BitmapDecoder", $sIBitmapDecoderStaticsGUID)
Global $oDecoderStatics = ObjCreateInterface($pDecoderStatics, $sIBitmapDecoderStaticsGUID, $tagIBitmapDecoderStatics, True)

Global $pTicket1 = 0
$oDecoderStatics.CreateAsync($pStream, $pTicket1)

; De Helper functie verzorgt het wachten en de VTable hack voor GetResults!
Global $pBitmapDecoder = _WinRT_WaitAsync($pTicket1, "Decoder")

If $pBitmapDecoder Then
    ConsoleWrite("    + Success! Decoder retrieved: " & $pBitmapDecoder & @CRLF)
Else
    Exit
EndIf
#endregion

#region --- STEP 5: SOFTWARE BITMAP ---
ConsoleWrite(">>> [Step 5] Converting Decoder to SoftwareBitmap..." & @CRLF)
; We dwingen WinRT om de Decoder pointer te voorzien van de SoftwareBitmap lens
Global $oBitmapFrame = ObjCreateInterface($pBitmapDecoder, $sIBitmapFrameWithSoftwareBitmapGUID, $tagIBitmapFrameWithSoftwareBitmap, True)

Global $pTicket2 = 0
$oBitmapFrame.GetSoftwareBitmapAsync($pTicket2)

Global $pSoftwareBitmap = _WinRT_WaitAsync($pTicket2, "SoftwareBitmap")

If $pSoftwareBitmap Then
    ConsoleWrite("    + Success! SoftwareBitmap retrieved: " & $pSoftwareBitmap & @CRLF)
Else
    Exit
EndIf
#endregion

#region --- STEP 6: OCR ENGINE RECOGNIZE ---
ConsoleWrite(">>> [Step 6] Feeding image to OCR Engine..." & @CRLF)
Global $pOcrStatics = _WinRT_RoGetActivationFactory("Windows.Media.Ocr.OcrEngine", $sIOcrEngineStaticsGUID)
Global $oOcrStatics = ObjCreateInterface($pOcrStatics, $sIOcrEngineStaticsGUID, $tagIOcrEngineStatics, True)

Global $pOcrEnginePtr = 0
$oOcrStatics.TryCreateFromUserProfileLanguages($pOcrEnginePtr)
Global $oOcrEngine = ObjCreateInterface($pOcrEnginePtr, $sIOcrEngineGUID, $tagIOcrEngine, True)

ConsoleWrite("    -> Calling RecognizeAsync..." & @CRLF)
Global $pTicket3 = 0
$oOcrEngine.RecognizeAsync($pSoftwareBitmap, $pTicket3)

Global $pOcrResult = _WinRT_WaitAsync($pTicket3, "OCR_Engine")

If $pOcrResult Then
    ConsoleWrite("    + ===========================================================" & @CRLF)
    ConsoleWrite("    + MEGA SUCCESS! OCR Result object retrieved: " & $pOcrResult & @CRLF)
    ConsoleWrite("    + THE IMAGE HAS BEEN SUCCESSFULLY READ BY WINDOWS!" & @CRLF)
    ConsoleWrite("    + ===========================================================" & @CRLF)
Else
    ConsoleWrite("    ! Error: OCR Recognition failed." & @CRLF)
EndIf
#endregion

#region --- STEP 7: EXTRACT TEXT, POSITIONS & VISUAL HUD DEMO ---
ConsoleWrite(">>> [Step 7] Extracting OCR Text and Highlighting Bounding Boxes..." & @CRLF)

Global $pVTable_Res = DllStructGetData(DllStructCreate("ptr", $pOcrResult), 1)

; 1. Complete tekst uitlezen
Global $pGetText_Res = DllStructGetData(DllStructCreate("ptr", $pVTable_Res + (8 * (@AutoItX64 ? 8 : 4))), 1)
Global $aCallText = DllCallAddress("long", $pGetText_Res, "ptr", $pOcrResult, "ptr*", 0)

If Not @error And $aCallText[0] = 0 Then
    Global $hFullText = $aCallText[2]
    ConsoleWrite("    [VOLLEDIGE TEKST] ===================================" & @CRLF)
    ConsoleWrite("    " & _WinRT_GetHStringText($hFullText) & @CRLF)
    ConsoleWrite("    =====================================================" & @CRLF)
    _WinRT_DeleteHString($hFullText)
EndIf

; 2. Lijnen en Woorden ophalen en live highlighten op het scherm
Global $pLinesVector = 0
Global $pGetLines = DllStructGetData(DllStructCreate("ptr", $pVTable_Res + (6 * (@AutoItX64 ? 8 : 4))), 1)
Global $aCallLines = DllCallAddress("long", $pGetLines, "ptr", $pOcrResult, "ptr*", 0)

If Not @error And $aCallLines[0] = 0 Then $pLinesVector = $aCallLines[2]

If $pLinesVector Then
    Global $pVTable_Lines = DllStructGetData(DllStructCreate("ptr", $pLinesVector), 1)
    Global $pGetAt_Lines = DllStructGetData(DllStructCreate("ptr", $pVTable_Lines + (6 * (@AutoItX64 ? 8 : 4))), 1)
    Global $pGetSize_Lines = DllStructGetData(DllStructCreate("ptr", $pVTable_Lines + (7 * (@AutoItX64 ? 8 : 4))), 1)

    Global $iLineCount = 0
    Global $aCallSize = DllCallAddress("long", $pGetSize_Lines, "ptr", $pLinesVector, "uint*", 0)
    If Not @error And $aCallSize[0] = 0 Then $iLineCount = $aCallSize[2]

    ConsoleWrite("    -> Aantal tekstregels gevonden: " & $iLineCount & @CRLF)

    For $i = 0 To $iLineCount - 1
        Global $pLine = 0
        Global $aCallAt = DllCallAddress("long", $pGetAt_Lines, "ptr", $pLinesVector, "uint", $i, "ptr*", 0)
        If Not @error And $aCallAt[0] = 0 Then $pLine = $aCallAt[3]

        If $pLine Then
            Global $pVTable_Line = DllStructGetData(DllStructCreate("ptr", $pLine), 1)
            Global $pGetWords = DllStructGetData(DllStructCreate("ptr", $pVTable_Line + (6 * (@AutoItX64 ? 8 : 4))), 1)

            Global $pWordsVector = 0
            Global $aCallWords = DllCallAddress("long", $pGetWords, "ptr", $pLine, "ptr*", 0)
            If Not @error And $aCallWords[0] = 0 Then $pWordsVector = $aCallWords[2]

            If $pWordsVector Then
                Global $pVTable_Words = DllStructGetData(DllStructCreate("ptr", $pWordsVector), 1)
                Global $pGetAt_Words = DllStructGetData(DllStructCreate("ptr", $pVTable_Words + (6 * (@AutoItX64 ? 8 : 4))), 1)
                Global $pGetSize_Words = DllStructGetData(DllStructCreate("ptr", $pVTable_Words + (7 * (@AutoItX64 ? 8 : 4))), 1)

                Global $iWordCount = 0
                Global $aCallWSize = DllCallAddress("long", $pGetSize_Words, "ptr", $pWordsVector, "uint*", 0)
                If Not @error And $aCallWSize[0] = 0 Then $iWordCount = $aCallWSize[2]

                For $j = 0 To $iWordCount - 1
                    Global $pWord = 0
                    Global $aCallWAt = DllCallAddress("long", $pGetAt_Words, "ptr", $pWordsVector, "uint", $j, "ptr*", 0)
                    If Not @error And $aCallWAt[0] = 0 Then $pWord = $aCallWAt[3]

                    If $pWord Then
                        Global $pVTable_Word = DllStructGetData(DllStructCreate("ptr", $pWord), 1)
                        Global $pGetRect = DllStructGetData(DllStructCreate("ptr", $pVTable_Word + (6 * (@AutoItX64 ? 8 : 4))), 1)
                        Global $pGetText_Word = DllStructGetData(DllStructCreate("ptr", $pVTable_Word + (7 * (@AutoItX64 ? 8 : 4))), 1)

                        ; Tekst ophalen
                        Global $hWordText = 0
                        Global $aCallWText = DllCallAddress("long", $pGetText_Word, "ptr", $pWord, "ptr*", 0)
                        If Not @error And $aCallWText[0] = 0 Then $hWordText = $aCallWText[2]

                        Global $sWordText = _WinRT_GetHStringText($hWordText)
                        _WinRT_DeleteHString($hWordText)

                        ; Coördinaten ophalen (Rect)
                        Global $tRect = DllStructCreate("float X; float Y; float Width; float Height")
                        DllCallAddress("long", $pGetRect, "ptr", $pWord, "ptr", DllStructGetPtr($tRect))

                        Local $fX = DllStructGetData($tRect, "X")
                        Local $fY = DllStructGetData($tRect, "Y")
                        Local $fW = DllStructGetData($tRect, "Width")
                        Local $fH = DllStructGetData($tRect, "Height")

                        ; --- DE DEMO MAGIE: RECHTHOEK TEKENEN OP HET SCHERM ---
                        ; Parameters: Left, Right, Top, Bottom (volgens jouw functie-definitie)
                        _UIA_DrawRect(Int($fX), Int($fX + $fW), Int($fY), Int($fY + $fH), 0x0000FF, 2)

                        ; Kleine pauze zodat je de scanner live over het scherm ziet trekken (optioneel, zet op 0 voor instant)
                        Sleep(15)

                        ConsoleWrite("       Regel " & $i & " | Woord: '" & $sWordText & "' | Box: X=" & $fX & ", Y=" & $fY & ", W=" & $fW & ", H=" & $fH & @CRLF)
                    EndIf
                Next
            EndIf
        EndIf
    Next
EndIf
#endregion

#region --- CLEANUP ---
FileDelete($sTempImage)
DllCall($hDLLComBase, "none", "RoUninitialize")
DllClose($hDLLComBase)
DllClose($hDLLOle32)
DllClose($hDLLShcore)
#endregion

#region --- HELPER FUNCTIONS ---
; De ultieme hybride Async Helper
Func _WinRT_WaitAsync($pAsyncTicket, $sName = "Async")
    If $pAsyncTicket = 0 Then Return 0

    ; 1. SAFE POLLING: We dwingen QueryInterface af via ObjCreateInterface
    Local $oAsyncInfo = ObjCreateInterface($pAsyncTicket, $sIAsyncInfoGUID, $tagIAsyncInfo, True)
    If Not IsObj($oAsyncInfo) Then Return 0

    Local $iStatus = 0
    While True
        $oAsyncInfo.get_Status($iStatus)
        If $iStatus >= 1 Then ExitLoop
        Sleep(10)
    WEnd

    If $iStatus <> 1 Then
        ConsoleWrite("       ! [" & $sName & "] Failed with status: " & $iStatus & @CRLF)
        Return 0
    EndIf

    ; 2. RAW VTABLE HACK VOOR RESULTS: We weten dat GetResults altijd Index 8 is!
    Local $pVTable = DllStructGetData(DllStructCreate("ptr", $pAsyncTicket), 1)
    Local $pGetResultsFunc = DllStructGetData(DllStructCreate("ptr", $pVTable + (8 * (@AutoItX64 ? 8 : 4))), 1)

    Local $aCall = DllCallAddress("long", $pGetResultsFunc, "ptr", $pAsyncTicket, "ptr*", 0)

    If @error Or $aCall[0] <> 0 Then Return 0
    Return $aCall[2] ; Het finale pointer resultaat!
EndFunc

Func _WinRT_RoGetActivationFactory($sClassID, $sIID)
    Local $hsClassID = _WinRT_CreateHString($sClassID)
    Local $tIID = _WinRT_CreateGUID($sIID)
    Local $aRes = DllCall($hDLLComBase, "long", "RoGetActivationFactory", "ptr", $hsClassID, "ptr", DllStructGetPtr($tIID), "ptr*", 0)
    _WinRT_DeleteHString($hsClassID)
    If @error Or $aRes[0] < 0 Then Return 0
    Return $aRes[3]
EndFunc

Func _WinRT_CreateHString($sString)
    Local $aRes = DllCall($hDLLComBase, "long", "WindowsCreateString", "wstr", $sString, "uint", StringLen($sString), "ptr*", 0)
    If @error Or $aRes[0] < 0 Then Return 0
    Return $aRes[3]
EndFunc

Func _WinRT_DeleteHString(ByRef $hString)
    If $hString = 0 Then Return
    DllCall($hDLLComBase, "long", "WindowsDeleteString", "ptr", $hString)
    $hString = 0
EndFunc
Func _WinRT_GetHStringText($hString)
    If $hString = 0 Then Return ""
    Local $aRes = DllCall($hDLLComBase, "ptr", "WindowsGetStringRawBuffer", "ptr", $hString, "uint*", 0)
    If @error Or $aRes[0] = 0 Then Return ""
    Local $iLength = $aRes[2]
    If $iLength = 0 Then Return ""
    Local $tString = DllStructCreate("wchar[" & ($iLength + 1) & "]", $aRes[0])
    Return DllStructGetData($tString, 1)
EndFunc

Func _WinRT_CreateGUID($sGUID)
    Local $tGUID = DllStructCreate("dword Data1;word Data2;word Data3;byte Data4[8]")
    DllCall($hDLLOle32, "long", "CLSIDFromString", "wstr", $sGUID, "struct*", $tGUID)
    Return $tGUID
EndFunc

; Draw rectangle on screen.
Func _UIA_DrawRect($tLeft, $tRight, $tTop, $tBottom, $color = 0xFF, $PenWidth = 4)
    Local $hDC, $hPen, $obj_orig, $x1, $x2, $y1, $y2
    $x1 = $tLeft
    $x2 = $tRight
    $y1 = $tTop
    $y2 = $tBottom
    $hDC = _WinAPI_GetWindowDC(0) ; DC of entire screen (desktop)
    $hPen = _WinAPI_CreatePen(0, $PenWidth, $color) ; $PS_SOLID = 0
    $obj_orig = _WinAPI_SelectObject($hDC, $hPen)

    _WinAPI_DrawLine($hDC, $x1, $y1, $x2, $y1) ; horizontal to right
    _WinAPI_DrawLine($hDC, $x2, $y1, $x2, $y2) ; vertical down on right
    _WinAPI_DrawLine($hDC, $x2, $y2, $x1, $y2) ; horizontal to left right
    _WinAPI_DrawLine($hDC, $x1, $y2, $x1, $y1) ; vertical up on left

    ; clear resources
    _WinAPI_SelectObject($hDC, $obj_orig)
    _WinAPI_DeleteObject($hPen)
    _WinAPI_ReleaseDC(0, $hDC)
EndFunc   ;==>_UIA_DrawRect
#endregion

 

Edited by junkew
Posted (edited)

well done :),

Quick tip - There are a few exceptions, but generally you should release any object that you "aquire".

You won't need to worry about any $pObject object "converted" to $oObject by ObjCreateInterface though. 
ObjCreateInterface does a QI/Release call on the ptr, so the refcount doesn't change at that point.  But an $oObject.Release() is automatically called when $oObject goes out of scope. So  that cancels out the original $pObject reference. 

But the collections etc. that you're passing directly to DllCallAddress() should probably be released at some point!

Edited by MattyD
Posted

And made a small WinRt WinMD explorer as a learning exercise (not sure whom learned more me or AI, both didnt understand but together we made it 😉 ) in hours instead of months

 

; =====================================================================================
; WINMD METADATA EXPLORER - OCR-STYLE HYBRID METHOD (UITGEBREID)
; Gebaseerd op de bewezen technieken uit het WinRT OCR script.
;
; UITBREIDINGEN:
;  - GUID per interface/type (via GuidAttribute custom attribute)
;  - Gedecodeerde method-signatures (echte parameter-types i.p.v. ptr*)
;  - Properties & Fields enumeratie
;  - CRLF na elke method-definitie in de dtag string
; =====================================================================================

#include <GUIConstantsEx.au3>
#include <TreeViewConstants.au3>
#include <WindowsConstants.au3>
#include <WinAPI.au3>

#region --- GLOBALS & DLLS (persistente handles) ---
Global $hDLLComBase    = DllOpen("combase.dll")
Global $hDLLOle32      = DllOpen("ole32.dll")
Global $hDLLRoMetaData = DllOpen("rometadata.dll")
#endregion

#region --- GUIDS (CORRECTE WinRT-varianten) ---
Global Const $sCLSID_CorMetaDataDispenser = "{E5CB7A31-7512-11D2-89CE-0080C792E5D8}"
Global Const $sIID_IMetaDataDispenser     = "{809C652E-7396-11D2-9771-00A0C9B4D50C}"
Global Const $sIID_IMetaDataImport        = "{7DAC8207-D3AE-4C75-9B67-92801A497D44}"

Global Const $ofRead = 0x00000000

; Token tabel-types
Global Const $MDT_TypeDef   = 0x02000000
Global Const $MDT_TypeRef   = 0x01000000
Global Const $MDT_TypeSpec  = 0x1B000000
Global Const $MDT_MethodDef = 0x06000000
#endregion

#region --- ELEMENT TYPE CONSTANTEN (voor signature decoding) ---
Global Const $ELEMENT_TYPE_VOID       = 0x01
Global Const $ELEMENT_TYPE_BOOLEAN    = 0x02
Global Const $ELEMENT_TYPE_CHAR       = 0x03
Global Const $ELEMENT_TYPE_I1         = 0x04
Global Const $ELEMENT_TYPE_U1         = 0x05
Global Const $ELEMENT_TYPE_I2         = 0x06
Global Const $ELEMENT_TYPE_U2         = 0x07
Global Const $ELEMENT_TYPE_I4         = 0x08
Global Const $ELEMENT_TYPE_U4         = 0x09
Global Const $ELEMENT_TYPE_I8         = 0x0A
Global Const $ELEMENT_TYPE_U8         = 0x0B
Global Const $ELEMENT_TYPE_R4         = 0x0C
Global Const $ELEMENT_TYPE_R8         = 0x0D
Global Const $ELEMENT_TYPE_STRING     = 0x0E
Global Const $ELEMENT_TYPE_BYREF      = 0x10
Global Const $ELEMENT_TYPE_VALUETYPE  = 0x11
Global Const $ELEMENT_TYPE_CLASS      = 0x12
Global Const $ELEMENT_TYPE_VAR        = 0x13
Global Const $ELEMENT_TYPE_GENERICINST = 0x15
Global Const $ELEMENT_TYPE_I          = 0x18
Global Const $ELEMENT_TYPE_U          = 0x19
Global Const $ELEMENT_TYPE_OBJECT     = 0x1C
Global Const $ELEMENT_TYPE_SZARRAY    = 0x1D

; Map: element-type -> leesbare naam
Global $g_mElementType[]
$g_mElementType[$ELEMENT_TYPE_VOID]    = "Void"
$g_mElementType[$ELEMENT_TYPE_BOOLEAN] = "Boolean"
$g_mElementType[$ELEMENT_TYPE_CHAR]    = "Char16"
$g_mElementType[$ELEMENT_TYPE_I1]      = "Int8"
$g_mElementType[$ELEMENT_TYPE_U1]      = "UInt8"
$g_mElementType[$ELEMENT_TYPE_I2]      = "Int16"
$g_mElementType[$ELEMENT_TYPE_U2]      = "UInt16"
$g_mElementType[$ELEMENT_TYPE_I4]      = "Int32"
$g_mElementType[$ELEMENT_TYPE_U4]      = "UInt32"
$g_mElementType[$ELEMENT_TYPE_I8]      = "Int64"
$g_mElementType[$ELEMENT_TYPE_U8]      = "UInt64"
$g_mElementType[$ELEMENT_TYPE_R4]      = "Single"
$g_mElementType[$ELEMENT_TYPE_R8]      = "Double"
$g_mElementType[$ELEMENT_TYPE_STRING]  = "String"
$g_mElementType[$ELEMENT_TYPE_OBJECT]  = "Object"

; Map: element-type -> AutoIt DllCall type (voor de dtag)
Global $g_mAutoItType[]
$g_mAutoItType[$ELEMENT_TYPE_VOID]    = "none"
$g_mAutoItType[$ELEMENT_TYPE_BOOLEAN] = "bool"
$g_mAutoItType[$ELEMENT_TYPE_CHAR]    = "word"
$g_mAutoItType[$ELEMENT_TYPE_I1]      = "byte"
$g_mAutoItType[$ELEMENT_TYPE_U1]      = "byte"
$g_mAutoItType[$ELEMENT_TYPE_I2]      = "short"
$g_mAutoItType[$ELEMENT_TYPE_U2]      = "ushort"
$g_mAutoItType[$ELEMENT_TYPE_I4]      = "long"
$g_mAutoItType[$ELEMENT_TYPE_U4]      = "ulong"
$g_mAutoItType[$ELEMENT_TYPE_I8]      = "int64"
$g_mAutoItType[$ELEMENT_TYPE_U8]      = "uint64"
$g_mAutoItType[$ELEMENT_TYPE_R4]      = "float"
$g_mAutoItType[$ELEMENT_TYPE_R8]      = "double"
$g_mAutoItType[$ELEMENT_TYPE_STRING]  = "ptr"
$g_mAutoItType[$ELEMENT_TYPE_OBJECT]  = "ptr"
#endregion

#region --- INTERFACE BLUEPRINTS (tags) ---
Global $tagIMetaDataDispenser = _
        "DefineScope hresult(ptr;dword;ptr;ptr*);" & _
        "OpenScope hresult(wstr;dword;struct*;ptr*);" & _
        "OpenScopeOnMemory hresult(ptr;dword;dword;struct*;ptr*);"

; IMetaDataImport - vtable in exacte cor.h volgorde
; Uitgebreid met GetPropertyProps, GetCustomAttributeByName, GetSigFromToken
Global $tagIMetaDataImport = _
        "CloseEnum none(ptr);" & _
        "CountEnum hresult(ptr;ulong*);" & _
        "ResetEnum hresult(ptr;ulong);" & _
        "EnumTypeDefs hresult(ptr*;ptr;ulong;ulong*);" & _
        "EnumInterfaceImpls hresult(ptr*;dword;ptr;ulong;ulong*);" & _
        "EnumTypeRefs hresult(ptr*;ptr;ulong;ulong*);" & _
        "FindTypeDefByName hresult(wstr;dword;dword*);" & _
        "GetScopeProps hresult(ptr;ulong;ulong*;ptr);" & _
        "GetModuleFromScope hresult(dword*);" & _
        "GetTypeDefProps hresult(dword;ptr;ulong;ulong*;dword*;dword*);" & _
        "GetInterfaceImplProps hresult(dword;dword*;dword*);" & _
        "GetTypeRefProps hresult(dword;dword*;ptr;ulong;ulong*);" & _
        "ResolveTypeRef hresult(dword;ptr;ptr*;dword*);" & _
        "EnumMembers hresult(ptr*;dword;ptr;ulong;ulong*);" & _
        "EnumMembersWithName hresult(ptr*;dword;wstr;ptr;ulong;ulong*);" & _
        "EnumMethods hresult(ptr*;dword;ptr;ulong;ulong*);" & _
        "EnumMethodsWithName hresult(ptr*;dword;wstr;ptr;ulong;ulong*);" & _
        "EnumFields hresult(ptr*;dword;ptr;ulong;ulong*);" & _
        "EnumFieldsWithName hresult(ptr*;dword;wstr;ptr;ulong;ulong*);" & _
        "EnumParams hresult(ptr*;dword;ptr;ulong;ulong*);" & _
        "EnumMemberRefs hresult(ptr*;dword;ptr;ulong;ulong*);" & _
        "EnumMethodImpls hresult(ptr*;dword;ptr;ptr;ulong;ulong*);" & _
        "EnumPermissionSets hresult(ptr*;dword;dword;ptr;ulong;ulong*);" & _
        "FindMember hresult(dword;wstr;ptr;ulong;dword*);" & _
        "FindMethod hresult(dword;wstr;ptr;ulong;dword*);" & _
        "FindField hresult(dword;wstr;ptr;ulong;dword*);" & _
        "FindMemberRef hresult(dword;wstr;ptr;ulong;dword*);" & _
        "GetMethodProps hresult(dword;dword*;ptr;ulong;ulong*;dword*;ptr*;ulong*;ulong*;dword*);" & _
        "GetMemberRefProps hresult(dword;dword*;ptr;ulong;ulong*;ptr*;ulong*);" & _
        "EnumProperties hresult(ptr*;dword;ptr;ulong;ulong*);" & _
        "EnumEvents hresult(ptr*;dword;ptr;ulong;ulong*);" & _
        "GetEventProps hresult(dword;dword*;ptr;ulong;ulong*;dword*;dword*;dword*;dword*;dword*;dword*;ptr;ulong;ulong*);" & _
        "EnumMethodSemantics hresult(ptr*;dword;ptr;ulong;ulong*);" & _
        "GetMethodSemantics hresult(dword;dword;dword*);" & _
        "GetClassLayout hresult(dword;dword*;ptr;ulong;ulong*;ulong*);" & _
        "GetFieldMarshal hresult(dword;ptr*;ulong*);" & _
        "GetRVA hresult(dword;ulong*;dword*);" & _
        "GetPermissionSetProps hresult(dword;dword*;ptr*;ulong*);" & _
        "GetSigFromToken hresult(dword;ptr*;ulong*);" & _
        "GetModuleRefProps hresult(dword;ptr;ulong;ulong*);" & _
        "EnumModuleRefs hresult(ptr*;ptr;ulong;ulong*);" & _
        "GetTypeSpecFromToken hresult(dword;ptr*;ulong*);" & _
        "GetNameFromToken hresult(dword;ptr*);" & _
        "EnumUnresolvedMethods hresult(ptr*;ptr;ulong;ulong*);" & _
        "GetUserString hresult(dword;ptr;ulong;ulong*);" & _
        "GetPinvokeMap hresult(dword;dword*;ptr;ulong;ulong*;dword*);" & _
        "EnumSignatures hresult(ptr*;ptr;ulong;ulong*);" & _
        "EnumTypeSpecs hresult(ptr*;ptr;ulong;ulong*);" & _
        "EnumUserStrings hresult(ptr*;ptr;ulong;ulong*);" & _
        "GetParamForMethodIndex hresult(dword;ulong;dword*);" & _
        "EnumCustomAttributes hresult(ptr*;dword;dword;ptr;ulong;ulong*);" & _
        "GetCustomAttributeProps hresult(dword;dword*;dword*;ptr*;ulong*);" & _
        "FindTypeRef hresult(dword;wstr;dword*);" & _
        "GetMemberProps hresult(dword;dword*;ptr;ulong;ulong*;dword*;ptr*;ulong*;dword*;dword*;dword*;ptr*;ulong*);" & _
        "GetFieldProps hresult(dword;dword*;ptr;ulong;ulong*;dword*;ptr*;ulong*;dword*;ptr*;ulong*);" & _
        "GetPropertyProps hresult(dword;dword*;ptr;ulong;ulong*;dword*;ptr*;ulong*;dword*;ptr*;ulong*;dword*;dword*;ptr;ulong;ulong*);" & _
        "GetParamProps hresult(dword;dword*;ulong*;ptr;ulong;ulong*;dword*;dword*;ptr*;ulong*);" & _
        "GetCustomAttributeByName hresult(dword;wstr;ptr*;ulong*);" & _
        "IsValidToken bool(dword);" & _
        "GetNestedClassProps hresult(dword;dword*);" & _
        "GetNativeCallConvFromSig hresult(ptr;ulong;ulong*);" & _
        "IsGlobal hresult(dword;int*);"
#endregion

#region --- PADEN ---
Global $sWinMDDir = @SystemDir & "\WinMetadata"
Global Const $sDefaultWinMDName = "Windows.Media.winmd"
#endregion

#region --- STEP 1: INIT WINRT & GUI ---
ConsoleWrite(">>> [Step 1] Initializing WinRT & GUI..." & @CRLF)
ConsoleWrite("    AutoIt bitness: " & (@AutoItX64 ? "x64" : "x86") & @CRLF)

If Not @AutoItX64 Then
    ConsoleWrite("    ! WAARSCHUWING: rometadata.dll vereist x64!" & @CRLF)
EndIf

DllCall($hDLLComBase, "long", "RoInitialize", "int", 1)

Global $hGUI = GUICreate("WinMD Explorer (OCR-stijl) - Uitgebreid", 1100, 750)

GUICtrlCreateLabel("Selecteer WinMD Bestand:", 10, 10, 360, 20)
Global $idComboWinMD = GUICtrlCreateCombo("", 10, 30, 360, 25)

Global $idTree = GUICtrlCreateTreeView(10, 65, 360, 675, BitOR($TVS_HASBUTTONS, $TVS_HASLINES, $TVS_LINESATROOT))
Global $idEditInfo = GUICtrlCreateEdit("", 380, 30, 710, 160)
Global $idEditDTag = GUICtrlCreateEdit("", 380, 215, 710, 260)
Global $idEditCode = GUICtrlCreateEdit("", 380, 500, 710, 240)

GUICtrlCreateLabel("Type Informatie (GUID, Properties, Fields):", 380, 10, 400, 20)
GUICtrlCreateLabel("Gegenereerde $dtag VTable String:", 380, 195, 400, 20)
GUICtrlCreateLabel("AutoIt WinRT Boilerplate Code:", 380, 480, 400, 20)

_PopulateWinMDCombo()
GUISetState(@SW_SHOW)
ConsoleWrite("    + GUI getoond." & @CRLF & @CRLF)
#endregion

; Globale state
Global $g_oImport = 0
Global $g_pImport = 0
Global $g_mTokens[10000]
Global $g_hLastSelectedTreeItem = 0
Global $g_sLastSelectedWinMD = ""

_LoadSelectedWinMD()

; --- MAIN LOOP ---
While 1
    Local $nMsg = GUIGetMsg()
    Switch $nMsg
        Case $GUI_EVENT_CLOSE
            ExitLoop
        Case $idComboWinMD
            _LoadSelectedWinMD()
    EndSwitch

    Local $hSelectedTree = GUICtrlRead($idTree)
    If $hSelectedTree <> 0 And $hSelectedTree <> $g_hLastSelectedTreeItem Then
        $g_hLastSelectedTreeItem = $hSelectedTree
        _OnTreeItemSelected($hSelectedTree)
    EndIf
WEnd

; --- CLEANUP ---
ConsoleWrite(">>> Cleanup..." & @CRLF)
DllCall($hDLLComBase, "none", "RoUninitialize")
DllClose($hDLLComBase)
DllClose($hDLLOle32)
DllClose($hDLLRoMetaData)
Exit

; =====================================================================================
; STEP 2: DISPENSER
; =====================================================================================
Func _WinRT_GetMetaDataDispenser()
    Local $tCLSID = _WinRT_CreateGUID($sCLSID_CorMetaDataDispenser)
    Local $tIID   = _WinRT_CreateGUID($sIID_IMetaDataDispenser)

    Local $aRes = DllCall($hDLLRoMetaData, "long", "MetaDataGetDispenser", _
            "struct*", $tCLSID, "struct*", $tIID, "ptr*", 0)

    If @error Then Return 0
    ConsoleWrite(StringFormat("    -> MetaDataGetDispenser HRESULT: 0x%08X | Ptr: 0x%X\n", $aRes[0], $aRes[3]))
    If $aRes[0] >= 0 And $aRes[3] <> 0 Then Return $aRes[3]
    Return 0
EndFunc   ;==>_WinRT_GetMetaDataDispenser

; =====================================================================================
; STEP 3: WINMD LADEN
; =====================================================================================
Func _LoadSelectedWinMD()
    Local $sSelectedFile = GUICtrlRead($idComboWinMD)
    If $sSelectedFile = "" Or $sSelectedFile = $g_sLastSelectedWinMD Then Return

    $g_sLastSelectedWinMD = $sSelectedFile
    Local $sFullPath = $sWinMDDir & "\" & $sSelectedFile

    ConsoleWrite("=================================================================" & @CRLF)
    ConsoleWrite(">>> LAAD: '" & $sSelectedFile & "'" & @CRLF)
    ConsoleWrite("=================================================================" & @CRLF)

    GUICtrlSendMsg($idTree, 0x1101, 0, 0)
    GUICtrlSetData($idEditInfo, "")
    GUICtrlSetData($idEditDTag, "")
    GUICtrlSetData($idEditCode, "")
    $g_hLastSelectedTreeItem = 0
    ReDim $g_mTokens[10000]

    If Not FileExists($sFullPath) Then
        ConsoleWrite("    ! Bestand niet gevonden: " & $sFullPath & @CRLF)
        Return
    EndIf

    Local $pDispenser = _WinRT_GetMetaDataDispenser()
    If Not $pDispenser Then Return

    Local $oDispenser = ObjCreateInterface($pDispenser, $sIID_IMetaDataDispenser, $tagIMetaDataDispenser, True)
    If Not IsObj($oDispenser) Then Return
    ConsoleWrite("    + IMetaDataDispenser object aangemaakt." & @CRLF)

    Local $tIID_Import = _WinRT_CreateGUID($sIID_IMetaDataImport)
    $g_pImport = 0
    Local $hr = $oDispenser.OpenScope($sFullPath, $ofRead, $tIID_Import, $g_pImport)
    ConsoleWrite(StringFormat("    -> OpenScope HRESULT: 0x%08X | Scope Ptr: 0x%X\n", $hr, $g_pImport))
    If $hr < 0 Or $g_pImport = 0 Then Return

    $g_oImport = ObjCreateInterface($g_pImport, $sIID_IMetaDataImport, $tagIMetaDataImport, True)
    If Not IsObj($g_oImport) Then Return
    ConsoleWrite("    + IMetaDataImport object aangemaakt." & @CRLF)

    _PopulateTreeView($sSelectedFile)
EndFunc   ;==>_LoadSelectedWinMD

; =====================================================================================
; STEP 4: TYPEDEFS ENUMEREREN
; =====================================================================================
Func _PopulateTreeView($sFileName)
    Local $idRootNode = GUICtrlCreateTreeViewItem($sFileName, $idTree)

    Local $hEnum = 0
    Local $tTokens = DllStructCreate("dword Tokens[5000]")
    Local $iCount = 0

    Local $hr = $g_oImport.EnumTypeDefs($hEnum, DllStructGetPtr($tTokens), 5000, $iCount)
    ConsoleWrite(StringFormat("    -> EnumTypeDefs HRESULT: 0x%08X | Aantal: %d\n", $hr, $iCount))

    If $hEnum <> 0 Then $g_oImport.CloseEnum($hEnum)
    If $hr < 0 Or $iCount = 0 Then Return

    Local $iAdded = 0
    For $i = 1 To $iCount
        Local $idToken = DllStructGetData($tTokens, "Tokens", $i)
        Local $tName = DllStructCreate("wchar Name[512]")
        Local $iNameLen = 0, $iFlags = 0, $iExtends = 0

        Local $hrProp = $g_oImport.GetTypeDefProps($idToken, DllStructGetPtr($tName), 512, $iNameLen, $iFlags, $iExtends)
        If $hrProp < 0 Then ContinueLoop

        Local $sTypeName = DllStructGetData($tName, "Name")
        If $sTypeName <> "" Then
            Local $idItem = GUICtrlCreateTreeViewItem($sTypeName, $idRootNode)
            If $idItem > 0 And $idItem < UBound($g_mTokens) Then $g_mTokens[$idItem] = $idToken
            $iAdded += 1
        EndIf
    Next

    ConsoleWrite(StringFormat("    + %d TypeDefs toegevoegd.\n\n", $iAdded))
EndFunc   ;==>_PopulateTreeView

; =====================================================================================
; EVENT: TreeView selectie
; =====================================================================================
Func _OnTreeItemSelected($idItem)
    Local $sText = GUICtrlRead($idItem, 1)
    If $idItem >= UBound($g_mTokens) Or $g_mTokens[$idItem] = 0 Then Return
    Local $idToken = $g_mTokens[$idItem]
    ConsoleWrite(StringFormat(">>> TypeDef geselecteerd: '%s' (Token: 0x%08X)\n", $sText, $idToken))

    _ShowTypeInfo($sText, $idToken)
    _GenerateClassCode($sText, $idToken)
EndFunc   ;==>_OnTreeItemSelected

; =====================================================================================
; UITBREIDING 1 & 3: TYPE INFO (GUID + Properties + Fields)
; =====================================================================================
Func _ShowTypeInfo($sClassName, $idTypeDefToken)
    Local $sInfo = "Type: " & $sClassName & @CRLF
    $sInfo &= "TypeDef Token: 0x" & Hex($idTypeDefToken, 8) & @CRLF

    ; --- GUID ophalen via GuidAttribute ---
    Local $sGUID = _MetaData_GetGUIDAttrib($idTypeDefToken)
    If $sGUID <> "" Then
        $sInfo &= "GUID (IID): " & $sGUID & @CRLF
    Else
        $sInfo &= "GUID (IID): <geen - dit type heeft geen GuidAttribute>" & @CRLF
    EndIf

    ; --- Properties enumereren ---
    $sInfo &= @CRLF & "--- Properties ---" & @CRLF
    Local $hEnum = 0
    Local $tPropTokens = DllStructCreate("dword Tokens[200]")
    Local $iPropCount = 0
    Local $hr = $g_oImport.EnumProperties($hEnum, $idTypeDefToken, DllStructGetPtr($tPropTokens), 200, $iPropCount)
    If $hEnum <> 0 Then $g_oImport.CloseEnum($hEnum)

    If $hr >= 0 And $iPropCount > 0 Then
        For $i = 1 To $iPropCount
            Local $idPropTkn = DllStructGetData($tPropTokens, "Tokens", $i)
            Local $sPropName = _MetaData_GetPropertyName($idPropTkn)
            If $sPropName <> "" Then $sInfo &= "  " & $sPropName & @CRLF
        Next
    Else
        $sInfo &= "  <geen properties>" & @CRLF
    EndIf

    ; --- Fields enumereren ---
    $sInfo &= @CRLF & "--- Fields ---" & @CRLF
    $hEnum = 0
    Local $tFieldTokens = DllStructCreate("dword Tokens[200]")
    Local $iFieldCount = 0
    $hr = $g_oImport.EnumFields($hEnum, $idTypeDefToken, DllStructGetPtr($tFieldTokens), 200, $iFieldCount)
    If $hEnum <> 0 Then $g_oImport.CloseEnum($hEnum)

    If $hr >= 0 And $iFieldCount > 0 Then
        For $i = 1 To $iFieldCount
            Local $idFieldTkn = DllStructGetData($tFieldTokens, "Tokens", $i)
            Local $sFieldName = _MetaData_GetFieldName($idFieldTkn)
            If $sFieldName <> "" Then $sInfo &= "  " & $sFieldName & @CRLF
        Next
    Else
        $sInfo &= "  <geen fields>" & @CRLF
    EndIf

    GUICtrlSetData($idEditInfo, $sInfo)
EndFunc   ;==>_ShowTypeInfo

; =====================================================================================
; STEP 5: METHODEN + SIGNATURE DECODING + BOILERPLATE
; =====================================================================================
Func _GenerateClassCode($sClassName, $idTypeDefToken)
    Local $hEnum = 0
    Local $tMethodTokens = DllStructCreate("dword Tokens[500]")
    Local $iMethodCount = 0

    Local $hr = $g_oImport.EnumMethods($hEnum, $idTypeDefToken, DllStructGetPtr($tMethodTokens), 500, $iMethodCount)
    ConsoleWrite(StringFormat("    -> EnumMethods HRESULT: 0x%08X | Aantal: %d\n", $hr, $iMethodCount))

    If $hEnum <> 0 Then $g_oImport.CloseEnum($hEnum)

    ; IInspectable-methoden staan altijd vooraan (elk op eigen regel met CRLF)
    Local $sDTag = "GetIids hresult(ulong*;ptr*);" & @CRLF
    $sDTag &= "GetRuntimeClassName hresult(ptr*);" & @CRLF
    $sDTag &= "GetTrustLevel hresult(int*);" & @CRLF

    If $hr >= 0 And $iMethodCount > 0 Then
        For $i = 1 To $iMethodCount
            Local $idMethodToken = DllStructGetData($tMethodTokens, "Tokens", $i)

            Local $tMethodName = DllStructCreate("wchar Name[256]")
            Local $iNameLen = 0, $iClass = 0, $iAttr = 0
            Local $pSig = 0, $cbSig = 0, $iRVA = 0, $iImpl = 0

            Local $hrM = $g_oImport.GetMethodProps($idMethodToken, $iClass, DllStructGetPtr($tMethodName), _
                    256, $iNameLen, $iAttr, $pSig, $cbSig, $iRVA, $iImpl)
            If $hrM < 0 Then ContinueLoop

            Local $sMethodName = DllStructGetData($tMethodName, "Name")

            ; Sla .ctor / interne methoden over
            If $sMethodName = "" Or StringLeft($sMethodName, 1) = "." Then ContinueLoop

            ; --- UITBREIDING 2: signature decoderen ---
            Local $sAutoItSig = _MetaData_DecodeMethodSig($pSig, $cbSig)

            ConsoleWrite(StringFormat("       + [%d/%d] %s %s\n", $i, $iMethodCount, $sMethodName, $sAutoItSig))

            ; Elke method op eigen regel met CRLF
            $sDTag &= $sMethodName & " " & $sAutoItSig & ";" & @CRLF
        Next
    EndIf

    Local $sClassSafe = StringReplace($sClassName, ".", "_")
    Local $sGUID = _MetaData_GetGUIDAttrib($idTypeDefToken)
    If $sGUID = "" Then $sGUID = "{PLAATS-HIER-DE-IID}"

    ; dtag string
    GUICtrlSetData($idEditDTag, '$tag' & $sClassSafe & ' = _' & @CRLF & '"' & $sDTag & '"')

    ; Boilerplate met echte GUID
    Local $sCode = _
        '; === WINRT BOILERPLATE: ' & $sClassName & ' ===' & @CRLF & _
        'Global Const $sIID_' & $sClassSafe & ' = "' & $sGUID & '"' & @CRLF & @CRLF & _
        '; Factory ophalen (OCR-stijl)' & @CRLF & _
        'Local $pFactory = _WinRT_RoGetActivationFactory("' & $sClassName & '", $sIID_' & $sClassSafe & ')' & @CRLF & _
        'Local $oObj = ObjCreateInterface($pFactory, $sIID_' & $sClassSafe & ', $tag' & $sClassSafe & ', True)' & @CRLF & _
        'If IsObj($oObj) Then' & @CRLF & _
        '    ConsoleWrite("+ Geactiveerd!" & @CRLF)' & @CRLF & _
        '    ; $oObj.MethodeNaam(...)' & @CRLF & _
        'EndIf'
    GUICtrlSetData($idEditCode, $sCode)
EndFunc   ;==>_GenerateClassCode

; =====================================================================================
; UITBREIDING 1: GUID via GuidAttribute custom attribute
; =====================================================================================
Func _MetaData_GetGUIDAttrib($idToken)
    Local $pData = 0, $cbData = 0
    Local $hr = $g_oImport.GetCustomAttributeByName($idToken, _
            "Windows.Foundation.Metadata.GuidAttribute", $pData, $cbData)

    ; S_OK = 0. S_FALSE = 1 (attribuut niet aanwezig)
    If $hr <> 0 Or $pData = 0 Or $cbData < 20 Then Return ""

    ; De blob begint met 2 prolog-bytes (0x01 0x00), daarna de 16-byte GUID
    ; Lees de GUID uit het geheugen op $pData + 2
    Local $tGUID = DllStructCreate("dword Data1;word Data2;word Data3;byte Data4[8]", $pData + 2)

    Local $iData1 = DllStructGetData($tGUID, "Data1")
    Local $iData2 = DllStructGetData($tGUID, "Data2")
    Local $iData3 = DllStructGetData($tGUID, "Data3")

    ; Data4 zijn 8 losse bytes
    Local $sData4 = ""
    For $i = 1 To 8
        $sData4 &= Hex(DllStructGetData($tGUID, "Data4", $i), 2)
    Next

    Return StringFormat("{%08X-%04X-%04X-%s-%s}", $iData1, $iData2, $iData3, _
            StringLeft($sData4, 4), StringMid($sData4, 5))
EndFunc   ;==>_MetaData_GetGUIDAttrib

; =====================================================================================
; UITBREIDING 3: Property naam ophalen
; =====================================================================================
Func _MetaData_GetPropertyName($idPropTkn)
    Local $tName = DllStructCreate("wchar Name[256]")
    Local $iClass = 0, $iNameLen = 0, $iFlags = 0
    Local $pSig = 0, $cbSig = 0, $iCPlusTypeFlag = 0
    Local $pDefVal = 0, $cbDefVal = 0
    Local $iSetter = 0, $iGetter = 0
    Local $tOtherMethods = DllStructCreate("dword[10]")
    Local $iOtherCount = 0

    Local $hr = $g_oImport.GetPropertyProps($idPropTkn, $iClass, DllStructGetPtr($tName), 256, $iNameLen, _
            $iFlags, $pSig, $cbSig, $iCPlusTypeFlag, $pDefVal, $cbDefVal, _
            $iSetter, $iGetter, DllStructGetPtr($tOtherMethods), 10, $iOtherCount)

    If $hr < 0 Then Return ""
    Return DllStructGetData($tName, "Name")
EndFunc   ;==>_MetaData_GetPropertyName

; =====================================================================================
; UITBREIDING 3: Field naam ophalen
; =====================================================================================
Func _MetaData_GetFieldName($idFieldTkn)
    Local $tName = DllStructCreate("wchar Name[256]")
    Local $iClass = 0, $iNameLen = 0, $iAttr = 0
    Local $pSig = 0, $cbSig = 0, $iCPlusTypeFlag = 0
    Local $pValue = 0, $cchValue = 0

    Local $hr = $g_oImport.GetFieldProps($idFieldTkn, $iClass, DllStructGetPtr($tName), 256, $iNameLen, _
            $iAttr, $pSig, $cbSig, $iCPlusTypeFlag, $pValue, $cchValue)

    If $hr < 0 Then Return ""
    Return DllStructGetData($tName, "Name")
EndFunc   ;==>_MetaData_GetFieldName

; =====================================================================================
; UITBREIDING 2: Method signature decoderen naar AutoIt DllCall types
; Geeft iets als: hresult(ptr;float;bool*) terug
; =====================================================================================
Func _MetaData_DecodeMethodSig($pSig, $cbSig)
    ; Fallback als er geen signature is
    If $pSig = 0 Or $cbSig = 0 Then Return "hresult(ptr*)"

    ; Kopieer de signature-blob naar een leesbare struct
    Local $tSig = DllStructCreate("byte[" & $cbSig & "]", $pSig)

    Local $iReadPtr = 1

    ; Byte 1: calling convention (HASTHIS etc.) - overslaan
    $iReadPtr += 1

    ; Byte 2: aantal parameters (compressed int)
    Local $iParamCount = __ReadCompressedInt($tSig, $iReadPtr)

    ; Return type (het eerste "type" na de param-count is de return type)
    Local $sRetType = __ReadSigType($tSig, $iReadPtr)

    ; WinRT-methoden geven altijd HRESULT terug; de "echte" return komt als
    ; laatste out-param. We bouwen de AutoIt-signatuur op met HRESULT.
    Local $sResult = "hresult("

    ; De parameters
    Local $sParams = ""
    For $p = 1 To $iParamCount
        Local $sParamType = __ReadSigType($tSig, $iReadPtr)
        If $sParams <> "" Then $sParams &= ";"
        $sParams &= $sParamType
    Next

    ; De WinRT-return-waarde wordt als out-param toegevoegd (behalve bij Void)
    If $sRetType <> "none" Then
        If $sParams <> "" Then $sParams &= ";"
        $sParams &= $sRetType & "*"
    EndIf

    ; Als er helemaal geen params/return zijn -> minimaal ptr*
    If $sParams = "" Then $sParams = "ptr*"

    $sResult &= $sParams & ")"
    Return $sResult
EndFunc   ;==>_MetaData_DecodeMethodSig

; --- Lees een enkel type uit de signature-blob ---
Func __ReadSigType(ByRef $tSig, ByRef $iReadPtr)
    Local $iType = DllStructGetData($tSig, 1, $iReadPtr)
    $iReadPtr += 1

    Switch $iType
        Case $ELEMENT_TYPE_VOID
            Return "none"
        Case $ELEMENT_TYPE_BOOLEAN
            Return "bool"
        Case $ELEMENT_TYPE_CHAR
            Return "word"
        Case $ELEMENT_TYPE_I1, $ELEMENT_TYPE_U1
            Return "byte"
        Case $ELEMENT_TYPE_I2
            Return "short"
        Case $ELEMENT_TYPE_U2
            Return "ushort"
        Case $ELEMENT_TYPE_I4
            Return "long"
        Case $ELEMENT_TYPE_U4
            Return "ulong"
        Case $ELEMENT_TYPE_I8
            Return "int64"
        Case $ELEMENT_TYPE_U8
            Return "uint64"
        Case $ELEMENT_TYPE_R4
            Return "float"
        Case $ELEMENT_TYPE_R8
            Return "double"
        Case $ELEMENT_TYPE_STRING
            Return "ptr" ; HSTRING
        Case $ELEMENT_TYPE_OBJECT
            Return "ptr"
        Case $ELEMENT_TYPE_I
            Return "ptr" ; IntPtr
        Case $ELEMENT_TYPE_U
            Return "ptr" ; UIntPtr
        Case $ELEMENT_TYPE_BYREF
            ; ByRef -> onderliggend type + *
            Return __ReadSigType($tSig, $iReadPtr) & "*"
        Case $ELEMENT_TYPE_VALUETYPE, $ELEMENT_TYPE_CLASS
            ; Gevolgd door een compressed token -> we lezen het weg
            __ReadCompressedInt($tSig, $iReadPtr)
            Return "ptr" ; struct of interface-pointer
        Case $ELEMENT_TYPE_GENERICINST
            ; Generic instance: type + arg-count + args. Behandel als ptr.
            __ReadSigType($tSig, $iReadPtr) ; het generieke type
            Local $iArgs = __ReadCompressedInt($tSig, $iReadPtr)
            For $a = 1 To $iArgs
                __ReadSigType($tSig, $iReadPtr) ; args wegleze
            Next
            Return "ptr"
        Case $ELEMENT_TYPE_SZARRAY
            ; Array: element-type + we behandelen als ptr
            __ReadSigType($tSig, $iReadPtr)
            Return "ptr"
        Case $ELEMENT_TYPE_VAR
            ; Generic type var: gevolgd door index
            __ReadCompressedInt($tSig, $iReadPtr)
            Return "ptr"
        Case Else
            Return "ptr" ; onbekend -> veilige fallback
    EndSwitch
EndFunc   ;==>__ReadSigType

; --- Lees een ECMA-335 compressed integer ---
Func __ReadCompressedInt(ByRef $tSig, ByRef $iReadPtr)
    Local $iByte1 = DllStructGetData($tSig, 1, $iReadPtr)

    If BitAND($iByte1, 0x80) = 0 Then
        ; 1-byte waarde
        $iReadPtr += 1
        Return $iByte1
    ElseIf BitAND($iByte1, 0xC0) = 0x80 Then
        ; 2-byte waarde
        Local $iByte2 = DllStructGetData($tSig, 1, $iReadPtr + 1)
        $iReadPtr += 2
        Return BitOR(BitShift(BitAND($iByte1, 0x3F), -8), $iByte2)
    Else
        ; 4-byte waarde
        Local $iByte2 = DllStructGetData($tSig, 1, $iReadPtr + 1)
        Local $iByte3 = DllStructGetData($tSig, 1, $iReadPtr + 2)
        Local $iByte4 = DllStructGetData($tSig, 1, $iReadPtr + 3)
        $iReadPtr += 4
        Return BitOR(BitShift(BitAND($iByte1, 0x1F), -24), _
                BitShift($iByte2, -16), _
                BitShift($iByte3, -8), $iByte4)
    EndIf
EndFunc   ;==>__ReadCompressedInt

; =====================================================================================
; DROPDOWN VULLEN
; =====================================================================================
Func _PopulateWinMDCombo()
    Local $hSearch = FileFindFirstFile($sWinMDDir & "\*.winmd")
    If $hSearch = -1 Then Return
    Local $sFileList = "", $sFileName = ""
    While 1
        $sFileName = FileFindNextFile($hSearch)
        If @error Then ExitLoop
        $sFileList &= $sFileName & "|"
    WEnd
    FileClose($hSearch)
    GUICtrlSetData($idComboWinMD, $sFileList, $sDefaultWinMDName)
EndFunc   ;==>_PopulateWinMDCombo

; =====================================================================================
; HELPER FUNCTIONS (OCR-stijl)
; =====================================================================================
Func _WinRT_CreateGUID($sGUID)
    Local $tGUID = DllStructCreate("dword Data1;word Data2;word Data3;byte Data4[8]")
    DllCall($hDLLOle32, "long", "CLSIDFromString", "wstr", $sGUID, "struct*", $tGUID)
    Return $tGUID
EndFunc   ;==>_WinRT_CreateGUID

Func _WinRT_CreateHString($sString)
    Local $aRes = DllCall($hDLLComBase, "long", "WindowsCreateString", "wstr", $sString, "uint", StringLen($sString), "ptr*", 0)
    If @error Or $aRes[0] < 0 Then Return 0
    Return $aRes[3]
EndFunc   ;==>_WinRT_CreateHString

Func _WinRT_DeleteHString(ByRef $hString)
    If $hString = 0 Then Return
    DllCall($hDLLComBase, "long", "WindowsDeleteString", "ptr", $hString)
    $hString = 0
EndFunc   ;==>_WinRT_DeleteHString

Func _WinRT_GetHStringText($hString)
    If $hString = 0 Then Return ""
    Local $aRes = DllCall($hDLLComBase, "ptr", "WindowsGetStringRawBuffer", "ptr", $hString, "uint*", 0)
    If @error Or $aRes[0] = 0 Then Return ""
    Local $iLength = $aRes[2]
    If $iLength = 0 Then Return ""
    Local $tString = DllStructCreate("wchar[" & ($iLength + 1) & "]", $aRes[0])
    Return DllStructGetData($tString, 1)
EndFunc   ;==>_WinRT_GetHStringText

 

Posted

And as we can dynamically explore we can also dynamically do all stuff without all the includes generated.

Should split it to a UDF logic and demo part, probably some delay while loading but probably not that visible
 

; =====================================================================================
; DEMO
; =====================================================================================
ConsoleWrite(">>> [Demo] Universele WinRT Import Engine..." & @CRLF)

Local $sTargetClass = "Windows.Globalization.Calendar"
ConsoleWrite(">>> [Demo] Importeren: " & $sTargetClass & @CRLF)

Local $bImportSuccess = _WinRT_Dynamic_Import($sTargetClass)

If $bImportSuccess Then
    ConsoleWrite(">>> [Demo] Succes! Interface runtime opgebouwd." & @CRLF)

    Local $oCalendar = Eval("WinRT_Windows_Globalization_Calendar")

    If IsObj($oCalendar) Then
        ConsoleWrite(">>> [Demo] Object benaderd via Eval()." & @CRLF)

        ; --- Test property getters (WinRT properties hebben get_ prefix!) ---
        Local $iYear = 0
        Local $hr = $oCalendar.get_Year($iYear)
        ConsoleWrite(">>> [Demo] get_Year  HRESULT: 0x" & Hex($hr, 8) & " | Jaar:  " & $iYear & @CRLF)

        Local $iMonth = 0
        $hr = $oCalendar.get_Month($iMonth)
        ConsoleWrite(">>> [Demo] get_Month HRESULT: 0x" & Hex($hr, 8) & " | Maand: " & $iMonth & @CRLF)

        Local $iDay = 0
        $hr = $oCalendar.get_Day($iDay)
        ConsoleWrite(">>> [Demo] get_Day   HRESULT: 0x" & Hex($hr, 8) & " | Dag:   " & $iDay & @CRLF)

        Local $iHour = 0
        $hr = $oCalendar.get_Hour($iHour)
        ConsoleWrite(">>> [Demo] get_Hour  HRESULT: 0x" & Hex($hr, 8) & " | Uur:   " & $iHour & @CRLF)

        MsgBox(64, "WinRT Dynamic Engine", _
                $sTargetClass & " is runtime geïmporteerd en geactiveerd!" & @CRLF & @CRLF & _
                "Huidige datum volgens WinRT Calendar:" & @CRLF & _
                "Jaar: " & $iYear & @CRLF & _
                "Maand: " & $iMonth & @CRLF & _
                "Dag: " & $iDay & @CRLF & _
                "Uur: " & $iHour)
    Else
        ConsoleWrite("! [Demo] Gegenereerd element is geen object." & @CRLF)
    EndIf
Else
    ConsoleWrite("! [Demo] Import mislukt. Foutcode: " & @error & @CRLF)
EndIf




Full script to copy/paste

#pragma compile(x64, true)
; =====================================================================================
; WINRT UNIVERSAL DYNAMIC ENGINE & DEMO (GECORRIGEERD)
;
; KERN-INZICHT: Een RuntimeClass (bv. Windows.Globalization.Calendar) is GEEN
; interface. Je moet via de metadata de DEFAULT INTERFACE (bv. ICalendar) vinden,
; die IID + methoden ophalen, RoActivateInstance de class, en dan QueryInterface
; naar de default interface.
; =====================================================================================

#include <AutoItConstants.au3>
#include <WinAPI.au3>
#include <Array.au3>

#region --- GLOBALS & DLL CONTEXT ---
; =====================================================================================
; De volledige IMetaDataImport tag (als globale const)
; =====================================================================================
Global Const $GLOBAL_tagImport = _
        "CloseEnum none(ptr);CountEnum hresult(ptr;ulong*);ResetEnum hresult(ptr;ulong);" & _
        "EnumTypeDefs hresult(ptr*;ptr;ulong;ulong*);EnumInterfaceImpls hresult(ptr*;dword;ptr;ulong;ulong*);" & _
        "EnumTypeRefs hresult(ptr*;ptr;ulong;ulong*);FindTypeDefByName hresult(wstr;dword;dword*);" & _
        "GetScopeProps hresult(ptr;ulong;ulong*;ptr);GetModuleFromScope hresult(dword*);" & _
        "GetTypeDefProps hresult(dword;ptr;ulong;ulong*;dword*;dword*);GetInterfaceImplProps hresult(dword;dword*;dword*);" & _
        "GetTypeRefProps hresult(dword;dword*;ptr;ulong;ulong*);ResolveTypeRef hresult(dword;ptr;ptr*;dword*);" & _
        "EnumMembers hresult(ptr*;dword;ptr;ulong;ulong*);EnumMembersWithName hresult(ptr*;dword;wstr;ptr;ulong;ulong*);" & _
        "EnumMethods hresult(ptr*;dword;ptr;ulong;ulong*);EnumMethodsWithName hresult(ptr*;dword;wstr;ptr;ulong;ulong*);" & _
        "EnumFields hresult(ptr*;dword;ptr;ulong;ulong*);EnumFieldsWithName hresult(ptr*;dword;wstr;ptr;ulong;ulong*);" & _
        "EnumParams hresult(ptr*;dword;ptr;ulong;ulong*);EnumMemberRefs hresult(ptr*;dword;ptr;ulong;ulong*);" & _
        "EnumMethodImpls hresult(ptr*;dword;ptr;ptr;ulong;ulong*);EnumPermissionSets hresult(ptr*;dword;dword;ptr;ulong;ulong*);" & _
        "FindMember hresult(dword;wstr;ptr;ulong;dword*);FindMethod hresult(dword;wstr;ptr;ulong;dword*);" & _
        "FindField hresult(dword;wstr;ptr;ulong;dword*);FindMemberRef hresult(dword;wstr;ptr;ulong;dword*);" & _
        "GetMethodProps hresult(dword;dword*;ptr;ulong;ulong*;dword*;ptr*;ulong*;ulong*;dword*);" & _
        "GetMemberRefProps hresult(dword;dword*;ptr;ulong;ulong*;ptr*;ulong*);" & _
        "EnumProperties hresult(ptr*;dword;ptr;ulong;ulong*);EnumEvents hresult(ptr*;dword;ptr;ulong;ulong*);" & _
        "GetEventProps hresult(dword;dword*;ptr;ulong;ulong*;dword*;dword*;dword*;dword*;dword*;dword*;ptr;ulong;ulong*);" & _
        "EnumMethodSemantics hresult(ptr*;dword;ptr;ulong;ulong*);GetMethodSemantics hresult(dword;dword;dword*);" & _
        "GetClassLayout hresult(dword;dword*;ptr;ulong;ulong*;ulong*);GetFieldMarshal hresult(dword;ptr*;ulong*);" & _
        "GetRVA hresult(dword;ulong*;dword*);GetPermissionSetProps hresult(dword;dword*;ptr*;ulong*);" & _
        "GetSigFromToken hresult(dword;ptr*;ulong*);GetModuleRefProps hresult(dword;ptr;ulong;ulong*);" & _
        "EnumModuleRefs hresult(ptr*;ptr;ulong;ulong*);GetTypeSpecFromToken hresult(dword;ptr*;ulong*);" & _
        "GetNameFromToken hresult(dword;ptr*);EnumUnresolvedMethods hresult(ptr*;ptr;ulong;ulong*);" & _
        "GetUserString hresult(dword;ptr;ulong;ulong*);GetPinvokeMap hresult(dword;dword*;ptr;ulong;ulong*;dword*);" & _
        "EnumSignatures hresult(ptr*;ptr;ulong;ulong*);EnumTypeSpecs hresult(ptr*;ptr;ulong;ulong*);" & _
        "EnumUserStrings hresult(ptr*;ptr;ulong;ulong*);GetParamForMethodIndex hresult(dword;ulong;dword*);" & _
        "EnumCustomAttributes hresult(ptr*;dword;dword;ptr;ulong;ulong*);GetCustomAttributeProps hresult(dword;dword*;dword*;ptr*;ulong*);" & _
        "FindTypeRef hresult(dword;wstr;dword*);GetMemberProps hresult(dword;dword*;ptr;ulong;ulong*;dword*;ptr*;ulong*;dword*;dword*;dword*;ptr*;ulong*);" & _
        "GetFieldProps hresult(dword;dword*;ptr;ulong;ulong*;dword*;ptr*;ulong*;dword*;ptr*;ulong*);" & _
        "GetPropertyProps hresult(dword;dword*;ptr;ulong;ulong*;dword*;ptr*;ulong*;dword*;ptr*;ulong*;dword*;dword*;ptr;ulong;ulong*);" & _
        "GetParamProps hresult(dword;dword*;ulong*;ptr;ulong;ulong*;dword*;dword*;ptr*;ulong*);" & _
        "GetCustomAttributeByName hresult(dword;wstr;ptr*;ulong*);IsValidToken bool(dword);" & _
        "GetNestedClassProps hresult(dword;dword*);GetNativeCallConvFromSig hresult(ptr;ulong;ulong*);IsGlobal hresult(dword;int*);"

Global $hDLLWinTypes = DllOpen("wintypes.dll")
Global $hDLLComBase    = DllOpen("combase.dll")
Global $hDLLOle32      = DllOpen("ole32.dll")
Global $hDLLRoMetaData = DllOpen("rometadata.dll")

Local $aInit = DllCall($hDLLComBase, "long", "RoInitialize", "int", 0) ; 0 = STA
If @error Then
    MsgBox(16, "Fout", "Kan WinRT niet initialiseren. Draai als x64!")
    Exit
EndIf
; S_OK (0), S_FALSE (1) en RPC_E_CHANGED_MODE (0x80010106) zijn allemaal OK
ConsoleWrite("    + RoInitialize HRESULT: 0x" & Hex($aInit[0], 8) & @CRLF)

; Vaste metadata GUIDs
Global Const $sCLSID_CorMetaDataDispenser = "{E5CB7A31-7512-11D2-89CE-0080C792E5D8}"
Global Const $sIID_IMetaDataDispenser     = "{809C652E-7396-11D2-9771-00A0C9B4D50C}"
Global Const $sIID_IMetaDataImport        = "{7DAC8207-D3AE-4C75-9B67-92801A497D44}"

; Token tabel-types
Global Const $MDT_TypeDef  = 0x02000000
Global Const $MDT_TypeRef  = 0x01000000
Global Const $MDT_TypeSpec = 0x1B000000

; TypeDef flag: interface
Global Const $tdInterface  = 0x00000020

; Globale metadata-objecten (herbruikbaar per winmd)
Global $g_oImport = 0
#endregion

; =====================================================================================
; DEMO
; =====================================================================================
ConsoleWrite(">>> [Demo] Universele WinRT Import Engine..." & @CRLF)

Local $sTargetClass = "Windows.Globalization.Calendar"
ConsoleWrite(">>> [Demo] Importeren: " & $sTargetClass & @CRLF)

Local $bImportSuccess = _WinRT_Dynamic_Import($sTargetClass)

If $bImportSuccess Then
    ConsoleWrite(">>> [Demo] Succes! Interface runtime opgebouwd." & @CRLF)

    Local $oCalendar = Eval("WinRT_Windows_Globalization_Calendar")

    If IsObj($oCalendar) Then
        ConsoleWrite(">>> [Demo] Object benaderd via Eval()." & @CRLF)

        ; --- Test property getters (WinRT properties hebben get_ prefix!) ---
        Local $iYear = 0
        Local $hr = $oCalendar.get_Year($iYear)
        ConsoleWrite(">>> [Demo] get_Year  HRESULT: 0x" & Hex($hr, 8) & " | Jaar:  " & $iYear & @CRLF)

        Local $iMonth = 0
        $hr = $oCalendar.get_Month($iMonth)
        ConsoleWrite(">>> [Demo] get_Month HRESULT: 0x" & Hex($hr, 8) & " | Maand: " & $iMonth & @CRLF)

        Local $iDay = 0
        $hr = $oCalendar.get_Day($iDay)
        ConsoleWrite(">>> [Demo] get_Day   HRESULT: 0x" & Hex($hr, 8) & " | Dag:   " & $iDay & @CRLF)

        Local $iHour = 0
        $hr = $oCalendar.get_Hour($iHour)
        ConsoleWrite(">>> [Demo] get_Hour  HRESULT: 0x" & Hex($hr, 8) & " | Uur:   " & $iHour & @CRLF)

        MsgBox(64, "WinRT Dynamic Engine", _
                $sTargetClass & " is runtime geïmporteerd en geactiveerd!" & @CRLF & @CRLF & _
                "Huidige datum volgens WinRT Calendar:" & @CRLF & _
                "Jaar: " & $iYear & @CRLF & _
                "Maand: " & $iMonth & @CRLF & _
                "Dag: " & $iDay & @CRLF & _
                "Uur: " & $iHour)
    Else
        ConsoleWrite("! [Demo] Gegenereerd element is geen object." & @CRLF)
    EndIf
Else
    ConsoleWrite("! [Demo] Import mislukt. Foutcode: " & @error & @CRLF)
EndIf

DllCall($hDLLComBase, "none", "RoUninitialize")
DllClose($hDLLComBase)
DllClose($hDLLOle32)
DllClose($hDLLRoMetaData)
Exit

; =====================================================================================
; UNIVERSAL ENGINE
; =====================================================================================
Func _WinRT_Dynamic_Import($sClassName)
    Local $sClassSafe = StringReplace($sClassName, ".", "_")
    If Eval("WinRT_" & $sClassSafe) <> "" Then Return True

    ; --- 1. Vind de .winmd file van deze class ---
    Local $sWinMDFullPath = _WinRT_LocateMetaDataFile($sClassName)
    If @error Or $sWinMDFullPath = "" Then Return SetError(2, 0, False)
    ConsoleWrite("    + Metadata: " & $sWinMDFullPath & @CRLF)

    ; --- 2. Open de winmd via dispenser + import ---
    Local $oImport = _WinRT_OpenMetaData($sWinMDFullPath)
    If @error Or Not IsObj($oImport) Then Return SetError(3, 0, False)
    $g_oImport = $oImport

    ; --- 3. Vind het TypeDef-token van de RuntimeClass ---
    Local $idClassToken = 0
    $oImport.FindTypeDefByName($sClassName, 0, $idClassToken)
    If $idClassToken = 0 Then Return SetError(5, 0, False)
    ConsoleWrite("    + RuntimeClass token: 0x" & Hex($idClassToken, 8) & @CRLF)

    ; --- 4. Vind de DEFAULT INTERFACE van deze class ---
    Local $idIfaceToken = _MetaData_GetDefaultInterfaceToken($idClassToken)
    If @error Or $idIfaceToken = 0 Then
        ConsoleWrite("    ! Kon default interface niet vinden." & @CRLF)
        Return SetError(6, 0, False)
    EndIf
    ConsoleWrite("    + Default interface token: 0x" & Hex($idIfaceToken, 8) & @CRLF)

    ; --- 5. Haal naam + IID + methoden van de DEFAULT INTERFACE op ---
    Local $sIfaceName = _MetaData_GetTypeName($idIfaceToken)
    ConsoleWrite("    + Default interface: " & $sIfaceName & @CRLF)

    Local $sIID = _MetaData_GetGUIDAttrib($idIfaceToken)
    If $sIID = "" Then
        ConsoleWrite("    ! Interface heeft geen GuidAttribute." & @CRLF)
        Return SetError(6, 0, False)
    EndIf
    ConsoleWrite("    + Interface IID: " & $sIID & @CRLF)

    ; Bouw de dtag van de INTERFACE (compact, geen CRLF)
    Local $sDTag = _MetaData_BuildDTag($idIfaceToken)
    ConsoleWrite("    + dtag opgebouwd (" & StringLen($sDTag) & " chars)." & @CRLF)

    ; --- 6. Activeer de RuntimeClass -> IInspectable* ---
    Local $pInspectable = _WinRT_ActivateInstance($sClassName)
    If @error Or $pInspectable = 0 Then
        ConsoleWrite("    ! RoActivateInstance mislukt (@error=" & @error & ")." & @CRLF)
        Return SetError(7, 0, False)
    EndIf
    ConsoleWrite("    + IInspectable pointer: 0x" & Hex($pInspectable) & @CRLF)

    ; --- 7. QueryInterface naar de default interface ---
    Local $tIID_Iface = _CreateGUID($sIID)

    ; QueryInterface is altijd vtable-index 0
    Local $pVTable = DllStructGetData(DllStructCreate("ptr", $pInspectable), 1)
    Local $pQI = DllStructGetData(DllStructCreate("ptr", $pVTable), 1)

    Local $aQI = DllCallAddress("long", $pQI, "ptr", $pInspectable, _
            "struct*", $tIID_Iface, "ptr*", 0)

    ConsoleWrite("    -> QueryInterface HRESULT: 0x" & Hex($aQI[0], 8) & " | Ptr: 0x" & Hex($aQI[3]) & @CRLF)

    If $aQI[0] < 0 Or $aQI[3] = 0 Then
        ConsoleWrite("    ! QueryInterface naar interface mislukt!" & @CRLF)
        Return SetError(8, 0, False)
    EndIf

    Local $pIface = $aQI[3]

    ; --- 8. ObjCreateInterface met de default interface IID + dtag (in EEN keer!) ---
    Local $oDynamicObject = ObjCreateInterface($pIface, $sIID, $sDTag)

    If IsObj($oDynamicObject) Then
        Assign("WinRT_" & $sClassSafe, $oDynamicObject, $ASSIGN_FORCEGLOBAL)
        ConsoleWrite("    + Object toegewezen aan WinRT_" & $sClassSafe & @CRLF)
        Return True
    EndIf

    ConsoleWrite("    ! ObjCreateInterface mislukt." & @CRLF)
    Return False
EndFunc   ;==>_WinRT_Dynamic_Import

; =====================================================================================
; KERNSTAP: Vind de default interface via EnumInterfaceImpls + DefaultAttribute
; =====================================================================================
Func _MetaData_GetDefaultInterfaceToken($idClassToken)
    Local $hEnum = 0
    Local $tImplTokens = DllStructCreate("dword Tokens[100]")
    Local $iCount = 0

    Local $hr = $g_oImport.EnumInterfaceImpls($hEnum, $idClassToken, DllStructGetPtr($tImplTokens), 100, $iCount)
    If $hEnum <> 0 Then $g_oImport.CloseEnum($hEnum)
    If $hr < 0 Or $iCount = 0 Then Return SetError(1, 0, 0)

    ConsoleWrite("      -> " & $iCount & " interface-implementaties gevonden." & @CRLF)

    ; Zoek de impl met DefaultAttribute
    Local $idFallbackIface = 0
    For $i = 1 To $iCount
        Local $idImplToken = DllStructGetData($tImplTokens, "Tokens", $i)

        ; Haal class + interface-token uit deze impl
        Local $iClassTkn = 0, $iIfaceTkn = 0
        $g_oImport.GetInterfaceImplProps($idImplToken, $iClassTkn, $iIfaceTkn)

        ; Onthoud de eerste als fallback
        If $idFallbackIface = 0 Then $idFallbackIface = $iIfaceTkn

        ; Check of deze impl de DefaultAttribute heeft
        Local $pData = 0, $cbData = 0
        Local $hrAttr = $g_oImport.GetCustomAttributeByName($idImplToken, _
                "Windows.Foundation.Metadata.DefaultAttribute", $pData, $cbData)

        If $hrAttr = 0 Then ; S_OK = default gevonden!
            ConsoleWrite("      -> DefaultAttribute gevonden op impl " & $i & @CRLF)
            ; $iIfaceTkn kan TypeRef of TypeDef zijn -> resolve naar TypeDef
            Return _MetaData_ResolveToTypeDef($iIfaceTkn)
        EndIf
    Next

    ; Geen default gevonden -> gebruik de eerste als fallback
    ConsoleWrite("      -> Geen DefaultAttribute; gebruik eerste interface." & @CRLF)
    Return _MetaData_ResolveToTypeDef($idFallbackIface)
EndFunc   ;==>_MetaData_GetDefaultInterfaceToken

; =====================================================================================
; Resolve een interface-token (TypeRef of TypeDef) naar een lokaal TypeDef-token
; =====================================================================================
Func _MetaData_ResolveToTypeDef($iToken)
    Local $iTableType = BitAND($iToken, 0xFF000000)

    If $iTableType = $MDT_TypeDef Then
        ; Al een TypeDef in deze scope
        Return $iToken

    ElseIf $iTableType = $MDT_TypeRef Then
        ; TypeRef -> haal de naam op en zoek de TypeDef via FindTypeDefByName
        Local $sName = _MetaData_GetTypeRefName($iToken)
        If $sName = "" Then Return SetError(1, 0, 0)

        Local $idTypeDef = 0
        Local $hr = $g_oImport.FindTypeDefByName($sName, 0, $idTypeDef)
        If $hr < 0 Or $idTypeDef = 0 Then
            ; Interface leeft in een ANDER winmd bestand
            ConsoleWrite("      ! Interface '" & $sName & "' zit in ander winmd (cross-scope)." & @CRLF)
            Return SetError(2, 0, 0)
        EndIf
        Return $idTypeDef

    Else
        ; TypeSpec (generiek) - niet ondersteund in deze basis-engine
        ConsoleWrite("      ! TypeSpec (generiek type) niet ondersteund." & @CRLF)
        Return SetError(3, 0, 0)
    EndIf
EndFunc   ;==>_MetaData_ResolveToTypeDef

; =====================================================================================
; Naam van een TypeDef-token
; =====================================================================================
Func _MetaData_GetTypeName($idTypeDefToken)
    Local $tName = DllStructCreate("wchar Name[512]")
    Local $iNameLen = 0, $iFlags = 0, $iExtends = 0
    Local $hr = $g_oImport.GetTypeDefProps($idTypeDefToken, DllStructGetPtr($tName), 512, $iNameLen, $iFlags, $iExtends)
    If $hr < 0 Then Return ""
    Return DllStructGetData($tName, "Name")
EndFunc   ;==>_MetaData_GetTypeName

; =====================================================================================
; Naam van een TypeRef-token
; =====================================================================================
Func _MetaData_GetTypeRefName($idTypeRefToken)
    Local $tName = DllStructCreate("wchar Name[512]")
    Local $iResScope = 0, $iNameLen = 0
    Local $hr = $g_oImport.GetTypeRefProps($idTypeRefToken, $iResScope, DllStructGetPtr($tName), 512, $iNameLen)
    If $hr < 0 Then Return ""
    Return DllStructGetData($tName, "Name")
EndFunc   ;==>_MetaData_GetTypeRefName

; =====================================================================================
; Bouw dtag string van een interface-TypeDef
; =====================================================================================
; =====================================================================================
; Bouw dtag string van een interface-TypeDef (COMPACT - geen CRLF!)
; =====================================================================================
Func _MetaData_BuildDTag($idIfaceToken)
    Local $hEnum = 0
    Local $tMethodTokens = DllStructCreate("dword Tokens[500]")
    Local $iMethodCount = 0
    $g_oImport.EnumMethods($hEnum, $idIfaceToken, DllStructGetPtr($tMethodTokens), 500, $iMethodCount)
    If $hEnum <> 0 Then $g_oImport.CloseEnum($hEnum)

    ; IInspectable-basis (compact, geen CRLF)
    Local $sDTag = "GetIids hresult(ulong*;ptr*);GetRuntimeClassName hresult(ptr*);GetTrustLevel hresult(int*);"

    Local $mSeenNames[] ; map voor unieke namen

    For $i = 1 To $iMethodCount
        Local $idMethodToken = DllStructGetData($tMethodTokens, "Tokens", $i)
        Local $tMethodName = DllStructCreate("wchar Name[256]")
        Local $iClass = 0, $iNameLen = 0, $iAttr = 0, $pSig = 0, $cbSig = 0, $iRVA = 0, $iImpl = 0

        $g_oImport.GetMethodProps($idMethodToken, $iClass, DllStructGetPtr($tMethodName), _
                256, $iNameLen, $iAttr, $pSig, $cbSig, $iRVA, $iImpl)
        Local $sMethodName = DllStructGetData($tMethodName, "Name")

        If $sMethodName = "" Or StringLeft($sMethodName, 1) = "." Then ContinueLoop

        ; Overloaded namen uniek maken (ObjCreateInterface eist unieke namen!)
        If MapExists($mSeenNames, $sMethodName) Then
            $mSeenNames[$sMethodName] += 1
            $sMethodName = $sMethodName & "_" & $mSeenNames[$sMethodName]
        Else
            $mSeenNames[$sMethodName] = 0
        EndIf

        Local $sAutoItSig = _MetaData_DecodeMethodSig($pSig, $cbSig)
        ConsoleWrite("      + " & $sMethodName & " " & $sAutoItSig & @CRLF)

        ; COMPACT: geen CRLF, alleen ; als scheiding (zoals werkende OCR-tags)
        $sDTag &= $sMethodName & " " & $sAutoItSig & ";"
    Next

    Return $sDTag
EndFunc   ;==>_MetaData_BuildDTag
; =====================================================================================
; GUID via GuidAttribute
; =====================================================================================
Func _MetaData_GetGUIDAttrib($idToken)
    Local $pData = 0, $cbData = 0
    Local $hr = $g_oImport.GetCustomAttributeByName($idToken, _
            "Windows.Foundation.Metadata.GuidAttribute", $pData, $cbData)
    If $hr <> 0 Or $pData = 0 Or $cbData < 20 Then Return ""

    Local $tGUID = DllStructCreate("dword Data1;word Data2;word Data3;byte Data4[8]", $pData + 2)
    Local $sData4 = ""
    For $i = 1 To 8
        $sData4 &= Hex(DllStructGetData($tGUID, "Data4", $i), 2)
    Next
    Return StringFormat("{%08X-%04X-%04X-%s-%s}", _
            DllStructGetData($tGUID, "Data1"), DllStructGetData($tGUID, "Data2"), _
            DllStructGetData($tGUID, "Data3"), StringLeft($sData4, 4), StringMid($sData4, 5))
EndFunc   ;==>_MetaData_GetGUIDAttrib

; =====================================================================================
; WinRT HELPERS
; =====================================================================================
Func _WinRT_LocateMetaDataFile($sClassName)
    Local $aHString = DllCall($hDLLComBase, "long", "WindowsCreateString", _
            "wstr", $sClassName, "dword", StringLen($sClassName), "ptr*", 0)
    If @error Or $aHString[0] < 0 Then Return SetError(1, 0, "")
    Local $hString = $aHString[3]

;~ Local $aGetFile = DllCall($hDLLComBase, "long", "RoGetMetaDataFile", _
;~         "ptr", $hString, "ptr", 0, "ptr*", 0, "ptr*", 0)
    #fix
Local $aGetFile = DllCall($hDLLWinTypes, "long", "RoGetMetaDataFile", _
        "ptr", $hString, _   ; name (HSTRING)
        "ptr", 0, _          ; metaDataDispenser (NULL)
        "ptr*", 0, _         ; metaDataFilePath (out HSTRING) -> $aGetFile[3]
        "ptr*", 0, _         ; metaDataImport (out) -> $aGetFile[4]
        "ptr*", 0)           ; typeDefToken (out) -> $aGetFile[5]

    DllCall($hDLLComBase, "long", "WindowsDeleteString", "ptr", $hString)
    If @error Or $aGetFile[0] < 0 Then Return SetError(2, 0, "")

    Local $hPath = $aGetFile[3]
    Local $aRaw = DllCall($hDLLComBase, "ptr", "WindowsGetStringRawBuffer", "ptr", $hPath, "uint*", 0)
    Local $sPath = ""
    If Not @error And $aRaw[0] <> 0 Then
        Local $tStr = DllStructCreate("wchar[" & ($aRaw[2] + 1) & "]", $aRaw[0])
        $sPath = DllStructGetData($tStr, 1)
    EndIf
    DllCall($hDLLComBase, "long", "WindowsDeleteString", "ptr", $hPath)
    Return $sPath
EndFunc   ;==>_WinRT_LocateMetaDataFile

Func _WinRT_OpenMetaData($sWinMDPath)
    Local $tCLSID = _CreateGUID($sCLSID_CorMetaDataDispenser)
    Local $tIID   = _CreateGUID($sIID_IMetaDataDispenser)
    Local $aDisp = DllCall($hDLLRoMetaData, "long", "MetaDataGetDispenser", _
            "struct*", $tCLSID, "struct*", $tIID, "ptr*", 0)
    If @error Or $aDisp[0] < 0 Then Return SetError(1, 0, 0)

    Local $tagDisp = "DefineScope hresult(ptr;dword;ptr;ptr*);OpenScope hresult(wstr;dword;struct*;ptr*);OpenScopeOnMemory hresult(ptr;dword;dword;struct*;ptr*);"
    Local $oDispenser = ObjCreateInterface($aDisp[3], $sIID_IMetaDataDispenser, $tagDisp, True)
    If Not IsObj($oDispenser) Then Return SetError(2, 0, 0)

    Local $tIID_Import = _CreateGUID($sIID_IMetaDataImport)
    Local $pImport = 0
    Local $hr = $oDispenser.OpenScope($sWinMDPath, 0, $tIID_Import, $pImport)
    If $hr < 0 Or $pImport = 0 Then Return SetError(3, 0, 0)

    Return ObjCreateInterface($pImport, $sIID_IMetaDataImport, $GLOBAL_tagImport, True)
EndFunc   ;==>_WinRT_OpenMetaData

Func _WinRT_ActivateInstance($sClassName)
    Local $aHString = DllCall($hDLLComBase, "long", "WindowsCreateString", _
            "wstr", $sClassName, "dword", StringLen($sClassName), "ptr*", 0)
    If @error Or $aHString[0] < 0 Then Return SetError(1, 0, 0)
    Local $hString = $aHString[3]

    Local $aAct = DllCall($hDLLComBase, "long", "RoActivateInstance", "ptr", $hString, "ptr*", 0)
    DllCall($hDLLComBase, "long", "WindowsDeleteString", "ptr", $hString)

    If @error Or $aAct[0] < 0 Or $aAct[2] = 0 Then Return SetError(2, 0, 0)
    Return $aAct[2]
EndFunc   ;==>_WinRT_ActivateInstance

Func _CreateGUID($sGUID)
    Local $tGUID = DllStructCreate("dword Data1;word Data2;word Data3;byte Data4[8]")
    DllCall($hDLLOle32, "long", "CLSIDFromString", "wstr", $sGUID, "struct*", $tGUID)
    Return $tGUID
EndFunc   ;==>_CreateGUID

; =====================================================================================
; ECMA-335 SIGNATURE DECODER
; =====================================================================================
Func _MetaData_DecodeMethodSig($pSig, $cbSig)
    If $pSig = 0 Or $cbSig = 0 Then Return "hresult(ptr*)"
    Local $tSig = DllStructCreate("byte[" & $cbSig & "]", $pSig)
    Local $iReadPtr = 2
    Local $iParamCount = __ReadCompressedInt($tSig, $iReadPtr)
    Local $sRetType = __ReadSigType($tSig, $iReadPtr)
    Local $sParams = ""
    For $p = 1 To $iParamCount
        Local $sParamType = __ReadSigType($tSig, $iReadPtr)
        If $sParams <> "" Then $sParams &= ";"
        $sParams &= $sParamType
    Next
    If $sRetType <> "none" Then
        If $sParams <> "" Then $sParams &= ";"
        $sParams &= $sRetType & "*"
    EndIf
    If $sParams = "" Then $sParams = "ptr*"
    Return "hresult(" & $sParams & ")"
EndFunc   ;==>_MetaData_DecodeMethodSig

Func __ReadSigType(ByRef $tSig, ByRef $iReadPtr)
    Local $iType = DllStructGetData($tSig, 1, $iReadPtr)
    $iReadPtr += 1
    Switch $iType
        Case 0x01
            Return "none"
        Case 0x02
            Return "bool"
        Case 0x03
            Return "word"
        Case 0x04, 0x05
            Return "byte"
        Case 0x06
            Return "short"
        Case 0x07
            Return "ushort"
        Case 0x08
            Return "long"
        Case 0x09
            Return "ulong"
        Case 0x0A
            Return "int64"
        Case 0x0B
            Return "uint64"
        Case 0x0C
            Return "float"
        Case 0x0D
            Return "double"
        Case 0x0E, 0x1C, 0x18, 0x19
            Return "ptr"
        Case 0x10
            Return __ReadSigType($tSig, $iReadPtr) & "*"
        Case 0x11, 0x12
            __ReadCompressedInt($tSig, $iReadPtr)
            Return "ptr"
        Case 0x15
            __ReadSigType($tSig, $iReadPtr)
            Local $iArgs = __ReadCompressedInt($tSig, $iReadPtr)
            For $a = 1 To $iArgs
                __ReadSigType($tSig, $iReadPtr)
            Next
            Return "ptr"
        Case 0x1D
            __ReadSigType($tSig, $iReadPtr)
            Return "ptr"
        Case 0x13
            __ReadCompressedInt($tSig, $iReadPtr)
            Return "ptr"
        Case Else
            Return "ptr"
    EndSwitch
EndFunc   ;==>__ReadSigType

Func __ReadCompressedInt(ByRef $tSig, ByRef $iReadPtr)
    Local $iByte1 = DllStructGetData($tSig, 1, $iReadPtr)
    If BitAND($iByte1, 0x80) = 0 Then
        $iReadPtr += 1
        Return $iByte1
    ElseIf BitAND($iByte1, 0xC0) = 0x80 Then
        Local $iByte2 = DllStructGetData($tSig, 1, $iReadPtr + 1)
        $iReadPtr += 2
        Return BitOR(BitShift(BitAND($iByte1, 0x3F), -8), $iByte2)
    Else
        Local $iByte2 = DllStructGetData($tSig, 1, $iReadPtr + 1)
        Local $iByte3 = DllStructGetData($tSig, 1, $iReadPtr + 2)
        Local $iByte4 = DllStructGetData($tSig, 1, $iReadPtr + 3)
        $iReadPtr += 4
        Return BitOR(BitShift(BitAND($iByte1, 0x1F), -24), _
                BitShift($iByte2, -16), BitShift($iByte3, -8), $iByte4)
    EndIf
EndFunc   ;==>__ReadCompressedInt

 

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...