junkew Posted Sunday at 04:36 PM Posted Sunday at 04:36 PM (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) expandcollapse popup; ===================================================================================== ; 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 Sunday at 04:46 PM by junkew mLipok 1 FAQ 31 How to click some elements, FAQ 40 Test automation with AutoIt, Multithreading CLR .NET Powershell CMDLets
junkew Posted Sunday at 08:14 PM Author Posted Sunday at 08:14 PM And allways fun to reinvent the wheel 😉 found this one after I learned making above script. https://github.com/DanysysTeam/UWPOCR @Danyfirex Danyfirex and mLipok 1 1 FAQ 31 How to click some elements, FAQ 40 Test automation with AutoIt, Multithreading CLR .NET Powershell CMDLets
MattyD Posted 23 hours ago Posted 23 hours ago (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 23 hours ago by MattyD junkew and Danyfirex 2
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