The MASM Forum

General => The Workshop => Topic started by: jj2007 on September 08, 2012, 10:27:18 AM

Title: CoInvoke
Post by: jj2007 on September 08, 2012, 10:27:18 AM
I have added a CoInvoke macro to the MasmBasic library (http://masmforum.com/~masm32/board/index.php?topic=94), usage (for example, more below):

   CoInvoke pInterface, IWebBrowserVtbl.get_HWND, addr hWin

There is a somewhat "unfinished" thread in the old forum (http://www.movsd.com/board/index.php?topic=16760.msg139404#msg139404), and I have a suspicion that the macro shown there might not work correctly. Below is my version (from MasmBasic.inc):

CoInvoke MACRO pInterface:REQ,Method:REQ,args:VARARG
LOCAL is, ct, ciPush$, tmp$, tmpAddr$, errEdx$
  ct=0
  ciPush$ equ <>
  FOR arg, <args>
   ct=ct+1
   ciPush$ CATSTR <arg>, <#>, ciPush$
  ENDM
  ciPush$ CATSTR ciPush$, < >
  WHILE ct
     ct=ct-1
   is INSTR ciPush$, <#>
   tmp$ SUBSTR ciPush$, 1, is-1
   tmpAddr$ CATSTR tmp$, <123>   ; addr
   tmpAddr$ SUBSTR tmpAddr$, 1, 4
   ifidni tmpAddr$, <addr>
      % errEdx$ equ tmp$
      tmp$ SUBSTR ciPush$, 5, is-5
      lea edx, tmp$
      push edx
   else
      ifidni tmp$, <edx>
         ifdef errEdx$
            % echo ### edx overwritten by errEdx$ ###
            .err
         endif
      endif
      push tmp$
   endif
   ciPush$ SUBSTR ciPush$, is+1
  ENDM
  ifdifi <pInterface>, <eax>
   mov eax, pInterface
  endif
  push eax
  mov eax, [eax]
  mov edx, [eax+Method]
  call dword ptr [eax+Method]   ; Method=4, 8, ...
ENDM

One question: In some very old posts by Ernie Murphy (e.g. here (http://yanaware.com/com4me/accesscom.php-author=Ernie%20MURPHY&mail=ernie@surfree.com&url=http---here.is-ComInAsm&idTute=38.htm)) and Japheth, you can find statements like:

.ERR <edx is not allowed as a coinvoke parameter>

Is there any good reason not to allow edx? My version throws an error if edx gets overwritten by an addr xyz, but otherwise I just can't see why edx should not work with CoInvoke... ::)

Just for fun, below a short COM app that launches MSIE (will not assemble with JWasm 2.08):

include \masm32\MasmBasic\MasmBasic.inc   ; download (http://masm32.com/board/index.php?topic=94.0) (version 8 Sept 2012 required)
.data?
WebInterface   dd ?   ; will be loaded by CoCreateInstance

.code
CLSID_InternetExplorer   GuidFromString("0002DF01-0000-0000-C000-000000000046")
IID_IWebBrowser2   GuidFromString(D30C1661-CDAF-11D0-8A3E-00C04FC9E26E)

MyBrowser proc url
LOCAL vEmpty:VARIANT, hWin
  ClearLocalVariables uses edi
  mov edi, offset WebInterface
  .if !dword ptr [edi]
   invoke CoCreateInstance, addr CLSID_InternetExplorer, NULL, CLSCTX_LOCAL_SERVER, addr IID_IWebBrowser2, edi
   cmp eax, S_OK
   jne cci_failed
   CoInvoke [edi], IWebBrowserVtbl.put_StatusBar, VARIANT_FALSE   ; OK, now configure the browser
   CoInvoke [edi], IWebBrowserVtbl.put_MenuBar, VARIANT_FALSE
   CoInvoke [edi], IWebBrowserVtbl.put_Visible, VARIANT_TRUE
   CoInvoke [edi], IWebBrowserVtbl.get_HWND, addr hWin
   invoke ShowWindow, hWin, SW_MINIMIZE   ; MSIE has the bad habit of showing by default a restored window
   invoke ShowWindow, hWin, SW_MAXIMIZE   ; the author likes it maximised ;-)
  .endif
  lea edx, vEmpty
  CoInvoke [edi], IWebBrowserVtbl.Navigate, Ole$(url), edx, edx, edx, edx   ; COM needs a BSTR, so the ANSI URL needs to be converted
cci_failed:
  PopUses
  ret
MyBrowser endp

   Init
      invoke OleInitialize, NULL
   .if eax==S_OK
      invoke MyBrowser, Chr$("http://masmforum.com/~masm32/board/index.php")
   .endif
      invoke OleUninitialize
   Exit
end start

Source & exe attached below.
Title: Re: CoInvoke
Post by: Ryan on September 08, 2012, 10:39:05 AM
(Nothing relevant)

My apologies if the original content of this post shifted the focus of the topic.
Title: Re: CoInvoke
Post by: jj2007 on September 08, 2012, 10:42:15 AM
Ok, but at that stage edx could have been used, without any risk, for one of the pushed args...
I have tested passing args with edx with my macros, and as expected, there are no problems.
Title: Re: CoInvoke
Post by: Ryan on September 08, 2012, 10:47:16 AM
Yeah, it appears my response doesn't really apply.  I was reading through that tutorial from CP.  I was just looking at Japheth's vf macro, and I was asking myself the same question.
Title: Re: CoInvoke
Post by: japheth on September 08, 2012, 12:09:09 PM

> Ok, but at that stage edx could have been used, without any risk, for one of the pushed args...
> I have tested passing args with edx with my macros, and as expected, there are no problems.

The problem is that expansion is done from left to right - even if the arguments of invoke are pushed from right to left. That means, the code that's using edx to dereference the vtable is created BEFORE the arguments of the function are being pushed.
Title: Re: CoInvoke
Post by: Ryan on September 08, 2012, 12:45:26 PM
Makes sense now.

Thanks for the explanation  :t
Title: Re: CoInvoke
Post by: jj2007 on September 08, 2012, 03:39:58 PM
Then why can...

  lea edx, vEmpty
  CoInvoke [edi], IWebBrowserVtbl.Navigate, Ole$(url), edx, edx, edx, edx   ; COM needs a BSTR, so the ANSI URL needs to be converted

... work smoothly, Japheth? Can you show an example where the left-to-right expansion matters in this context?
Thanks, JJ
Title: Re: CoInvoke
Post by: japheth on September 08, 2012, 04:34:44 PM
Quote from: jj2007 on September 08, 2012, 03:39:58 PM
Then why can...

  lea edx, vEmpty
  CoInvoke [edi], IWebBrowserVtbl.Navigate, Ole$(url), edx, edx, edx, edx   ; COM needs a BSTR, so the ANSI URL needs to be converted
... work smoothly, Japheth?

Perhaps because your version of CoInvoke has little to do with the CoInvoke macro of Ernest Murphi?

Title: Re: CoInvoke
Post by: TouEnMasm on September 08, 2012, 04:52:45 PM

edx is not allowed as parameter because he is used as the pointer of interface.
This macro is a modify of the original coinvoke.
Quote
;add: DECLARE_pointeur TEXTEQU <STInterFace>
;where pointeur is
; pointeur dd     ;all name who get the com pointer
; USAGE:
; COM pointeur,Release  ;usefull with duplicate pointer
COM MACRO ppv:REQ,fonction:REQ,args:VARARG
   local InvokeInterface
   computeoffset CATSTR <DECLARE_>,<ppv>
   ;---------- controle les arguments
    FOR arg, <args>     ; edx couldn't be used as parameter
        IFIDNI <&arg>, <edx>   ;
            .ERR <edx is not allowed as a coinvoke parameter>
        ENDIF
    ENDM
    IFIDNI <&pInterface>, <edx>
        .ERR <edx is not allowed as a coinvoke parameter>
    ENDIF
   ;------------ fin de controle --------------------------------
   ;InvokeInterface = concatene ...CATSTR(concatene) MACRO instruction MASM32   
   ;--- ppv is the first arg in STANDARD COM PROTOTYPE ------------
                ;--- the virtual proto need ecx with ppv
    InvokeInterface CATSTR <invoke (computeoffset ptr  [edx]).>,<&fonction,ppv>
    IFNB <args>     ; add the list of parameter arguments if any
        InvokeInterface CATSTR InvokeInterface, <, >, <&args>
    ENDIF
   ; -- charge le pointeur ---------------------
    mov edx, ppv
    mov edx, [edx]
    InvokeInterface      
ENDM

Title: Re: CoInvoke
Post by: jj2007 on September 08, 2012, 04:58:54 PM
Quote from: ToutEnMasm on September 08, 2012, 04:52:45 PM
edx is not allowed as parameter because he is used as the pointer of interface.

By often repeating something, it does not become more true. AFAIK there is no valid reason not to use edx in a CoInvoke macro. If you can't believe it, launch Olly to see this:
Original source:
  lea edx, vEmpty
  CoInvoke [edi], IWebBrowserVtbl.Navigate, Ole$(url), edx, edx, edx, edx
Disassembly:
004010D2      .  8D55 F2                 lea edx, [ebp-0E]
004010D5      .  52                      push edx
... get Ole$(URL) in eax ...
004010FE      .  5A                      pop edx
004010FF      .  52                      push edx
00401100      .  52                      push edx
00401101      .  52                      push edx
00401102      .  52                      push edx
00401103      .  50                      push eax <<< push URL
00401104      .  8B07                    mov eax, [edi]
00401106      .  50                      push eax
00401107      .  8B00                    mov eax, [eax]
0040110C      .  FF50 2C                 call near [eax+2C]
Title: Re: CoInvoke
Post by: TouEnMasm on September 08, 2012, 05:07:26 PM

No need of olly,i have made enough control.
When masm translate the macro , he put the load of edx at the first place.
So if you use edx as argument,he is overwritten.
A simple listing is enough to show how it work.
Title: Re: CoInvoke
Post by: TouEnMasm on September 08, 2012, 05:12:47 PM

Proof is here with the  listing of a macro.
   .LISTALL
   ITextServices TxDraw,DVASPECT_DOCPRINT,0,NULL,NULL,hdcDraw,NULL,prc,\
      NULL,   NULL,NULL,NULL,NULL ;TXTVIEW_INACTIVE
   .NOLIST

Quote
              1C    ;   forme les lignes de codes
0000028D  55         *       push   ebp
0000028E  8B EC      *       mov    ebp, esp
00000290  8B 15 00000244 R  1C     mov edx, ppvITextServices
00000296  8B 12        1C     mov edx, [edx]
00000298  8B 0D 00000244 R  1C     mov ecx,ppvITextServices   
0000029E  6A 00      *       push   +000000000h
000002A0  6A 00      *       push   +000000000h
000002A2  6A 00      *       push   +000000000h
000002A4  6A 00      *       push   +000000000h
000002A6  6A 00      *       push   +000000000h
000002A8  FF 75 0C      *       push   dword  ptr ss:[ebp]+00000000Ch
000002AB  6A 00      *       push   +000000000h
000002AD  FF 75 08      *       push   dword  ptr ss:[ebp]+000000008h
000002B0  6A 00      *       push   +000000000h
000002B2  6A 00      *       push   +000000000h
000002B4  6A 00      *       push   +000000000h
000002B6  6A 08      *       push   +000000008h
000002B8  FF 52 10      *       call   dword  ptr [edx]+000000010h

Title: Re: CoInvoke
Post by: jj2007 on September 08, 2012, 05:21:31 PM
Sorry, I can't see your point. My macro works.
Title: Re: CoInvoke
Post by: japheth on September 08, 2012, 05:31:03 PM
Quote from: jj2007 on September 08, 2012, 05:21:31 PM
Sorry, I can't see your point. My macro works.

Noone has claimed that it is IMPOSSIBLE to write a CoInvoke macro that allows to used register EDX. Both ToutEnMasm and me were very clearly referring to the CoInvoke macro of Ernest Murphy, not yours.

JJ, deliberately misunderstanding people is an indication of a TROLL. Are you a TROLL?
Title: Re: CoInvoke
Post by: Ryan on September 08, 2012, 05:45:58 PM
I'm curious.  The second last line in the macro has
mov edx, [eax+Method]

What is the point of this?  edx is not used in the last line of the macro, so is this necessary?
Title: Re: CoInvoke
Post by: Ryan on September 08, 2012, 06:18:09 PM
Quote from: jj2007 on September 08, 2012, 10:27:18 AM
My version throws an error if edx gets overwritten by an addr xyz
addr uses edx?  I thought it used eax exclusively?
Title: Re: CoInvoke
Post by: jj2007 on September 08, 2012, 07:34:41 PM
Quote from: japheth on September 08, 2012, 05:31:03 PM
JJ, deliberately misunderstanding people is an indication of a TROLL. Are you a TROLL?

Japheth,

I am not deliberately misunderstanding anybody. Ernie wrote that macro ten years ago, and I asked why he so firmly states that edx cannot be used. Not because I am a troll, but because I failed to see a concrete reason for this statement.

I posted my version, it works fine even with edx as parameter, and in "response" to that somebody dumps idiosyncratic macros in this thread that do not prove anything and have little to do with the original question.

You wrote "The problem is that expansion is done from left to right". That is correct but irrelevant, if during expansion edx is not getting destroyed (and Ole$() won't destroy edx for that precise reason). And it is completely irrelevant for the final part of the macro where edx (or eax) is being used to dereference the pointer.

Regarding the problem that JWasm doesn't like the GuidFromString macro, I will post it on SourceFourge.

That was a problem for JWasm 2.08 of 30 August, but I just downloaded version 7 September, and voilà, it works :t
Title: Re: CoInvoke
Post by: jj2007 on September 08, 2012, 07:48:26 PM
Quote from: Ryan on September 08, 2012, 06:18:09 PM
Quote from: jj2007 on September 08, 2012, 10:27:18 AM
My version throws an error if edx gets overwritten by an addr xyz
addr uses edx?  I thought it used eax exclusively?

In a macro you can use whatever you like. The standard implementation of invoke uses the lea eax, localvar/push eax sequence, but eax is frequently a return value from some API call, so it can be convenient to use edx instead. You may have seen an error message that eax will be overwritten - see snippet below for a test of the options.

include \masm32\include\masm32rt.inc

.code
AppName db "Masm32 is great!", 0
Hello db "A message:", 0

start:
call MyTest
exit

MyTest proc
LOCAL bufText[100]:BYTE
LOCAL bufTitle[100]:BYTE
; prepare local buffers for testing:
invoke lstrcpy, addr bufText, offset AppName
invoke lstrcpy, addr bufTitle, offset Hello

; standard behaviour:
invoke MessageBox, 0, addr bufText, addr bufTitle, MB_OK

; no error because eax gets pushed before lea is being used:
lea eax, bufTitle
invoke MessageBox, 0, addr bufText, eax, MB_YESNO

; Error A2165: Register value overwritten by INVOKE:
; lea eax, bufText
; invoke MessageBox, 0, eax, addr bufTitle, MB_OK

; workaround using edx instead of eax:
lea edx, bufText
invoke MessageBox, 0, edx, addr bufTitle, MB_YESNOCANCEL

ret
MyTest endp

end start


P.S.: "edx is not used in the last line of the macro, so is this necessary?"
No, mov edx, [eax+Method] is just a leftover from testing.
Title: Re: CoInvoke
Post by: TouEnMasm on September 08, 2012, 09:52:45 PM

All macros calling an interface are differents from other macros.
The first thing they do is loading the pointer off interface (ppv) in one register.
Others macros don't do that.
Then the normal contrust for call is made.At this time , if you reuse the register pointing on the interface this one is overwritten.
Title: Re: CoInvoke
Post by: jj2007 on September 08, 2012, 10:17:57 PM
Yves,

My macros are different from yours, and they work just fine.

Bonne journée,
Jochen
Title: Re: CoInvoke
Post by: TouEnMasm on September 08, 2012, 10:27:45 PM

Here is your original question:
Quote
Is there any good reason not to allow edx? My version throws an error if edx gets overwritten by an addr xyz, but otherwise I just can't see why edx should not work with CoInvoke...
answer is made.