Archive: Playing with COM


Playing with COM
  I'm attempting to recreate the example posted at: http://en.wikipedia.org/wiki/OLE_Automation#Examples

My code is choking at GetIDsOfNames... Can anyone add some insight?

    /*

!define IID_StdOle '{00020430-0000-0000-C000-000000000046}'
!define IID_IDispatch '{00020400-0000-0000-C000-000000000046}'
!define IID_IUnknown '{00000000-0000-0000-C000-000000000046}'
!define IID_NULL '{00000000-0000-0000-0000-000000000000}'

!define CLSCTX_SERVER 0x015
!define LOCALE_SYSTEM_DEFAULT 0x800
*/

DetailPrint CLSIDFromProgID
System
::Call `Ole32::CLSIDFromProgID(w,&g16)i ("Excel.Application",.r0).r2`
DetailPrint "ERR=$2"
DetailPrint "CLSID=$0"


DetailPrint CoCreateInstance
System::Call `Ole32::CoCreateInstance(g r0,i 0,i ${CLSCTX_SERVER},g '${IID_IDispatch}',*i .r1) i .r2`
DetailPrint "ERR=$2"

DetailPrint GetIDsOfNames
System::Call `$1->GetIDsOfNames(g ${IID_NULL},w "Visible",i 1,i ${LOCALE_SYSTEM_DEFAULT},*i .r3)i .r2` ;<-- Errors here... :'(
DetailPrint "ERR=$2" ; <-- I'm getting error 0x80004002 (AKA E_NOINTERFACE)
DetailPrint $$3=$3
>

Originally posted by Zinthose
My code is choking at GetIDsOfNames... Can anyone add some insight?


    System::Call `$1->***91;B***93;GetIDsOfNames***91;/B***93;(g ${IID_NULL},w "Visible",i 1,i ${LOCALE_SYSTEM_DEFAULT},*i .r3)i .r2` ;<-- Errors here... :'(

DetailPrint "ERR=$2" ; <-- I'
m getting error 0x80004002 (AKA E_NOINTERFACE)
GetIDsOfNames should be replaced by the numerical entry of that bit in the interface. I don't recall where you can find that, though..

It's the zero-based index of the method as it is listed physically in the interface. This includes methods from interfaces that the interface you are using implements. As IUnknown has 3 methods, your first interface method will typically be #3.

Stu


Originally posted by Afrow UK
It's the zero-based index of the method as it is listed physically in the interface. This includes methods from interfaces that the interface you are using implements. As IUnknown has 3 methods, your first interface method will typically be #3.
Right - as per the System plugin docs;
To find out the index of a member in a COM interface, you need to search for the definition of this COM interface in the header files that come with Visual C/C++ or the Platform SDK. Remember the index is zero based.
What I meant was.. I don't recall where the header files for this interface can be found online (many of us don't actually touch the SDK bits and pieces) - but then I remembered; WINE usually has 'm :)

http://source.winehq.org/source/include/oleauto.h

Aight... Using the VTable index to lookup the interfaces methods I have now updated with the following..

    DetailPrint"== CLSIDFromProgID =="
System::Call `Ole32::CLSIDFromProgID(w,&g16)i ("Excel.Application",.r0).r2`
DetailPrint " ERR=$2"
DetailPrint " CLSID=$0"

DetailPrint "== CoCreateInstance =="
System::Call `Ole32::CoCreateInstance(g r0,i 0,i ${CLSCTX_SERVER},g '${IID_IDispatch}',*i .r1) i .r2`
DetailPrint " ERR=$2"

DetailPrint "== IUnknown::GetTypeInfoCount =="
System::Call `$1->3(*i .r3)i .r2`
DetailPrint " ERR=$2"
DetailPrint " $$3=$3"

DetailPrint "== IDispatch::GetTypeInfo =="
System::Call `$1->4(i 0,i ${LOCALE_SYSTEM_DEFAULT},*i .r3)i .r2`
DetailPrint " ERR=$2"
DetailPrint " $$3=$3"

DetailPrint "== IDispatch::GetIDsOfNames =="
;System::Call `*(i 7,w "Visible")i .s`
;System::Call `$1->5(g ${IID_NULL},i s,i 1,i ${LOCALE_SYSTEM_DEFAULT},*i .r3)i .r2` ;<-- Errors here... :'(
System::Call `$1->5(g ${IID_NULL},w "Visible",i 1,i ${LOCALE_SYSTEM_DEFAULT},*i .r3)i .r2` ;<-- Errors here... :'(
DetailPrint " ERR=$2"
DetailPrint " $$3=$3"
I think the issue may be with the BSTR. I tried creating it manually but I think I'm doint it wrong too.. :/

UPDATE: I'm convinced it is the BSTR.. Reading up on how to implement them now... http://msdn.microsoft.com/en-us/library/ms221069.aspx

Is it index 6 ? See, http://www.clarionopensource.com/Cla...hInterface.htm

I was recently fighting with something similar, ITypeLib :)


Woot! It was the BSTR!

    DetailPrint "== CLSIDFromProgID =="

System::Call `Ole32::CLSIDFromProgID(w,&g16)i ("Excel.Application",.r0).r2`
DetailPrint " ERR=$2"
DetailPrint " CLSID=$0"

DetailPrint "== CoCreateInstance =="
System::Call `Ole32::CoCreateInstance(g r0,i 0,i ${CLSCTX_SERVER},g '${IID_IDispatch}',*i .r1) i .r2`
DetailPrint " ERR=$2"

DetailPrint "== IUnknown::GetTypeInfoCount =="
System::Call `$1->3(*i .r3)i .r2`
DetailPrint " ERR=$2"
DetailPrint " $$3=$3"

DetailPrint "== IDispatch::GetTypeInfo =="
System::Call `$1->4(i 0,i ${LOCALE_SYSTEM_DEFAULT},*g .r3)i .r2`
DetailPrint " ERR=$2"
DetailPrint " $$3=$3"

DetailPrint "== BSTR =="
System::Call `Oleaut32::SysAllocString(w "Visible")i .s`

DetailPrint "== IDispatch::GetIDsOfNames =="
System::Call `$1->5(g ${IID_NULL},*i s,i 1,i ${LOCALE_SYSTEM_DEFAULT},*i .r3)i .r2` ;<-- Errors here... :(
DetailPrint " ERR=$2"
DetailPrint " $$3=$3"
Now to work out the rest... ;)

Just a warning... if anyone is playing around with this.. check your TaskManager.. I just found 23 instances of Excel running..... :eek:

Here is the updated code with the COM Release at the end.

    DetailPrint"== CLSIDFromProgID =="
System::Call `Ole32::CLSIDFromProgID(w,&g16)i ("Excel.Application",.r0).r2`
DetailPrint " ERR=$2"
DetailPrint " CLSID=$0"

DetailPrint "== CoCreateInstance =="
System::Call `Ole32::CoCreateInstance(g r0,i 0,i ${CLSCTX_SERVER},g '${IID_IDispatch}',*i .r1) i .r2`
DetailPrint " ERR=$2"

DetailPrint "== IDispatch::GetTypeInfoCount =="
System::Call `$1->3(*i .r3)i .r2`
DetailPrint " ERR=$2"
DetailPrint " $$3=$3"

DetailPrint "== IDispatch::GetTypeInfo =="
System::Call `$1->4(i 0,i ${LOCALE_SYSTEM_DEFAULT},*g .r3)i .r2`
DetailPrint " ERR=$2"
DetailPrint " $$3=$3"

DetailPrint "== Oleaut32::SysAllocString =="
System::Call `Oleaut32::SysAllocString(w "Visible")i .r4`
DetailPrint " RC=$4"

DetailPrint "== IDispatch::GetIDsOfNames =="
System::Call `$1->5(g ${IID_NULL},*i r4,i 1,i ${LOCALE_SYSTEM_DEFAULT},*i .r3)i .r2` ;<-- Errors here... :(
DetailPrint " ERR=$2"
DetailPrint " $$3=$3"

DetailPrint "== Oleaut32::SysFreeString =="
System::Call `Oleaut32::SysFreeString(*i r4)`

DetailPrint "== IUnknown::Release =="
System::Call `$1->2()i .r2`
DetailPrint " RefCount=$2"

heehee

good job :)

one thing you might want to do, just for readability, is defining the interface bits.. e.g.

!define GetIDsOfNames 5

or even

!define IDispatch->GetIDsOfNames 5

Then later on you can just use..
${IDispatch->GetIDsOfNames} (g ${IID_NULL, etc.
..bit cleaner to read :)


Good idea!!

Here is what I thew together.. needs more optimizations but it's a good start..

   ## Simplyfy the Evil that is COM
## Example:
## ${IDispatch::GetTypeInfo} $1 `(i 0,i ${LOCALE_SYSTEM_DEFAULT},*g .r3)i .r2`
!macro _COM_Function _vTableID _ObjectPointer _Parameters
System::Call `${_ObjectPointer}->${_vTableID}${_Parameters}`
!macroend
!define IUnknown::Release `!insertmacro _COM_Function 2 `
!define IDispatch::GetTypeInfoCount `!insertmacro _COM_Function 3 `
!define IDispatch::GetTypeInfo `!insertmacro _COM_Function 4 `
!define IDispatch::GetIDsOfNames `!insertmacro _COM_Function 5 `
!define IDispatch::Invoke `!insertmacro _COM_Function 6 `

I have been using some helper macros that are very similar to this (See http://nsis.pastebin.com/BqQFTRv3 ) but I also declare the parameter types in my "_COM_Function" macro so you don't have to specify them when you invoke the method (Can also be used to set default parameter values IIRC) I also use -> and not ::. It might be a good idea to come up with a standard and include it in some header (There is no way we can define all COM interfaces, but some of the shell interfaces would be a good start)