Skip to content
조회 수 17810 추천 수 0 댓글 0
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄

AHK에서 가능한 COM 인터넷 익스플로러 및 GUI 브라우저

IE and Gui Browser Com Tutorial
Goto page 12345  Next
 

Basic understanding of COM Pointer





AHK COM Internet Explorer and Gui Browser documentation 
First if you have not done so an understanding of basic AHK is required 
see the quick-start tutorial. 
I will simply ignore basic syntax questions like when to or not to use % 
Using Seans COM library to automate web tasks takes some learning how but 
removes the need to use some other clumbsy tactics like sending javascript thru address bar or using image search etc. 
Many will find this tutorial over there head and that is fine. take your time to learn the lessons in this tutorial and soon you will know some very usefull skills. 
this tutorial will make use of HTML DOM 
DHTML 
as well as JavaScript 

you can also check out this post some time ago by daononlyfreeze 
http://www.autohotkey.com/forum/viewtopic.php?t=6597&start=0 

I will not be giving a tutorial on these but the links included should adequetely cover these topics from a learining perspective 
I also want to make it clear you will not find any nice wrapper functions. Nor did COM or am the origional conceiver of the subject matter. 
What I have done is taken posts of questions and answers and tidbits from all over the forum and Created a tutorial from it. 

    If you want to thank someone thank the following 
    Chris Malet Autohotkey creator 
    Sean who created the COM functions 
    Lexikos whose in dept understanding and hand holding has given me the understanding necesary to do this 
    ahklerner whom Posted the first well written function to push javascritp to a webpage via ahk that I had found

First and foremost I am like you the benificiery of these Genious Very Happy 
Learning I beleive takes a real life example. 
lets define our automation job 
I like Logging into Gmail 
our other progressively more robust learning will come in the version of 
selecting an looking up a word on dictionary .com 
recording data from a web result and reacting to those results 


For the most part the COM library is based on actual C++ so i will include the relavent MSDN but will not go into to much detail as they are documented in the MSDN quite adequetly 
it is not entirely necesary that you fully grasp these. I put the links here for those who want to broaden there depth of understanding. It is however necesary to have even the most highlevel understanding of there purpose 
Goals 
    Basic understanding of COM Pointer 
    Understanding of COM_CreateObject MSDN 
      MSDN does a fine job explaining this i wont re hash it just remember creates a parent object for use with our invoke calls

    Understanding of COM_AtlAxGetControl MSDN 
    Understanding of COM_AtlAxCreateContainer MSDN 
      we will be using COM_AtlAxCreateContainer and COM_AtlAxGetControl in our examples just remember this is a way of getting a control pointer from a window control that we are in our case creating

    Understanding of COM_Invoke MSDN 
      Sean gives us a Basic Example. Basically this is an execution function. if you need to get or set a value execute a method of move from a parent object to a member object you can do so from COM_Invoke

    Understanding of COM_Release MSDN 
      In the interest of trying to dumb this down here goes. 
      everytime you create a pointer to an object or interface you should release it for each time you create it you do not release strings just object and interface pointers. Its a way of freeing memory and its good for your program but well if you dont do it the worst that can happen on most beginner scripts is some semi slow performance

    Understanding of COM_QueryService MSDN 
      In our examples we will use this function to get one object pointer from another using CLSID. there are instances when getting pwin can bypass some security zone settings which can cause scripts to fail at times

    Understanding of COM_QueryInterface MSDN 
      Returns a pointer to a specified interface on an object to which a client currently holds an interface pointer.

    Understanding of COM_ConnectObject MSDN 
      Just remember this is how we can setup ahk access to events. Gonna be a much later before I cover events but be patient


It is also imperitive that we have an understanding of the following 

    Basic Understanding of IE architecture MSDN 
    Basic understanding of the webrowser object for Gui MSDN [url=http://msdn.microsoft.com/en-us/library/aa741312(VS.85).aspx]MSDN[/url]


For our automation tasks we will be aquireing and interface pointer to iWebrowser2 through out my examples you will see this reflected as pwb 
    First I want to remind all please have COM in the standard libraries such as c:\program files\autohotkey\lib 

    Always initialize COM once per script 
    If you will not be using a GUI object

Code:
COM_Init()

    With GUI

Code:
COM_AtlAxWinInit()

    there is no need to do it more than once. Nor is there any need to do both when in dowbt use COM_AtlAxWinInit()


There are quite a few ways to create this interface pointer I will try cover the most common usefull ones 
For our, or really any purpose we can use a GUI browser or an instance of IE 
    New instance of IE
    Code:
    pwb := COM_CreateObject("InternetExplorer.Application")
    Now if you have at this point been paying attention and if you are a good student you have no doubt tried to Initialize COM and then create an instance of IE. If you havent yet do it now. .... Its ok i exxpect confusion seems like nothing happened. thats because an instance of IE is not by default visible and I strategically left something out MSDN
    Code:
    COM_Invoke(pwb , "Visible=", "True") ;"False" ;"True" ;
    All together now
    Code:
    COM_Init()
    pwb := COM_CreateObject("InternetExplorer.Application")
    COM_Invoke(pwb , "Visible=", "True") ;"False" ;"True" ;
    Dont get discouraged if you see there is no web page loaded no home page. There shouldnt be we have not triggered navigation yet we will do that next 
    Firstly the MSDN and then the code
    Code:
    url:="http://www.google.com"
    COM_Invoke(pwb, "Navigate", url)
    lets try this out
    Code:
    COM_Init()
    pwb := COM_CreateObject("InternetExplorer.Application")
    COM_Invoke(pwb , "Visible=", "True") ;"False" ;"True" ;
    url:="http://www.google.com"
    COM_Invoke(pwb, "Navigate", url)
    url:="http://www.Yahoo.com"
    COM_Invoke(pwb, "Navigate", url)
    What is goin on here:?:Evil or Very Mad No its not some sort of Microsoft conspiricy to keep us from getting to Google we just didnt tell our script to wait for a page load in between navigations..... what can we do 
    Certainly we could
    Code:
    Sleep,10000 ; 10 second pause
    What's this you want me to wait 10 seconds just because your crappy PC cant load a web page any faster than that.? so how do we make it so no matter how long or short we always halt the script till the exact second the page has loaded. We test the readystate property of PWB MSDN can you guess how we will retreive the readystate property..... Idea
    Code:
    rdy:=COM_Invoke(pwb,"readyState")
    you guessed right Very Happy Now lets loop and test it
    Code:
    loop
          If (rdy:=COM_Invoke(pwb,"readyState") = 4)
             break

    OK put it all together now
    Code (Expand):
    COM_Init()
    pwb := COM_CreateObject("InternetExplorer.Application")
    COM_Invoke(pwb , "Visible=", "True") ;"False" ;"True" ;
    url:="http://www.google.com"
    COM_Invoke(pwb, "Navigate", url)
    loop
          If (rdy:=COM_Invoke(pwb,"readyState") = 4)
             break
    url:="http://www.Yahoo.com"
    COM_Invoke(pwb, "Navigate", url)
    loop
          If (rdy:=COM_Invoke(pwb,"readyState") = 4)
             break
    MsgBox, 262208, Done, Goodbye,5
    COM_Invoke(pwb, "Quit")
    COM_Term()
    Must have slipped me a micky there did I slip some code in we hadnt discussed yet. Yes I did im not gonna explain any more simple Invoke concepts now your all grown up its time to move on. You can look up the Quit method in the MSDN pages you should already have open ill give you a hint there is a link to it in the Methods MSDN

    Lets Focus on a GUI version of what we have done above

    First you guessed it we need to create a pwb.
    Code:
    Gui, +LastFound +Resize
    ;pwb := COM_AtlAxGetControl(COM_AtlAxCreateContainer(WinExist(),top,left,width,height, "Shell.Explorer") )  ;left these here just for reference of the parameters
    pwb := COM_AtlAxGetControl(COM_AtlAxCreateContainer(WinExist(),0,0,510,600, "Shell.Explorer") )
    and of course to be able to see it we need
    Code:
    gui,show, w510 h600 ,Gui Browser

    Lets put this together with the navigation we had from the instance of IE dont forget we use the COM_AtlAxWinInit() to initialize com since we are using a GUI control COM_AtlAxWinInit()
    Code (Expand):
    COM_AtlAxWinInit()
    Gui, +LastFound +Resize
    ;pwb := COM_AtlAxGetControl(COM_AtlAxCreateContainer(WinExist(),top,left,width,height, "Shell.Explorer") )  ;left these here just for reference of the parameters
    pwb := COM_AtlAxGetControl(COM_AtlAxCreateContainer(WinExist(),0,0,510,600, "Shell.Explorer") )
    gui,show, w510 h600 ,Gui Browser
    ;take from the IE example
    url:="http://www.google.com"
    COM_Invoke(pwb, "Navigate", url)
    loop
          If (rdy:=COM_Invoke(pwb,"readyState") = 4)
             break
    url:="http://www.Yahoo.com"
    COM_Invoke(pwb, "Navigate", url)
    loop
          If (rdy:=COM_Invoke(pwb,"readyState") = 4)
             break
    MsgBox, 262208, Done, Goodbye,5
    Gui, Destroy
    COM_AtlAxWinTerm()
    ExitApp
    From here we see that that iWebrowser2 interface pointer is used exactly the same way once created. The only difference is the way we created it and the way we destroyed it 
    There are some differences in our Gui Browser 
    We cant do any of the follwoing 
      Keyboard navigation 
      use shortcut keys like ctrl + m to submit a form 
      send tab keystrokes 
      resize our browser with the window

    For all but the resize problem we use the following code 
    This will be our first exposure to the COM_QueryInterface MSDN 
    We use this to gain access to the IOleInPlaceActiveObject_Interface MSDN based on CLSID. which Enables a top-level container to manipulate an in-place object.(ie send keystrokes wehn normally only the gui control could receive them
    Code (Expand):
    ;http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.ole.interop.ioleinplaceactiveobject(VS.80).aspx
    IOleInPlaceActiveObject_Interface:="{00000117-0000-0000-C000-000000000046}"
    pipa := COM_QueryInterface(pwb, IOleInPlaceActiveObject_Interface)
    OnMessage(WM_KEYDOWN:=0x0100, "WM_KEYDOWN") 
    OnMessage(WM_KEYUP:=0x0101, "WM_KEYDOWN") 
    WM_KEYDOWN(wParam, lParam, nMsg, hWnd) 

    ;  Critical 20 
    ;tooltip % wparam 
      If  (wParam = 0x09 || wParam = 0x0D || wParam = 0x2E || wParam = 0x26 || wParam = 0x28) ; tab enter delete up down 
      ;If  (wParam = 9 || wParam = 13 || wParam = 46 || wParam = 38 || wParam = 40) ; tab enter delete up down 
      { 
          WinGetClass, Class, ahk_id %hWnd% 
          ;tooltip % class 
          If  (Class = "Internet Explorer_Server") 
              { 
                Global pipa 
                 VarSetCapacity(Msg, 28) 
                 NumPut(hWnd,Msg), NumPut(nMsg,Msg,4), NumPut(wParam,Msg,8), NumPut(lParam,Msg,12) 
                 NumPut(A_EventInfo,Msg,16), NumPut(A_GuiX,Msg,20), NumPut(A_GuiY,Msg,24) 
                 DllCall(NumGet(NumGet(1*pipa)+20), "Uint", pipa, "Uint", &Msg) 
                 Return 0 
              } 
      } 
    }
    and then for the sizing
    Code:
    GuiSize:
    WinMove, % "ahk_id " . COM_AtlAxGetContainer(pwb), , 0,0, A_GuiWidth, A_GuiHeight
    return
    All together Now
    Code (Expand):
    COM_AtlAxWinInit()
    Gui, +LastFound +Resize
    ;pwb := COM_AtlAxGetControl(COM_AtlAxCreateContainer(WinExist(),top,left,width,height, "Shell.Explorer") )  ;left these here just for reference of the parameters
    pwb := COM_AtlAxGetControl(COM_AtlAxCreateContainer(WinExist(),0,0,510,600, "Shell.Explorer") )
    ;http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.ole.interop.ioleinplaceactiveobject(VS.80).aspx
    IOleInPlaceActiveObject_Interface:="{00000117-0000-0000-C000-000000000046}"
    pipa := COM_QueryInterface(pwb, IOleInPlaceActiveObject_Interface)
    OnMessage(WM_KEYDOWN:=0x0100, "WM_KEYDOWN") 
    OnMessage(WM_KEYUP:=0x0101, "WM_KEYDOWN") 
    gui,show, w510 h600 ,Gui Browser
    ;take from the IE example
    url:="http://www.google.com"
    COM_Invoke(pwb, "Navigate", url)
    loop
          If (rdy:=COM_Invoke(pwb,"readyState") = 4)
             break
    url:="http://www.Yahoo.com"
    COM_Invoke(pwb, "Navigate", url)
    loop
          If (rdy:=COM_Invoke(pwb,"readyState") = 4)
             break
    return

    GuiClose:
    Gui, Destroy
    COM_CoUninitialize()
    COM_AtlAxWinTerm()
    ExitApp

    GuiSize:
    WinMove, % "ahk_id " . COM_AtlAxGetContainer(pwb), , 0,0, A_GuiWidth, A_GuiHeight
    return
    WM_KEYDOWN(wParam, lParam, nMsg, hWnd) 

    ;  Critical 20 
    ;tooltip % wparam 
      If  (wParam = 0x09 || wParam = 0x0D || wParam = 0x2E || wParam = 0x26 || wParam = 0x28) ; tab enter delete up down 
      ;If  (wParam = 9 || wParam = 13 || wParam = 46 || wParam = 38 || wParam = 40) ; tab enter delete up down 
      { 
          WinGetClass, Class, ahk_id %hWnd% 
          ;tooltip % class 
          If  (Class = "Internet Explorer_Server") 
              { 
                Global pipa 
                 VarSetCapacity(Msg, 28) 
                 NumPut(hWnd,Msg), NumPut(nMsg,Msg,4), NumPut(wParam,Msg,8), NumPut(lParam,Msg,12) 
                 NumPut(A_EventInfo,Msg,16), NumPut(A_GuiX,Msg,20), NumPut(A_GuiY,Msg,24) 
                 DllCall(NumGet(NumGet(1*pipa)+20), "Uint", pipa, "Uint", &Msg) 
                 Return 0 
              } 
      } 
    }
    Now tank thats alot more script to get the same results 
    Yes it is the choice of method is yours to make I am giving you tools but there are benifets to both you will have to discover these in your own time and based on projects you endeavor 
    We will endeavor some other GUI excersizes later for breivity im moving us back to IE 
    Before we go on there is one more method of getting a pwb 

    Re-using exisitng instances of IE 
    The Shell.Application has a collection of windows. Be warned this will include windows explorer and IE. But best of all each instance of IE or a tab within is a separate instance of iWebrowser2. So this approach is ideal for itenerating all of those tabs and windows 
    The windows collection like almost everything in Microsofts world is 0 based meaning first object is 0 second is 1 .... etc
    Code:
    psw := COM_Invoke(psh:=COM_CreateObject("Shell.Application"), "Windows"),
    Microsoft cleverly exposes a count property we would get a total count with
    Code:
    COM_Invoke(psw := COM_Invoke(psh:=COM_CreateObject("Shell.Application"), "Windows"), "Count")
    Then we loop thru windows we need to take into account that the first instance is 0 and a-index starts at 1 
    Much ias a despise using window titles to distinguish windows its still one of the best ways
    Code:
    pageSearched:="Google" 
    Loop, %   COM_Invoke(psw := COM_Invoke(psh:=COM_CreateObject("Shell.Application"), "Windows"), "Count") 
       { 
          LocationName:=COM_Invoke(pwb:=COM_Invoke(psw, "Item", A_Index-1), "LocationName") 
          IfInString,LocationName,%pageSearched% 
             Break 
          COM_Release(pwb) ;didnt break so release the one we didnt use 
       }
    We break the loop on the first match but could instead creat an ahk array of matches and not break the loop
    Code:
    pageSearched:="google" 
    Loop, %   COM_Invoke(psw := COM_Invoke(psh:=COM_CreateObject("Shell.Application"), "Windows"), "Count") 
       { 
          LocationName:=COM_Invoke(pwb%a_index%:=COM_Invoke(psw, "Item", A_Index-1), "LocationName") 
          IfNotInString,LocationName,%pageSearched% 
          COM_Release(pwb%a_index%),   VarSetCapacity(pwb%a_index%,   0) ;didnt break so release the one we didnt use 
       }

    COM_Release(pwb%a_index%) we release the undused pointer here from memory 
    VarSetCapacity(pwb%a_index%, 0) here we make sure there is no value to the indexed pwb as release does not destroy the value just the memory reference 
    I am not going to spend alot of time on this because automating multiple instances of the same site is rare even in my world and i automate data jobs for a large corperation 


Lets recap what we know how to do now 
we know how to use pwb to create a pointer to iWebrowser2 
Trigger Navigation 
Wait for a page to load 
Create a gui browser object 
set it to autoresize if the gui resizes 
accept keystrokes 
    Pushing Data to a website 
    By and large javascript has to be the most universally known way to get this job done bu non programmers. so if you know javascript your learning curve for AHK to push data to some site is nearly over 
    Remember the Navigate method for the pwb 
    Well in HTML it is common the set an href to something like
    Code:
    <a href="javascript:somejavascripthere">click here to execute some javascript</a>

    back to using the Navigation method for this
    Quote:
    The WebBrowser control or InternetExplorer object can browse to any location in the local file system, on the network, or on the World Wide Web. 

    In Microsoft Internet Explorer 6 or later, you can navigate through code only within the same domain as the application hosting the WebBrowser control. Otherwise, this method and Navigate2 are disabled. 

    In Internet Explorer 7, when you specify the navOpenInNewTab flag or the navOpenInBackgroundTab flag, do not combine them with other parameters (TargetFrameName, PostData, Headers) or with other BrowserNavConstants flags. If tabbed browsing is disabled, or if a tab cannot be created, the call will fail. If this happens, choose another navigation method, such as navOpenInNewWindow. 

    Note New tabs are opened asynchronously; this method returns as soon as the tab is created, which can be before navigation in the new tab has started. The IWebBrowser2 object for the destination tab is not available to the caller. Tab order is not guaranteed, especially if this method is called many times quickly in a row. 
    When navOpenInNewWindow or navOpenInNewTab is specified, the caller does not receive a reference to the WebBrowser object for the new window, so there is no immediate way to manipulate it. 
    6029565920 
    todd stebbing 
    602-840-2296 
    amsi 
    Code:
    digest this and take your time what it is saying is that in the event there are frames or content with other domains content from other than the parent domain is not available 
    Next have a look at
    Quote:
    navOpenInNewWindow = 0x1, 
    navNoHistory = 0x2, 
    navNoReadFromCache = 0x4, 
    navNoWriteToCache = 0x8, 
    navAllowAutosearch = 0x10, 
    navBrowserBar = 0x20, 
    navHyperlink = 0x40, 
    navEnforceRestricted = 0x80, 
    navNewWindowsManaged = 0x0100, 
    navUntrustedForDownload = 0x0200, 
    navTrustedForActiveX = 0x0400, 
    navOpenInNewTab = 0x0800, 
    navOpenInBackgroundTab = 0x1000, 
    navKeepWordWheelText = 0x2000, 
    navVirtualTab = 0x4000 
    We want to be sure that the browser isnt going to apply some security rules that will prevent the script from working such as Security zone local intranet not allowing active x. hav a look above and see if you can tell which flag you might use 
    Quote:
    Navigate( _ 
    url As String, _ 
    [Flags As Variant,] _ 
    [TargetFrameName As Variant,] _ 
    [PostData As Variant,] _ 
    [Headers As Variant]

    So far we only passed a url parameter 
    Lets do some real damage 
    Code:
    jsAlert:="javascript:alert('js pased to navigation');"
    COM_Invoke(pwb, "Navigate", jsAlert,0x0400)
    COM_QueryService MSDN will now be used to get an html window object handle from the iWebrowser2 pointer pwb You will notice its used much the same way as COM_QueryInterface MSDN
    Code:
    jsAlert:="javascript:var x='10';"
    COM_Invoke(pwb, "Navigate", jsAlert,0x0400)
    IID_IHTMLWindow2   := "{332C4427-26CB-11D0-B483-00C04FD90119}" 
    pwin := COM_QueryService(pacc,IID_IHTMLWindow2,IID_IHTMLWindow2) 
    msgbox % COM_Invoke(pwin , "x")
    As we can see here global bariables are available thru the window object 

Thats all in this lesson our next lesson will tear into HTML DOM. While we can manipulate HTML DOM via javascript injects there is a nice plain COM way to do it 
till next lessen go ahead an peruse below some other hints from our master of arms Sean 
I'm really just putting all in one place 


Sean wrote:
Try this, need the standard library COM.ahk
Code (Expand):
sURL :=   "http://www.autohotkey.com/forum/"
COM_CoInitialize()
If Not   pweb := GetWebBrowser()
   ExitApp
COM_Invoke(pweb, "Navigate", sURL)
;COM_Invoke(pweb, "Navigate", sURL, 0x1)   ; navOpenInNewWindow
/*   IE7 Only!
COM_Invoke(pweb, "Navigate", sURL, 0x800)   ; navOpenInNewTab
COM_Invoke(pweb, "Navigate", sURL, 0x1000)   ; navOpenInBackgroundTab
*/

COM_Release(pweb)
COM_CoUninitialize()
Return

GetWebBrowser()
{
   ControlGet, hIESvr, hWnd, , Internet Explorer_Server1, ahk_class IEFrame
   If Not   hIESvr
      Return
   DllCall("SendMessageTimeout", "Uint", hIESvr, "Uint", DllCall("RegisterWindowMessage", "str", "WM_HTML_GETOBJECT"), "Uint", 0, "Uint", 0, "Uint", 2, "Uint", 1000, "UintP", lResult)
   DllCall("oleacc\ObjectFromLresult", "Uint", lResult, "Uint", COM_GUID4String(IID_IHTMLDocument2,"{332C4425-26CB-11D0-B483-00C04FD90119}"), "int", 0, "UintP", pdoc)
   IID_IWebBrowserApp := "{0002DF05-0000-0000-C000-000000000046}"
   pweb := COM_QueryService(pdoc,IID_IWebBrowserApp,IID_IWebBrowserApp)
   COM_Release(pdoc)
   Return   pweb
}

Sean wrote:
This script retrieves hWnd/URL/Title/StatusText of all running iexplore/explorer Windows, including Tabs. 
The shell should be set to explorer.exe for the script to work. 

NEED COM Standard Library

Code:
COM_Init()
psh :=   COM_CreateObject("Shell.Application")
psw :=   COM_Invoke(psh, "Windows")
Loop, %   COM_Invoke(psw, "Count")
   pwb := COM_Invoke(psw, "Item", A_Index-1), sInfo .= COM_Invoke(pwb, "hWnd") . " : """ . COM_Invoke(pwb, "LocationURL") . """,""" . COM_Invoke(pwb, "LocationName") . """,""" . COM_Invoke(pwb, "StatusText") . """,""" . COM_Invoke(pwb, "Name") . """,""" . COM_Invoke(pwb, "FullName") . """`n", COM_Release(pwb)
COM_Release(psw)
COM_Release(psh)
COM_Term()

MsgBox, % sInfo
Sean wrote:
IE7 supports multi-tab windows, but the conventional WebBrowser control is lacking of a function to differentiate one among them, the HWND function in IWebBrowser2 object always returns the window handle of IEFrame, not the new window classTabWindowClass

Here is a method to obtain the window handle of TabWindowClass from the IWebBrowser2 object pwb (:if applied to IE6 etc, I think it'll just return the window handle of IEFrame). 

Code:
IETabWindow(pwb)
{
   psb  :=   COM_QueryService(pwb,IID_IShellBrowser:="{000214E2-0000-0000-C000-000000000046}")
   DllCall(NumGet(NumGet(1*psb)+12), "Uint", psb, "UintP", hwndTab)
   DllCall(NumGet(NumGet(1*psb)+ 8), "Uint", psb)
   Return   hwndTab
}
















로그인 후 댓글쓰기가 가능합니다.

?

http://urin79.com

우린친구블로그

sketchbook5, 스케치북5

sketchbook5, 스케치북5

나눔글꼴 설치 안내


이 PC에는 나눔글꼴이 설치되어 있지 않습니다.

이 사이트를 나눔글꼴로 보기 위해서는
나눔글꼴을 설치해야 합니다.

설치 취소