Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

COM interface plugin

Started by jj2007, June 09, 2017, 10:23:40 PM

Previous topic - Next topic


This should work on any machine that has a VC installation - shout foul if it doesn't:

- extract the DLL from attachment to \Masm32\MasmBasic\Plugins\GetComInterface.dll
- launch \Masm32\MasmBasic\RichMasm.exe
- select text such as IFilterGraph, IDispatch, IWebBrowser2, etc...
- go to menu System & Plugins and click on Get COM interface

(background: I needed the IGraphBuilder interface, so I went to MSDN and copied the members by hand. Disgusting experience, and my code crashed. So I checked, and checked, and checked three times, until I vaguely remembered that I had stumbled over this problem before... the reason for my crashes was that MSDN lists the members, but not in the correct order. It's Micros**t... :()

P.S.: Pick version 2 below, called - autocorrection for members called "invoke" or "pause".


strmif.h (IGraphBuilder : public IFilterGraph)

typedef struct IGraphBuilderVtbl
        HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
            IGraphBuilder * This,
            /* [in] */ REFIID riid,
            /* [annotation][iid_is][out] */
            __RPC__deref_out  void **ppvObject);
            IGraphBuilder * This);
            IGraphBuilder * This);
            IGraphBuilder * This,
            /* [in] */ IBaseFilter *pFilter,
            /* [string][in] */ LPCWSTR pName);
        HRESULT ( STDMETHODCALLTYPE *RemoveFilter )(
            IGraphBuilder * This,
            /* [in] */ IBaseFilter *pFilter);
            IGraphBuilder * This,
            /* [annotation][out] */
            __out  IEnumFilters **ppEnum);
        HRESULT ( STDMETHODCALLTYPE *FindFilterByName )(
            IGraphBuilder * This,
            /* [string][in] */ LPCWSTR pName,
            /* [annotation][out] */
            __out  IBaseFilter **ppFilter);
        HRESULT ( STDMETHODCALLTYPE *ConnectDirect )(
            IGraphBuilder * This,
            /* [in] */ IPin *ppinOut,
            /* [in] */ IPin *ppinIn,
            /* [annotation][unique][in] */
            __in_opt  const AM_MEDIA_TYPE *pmt);
            IGraphBuilder * This,
            /* [in] */ IPin *ppin);
        HRESULT ( STDMETHODCALLTYPE *Disconnect )(
            IGraphBuilder * This,
            /* [in] */ IPin *ppin);
        HRESULT ( STDMETHODCALLTYPE *SetDefaultSyncSource )(
            IGraphBuilder * This);
            IGraphBuilder * This,
            /* [in] */ IPin *ppinOut,
            /* [in] */ IPin *ppinIn);
            IGraphBuilder * This,
            /* [in] */ IPin *ppinOut);
            IGraphBuilder * This,
            /* [in] */ LPCWSTR lpcwstrFile,
            /* [annotation][unique][in] */
            __in_opt  LPCWSTR lpcwstrPlayList);
        HRESULT ( STDMETHODCALLTYPE *AddSourceFilter )(
            IGraphBuilder * This,
            /* [in] */ LPCWSTR lpcwstrFileName,
            /* [annotation][unique][in] */
            __in_opt  LPCWSTR lpcwstrFilterName,
            /* [annotation][out] */
            __out  IBaseFilter **ppFilter);
            IGraphBuilder * This,
            /* [in] */ DWORD_PTR hFile);
            IGraphBuilder * This);
        HRESULT ( STDMETHODCALLTYPE *ShouldOperationContinue )(
            IGraphBuilder * This);
    } IGraphBuilderVtbl;

    interface IGraphBuilder
        CONST_VTBL struct IGraphBuilderVtbl *lpVtbl;


What is the problem? I get this:IGraphBuilder STRUCT
QueryInterface dd ?
AddRef dd ?
Release dd ?
AddFilter dd ?
RemoveFilter dd ?
EnumFilters dd ?
FindFilterByName dd ?
ConnectDirect dd ?
Reconnect dd ?
Disconnect dd ?
SetDefaultSyncSource dd ?
Connect dd ?
Render dd ?
RenderFile dd ?
AddSourceFilter dd ?
SetLogFile dd ?
Abort dd ?
ShouldOperationContinue dd ?
IGraphBuilder ENDS


Maybe have a look at my directshow webcam example. It uses the IGraphBuilder interface.

Creative coders use backward thinking techniques as a strategy.


You mean this one? How did you generate it?_vtIGraphBuilder MACRO CastName:REQ
    _vtIUnknown CastName
    &CastName&_AddFilter                comethod3 ?
    &CastName&_RemoveFilter             comethod2 ?
    &CastName&_EnumFilters              comethod2 ?
    &CastName&_FindFilterByName         comethod3 ?
    &CastName&_ConnectDirect            comethod4 ?
    &CastName&_Reconnect                comethod2 ?
    &CastName&_Disconnect               comethod2 ?
    &CastName&_SetDefaultSyncSource     comethod1 ?
    &CastName&_Connect                  comethod3 ?
    &CastName&_Render                   comethod2 ?
    &CastName&_RenderFile               comethod3 ?
    &CastName&_AddSourceFilter          comethod4 ?
    &CastName&_SetLogFile               comethod2 ?
    &CastName&_Abort                    comethod1 ?
    &CastName&_ShouldOperationContinue  comethod1 ?


I got it from "strmif.h" and converted it by hand to asm so i could use it with the coinvoke macro.
The interface GUIDS are in it as well.

    IGraphBuilder : public IFilterGraph

#define IGraphBuilder_QueryInterface(This,riid,ppvObject) \
    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )

#define IGraphBuilder_AddRef(This) \
    ( (This)->lpVtbl -> AddRef(This) )

#define IGraphBuilder_Release(This) \
    ( (This)->lpVtbl -> Release(This) )

#define IGraphBuilder_AddFilter(This,pFilter,pName) \
    ( (This)->lpVtbl -> AddFilter(This,pFilter,pName) )

#define IGraphBuilder_RemoveFilter(This,pFilter) \
    ( (This)->lpVtbl -> RemoveFilter(This,pFilter) )

#define IGraphBuilder_EnumFilters(This,ppEnum) \
    ( (This)->lpVtbl -> EnumFilters(This,ppEnum) )

#define IGraphBuilder_FindFilterByName(This,pName,ppFilter) \
    ( (This)->lpVtbl -> FindFilterByName(This,pName,ppFilter) )

#define IGraphBuilder_ConnectDirect(This,ppinOut,ppinIn,pmt) \
    ( (This)->lpVtbl -> ConnectDirect(This,ppinOut,ppinIn,pmt) )

#define IGraphBuilder_Reconnect(This,ppin) \
    ( (This)->lpVtbl -> Reconnect(This,ppin) )

#define IGraphBuilder_Disconnect(This,ppin) \
    ( (This)->lpVtbl -> Disconnect(This,ppin) )

#define IGraphBuilder_SetDefaultSyncSource(This) \
    ( (This)->lpVtbl -> SetDefaultSyncSource(This) )

#define IGraphBuilder_Connect(This,ppinOut,ppinIn) \
    ( (This)->lpVtbl -> Connect(This,ppinOut,ppinIn) )

#define IGraphBuilder_Render(This,ppinOut) \
    ( (This)->lpVtbl -> Render(This,ppinOut) )

#define IGraphBuilder_RenderFile(This,lpcwstrFile,lpcwstrPlayList) \
    ( (This)->lpVtbl -> RenderFile(This,lpcwstrFile,lpcwstrPlayList) )

#define IGraphBuilder_AddSourceFilter(This,lpcwstrFileName,lpcwstrFilterName,ppFilter) \
    ( (This)->lpVtbl -> AddSourceFilter(This,lpcwstrFileName,lpcwstrFilterName,ppFilter) )

#define IGraphBuilder_SetLogFile(This,hFile) \
    ( (This)->lpVtbl -> SetLogFile(This,hFile) )

#define IGraphBuilder_Abort(This) \
    ( (This)->lpVtbl -> Abort(This) )

#define IGraphBuilder_ShouldOperationContinue(This) \
    ( (This)->lpVtbl -> ShouldOperationContinue(This) )

Creative coders use backward thinking techniques as a strategy.


Quote from: Siekmanski on June 10, 2017, 04:49:12 AM
I got it from "strmif.h" and converted it by hand to asm

That's what I tried, too - but I am lazy, so I coded a plugin. There are over 400 header files that contain OLE interfaces, with over 100,000 members :biggrin:


Attachment #1 has a new version of the GetComInterface plugin; extract the DLL as \Masm32\MasmBasic\Plugins\GetComInterface.dll, then restart RichMasm with DSound8.asc file from the second attachment.

How does it work?
The IDirectSound8 is not included in the Masm32 SDK (this is one of the rare things that are missing - Hutch has included almost everything relevant :t). So what to do?

- select IDirectSound8 in your source
- click menu System & Plugins/Get COM interface
- when you see the MessageBox, click OK to copy it
- paste it on top of your code
- use with CoInvoke as demonstrated below

include \masm32\MasmBasic\         ; download

  SetGlobals pDs8, dscaps:DSCAPS
  .if !rv(DirectSoundCreate8, 0, addr pDs8, 0)
        m2m dscaps.dwSize, DSCAPS       ; this structure must be initialised
        CoInvoke pDs8, IDirectSound8.GetCaps, addr dscaps
        deb 4, "IDirectSound8.GetCaps", dscaps.dwMinSecondarySampleRate, dscaps.dwMaxSecondarySampleRate

Output on my machine:IDirectSound8.GetCaps
dscaps.dwMinSecondarySampleRate 100
dscaps.dwMaxSecondarySampleRate 200000


Here is a simple program that opens a Microsoft Internet Explorer window and loads a well-known web page:

include \masm32\MasmBasic\
  Init                          ; ## COM demo: open Internet Explorer ##
  invoke OleInitialize, NULL
  .if eax==S_OK
        push Chr$("")
        call MyBrowser
  invoke OleUninitialize

CLSID_IExplorer        GuidFromString("0002DF01-0000-0000-C000-000000000046")  ; either use quoted text syntax or...
IID_IWebBrowser2       GuidFromString({D30C1661-CDAF-11D0-8A3E-00C04FC9E26E})  ; ... paste copied registry key name

MyBrowser proc uses esi url
  push eax                      ; create a DWORD variable on the stack
  ; now ask the OS gently to create a table with addresses that you can call to configure your Explorer Window (MSDN)
  invoke CoCreateInstance, addr CLSID_IExplorer, NULL, CLSCTX_LOCAL_SERVER, addr IID_IWebBrowser2, esp  ; esp is the address of that variable
  pop esi                               ; CoCreateInstance has filled the variable with the address to the vtable
  pInterface equ esi
  .if eax==S_OK
       CoInvoke pInterface, IWebBrowserVtbl.put_StatusBar, VARIANT_FALSE       ; OK, now configure the browser
       CoInvoke pInterface, IWebBrowserVtbl.put_MenuBar, VARIANT_TRUE          ; false = no menu
       CoInvoke pInterface, IWebBrowserVtbl.put_Visible, VARIANT_TRUE
       ; CoInvoke pInterface, IWebBrowserVtbl.get_HWND, addr hWin              ; optional: get handle of the Explorer window
        lea edx, vEmpty         ; Navigate needs pointers to four empty VARIANTS
       ; Ole$(url): COM wants a BSTR, so the ANSI URL needs to be converted
       CoInvoke pInterface, IWebBrowserVtbl.Navigate, Ole$(url), edx, edx, edx, edx
MyBrowser endp
end start

If you love cryptic jargon, open the MSDN link above; otherwise, continue reading: What does this code do?
- after initialisation, it pushes the URL and calls MyBrowser
- push eax decreases esp and thus creates a DWORD slot for the OS
- CoCreateInstance uses the two GUID identifiers to find out what the user wants, and then fills the DWORD slot with a pointer to a so-called "interface"
- we pop that pointer into esi
- CoInvoke pInterface, IWebBrowserVtbl.put_StatusBar, VARIANT_FALSE does the following:
  - it pushes one argument on the stack (VARIANT_FALSE)
  - it gets the address of a proc called "put_StatusBar" that needs one argument on the stack:
push 0                      ; VARIANT_FALSE
push esi                    ; pInterface
mov eax, [esi]              ; get first DWORD from that interface
call near [eax+0AC]         ; call the address in the put_StatusBar offset

That's all, folks. It is that simple.

Now one might ask the developers of COM why CoCreateInstance needs the address of a variable to deliver pInterface, instead of simply returning it in eax. The answer will be "it's by design" or "why do something simple if there is a more complicated way?". Redmond speaking... 8)


New version attached: if you select e.g. IWebBrowser2 in RichMasm and click System & Plugins/Get COM interface, you'll get this (the lousy formatting is a SMF quirk, it looks better in the editor):

IWebBrowser2 STRUCT
QueryInterface    dd ?
AddRef    dd ?
Release    dd ?
GetTypeInfoCount    dd ?
GetTypeInfo    dd ?
GetIDsOfNames    dd ?
Invoke_    dd ?
GoBack    dd ?
GoForward    dd ?
GoHome    dd ?
GoSearch    dd ?
Navigate    dd ?
Refresh    dd ?
Refresh2    dd ?
Stop    dd ?
get_Application    dd ?
get_Parent    dd ?
get_Container    dd ?
get_Document    dd ?
get_TopLevelContainer dd ?
get_Type    dd ?
get_Left    dd ?
put_Left    dd ?
get_Top    dd ?
put_Top    dd ?
get_Width    dd ?
put_Width    dd ?
get_Height    dd ?
put_Height    dd ?
get_AddressBar    dd ?
put_AddressBar    dd ?
get_Resizable    dd ?
put_Resizable    dd ?
IWebBrowser2 ENDS        ; modified (_ appended): Invoke

IID_IWebBrowser2    GuidFromString("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E")

extracted from D:\Masm32\C_Headers\ExDisp.h

Note the last line: my GetComInterface.ini contains the string D:\Masm32\C_Headers\*.h because the default, i.e. the Registry key HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows, CurrentInstallFolder is not present on my machine.

In this example, only invoke was modified to invoke_ to avoid a name conflict. The software does that currently for invoke & pause (case-insensitive), plus Open, Close, Seek, Delete (case-sensitive MasmBasic macros). Please let me know if you stumble over other name conflicts.


The next version of the RichMasm IDE will allow to insert links to the registry:

When clicking on the link, you can copy the results to the clipboard :cool: