Ward

Web Screenshot

29 posts in this topic

#1 ·  Posted (edited)

This script demonstrate how to save the web page, or just the specified element to an image file.

Two COM interface were used in the script (IViewObject2 for entire page, and IHTMLElementRender for an element)

Some code was modified from IECapt.

This script needs (Thanks to trancexx, she help me to solve a problem.)

#Include <IE.au3>
#Include <WinAPI.au3>
#Include <ScreenCapture.au3>
#Include <WindowsConstants.au3>
#Include "AutoItObject.au3"

_AutoItObject_StartUp()
OnAutoItExitRegister('OnExit')

WebCaptureTest("http://en.wikipedia.org/wiki/AutoIt", "Test1.png")
ElementCaptureTest("http://en.wikipedia.org/wiki/AutoIt", "Test2.png")


Func OnExit()
    _AutoItObject_Shutdown()
EndFunc

Func WebCaptureTest($Url, $ImageName)
    Local $hBmp = WebCapture($Url, 1280)
    _ScreenCapture_SaveImage($ImageName, $hBmp, True)   
EndFunc

Func ElementCaptureTest($Url, $ImageName)
    Local $WebWidth = 1280
    Local $oIE = ObjCreate("Shell.Explorer.2")
    GUICreate("", $WebWidth, 768, -1, -1)
    GUICtrlCreateObj($oIE, 0, 0 , $WebWidth, 768)
    _IENavigate($oIE, $Url)

    Local $oElement = $oIE.document.documentElement
    If $oElement.clientWidth = 0 Then $oElement = $oIE.document.body
    Local $hBmp = ElementCapture($oElement)
    _ScreenCapture_SaveImage($ImageName, $hBmp, True)
EndFunc

Func WebCapture($Url, $WebWidth = 1024)
    Local Const $DVASPECT_CONTENT = 1
    Local $tIID_IViewObject2 = _AutoItObject_CLSIDFromString("{00000127-0000-0000-C000-000000000046}")
    Local $dtagIViewObject2 = $dtagIUnknown & "Draw hresult(dword;long;ptr;ptr;dword;dword;ptr;ptr;int;dword);"
    Local $oIE = ObjCreate("Shell.Explorer.2")

    GUICreate("", $WebWidth, 768, -1, -1)
    GUICtrlCreateObj($oIE, 0, 0 , $WebWidth, 768)

    _IENavigate($oIE, $url)

    Local $oDocument = $oIE.document
    Local $oBody = $oIE.document.body
    Local $oHtml = $oIE.document.documentElement
    
    $oBody.scroll = "no"
    $oBody.style.borderStyle = "none"
    $oHtml.style.overflow = 'hidden'
    $oBody.style.overflow = 'hidden'

    Local $oIViewObject2 = _AutoItObject_WarpInterface($oDocument, $tIID_IViewObject2, $dtagIViewObject2)
    If @Error Then Return SetError(1, 0, 0)

    Local $BodyWidth = $oBody.scrollWidth
    Local $BodyHeight = $oBody.scrollHeight
    Local $RootWidth = $oHtml.scrollWidth
    Local $RootHeight = $oHtml.scrollHeight

    Local $Width = $BodyWidth
    Local $Height = $RootHeight
    If $BodyHeight > $Height Then $Height = $BodyHeight

    $oIE.width = $Width
    $oIE.height = $Height
    
    Local $hDC = _WinAPI_GetDC(0)
    Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC)
    Local $hBmp = _WinAPI_CreateCompatibleBitmap($hDC, $Width, $Height)
    _WinAPI_SelectObject($hMemDc, $hBmp)

    Local $sRECT = DllStructCreate($tagRECT)
    DllStructSetData($sRECT, "Top", 0)
    DllStructSetData($sRECT, "Left", 0)
    DllStructSetData($sRECT, "Right", $Width)
    DllStructSetData($sRECT, "Bottom", $Height)

    $oIViewObject2.Draw($DVASPECT_CONTENT, -1, 0, 0, Number($hDC), Number($hMemDC), Number(DllStructGetPtr($sRECT)), 0, 0, 0)

    _WinAPI_DeleteDC($hMemDC)
    _WinAPI_ReleaseDC(0, $hDC)
    Return $hBmp
EndFunc

Func ElementCapture($oElement, $Border = 2)
    If Not IsObj($oElement) Then Return SetError(1, 0, 0)
    If $oElement.clientWidth = 0 Or $oElement.clientHeight = 0 Then Return SetError(1, 0, 0)
    
    Local $tIID_IHTMLElementRender = _AutoItObject_CLSIDFromString("{3050F669-98B5-11CF-BB82-00AA00BDCE0B}")
    Local $dtagIHTMLElementRender = $dtagIUnknown & "DrawToDC hresult(hwnd);"   
    
    Local $PageHeight = $oElement.scrollHeight - $Border
    Local $PageWidth = $oElement.scrollWidth - $Border

    Local $oIHTMLElementRender = _AutoItObject_WarpInterface($oElement, $tIID_IHTMLElementRender, $dtagIHTMLElementRender)
    Local $hDC = _WinAPI_GetDC(0)
    Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC)
    Local $hBmp = _WinAPI_CreateSolidBitmap(0, 0xffffff, $oElement.clientWidth, $oElement.clientHeight)
    Local $hMemDcFull = _WinAPI_CreateCompatibleDC($hDC)
    Local $hBmpFull = _WinAPI_CreateSolidBitmap(0, 0xffffff, $PageWidth, $PageHeight)
    
    _WinAPI_SelectObject($hMemDc, $hBmp)
    _WinAPI_SelectObject($hMemDcFull, $hBmpFull)

    Local $DrawWidth = $oElement.clientWidth - $Border
    Local $DrawHeight = $oElement.clientHeight - $Border
    Local $CurrentX = 0
    Local $CurrentY = 0
    
    While $CurrentX < $PageWidth
        While $CurrentY < $PageHeight
            $oElement.scrollLeft = $CurrentX
            $oElement.scrollTop = $CurrentY
            $oIHTMLElementRender.DrawToDC(Number($hMemDC))
            _WinAPI_BitBlt($hMemDcFull, $oElement.scrollLeft, $oElement.scrollTop, $DrawWidth, $DrawHeight, $hMemDC, $Border, $Border, $SRCCOPY)
            $CurrentY += $DrawHeight
        WEnd
        $CurrentY = 0
        $CurrentX += $DrawWidth
    WEnd
    
    _WinAPI_DeleteDC($hMemDC)
    _WinAPI_DeleteDC($hMemDcFull)
    _WinAPI_ReleaseDC(0, $hDC)
    _WinAPI_DeleteObject($hBmp)
    
    Return $hBmpFull
EndFunc

Func _AutoItObject_WarpInterface($Obj, $IID, $Tag, $fNoUnknown = False)
    If Not IsDllStruct($IID) Then $IID = _AutoItObject_CLSIDFromString($IID)

    If Not IsObj($Obj) Then Return SetError(1, 0, 0)

    Local $pObj = _AutoItObject_IDispatchToPtr($Obj)
    If Not _AutoItObject_IUnknownAddRef($Obj) Then Return SetError(1, 0, 0)
    Local $ObjWarpped = _AutoItObject_WrapperCreate($pObj, $dtagIUnknown, $fNoUnknown)
    Local $aCall = $ObjWarpped.QueryInterface(Number(DllStructGetPtr($IID)), 0)
    If Not IsArray($aCall) And $aCall[0] <> 0 Then Return SetError(1, 0, 0)
    
    Local $pInterface = $aCall[2]
    Return _AutoItObject_WrapperCreate($pInterface, $Tag)
EndFunc
Edited by Ward
3 people like this

新版 _ArrayAdd 的白痴作者,不管是誰,去死一死好了

 

Share this post


Link to post
Share on other sites



Well done Ward, nice peace of code!

Br,

UEZ


Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯

Share this post


Link to post
Share on other sites

Thank you for sharing, Ward! I've tried with AutoitObject v1.2.0 and had an error, then with v1.2.2, with no error.

The AutoitObject site has a problem (in origo_releases.inc, line 420)...

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

hi

ok worked

thank you

Edited by mesale0077

Share this post


Link to post
Share on other sites

#5 ·  Posted (edited)

mesale0077, use the latest version of AutoitObject.

I had the same error, with v1.20, but not with v1.2.2

[EDIT] Now the problem with the AO site is gone

Edited by taietel

Share this post


Link to post
Share on other sites

#6 ·  Posted (edited)

Ward, another most excellent UDF crying for full-blown apps to utilize these great functions :x, thanks for sharing another gem!

Edited by KaFu

Share this post


Link to post
Share on other sites

The ability to capture an image of an entire web page in AutoIt has been a long time coming. And without having to display the web page in the process.

Ward - Nicely done. You da man.

On my xp, your WebCaptureTest() and ElementCaptureTest() functions return the same full web page image.

With this slightly modified example of the ElementCaptureTest() function only, I manage to capture some elements of a web page using the _IETableGetCollection() function (from the help file). _IEImgGetCollection() also works. When the element index is exceeded making the requested element non-existent, then the full web page image is returned.

#include <IE.au3>
#include <WinAPI.au3>
#include <ScreenCapture.au3>
#include <WindowsConstants.au3>
#include "AutoItObject.au3"  ; The AutoItObject.au3 file is present in same directory as this script.

; From  http://www.autoitscript.com/forum/topic/124002-web-screenshot/page__view__findpost__p__861114

Opt("WinTitleMatchMode", 2) ;1=start, 2=subStr, 3=exact, 4=advanced, -1 to -4=Nocase
Global $sFileName = "Test2.png"

_AutoItObject_StartUp()
OnAutoItExitRegister('OnExit')

ElementCaptureTest("http://www.autoitscript.com/autoit3/downloads.shtml", $sFileName)


Func ElementCaptureTest($Url, $ImageName)
    Local $WebWidth = @DesktopWidth
    Local $oIE = ObjCreate("Shell.Explorer.2")
    GUICreate("", $WebWidth, 368, -1, -1)
    GUICtrlCreateObj($oIE, 0, 0, $WebWidth, 368)
    _IENavigate($oIE, $Url)

    ; Get number of elements in collection.
    _IETableGetCollection($oIE, -1)
    ;_IEImgGetCollection($oIE, -1)   ; <========================== or
    Local $iNum = @extended

    For $item = 0 To $iNum + 1

        Dim $oElement = _IETableGetCollection($oIE, $item)
        ;Dim $oElement = _IEImgGetCollection($oIE, $item)  ; <====== or, also works

        If IsObj($oElement) = 0 Then $oElement = $oIE.document.body
        Dim $hBmp = ElementCapture($oElement)
        _ScreenCapture_SaveImage($ImageName, $hBmp, True)
        ShellExecute($ImageName)
        WinWaitActive($ImageName)
        MsgBox(0, "Item " & $item, "Press OK to Continue", 3, WinGetHandle($ImageName))
    Next
EndFunc   ;==>ElementCaptureTest

Func ElementCapture($oElement, $Border = 2)
    If Not IsObj($oElement) Then Return SetError(1, 0, 0)
    If $oElement.clientWidth = 0 Or $oElement.clientHeight = 0 Then Return SetError(1, 0, 0)

    Local $tIID_IHTMLElementRender = _AutoItObject_CLSIDFromString("{3050F669-98B5-11CF-BB82-00AA00BDCE0B}")
    Local $dtagIHTMLElementRender = $dtagIUnknown & "DrawToDC hresult(hwnd);"

    Local $PageHeight = $oElement.scrollHeight - $Border
    Local $PageWidth = $oElement.scrollWidth - $Border
    Local $oIHTMLElementRender = _AutoItObject_WarpInterface($oElement, $tIID_IHTMLElementRender, $dtagIHTMLElementRender)
    Local $hDC = _WinAPI_GetDC(0)
    Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC)
    Local $hBmp = _WinAPI_CreateSolidBitmap(0, 0xffffff, $oElement.clientWidth, $oElement.clientHeight)
    Local $hMemDcFull = _WinAPI_CreateCompatibleDC($hDC)
    Local $hBmpFull = _WinAPI_CreateSolidBitmap(0, 0xffffff, $PageWidth, $PageHeight)

    _WinAPI_SelectObject($hMemDC, $hBmp)
    _WinAPI_SelectObject($hMemDcFull, $hBmpFull)

    Local $DrawWidth = $oElement.clientWidth - $Border
    Local $DrawHeight = $oElement.clientHeight - $Border
    Local $CurrentX = 0
    Local $CurrentY = 0

    While $CurrentX < $PageWidth
        While $CurrentY < $PageHeight
            $oElement.scrollLeft = $CurrentX
            $oElement.scrollTop = $CurrentY
            $oIHTMLElementRender.DrawToDC(Number($hMemDC))
            _WinAPI_BitBlt($hMemDcFull, $oElement.scrollLeft, $oElement.scrollTop, $DrawWidth, $DrawHeight, $hMemDC, $Border, $Border, $SRCCOPY)
            $CurrentY += $DrawHeight
        WEnd
        $CurrentY = 0
        $CurrentX += $DrawWidth
    WEnd

    _WinAPI_DeleteDC($hMemDC)
    _WinAPI_DeleteDC($hMemDcFull)
    _WinAPI_ReleaseDC(0, $hDC)
    _WinAPI_DeleteObject($hBmp)

    Return $hBmpFull
EndFunc   ;==>ElementCapture

Func _AutoItObject_WarpInterface($Obj, $IID, $Tag, $fNoUnknown = False)
    If Not IsDllStruct($IID) Then $IID = _AutoItObject_CLSIDFromString($IID)

    If Not IsObj($Obj) Then Return SetError(1, 0, 0)

    Local $pObj = _AutoItObject_IDispatchToPtr($Obj)
    If Not _AutoItObject_IUnknownAddRef($Obj) Then Return SetError(1, 0, 0)
    Local $ObjWarpped = _AutoItObject_WrapperCreate($pObj, $dtagIUnknown, $fNoUnknown)
    Local $aCall = $ObjWarpped.QueryInterface(Number(DllStructGetPtr($IID)), 0)
    If Not IsArray($aCall) And $aCall[0] <> 0 Then Return SetError(1, 0, 0)

    Local $pInterface = $aCall[2]
    Return _AutoItObject_WrapperCreate($pInterface, $Tag)
EndFunc   ;==>_AutoItObject_WarpInterface

Func OnExit()
    _AutoItObject_Shutdown()
    If FileExists($sFileName) Then FileDelete($sFileName)
EndFunc   ;==>OnExit

Share this post


Link to post
Share on other sites

Nice Demonstration Malkey ! Posted Image


AutoIt 3.3.14.2 X86 - SciTE 3.6.0WIN 8.1 X64 - Other Example Scripts

Share this post


Link to post
Share on other sites

I wonder if I'm missing something here, the webcap works fine but the elementcap I cant seen to get working.

I get an error from malkeys example

C:\Users\john\Downloads\AUDFs\AutoItObject Package 1.2.2.0\element test.au3 (72) : ==> Variable must be of type "Object".:

$oIHTMLElementRender.DrawToDC(Number($hMemDC))

$oIHTMLElementRender^ ERROR

Anyidea what Ive got going on here, I mean I can see that whatever $oIHTMLElementRender is, is isnt an object. But the code is over my head.


AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Share this post


Link to post
Share on other sites

I was wondering how I could do this about a month ago and just brushed it off, suddenly I stumbled into this post after looking at wards sig after searching for autoit related asm stuff and spotting the "web screenshot" , this is awesome really, 5 stars for a topnotch post.


Things that I've done..

Icon Resource Editor: icon resource editor 

AutoIt Piano: a piano

AutoIt Unlocker: unlocks files when you want to delete them

Colorful tooltips: a wrapper for the tool tips UDF

Rouge GoogleBot: a full screen animation

ASciTE text editor: a text editor written in autoit

Warning: Posts by this user are subject to change or may disappear without notice.

Share this post


Link to post
Share on other sites

That is a great work!!

Do you have any idea if I want to do following scenario?

1. open browser manually instead of creating IE object within AutoIt.

2. capture entire webpage.

Share this post


Link to post
Share on other sites

Is it possible to covert this code as AIO less code? i mean by using ObjCreateInterface?


73 108 111 118 101 65 117 116 111 105 116

Share this post


Link to post
Share on other sites

Is it possible to covert this code as AIO less code? i mean by using ObjCreateInterface?

You can relpace this line in my script:

Local $oIViewObject2 = _AutoItObject_WarpInterface($oDocument, $tIID_IViewObject2, $dtagIViewObject2)

To:

Local $pObj = _AutoItObject_IDispatchToPtr($oDocument)

Local $oIViewObject2 = ObjCreateInterface($pObj, "{00000127-0000-0000-C000-000000000046}", "Draw hresult(dword;long;ptr;ptr;dword;dword;ptr;ptr;int;dword);")

But it seems there is no replacement of "_AutoItObject_IDispatchToPtr".

(It's strange. ObjCreateInterface accepts "Class identifier or object pointer", but there is no way to get the object pointer by build-in functions...)


新版 _ArrayAdd 的白痴作者,不管是誰,去死一死好了

 

Share this post


Link to post
Share on other sites

But it seems there is no replacement of "_AutoItObject_IDispatchToPtr".

(It's strange. ObjCreateInterface accepts "Class identifier or object pointer", but there is no way to get the object pointer by build-in functions...)

You are right, it's strange.

♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites

#16 ·  Posted (edited)

You are right, it's strange.

Hey, trancexx. Since you are a developer now, maybe you can do something!! :) Edited by Ward

新版 _ArrayAdd 的白痴作者,不管是誰,去死一死好了

 

Share this post


Link to post
Share on other sites

Hey, trancexx. Since you are a developer now, maybe you can do something!! :)

Yes, that's true. ;)

The pointer that you mention is get by calling QueryInterface or some flat API function (that returns object pointer).

But still, if object is not InterfaceDispach then you can't use QueryInterface since IUnknown methods are unavailable through dispatch access.

COM object is always stored as pointer and accessed through that pointer, meaning that pointer representation of object should in fact be object pointer. Definition of Ptr() is Converts an expression into a pointer variant. Currently Ptr() returns null pointer for all objects. I have qualified that as a bug and fixed it accordingly.

New beta will have Ptr() returning object pointer for object input. This also means that ObjCreateInterface() will be able to accept objects as first parameter.


♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites

thanks for sharing,worked well for me! :ILA2:


Thanksgiving...

Share this post


Link to post
Share on other sites

#19 ·  Posted (edited)

Just remembered this one and tried to make it work without AutoItObject, requires Beta to run properly :).

#include <IE.au3>
#include <WinAPI.au3>
#include <ScreenCapture.au3>
#include <WindowsConstants.au3>

; Runs fine with Beta 3.3.9.5+
If Int(StringReplace(@AutoItVersion, ".", "")) <= 3381 Then
    MsgBox(0, "Exit", "Requires AutoIt version > 3.3.8.1, try with Beta")
    Exit
EndIf

Global $dtagIUnknown = "QueryInterface hresult(ptr;ptr*);" & _
        "AddRef dword();" & _
        "Release dword();"
Global Const $gh_AU3Obj_ole32dll = DllOpen("ole32.dll")

WebCaptureTest("http://funk.eu", "Test1.png")

Func WebCaptureTest($Url, $ImageName)
    Local $hBmp = WebCapture($Url, 1280)
    _ScreenCapture_SaveImage($ImageName, $hBmp, True)
EndFunc   ;==>WebCaptureTest

Func WebCapture($Url, $WebWidth = 1024)
    Local Const $DVASPECT_CONTENT = 1
    Local $tIID_IViewObject2 = _CLSIDFromString("{00000127-0000-0000-C000-000000000046}")
    Local $dtagIViewObject2 = $dtagIUnknown & "Draw hresult(dword;long;ptr;ptr;dword;dword;ptr;ptr;int;dword);"
    Local $oIE = ObjCreate("Shell.Explorer.2")

    GUICreate("", $WebWidth, 768, -1, -1)
    GUICtrlCreateObj($oIE, 0, 0, $WebWidth, 768)

    _IELoadWaitTimeout(20000) ; 20 sec
    _IENavigate($oIE, $Url)
    _IELoadWait($oIE)

    Local $oDocument = $oIE.document
    Local $oBody = $oIE.document.body
    Local $oHtml = $oIE.document.documentElement

    $oBody.scroll = "no"
    $oBody.style.borderStyle = "none"
    $oHtml.style.overflow = 'hidden'
    $oBody.style.overflow = 'hidden'

    Local $oIViewObject2 = ObjCreateInterface($oDocument, "{00000127-0000-0000-C000-000000000046}", "Draw hresult(dword;long;ptr;ptr;dword;dword;ptr;ptr;int;dword);")

    If @error Then Return SetError(1, 0, 0)

    Local $BodyWidth = $oBody.scrollWidth
    Local $BodyHeight = $oBody.scrollHeight
    Local $RootWidth = $oHtml.scrollWidth
    Local $RootHeight = $oHtml.scrollHeight

    Local $Width = $BodyWidth
    Local $Height = $RootHeight
    If $BodyHeight > $Height Then $Height = $BodyHeight

    $oIE.width = $Width
    $oIE.height = $Height

    Local $hDC = _WinAPI_GetDC(0)
    Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC)
    Local $hBmp = _WinAPI_CreateCompatibleBitmap($hDC, $Width, $Height)
    _WinAPI_SelectObject($hMemDC, $hBmp)

    Local $sRECT = DllStructCreate($tagRECT)
    DllStructSetData($sRECT, "Top", 0)
    DllStructSetData($sRECT, "Left", 0)
    DllStructSetData($sRECT, "Right", $Width)
    DllStructSetData($sRECT, "Bottom", $Height)

    $oIViewObject2.Draw($DVASPECT_CONTENT, -1, 0, 0, Number($hDC), Number($hMemDC), Number(DllStructGetPtr($sRECT)), 0, 0, 0)

    _WinAPI_DeleteDC($hMemDC)
    _WinAPI_ReleaseDC(0, $hDC)
    Return $hBmp
EndFunc   ;==>WebCapture

Func _CLSIDFromString($sString)
    Local $tCLSID = DllStructCreate("dword;word;word;byte[8]")
    Local $aResult = DllCall($gh_AU3Obj_ole32dll, 'long', 'CLSIDFromString', 'wstr', $sString, 'ptr', DllStructGetPtr($tCLSID))
    If @error Then Return SetError(1, @error, 0)
    If $aResult[0] <> 0 Then Return SetError(2, $aResult[0], 0)
    Return $tCLSID
EndFunc   ;==>_CLSIDFromString
Edited by KaFu
1 person likes this

Share this post


Link to post
Share on other sites

Cool stuff. Can be usefull for my screenshooter app to enable web screenshoots when areo is disabled.

Thanks for sharing!

Br,

UEZ


Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯

Share this post


Link to post
Share on other sites

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