views:

758

answers:

1

Alright this is a pretty complicated problem but it looks like the hard work has been done already by the autoit IE.au3 project. So now it just takes someone experienced with ruby and win32api, and possibly autoit as well to figure out how to translate a function written in IE.au3 to ruby. The end result is I want to use Watir with an embedded IE browser (IE is embedded in another application). I have tested that the autoit function works fine with the application i'm using, however I'd prefer to use watir (which is ruby) rather than autoit to work with testing the html.

I can get the hwnd of the embedded browser no problem (by interfacing with autoit and using autoits ControlGetHandle function --note that i can't interface with IE.au3 because its not built into the autoit dll, its a user defined function library -- UDF). Below is the function i need to translate (i also included 2 other short functions which this function calls but i don't really need those, i've confirmed my translation works past that).

;===============================================================================
;
; Function Name:    __IEControlGetObjFromHWND()
; Description:   Returns a COM Object Window reference to an embebedded Webbrowser control
; Parameter(s):  $hWin  - HWND of a Internet Explorer_Server1 control obtained for example:
;       $hwnd = ControlGetHandle("MyApp","","Internet Explorer_Server1")
; Requirement(s):   Windows XP, Windows 2003 or higher.
;       Windows 2000; Windows 98; Windows ME; Windows NT may install the
;       Microsoft Active Accessibility 2.0 Redistributable:
;       http://www.microsoft.com/downloads/details.aspx?FamilyId=9B14F6E1-888A-4F1D-B1A1-DA08EE4077DF&displaylang=en
; Return Value(s):  On Success  - Returns DOM Window object
;                   On Failure  - 0  and sets @ERROR = 1
; Author(s):        Larry with thanks to Valik
;
;===============================================================================

Func __IEControlGetObjFromHWND(ByRef $hWin)
    DllCall("ole32.dll", "int", "CoInitialize", "ptr", 0)
    Local Const $WM_HTML_GETOBJECT = __IERegisterWindowMessage("WM_HTML_GETOBJECT")
    Local Const $SMTO_ABORTIFHUNG = 0x0002
    Local $lResult, $typUUID, $aRet, $oIE
MsgBox(0, "msg", $WM_HTML_GETOBJECT)


    __IESendMessageTimeout($hWin, $WM_HTML_GETOBJECT, 0, 0, $SMTO_ABORTIFHUNG, 1000, $lResult)

    $typUUID = DllStructCreate("int;short;short;byte[8]")
    DllStructSetData($typUUID, 1, 0x626FC520)
    DllStructSetData($typUUID, 2, 0xA41E)
    DllStructSetData($typUUID, 3, 0x11CF)
    DllStructSetData($typUUID, 4, 0xA7, 1)
    DllStructSetData($typUUID, 4, 0x31, 2)
    DllStructSetData($typUUID, 4, 0x0, 3)
    DllStructSetData($typUUID, 4, 0xA0, 4)
    DllStructSetData($typUUID, 4, 0xC9, 5)
    DllStructSetData($typUUID, 4, 0x8, 6)
    DllStructSetData($typUUID, 4, 0x26, 7)
    DllStructSetData($typUUID, 4, 0x37, 8)


    MsgBox(0, "lResult", $lResult)


    $aRet = DllCall("oleacc.dll", "long", "ObjectFromLresult", "lresult", $lResult, "ptr", DllStructGetPtr($typUUID), _
      "wparam", 0, "idispatch*", 0)
MsgBox(0, "aRet4", $aRet[4])
    If IsObj($aRet[4]) Then
     $oIE = $aRet[4] .Script()
     ; $oIE is now a valid IDispatch object
     Return $oIE.Document.parentwindow
    Else
     SetError(1)
     Return 0
    EndIf
EndFunc   ;==>__IEControlGetObjFromHWND
;===============================================================================
; Function Name:    __IERegisterWindowMessage()
; Description:   Required by __IEControlGetObjFromHWND()
; Author(s):        Larry with thanks to Valik
;===============================================================================
Func __IERegisterWindowMessage($sMsg)
    Local $aRet = DllCall("user32.dll", "int", "RegisterWindowMessage", "str", $sMsg)
    If @error Then Return SetError(@error, @extended, 0)
    Return $aRet[0]
EndFunc   ;==>__IERegisterWindowMessage

;===============================================================================
; Function Name:    __IESendMessageTimeout()
; Description:   Required by __IEControlGetObjFromHWND()
; Author(s):        Larry with thanks to Valik
;===============================================================================
Func __IESendMessageTimeout($hWnd, $msg, $wParam, $lParam, $nFlags, $nTimeout, ByRef $vOut, $r = 0, $t1 = "int", $t2 = "int")
    Local $aRet
    $aRet = DllCall("user32.dll", "long", "SendMessageTimeout", "hwnd", $hWnd, "int", $msg, $t1, $wParam, _
      $t2, $lParam, "int", $nFlags, "int", $nTimeout, "int*", "")
    If @error Then
     $vOut = 0
     Return SetError(@error, @extended, 0)
    EndIf
    $vOut = $aRet[7] 
    If $r >= 0 And $r <= 4 Then Return $aRet[$r]
    Return $aRet
EndFunc   ;==>__IESendMessageTimeout

Here is what I have so far (which mostly came from another poster on the net who was trying to solve the same problem and seemed to be on the right track):

def get_control_from_hwnd(hnd)  
    Win32API.new("ole32", "CoInitialize", ['P'] , 'I').call(0)

    reg_msg = Win32API.new("user32", "RegisterWindowMessage", ['P'] ,'I').call("WM_HTML_GETOBJECT")
    puts "msg: " + reg_msg.to_s
    result=" "*16 
    aInt = [0xA7, 0x31, 0x0, 0xA0, 0xC9, 0x8, 0x26, 0x37].pack 'I*'
    a = [0x626FC520, 0xA41E, 0x11CF, aInt].pack 'IIIP'

    sendMessagetimeout = Win32API.new("user32", "SendMessageTimeout", ['L','I','I','I','I','I','P'] , 'L')
    sendMessagetimeout.call(hnd.hex, reg_msg, 0, 0, SMTO_ABORTIFHUNG, 1000, result)

    puts "result unpacked: " + result.unpack("L").to_s  #i can confirm this is the same as the lResult from the autoit functioin

    idisp=0 
    #the problem is likely either here or the next line afterwards
    oIE = Win32API.new("oleacc", "ObjectFromLresult", ['P','P','I','P'] , 'L')


    oIE.call(result, a, 0, idisp)
    puts "idisp: " + idisp.to_s
    # returning zero
    puts idisp.unpack("L")  

end

Alright I know that's way more work than anyone on here is willing to do, but perhaps if you take a look at my translation and the original, something may jump out at you as being wrong. I'll certainly offer a bounty for anyone who comes up with a solution that works

A: 

Can't believe we are doing the same, albeit I'm trying to do it in Perl. I dunno Ruby but looking at the script, it almost looks like similar to Perl. Take a look at this thread at autoIt, where I am still trying to tackle the issue http://www.autoitscript.com/forum/index.php?showtopic=104894. But I think I might have gotten the idisp though. Two changes:

1) The way I am packing the struct is a bit different. My $iid looks like this: pack('LSSC8',0x626FC520,0xA41E,0x11CF,0xA7,0x31,0x00,0xA0,0xC9,0x08,0x26,0x37). So in Ruby I guess it'd be [0x626FC520,0xA41E,0x11CF,0xA7,0x31,0x00,0xA0,0xC9,0x08,0x26,0x37].pack 'LSSC8'? I dunno the right syntax, but you get the idea. 2) I unpack the "result" from SendMessageTimeout & then pass it to ObjectFromLresult. If I pass the result directly, I get 0 as you were getting it.

But that is as far as I've gone.

John