AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

COM_InvokeDeep - climb a COM tree more easily

 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
paulwarr



Joined: 21 Sep 2006
Posts: 32

PostPosted: Fri Mar 21, 2008 8:58 pm    Post subject: COM_InvokeDeep - climb a COM tree more easily Reply with quote

COM_InvokeDeep(res, dotted-path, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
(COM Standard Library required)

The COM Standard Library allows us to drill deeply into COM objects. The function below may make that a bit easier for you to use. It parses the "dotted-path" of a COM object (using COM_Invoke to obtain the appropriate pointer to each parent, where objects in a hierarchy are separated by "."). In this example,
    pBody2 := COM_InvokeDeep(pweb, "document.frames[1].document.body")
pBody2 returns a pointer to the body portion of the HTML document located in the second frame of the loaded web page, where pweb is a pointer to the parent IID_IHTMLWindow2.

COM_InvokeDeep can make both method calls - for example, document.getElementsByTagName[span] - and element[item] calls - for example, document.childNodes[1]

For the last loop (the element after the last period), one can pass up to 8 additional arguments to the COM_Invoke command (Note: one can pass up to 10 arguments to a COM object with COM_Invoke, but COM_InvokeDeep will consume up to two of those arguments).

Rules for use:
    a) If there's a period in an element's name, don't use that name. Instead, refer to it by its element ordinal. So, for example, one will occasionally find frames in web pages that are named like the following: this.isanoddly.named.frame . Since COM_InvokeDeep (obviously) parses on the periods, returned pointers will be invalid. Instead, identify the frame by its ordinal: document.frames[0] (for first frame in the document)

    b) Don't use parentheses () inside the "dotted-path" - use square brackets [] instead.

    c) Don't put quotes (single or double) inside the brackets when passing a parameter to a COM object's method:
      bad: document.getElementsByTagName["span"] or document.getElementsByTagName['span']
      good: document.getElementsByTagName[span]
Sean's COM_Invoke runs the guts of this function. COM_InvokeDeep was developed by lots of folks in the AHK community in this thread (wOxxOm, daonlyfreez, Lexikos, me). Hope it's helpful to you. Smile
Code:
; Usage:
;   res := COM_InvokeDeep(res, dotted-path, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
; Example:
;   pBody2 := COM_InvokeDeep(pweb, "document.frames[1].document.body")
;   (returns pointer to the body portion of the HTML document located in the second frame on the
;   loaded web page, where pweb is a pointer to the parent IID_IHTMLWindow2.
COM_InvokeDeep(obj, path, arg1="vT_NoNe", arg2="vT_NoNe", arg3="vT_NoNe", arg4="vT_NoNe", arg5="vT_NoNe", arg6="vT_NoNe", arg7="vT_NoNe", arg8="vT_NoNe")
{
   res := obj
   COM_AddRef(res) ; compensate for loop's Release()
   PathCt := 0
   Loop, Parse, Path, .
   {
      PathCt++
   }
   Loop, Parse, Path, ., ]
   {
      prop := A_LoopField
      value =
      StringGetPos, i, A_LoopField, [
      IfEqual, ErrorLevel, 0 ; contains index
      {
         StringLeft, prop, A_LoopField, %i%
         StringMid, value, A_LoopField, % i+2
      }
      If (value != "") ; contains index or parameter passed to a method, enclosed in "[]"
      {
         If (prop = "item") or (RegExMatch(value, "^[0-9]+$") = false) ; "item" already specified, or method call
         {
            If (A_Index < PathCt)
               propobj := COM_Invoke(res, prop, value)
            Else
               propobj := COM_Invoke(res, prop, value, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
            COM_Release(res)
            res := propobj
         }
         Else
         {
            propobj := COM_Invoke(res, prop)
            If (A_Index < PathCt)
               itemobj := COM_Invoke(propobj, "Item", value)
            Else
               itemobj := COM_Invoke(propobj, "Item", value, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
            COM_Release(res)
            COM_Release(propobj)
            res := itemobj
         }
      }
      Else
      {
         If (A_Index < PathCt)
            propobj := COM_Invoke(res, prop)
         Else
            propobj := COM_Invoke(res, prop, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
         COM_Release(res)
         res := propobj
      }
      if !res ; no sense in continuing - object not found (returns 0 or null)
         break
   }
   Return res
}
Back to top
View user's profile Send private message
daonlyfreez



Joined: 16 Mar 2005
Posts: 838
Location: Berlin

PostPosted: Sat Mar 22, 2008 12:45 pm    Post subject: Reply with quote

Thank you very much for this!

Cool
_________________
My AHK stuff on ahk.net / on DropBox (mirror) / @home (if online)
Back to top
View user's profile Send private message
paulwarr



Joined: 21 Sep 2006
Posts: 32

PostPosted: Sat Mar 22, 2008 2:01 pm    Post subject: Reply with quote

Thanks! But all I did was tweak what you and wOxxOm had already posted. And thankfully, Lexikos has a much better eye for code than I do! Very Happy
Back to top
View user's profile Send private message
amokoura



Joined: 14 Nov 2006
Posts: 15

PostPosted: Fri Oct 10, 2008 10:30 am    Post subject: Reply with quote

The standard library should have this included, very nice!
_________________
I recommend AutoIt instead of AHK.
Back to top
View user's profile Send private message
tank



Joined: 21 Dec 2007
Posts: 2294
Location: Louisville KY USA

PostPosted: Wed Oct 15, 2008 9:11 pm    Post subject: Reply with quote

I have modified my copy of this as such
the reason is because when sending a 64 bit integer (Common with account numbers) the value became truncated. very nice by the way Laughing Cool Very Happy
Code:
COM_InvokeDeep(obj, path, arg1="vT_NoNe", arg2="vT_NoNe", arg3="vT_NoNe", arg4="vT_NoNe", arg5="vT_NoNe", arg6="vT_NoNe", arg7="vT_NoNe", arg8="vT_NoNe")
{
   res := obj
   COM_AddRef(res) ; compensate for loop's Release()
   PathCt := 0
   Loop, Parse, Path, .
   {
     PathCt++
   }
   Loop, Parse, Path, ., ]
   {
      prop := A_LoopField
      value =
      StringGetPos, i, A_LoopField, [
      IfEqual, ErrorLevel, 0 ; contains index
      {
         StringLeft, prop, A_LoopField, %i%
         StringMid, value, A_LoopField, % i+2
      }
      If (value != "") ; contains index or parameter passed to a method, enclosed in "[]"
      {
         If (prop = "item") or (RegExMatch(value, "^[0-9]+$") = false) ; "item" already specified, or method call
         {
            If (A_Index < PathCt)
               propobj := COM_Invoke(res, prop, value)
            Else
               propobj := COM_Invoke(res, prop, value, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
            COM_Release(res),   VarSetCapacity(res,      0)
            res := propobj
         }
         Else
         {
            propobj := COM_Invoke(res, prop)
            If (A_Index < PathCt)
               itemobj := COM_Invoke(propobj, "Item", value)
            Else
               itemobj := COM_Invoke(propobj, "Item", value, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
            COM_Release(res),   VarSetCapacity(res,      0)
            COM_Release(propobj),   VarSetCapacity(propobj,      0)
            res := itemobj
         }
      }
      Else
      {
         If (A_Index < PathCt)
            propobj := COM_Invoke(res, prop)
         Else
         {
         sParams   := 12345678
         int:=False
         Loop,   Parse,   sParams
            If   arg%A_LoopField% is Integer
            {
               int:=true
               Break
            }
         If int
         {   
            Loop,   Parse,   sParams
            {   
                  If   (arg%A_LoopField% == "vT_NoNe")
                  {   
                     arg%A_LoopField% := ""
                     VT_BSTR%A_LoopField%:=""
                  }
                  Else
                     VT_BSTR%A_LoopField%:=8
            }
            propobj := COM_Invoke_(res, prop, VT_BSTR1,arg1,VT_BSTR2,arg2, VT_BSTR3,arg3, VT_BSTR4,arg4, VT_BSTR5,arg5, VT_BSTR6,arg6, VT_BSTR7,arg7, VT_BSTR8,arg8)
         }
         Else
            propobj := COM_Invoke(res, prop, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
       }
         COM_Release(res),   VarSetCapacity(res,      0)
         res := propobj
      }
      if !res ; no sense in continuing - object not found (returns 0 or null)
         break
   }
   Return res
}

_________________
Basic Webpage Controls with JavaScript / COM - Tutorial by Jethrow
Back to top
View user's profile Send private message
Sean



Joined: 12 Feb 2007
Posts: 2141

PostPosted: Tue Feb 03, 2009 11:24 am    Post subject: Reply with quote

Inspired by this post, I wrote this. If there is no objection, I may combine this into COM_Invoke().
Code:
COM_InvokeDeep(pdsp, name,  arg0="vT_NoNe", arg1="vT_NoNe", arg2="vT_NoNe", arg3="vT_NoNe", arg4="vT_NoNe", arg5="vT_NoNe", arg6="vT_NoNe", arg7="vT_NoNe", arg8="vT_NoNe", arg9="vT_NoNe")
{
   If   InStr(name,".")
   {
      name .=   "[["
      COM_AddRef(pdsp)
      Loop,   Parse,   name, .
      {
         name :=   A_LoopField
         If   InStr(name,"[")
         Loop,   Parse,   name, [,'"]
         If   A_Index = 1
            name :=   A_LoopField
         Else If   A_Index = 2
            argn :=   A_LoopField
         Else   bend :=   True
         Else   argn :=   "vT_NoNe"
         If  Not   bend
            pobj :=   COM_Invoke(pdsp,name,argn)
         Else If   argn !=
            pobj :=   COM_Invoke(pdsp,name,argn,arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8)
         Else   pobj :=   COM_Invoke(pdsp,name,arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9)
         COM_Release(pdsp)
         pdsp :=   pobj
      }
      Return   pdsp
   }
}
Back to top
View user's profile Send private message
tank



Joined: 21 Dec 2007
Posts: 2294
Location: Louisville KY USA

PostPosted: Tue Feb 03, 2009 8:01 pm    Post subject: Reply with quote

Fabulously shorter and distinct.
Couple things come to mind
I for one and perhaps I am the only one Confused
Use COM_InvokeDeep with excell and account numbers. As Invoke will truncate large numbers it might be good to allow BSTR
consider the following code that works untill we get a large number
Code:
COM_CoInitialize()
comoWB:=COM_InvokeDeep(oExcel:=COM_ActiveXObject("Excel.Application")
               ,"workbooks.add")
COM_Invoke(oExcel, "Visible=", true)
COM_Release(oExcel),oExcel:=0

;~ COM_InvokeDeep(oWB
;~                ,"worksheets.item[Name or sheetn style reference].cells.item[Column].item[Row].value"
;~                ,"Some Value to write")
loop 10 ; loop thru cells in a workbook
COM_InvokeDeep(oWB
               ,"worksheets.item[sheet1].cells.item[" A_Index "].item[2].value"
               ,A_Index)
COM_Release(oWB),oWB:=0
COM_CoUninitialize()

if i pass say a 16 digit integer it of course gets truncated
Now I could easily for my own private purposes test the arg0 to see if its an digits only and sub in COM_Invoke_() and use 8 to make it a string for the type
But I'm having some dificulty making it as short and clean as yours
My previous post in this thread describes the best tactic i could come up with

Ideas?
_________________
Basic Webpage Controls with JavaScript / COM - Tutorial by Jethrow
Back to top
View user's profile Send private message
Sean



Joined: 12 Feb 2007
Posts: 2141

PostPosted: Wed Feb 04, 2009 12:05 am    Post subject: Reply with quote

tank wrote:
As Invoke will truncate large numbers it might be good to allow BSTR
Code:
if  var is integer
may be rewritten in AHK as
Code:
if  var+0!="" && !InStr(var,".")

So, something like
Code:
if  var is int32
can be written as
Code:
if  var+0!="" && !InStr(var,".") && var<0x80000000 && var>=-0x80000000
Back to top
View user's profile Send private message
tank



Joined: 21 Dec 2007
Posts: 2294
Location: Louisville KY USA

PostPosted: Wed Feb 04, 2009 1:21 am    Post subject: Reply with quote

This is what i ended up with in the end it was shorter to just do this
Code:
COM_InvokeDeep(pdsp, name,  arg0="vT_NoNe", arg1="vT_NoNe", arg2="vT_NoNe", arg3="vT_NoNe", arg4="vT_NoNe", arg5="vT_NoNe", arg6="vT_NoNe", arg7="vT_NoNe", arg8="vT_NoNe", arg9="vT_NoNe")
{
   If   InStr(name,".")
   {
   name .=   "[["
   COM_AddRef(pdsp)
   Loop,   Parse,   name, .
   {
      name :=   A_LoopField
      If   InStr(name,"[")
         Loop,   Parse,   name, [,'"]
            If   A_Index = 1
               name :=   A_LoopField
            Else If   A_Index = 2
               argn :=   A_LoopField
            Else   bend :=   True
      Else   argn :=   "vT_NoNe"
      If  Not   bend
         pobj :=   COM_Invoke(pdsp,name,argn)
      Else If   argn !=
            pobj :=   COM_Invoke(pdsp,name,argn,arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8)
      Else   pobj := COM_Invoke_(pdsp,name
                                 ,BSTR0:=(arg0!="vT_NoNe" ? 8 : ""),arg0
                                 ,BSTR1:=(arg1!="vT_NoNe" ? 8 : ""),arg1
                                 ,BSTR2:=(arg2!="vT_NoNe" ? 8 : ""),arg2
                                 ,BSTR3:=(arg3!="vT_NoNe" ? 8 : ""),arg3
                                 ,BSTR4:=(arg4!="vT_NoNe" ? 8 : ""),arg4
                                 ,BSTR4:=(arg5!="vT_NoNe" ? 8 : ""),arg5
                                 ,BSTR5:=(arg6!="vT_NoNe" ? 8 : ""),arg6
                                 ,BSTR6:=(arg7!="vT_NoNe" ? 8 : ""),arg7
                                 ,BSTR7:=(arg8!="vT_NoNe" ? 8 : ""),arg8
                                 ,BSTR8:=(arg9!="vT_NoNe" ? 8 : ""),arg9)

      COM_Release(pdsp)
      pdsp :=   pobj
   }
   Return   pdsp
   }
}
and it works pretty fast
I am certain that someone much smarter than me will resolve this in a short method using Sean's suggestions. But I dont seem equal to the task

Sean I(am i the only one?) have no objection to either this or any other version of Deep i have long been adding it any how
_________________
Basic Webpage Controls with JavaScript / COM - Tutorial by Jethrow
Back to top
View user's profile Send private message
Sean



Joined: 12 Feb 2007
Posts: 2141

PostPosted: Wed Feb 04, 2009 2:14 am    Post subject: Reply with quote

tank wrote:
This is what i ended up with in the end it was shorter to just do this
You don't need to use COM_Invoke_(). What I meant was to replace the following condition in COM_Invoke()
Code:
If   arg%A_LoopField% Is Not Integer
with
Code:
If   arg%A_LoopField%+0=="" || InStr(arg%A_LoopField%,".") || arg%A_LoopField%>=0x80000000 || arg%A_LoopField%<-0x80000000

Quote:
Sean I(am i the only one?) have no objection to either this or any other version of Deep i have long been adding it any how
I didn't try to incorporate it as I hadn't decided the level of support, should be full support or sufficient with limited support, and there already existed ones like ez_invoke() and/or COM_InvokeDeep(). Anyway, I added to COM.ahk the above condition after commenting out, so, you may just switch the two. I also incorporated the code so as to call COM_InvokeDeep() directly inside COM_Invoke(). You may download and experiment it.
Back to top
View user's profile Send private message
tank



Joined: 21 Dec 2007
Posts: 2294
Location: Louisville KY USA

PostPosted: Wed Feb 04, 2009 2:26 am    Post subject: Reply with quote

Doh i almost tried that but im pretty dumb thanks I Have Tested Very Happy
I tested out with the alternate if statement and it works perfectly with my large integers
Thank you tons Sean Very Happy
you are still my hero
_________________
Basic Webpage Controls with JavaScript / COM - Tutorial by Jethrow
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Page 1 of 1

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group