Jump to content

com & Perl


Aditya
 Share

Recommended Posts

First of all I am not a windows programmer, so some of my terminology might be off. So bear with me. I am trying to translate IE.au3 UDF in to Perl - specifically _IEAttach function. I got as far as to get the HWND of an embedded IE in Perl. The code is pretty simple:

use strict;
use warnings;

use Win32::API;
use Win32::OLE;

my $autoIt = Win32::OLE->new("AutoItX3.Control");

my $app_title = "Test App";
my $nc_path = "C:/Program Files/Test App/TestApp.exe";

my $win_title_match = $autoIt->Opt("WinTitleMatchMode", 1);
my $hwnd = $autoIt->ControlGetHandle($nc_title, "", "[CLASS:Internet Explorer_Server; INSTANCE:1]");

Now that I have got the HWND of the embedded IE in my test application, the next step is to get the COM object or rather IDispatch object using that HWND. In IE.au3, __IEControlGetObjFromHWND() does that job & as I was reading that code, it was making calls to DllCall(). Where is this method from? Is is part of the AutoItX? I couldn't find it in the AutoItX help file. Or is it from the Win32 API?

Or even better, does any one know how to get IDispatch object from a HWND directly in Perl? I am guessing, probably Win32::API but I am not sure. Any ideas?

Link to comment
Share on other sites

Here is how far I got and the code below is continued from the post above.

my $reg_window = new Win32::API('user32', 'RegisterWindowMessage', 'P', 'I');
my $html_obj = $reg_window->Call('WM_HTML_GETOBJECT');

my $SMTO_ABORTIFHUNG = 0x0002;
my $lresult = " " x 80;

my $send_msg = new Win32::API('user32', 'SendMessageTimeout', 'NIIIIIP', 'N');
$send_msg->Call(hex($hwnd), $html_obj, 0, 0, $SMTO_ABORTIFHUNG, 1000, $lresult);

my $aInt = pack('IIII', 0x626FC520, 0xA41E, 0x11CF, [0xA7, 0x31, 0x0, 0xA0, 0xC9, 0x8, 0x26, 0x37]);

my $obj = new Win32::API('oleacc', 'ObjectFromLresult', 'PPIP' , 'N');
my $idisp = 0;
my $ret = $obj->Call($lresult, $aInt, 0, $idisp);
print "ret: ", $ret, "\n", "ie: ", $idisp, "\n";

But $idsp is always 0. So something is wrong in my code. Any suggestions?

Link to comment
Share on other sites

Seems you're missing CoInitializeEx before you call ObjectFromLresult. Refer to trancexx's post here

Thanks for pointing it out, but adding that didn't help. Also the solution in the link you provided uses SendMessage, which works only with Vista & higher. I am trying to make this work in XP.

Link to comment
Share on other sites

The value you set to $aInt should be so or it should be IID_IHTMLDocument?

IID_IHTMLDocument is 626FC520-A41E-11CF-A731-00A0C9082637.

Edit Anyway, you said you need to use SendMessageTimeout instead right? This is from the same topic I've linked to, just a minor fix to someone's wrong DllCall() arguments types:

#include <WinAPI.au3>

ShellExecute('iexplore.exe', 'www.google.com')
WinWaitActive('[CLASS:IEFrame]')
$hWnd = WinGetHandle('[CLASS:IEFrame]')

$IE_hWnd = 0
Do
    $IE_hWnd = ControlGetHandle($hWnd, '', 'Internet Explorer_Server1')
    Sleep(200)
Until $IE_hWnd

Sleep(2000)
$oIE = _ObjGetFromHWND($IE_hWnd)
$oIE.navigate('http://www.autoitscript.com')
 

Func _RegisterWindowMessage($sMsg)
    Local $aRet = DllCall("user32.dll", "int", "RegisterWindowMessage", "str", $sMsg)
    If @error Then Return SetError(@error, @extended, 0)
    Return $aRet[0]
EndFunc

Func _SendMessageTimeout($hWnd, $msg, $wParam, $lParam, $nFlags, $nTimeout, ByRef $vOut, $r = 0, $t1 = "int", $t2 = "int")
    Local $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

Func _ObjGetFromHWND(ByRef $hWin)
    DLLCall("ole32.dll","int","CoInitialize","ptr",0)
    If @error Then Return SetError(@error,@extended,0)

    Local Const $WM_HTML_GETOBJECT = _RegisterWindowMessage("WM_HTML_GETOBJECT")
    
    If @error Then Return SetError(@error,@extended,0)
    
    Local Const $SMTO_ABORTIFHUNG = 0x0002
    Local $lResult, $typUUID, $aRet, $oIE

    _SendMessageTimeout($hWin, $WM_HTML_GETOBJECT, 0, 0, $SMTO_ABORTIFHUNG, 1000, $lResult)
    If @error Then Return SetError(@error,@extended,0)

    $typUUID = _WinAPI_GUIDFromString('{626FC520-A41E-11cf-A731-00A0C9082637}')
    $aRet = DllCall("oleacc.dll", "int", "ObjectFromLresult", "int", $lResult, _
            "ptr", DLLStructGetPtr($typUUID), "int", 0, "idispatch*", "")
    If @error Then Return SetError(@error,@extended,0)
   ; If Not IsObj($aRet[4]) Then Return SetError(1,@extended,0)
    $oIE = $aRet[4].Script()
; $oIE is now a valid IDispatch object
;Return $oIE.document.parentwindow
    Return $oIE
EndFunc

It seems you're using to correct object GUID but I know nothing about perl so it may be something with how you set the variables.

Edited by Authenticity
Link to comment
Share on other sites

The value you set to $aInt should be so or it should be IID_IHTMLDocument?

IID_IHTMLDocument is 626FC520-A41E-11CF-A731-00A0C9082637.

Yup, my packing was wrong - here is the updated one:

my $aInt = pack('LSSC8',0x626FC520,0xA41E,0x11CF,0xA7,0x31,0x00,0xA0,0xC9,0x08,0x26,0x37);

Another change I made was to unpack the $lresult (I dunno why it was accepting the unpacked lresult) that I get back from SendMessageTimeout. So my updated code looks like this:

my $co_init = new Win32::API('ole32', 'CoInitialize', 'P', 'I');
$co_init->Call(0);

my $reg_window = new Win32::API('user32', 'RegisterWindowMessage', 'P', 'I');
my $html_obj = $reg_window->Call('WM_HTML_GETOBJECT');

my $SMTO_ABORTIFHUNG = 0x0002;
my $lresult = " " x 80;

my $send_msg = new Win32::API('user32', 'SendMessageTimeout', 'NIIIIIP', 'N');
$send_msg->Call(hex($hwnd), $html_obj, 0, 0, $SMTO_ABORTIFHUNG, 1000, $lresult);

$lresult = unpack('L', $lresult);

my $aInt = pack('LSSC8',0x626FC520,0xA41E,0x11CF,0xA7,0x31,0x00,0xA0,0xC9,0x08,0x26,0x37);

my $obj = new Win32::API('oleacc', 'ObjectFromLresult', 'PPIP' , 'N');
my $idisp = " " x 8;
my $obj = new Win32::API('oleacc', 'ObjectFromLresult', 'NPIP' , 'N');
my $ret = $obj->Call($lresult, $aInt, 0, $idisp);
print "Error: ", $autoIt->error(), "\n";
print "ret: ", $ret, "\n", "ie: ", $idisp, "\n", "idisp: ", unpack('L', $idisp), "\n";

Now, I do get some value set for $idisp and is a scalar reference. I guess this is the COM object I am looking for, but I am unable to call methods like Script.Document etc on it directly. So I guess I have to dig a little more.

Edited by Aditya
Link to comment
Share on other sites

Looking at it from another angle, can't I just convert the IE.au3 to a dll & call the methods directly from Perl (or any other language for that matter)? Will that work?

Never mind, I got the answer to that question in the forums.

Link to comment
Share on other sites

I think I finally got the answer to my original question, when I posted it here - http://perlmonks.org/?node_id=804864. The function was returning a pointer and not a Perl object. So I have to create a Perl object from that pointer. I still have to do that part but it is the answer I was looking for. When I finish my code, I'll post it here for future reference. Thanks for all your help.

Edited by Aditya
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...