 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
paulwarr
Joined: 21 Sep 2006 Posts: 32
|
Posted: Fri Mar 21, 2008 8:58 pm Post subject: COM_InvokeDeep - climb a COM tree more easily |
|
|
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.
| 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 |
|
 |
daonlyfreez
Joined: 16 Mar 2005 Posts: 838 Location: Berlin
|
Posted: Sat Mar 22, 2008 12:45 pm Post subject: |
|
|
Thank you very much for this!
 _________________
My AHK stuff on ahk.net / on DropBox (mirror) / @home (if online) |
|
| Back to top |
|
 |
paulwarr
Joined: 21 Sep 2006 Posts: 32
|
Posted: Sat Mar 22, 2008 2:01 pm Post subject: |
|
|
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!  |
|
| Back to top |
|
 |
amokoura
Joined: 14 Nov 2006 Posts: 15
|
Posted: Fri Oct 10, 2008 10:30 am Post subject: |
|
|
The standard library should have this included, very nice! _________________ I recommend AutoIt instead of AHK. |
|
| Back to top |
|
 |
tank
Joined: 21 Dec 2007 Posts: 2294 Location: Louisville KY USA
|
Posted: Wed Oct 15, 2008 9:11 pm Post subject: |
|
|
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
| 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 |
|
 |
Sean
Joined: 12 Feb 2007 Posts: 2141
|
Posted: Tue Feb 03, 2009 11:24 am Post subject: |
|
|
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 |
|
 |
tank
Joined: 21 Dec 2007 Posts: 2294 Location: Louisville KY USA
|
Posted: Tue Feb 03, 2009 8:01 pm Post subject: |
|
|
Fabulously shorter and distinct.
Couple things come to mind
I for one and perhaps I am the only one
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 |
|
 |
Sean
Joined: 12 Feb 2007 Posts: 2141
|
Posted: Wed Feb 04, 2009 12:05 am Post subject: |
|
|
| tank wrote: | | As Invoke will truncate large numbers it might be good to allow BSTR | may be rewritten in AHK as | Code: | | if var+0!="" && !InStr(var,".") |
So, something like can be written as
| Code: | | if var+0!="" && !InStr(var,".") && var<0x80000000 && var>=-0x80000000 |
|
|
| Back to top |
|
 |
tank
Joined: 21 Dec 2007 Posts: 2294 Location: Louisville KY USA
|
Posted: Wed Feb 04, 2009 1:21 am Post subject: |
|
|
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 |
|
 |
Sean
Joined: 12 Feb 2007 Posts: 2141
|
Posted: Wed Feb 04, 2009 2:14 am Post subject: |
|
|
| 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 |
|
 |
tank
Joined: 21 Dec 2007 Posts: 2294 Location: Louisville KY USA
|
Posted: Wed Feb 04, 2009 2:26 am Post subject: |
|
|
Doh i almost tried that but im pretty dumb thanks I Have Tested
I tested out with the alternate if statement and it works perfectly with my large integers
Thank you tons Sean
you are still my hero _________________ Basic Webpage Controls with JavaScript / COM - Tutorial by Jethrow
 |
|
| Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|