Standard for COM in MASM32
ABSTRACT
The basis and rational for Component Object Programming 'include' files as defined in these files is explained.
INTRODUCTION
Attempting to perform COM operations without any include files can be a frustrating proposition. Several people have made some attempts, with some success, but each are working to their own "standard" as to COM information. I have seen some of this work when published, but found it lacking in some respects.
Many months of efforts, and several sometimes major revision has gone into these files. Public comments were sought and included where applicable.
The goal of this document is to get new and existing COM in ASM players to 'play by the same rules,' such that any new file may be added as is to this hopefully growing library of include files and definitions.
Include File Features
Any .inc file should have the following features:  
1) The MASM32 convention of loose typedef will be continued. This means parameters will be defined as their base type, typically a DWORD.
2) The information should not create any code lines at all. It will merely define information for the mail application. Should a file need to make functions, these should be in the form of macros so they are not expanded to code unless explicitly instructed to.  
3) Structures shall be defined to align with their "C" prototypes. Structure  curly brackets { } are preferred over angle brackets < >.
4) The GUID structure itself is defined in windows.inc. This definition is correct. GUID values shall be defined through a textequ so they do not directly generate any code. See the following sections on GUIDS.
5) Interfaces are defined in a two-step process where first a general macro is declared to create a general Interface structure, then the structure itself is defined with the Interface name as name decoration on the method name. This both makes methods immune to namespace duplication (necessary for method polymorphism) and allows Interface structure definitions to be 'inherited' in succeeding Interfaces. See the following section on Interfaces.
6) COM Interface method calls will be accomplished with the coinvoke Macro. This macro uses the Interface definition as given to de-reference the object pointer to the function table pointer, and then generate the offset into the function table pointer list and call that function. Simple parameter count and size checking is also performed at compile time. See
the following section on the coinvoke Macro.
GUIDS
GUID values shall be defined through a textequ so they do not directly generate any code. This equate shall have a label consisting of the interface  name with a "s" (for string) prefix. 
EXAMPLE:
sIID_IUnknown  TEXTEQU  <{000000000H, 00000H, 00000H, \                            {0C0H, 000H, 000H, 000H, 000H, 000H, 000H, 046H}}
This can be employed as such:
IID_IUnknown   GUID    sIID_IUnknown
The DeclareGuid Macro is provided as an aid to defining GUIDs It has the form:
DeclareGuid GuidName, [<guid value>]
 
INTERFACES
Due to the MASM32 prototype convention of loose type checking in invokes, all that is really checked at compile time is a simple count of parameters. This fact can extremely simplify our definitions of an interface, as the same small set of function prototypes can be used over and over. Interface structure members (methods) will be defined with the interface name plus an underscore as name decoration.
With the sole exception of IUnknown, all interfaces must inherit from another interface. It would be convenient to have some sort of macro provide us this function. Thus, the manor interfaces are created from a macro will allow that macro to be re-used (thus "inherited) in further definitions.
The following definitions are first established for all interfaces to use:
comethod1Proto   typedef proto :DWORD
comethod2Proto   typedef proto :DWORD, :DWORD
comethod3Proto   typedef proto :DWORD, :DWORD, :DWORD
comethod4Proto   typedef proto :DWORD, :DWORD, :DWORD, :DWORD
comethod4Proto   typedef proto :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
comethod1        typedef ptr comethod1Proto
comethod2        typedef ptr comethod2Proto
comethod3        typedef ptr comethod3Proto
comethod4        typedef ptr comethod4Proto
comethod5        typedef ptr comethod4Proto
Actually, these definitions are carried out for 15 parameters. I arbitrarily chose this number, should a function come along with more then 15 parameters, of course this table should be extended. This may seem to be strongly in disagreement with my previous writings. However, the same coinvoke macro will handle this. All I have done was eliminate a ton of proto names that never got used by the compiler.
There are a few methods that do not take DWORD size parameters (most notable, the IDispatch Interface). These methods are handled on a case-by-case basis, at the place in the include file where that differing method is declared.
Let us look at some familiar interfaces generated with these methods:

EXAMPLE 1 The IUnknown Interface: IUnknown is the basic interface, all others inherit from this. Function prototypes are defined as above. The form of the vtable structure is defined in a macro, then this macro is used to create the structure itself.
_vtIUnknown MACRO CastName:REQ
    ; IUnknown methods
    &CastName&_QueryInterface comethod3 ?
    &CastName&_AddRef         comethod1 ?
    &CastName&_Release        comethod1 ?
ENDM
IUnknown                        STRUCT
    _vtIUnknown IUnknown
IUnknown                        ENDS
This will expand to the following:
IUnknown                        STRUCT
    IUnknown_QueryInterface comethod3 ?
    IUnknown_AddRef         comethod1 ?
    IUnknown_Release        comethod1 ?
IUnknown                        ENDS
The macro is defined with a leading underscore, since the "vt" prefix is handy to leave undefined when it comes time to instance a vtable.
Thus, one may wish to provide an instance of the vtable as such:
.data
vtIUnknown _vtIUnknown {MyQueryInterface, MyAddRef, MyRelease}
Method names are defined with the interface name added on as name decoration. Different Interfaces may have methods with the same name (that is simple polymorphism), but without this name decoration the MASM compile cannot tell them apart.
(Note that it is legal for two different interfaces may have the same name, same method names, but differing parameters. That is valid (only the IID GUID's need be different), but bad coding practice, so we'll ignore that condition with just this warning here. This standard cannot tell those interfaces apart, and some workaround must be sought.)
EXAMPLE 2 The IClassFactory Interface  IClassFactory inherits from IUnknown. It begins with the IUnknown methods, then adds it's own 2 methods.
_vtIClassFactory MACRO CastName:REQ
    ; IUnknown methods 
    _vtIUnknown CastName
    ; IClassFactory methods
    &CastName&_CreateInstance comethod4 ?
    &CastName&_LockServer     comethod2 ?
ENDM
 
IClassFactory                   STRUCT
    _vtIClassFactory IClassFactory
IClassFactory                   ENDS
This will expand to the following:
IClassFactory                   STRUCT
    IClassFactory_QueryInterface comethod3 ?
    IClassFactory_AddRef         comethod1 ?
    IClassFactory_Release        comethod1 ?
    IClassFactory_CreateInstance comethod4 ?
    IClassFactory_LockServer     comethod2 ?
IClassFactory                   ENDS
coinvoke, a MACRO to perform COM Interface Methods
We can simplify a COM invoke further with a macro:
;---------------------------------------------------------------------
; coinvoke MACRO
;
; invokes an abritrary COM interface
;
; revised 12/29/00 to check for edx as a param and force compilation
; error(thanks to Andy Car for a how-to suggestion)
; revised 7/18/00 to pass pointer in edx not eax to avoid confusion
; with parmas passed with ADDR (Jeremy Collake's excellent suggestion)
; revised 5/4/00 for member function name decoration
;
; pInterface pointer to a specific interface instance
; Interface the Interface's struct typedef
; Function which function or method of the interface to perform
; args all required arguments
; (type, kind and count determined by the function)
;
coinvoke MACRO pInterface:REQ, Interface:REQ, Function:REQ, args:VARARG
LOCAL istatement, arg
FOR arg, <args> ;; run thru args to see if edx is lurking in there
IFIDNI <&arg>, <edx>
.ERR <edx is not allowed as a coinvoke parameter>
ENDIF
ENDM
istatement CATSTR <invoke (Interface PTR[edx]).&Interface>,<_>,<&Function, pInterface>
IFNB <args> ;; add the list of parameter arguments if any
istatement CATSTR istatement, <, >, <&args>
ENDIF
mov edx, pInterface
mov edx, [edx]
istatement
ENDM
;---------------------------------------------------------------------
Thus, the same QueryInterface method as before can be invoked in a single line:
coinvoke ppv ,IUnknown, QueryInterface, ADDR IID_SomeOtherInterface,
ADDR ppnew
Note that now the name decoration is done for us by the macro.
Testing HRESULTS
The return parameter for every COM call is an hResult, a 4 byte return value in eax. It is used to signal success or failure. Since the most significant digit is used to indicate failure, you can test the result with a simple:
.IF !SIGN? ; function passed .ELSE ; function failed .ENDIF
Again, this can be simplified with some more simple macros:
.IF SUCCEEDED ; TRUE if SIGN bit not set.IF FAILED ; TRUE is SIGN bit set
And, for the truly code-frugal person who doesn't with to explicitly test eax, these macros will be useful:
.IF_SUCCEEDED ; TRUE if eax >= 0
.IF_FAILED ; TRUE if eax < 0
These macros also perform the test for you.
Conclusion
That's about all you need to fully invoke and use interfaces from COM objects from assembly. These techniques work with any COM or activeX object.