Jump to content

Web Screenshot


Ward
 Share

Recommended Posts

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

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

 

Link to comment
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)...

Link to comment
Share on other sites

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
Link to comment
Share on other sites

Link to comment
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
Link to comment
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.

Link to comment
Share on other sites

  • 10 months later...

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.

Link to comment
Share on other sites

  • 2 months later...

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 的白痴作者,不管是誰,去死一死好了

 

Link to comment
Share on other sites

You are right, it's strange.

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

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

 

Link to comment
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

Link to comment
Share on other sites

  • 9 months later...
  • 1 month later...

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
Link to comment
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!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...