home *** CD-ROM | disk | FTP | other *** search
- COM in ASM
- ------------------------------------------------------------------------------
-
- This article will discuss how to use COM interfaces in your assembly language
- programs. It will not discuss what COM is and how it is used, but rather how
- it can be used when programming in assembler. It will discuss only how to
- use existing interfaces, and not how to actually implement new ones; this will
- be shown in a future atricle.
-
-
- About COM
- ------------------------------------------------------------------------------
-
- Here is a brief introduction to the basics behind COM.
-
- A COM object is one in which access to an object's data is achieved
- exclusively through one or more sets of related functions. These function
- sets are called interfaces, and the functions of an interface are called
- methods. COM requires that the only way to gain access to the methods of an
- interface is through a pointer to the interface.
-
- An interface is actually a contract that consists of a group of related
- function prototypes whose usage is defined but whose implementation
- is not. An interface definition specifies the interface's member functions,
- called methods, their return types, the number and types of their parameters,
- and what they must do. There is no implementation associated with an
- interface. An interface implementation is the code a programmer supplies to
- carry out the actions specified in an interface definition.
-
- An instance of an interface implementation is actually a pointer to an array
- of pointers to methods (a function table that refers to an implementation of
- all of the methods specified in the interface). Any code that has a pointer
- through which it can access the array can call the methods in that interface.
-
-
-
- Using a COM object assembly language
- -------------------------------------------------------------------------------
-
- Access to a COM object occurs through a pointer. This pointer points to a
- table of function pointers in memory, called a virtual function table, or
- vtable in short. This vtable contains the addresses of each of the objects
- methods. To call a method, you indirectly call it through this pointer table.
-
- Here is an example of a C++ interface, and how its methods are called:
-
- interface IInterface
- {
- HRESULT QueryInterface( REFIID iid, void ** ppvObject );
- ULONG AddRef();
- ULONG Release();
- Function1( INT param1, INT param2);
- Function2( INT param1 );
- }
-
- // calling the Function1 method
- pObject->Function1( 0, 0);
-
- Now here is how the same functionality can be implemented using assembly
- language:
-
- ; defining the interface
- ; each of these values are offsets in the vtable
- QueryInterface equ 0h
- AddRef equ 4h
- Release equ 8h
- Function1 equ 0Ch
- Function2 equ 10h
-
- ; calling the Function1 method in asm
- ; the method is called by obtaining the address of the objects
- ; vtable and then calling the function addressed by the proper
- ; offset in the table
- push param2
- push param1
- mov eax, pObject
- push eax
- mov eax, [eax]
- call [eax + Function1]
-
- You can see this is somewhat different than calling a function normally.
- Here, pObject points to the Interface's vTable. At the Function1(0Ch) offset
- in this table is a pointer to the actual function we wish to call.
-
-
-
- Using HRESULT's
- -------------------------------------------------------------------------------
-
- The return value of OLE APIs and methods is an HRESULT. This is not a handle
- to anything, but is merely a 32-bit value with several fields encoded in the
- value. The parts of an HRESULT are shown below.
-
- HRESULTs are 32 bit values layed out as follows:
-
- 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
- 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
- +-+-+-+-+-+---------------------+-------------------------------+
- |S|R|C|N|r| Facility | Code |
- +-+-+-+-+-+---------------------+-------------------------------+
-
- S - Severity Bit
- Used to indicate success or failure
- 0 - Success
- 1 - Fail
-
- By noting that this bit is actually the sign bit of the 32-bit value,
- checking success/failure is simply performed by checking its sign:
-
- call ComFunction ; call the function
- test eax,eax ; now check its return value
- js error ; jump if signed (meaning error returned)
- ; success, so continue
-
- R - reserved portion of the facility code, corresponds to NT's
- second severity bit.
-
- C - reserved portion of the facility code, corresponds to NT's
- C field.
-
- N - reserved portion of the facility code. Used to indicate a
- mapped NT status value.
-
- r - reserved portion of the facility code. Reserved for internal
- use. Used to indicate HRESULT values that are not status
- values, but are instead message ids for display strings.
-
- Facility - is the facility code
- FACILITY_WINDOWS = 8
- FACILITY_STORAGE = 3
- FACILITY_RPC = 1
- FACILITY_WIN32 = 7
- FACILITY_CONTROL = 10
- FACILITY_NULL = 0
- FACILITY_ITF = 4
- FACILITY_DISPATCH = 2
-
- To retreive the Facility,
-
- call ComFunction ; call the function
- shr eax, 16 ; shift the HRESULT to the right by 16 bits
- and eax, 1FFFh ; mask the bits, so only the facility remains
- ; eax now contains the HRESULT's Facility code
-
- Code - is the facility's status code
-
- To get the Facility's status code,
- call ComFunction ; call the function
- and eax, 0000FFFFh ; mask out the upper 16 bits
- ; eax now contains the HRESULT's Facility's status code
-
-
-
- Using COM with MASM
- ------------------------------------------------------------------------------
- If you use MASM to assemble your programs, you can use some of its
- capabilities to make calling COM functions very easy. Using invoke, you can
- make COM calls look almost as clean as regular calls, plus you can add type
- checking to each function.
-
-
- Defining the interface:
-
- IInterface_Function1Proto typedef proto :DWORD
- IInterface_Function2Proto typedef proto :DWORD, :DWORD
-
- IInterface_Function1 typedef ptr IInterface_Function1Proto
- IInterface_Function2 typedef ptr IInterface_Function2Proto
-
- IInterface struct DWORD
- QueryInterface IUnknown_QueryInterface ?
- AddRef IUnknown_AddRef ?
- Release IUnknown_Release ?
- Function1 IInterface_Function1 ?
- Function2 Interface_Function2 ?
- IInterface ends
-
- Using the interface to call COM functions:
-
- mov eax, pObject
- mov eax, [eax]
- invoke (IInterface [eax]).Function1, 0, 0
-
- As you can see, the syntax may seem a bit strange, but it allows for a simple
- method using the function name itself instead of offsets, as well as type
- checking.
-
-
-
- A Sample program written using COM
- ------------------------------------------------------------------------------
-
- Here is some sample source code which uses COM written in straight assembly
- language, so it should be compatable with any assembler you prefer with only
- minor changes necessary.
-
- This program uses the Windows Shell Interfaces to show the contents of the
- Desktop folder in a window. The program is not complete, but shows how the
- COM library is initialized, de-initialized, and used. I also shows how the
- shell library is used to get folders and obcets, and how to perform
- actions on them.
-
-
- .386
- .model flat, stdcall
-
- include windows.inc ; include the standard windows header
- include shlobj.inc ; this include file contains the shell namespace
- ; definitions and constants
-
- ;----------------------------------------------------------
- .data
- wMsg MSG <?>
- g_hInstance dd ?
- g_pShellMalloc dd ?
-
- pshf dd ? ; shell folder object
- peidl dd ? ; enum id list object
-
- lvi LV_ITEM <?>
- iCount dd ?
- strret STRRET <?>
- shfi SHFILEINFO <?>
- ...
-
- ;----------------------------------------------------------
- .code
- ; Entry Point
- start:
- push 0h
- call GetModuleHandle
- mov g_hInstance,eax
-
- call InitCommonControls
-
- ; initialize the Component Object Model(COM) library
- ; this function must be called before any COM functions are called
- push 0
- call CoInitialize
- test eax,eax ; error when the MSB = 1
- ; (MSB = the sign bit)
- js exit ; js = jump if signed
-
- ; Get the Shells IMalloc object pointer, and save it to a global variable
- push offset g_pShellMalloc
- call SHGetMalloc
- cmp eax, E_FAIL
- jz shutdown
-
-
- ; here we would set up the windows, list view, message loop, and so on....
- ; we would also call the FillListView procedure...
- ; ....
-
-
- ; Cleanup
- ; Release IMalloc Object pointer
- mov eax, g_pShellMalloc
- push eax
- mov eax, [eax]
- call [eax + Release] ; g_pShellMalloc->Release();
-
- shutdown:
- ; close the COM library
- call CoUninitialize
-
- exit:
- push wMsg.wParam
- call ExitProcess
- ; Program Terminates Here
-
-
- ;----------------------------------------------------------
- FillListView proc
-
- ; get the desktop shell folder, saved to pshf
- push offset pshf
- call SHGetDesktopFolder
-
- ; get the objects of the desktop folder using the EnumObjects method of
- ; the desktop's shell folder object
- push offset peidl
- push SHCONTF_NONFOLDERS
- push 0
- mov eax, pshf
- push eax
- mov eax, [eax]
- call [eax + EnumObjects]
-
- xor ebx, ebx ;use ebx for the counter
-
- ; now loop through the enum id list
- idlist_loop:
- ; Get next id list item
- push 0
- push offset pidl
- push 1
- mov eax, peidl
- push eax
- mov eax, [eax]
- call [eax + Next]
- test eax,eax
- jnz idlist_endloop
-
- mov lvi.imask, LVIF_TEXT or LVIF_IMAGE
- mov lvi.iItem, ebx
-
- ; Get the item's name by using the GetDisplayNameOf method
- push offset strret
- push SHGDN_NORMAL
- push offset pidl
- mov eax, pshf
- push eax
- mov eax, [eax]
- call [eax + GetDisplayNameOf]
- ; GetDisplayNameOf returns the name in 1 of 3 forms, so get the correct
- ; form and act accordingly
- cmp strret.uType, STRRET_CSTR
- je strret_cstr
- cmp strret.uType, STRRET_OFFSET
- je strret_offset
-
- strret_olestr:
- ; here you could use WideCharToMultiByte to get the string,
- ; I have left it out because I am lazy
- jmp strret_end
-
- strret_cstr:
- lea eax, strret.cStr
- jmp strret_end
-
- strret_offset:
- mov eax, pidl
- add eax, strret.uOffset
-
- strret_end:
- mov lvi.pszText, eax
-
- ; Get the items icon
- push SHGFI_PIDL or SHGFI_SYSICONINDEX or SHGFI_SMALLICON or SHGFI_ICON
- push sizeof SHFILEINFO
- push offset shfi
- push 0
- push pidl
- call SHGetFileInfo
- mov eax, shfi.iIcon
- mov lvi.iImage, eax
-
- ; now add item to the list
- push offset lvi
- push 0
- push LVM_INSERTITEM
- push hWndListView
- call SendMessage
-
- ; increment counter ebx and repeat the loop
- inc ebx, ebx
- jmp idlist_loop
-
- idlist_endloop:
-
- ; now free the enum id list
- ; Remember all allocated objects must be released...
- mov eax, peidl
- push eax
- mov eax,[eax]
- call [eax + Release]
-
- ; free the desktop shell folder object
- mov eax, pshf
- push eax
- mov eax,[eax]
- call [eax + Release]
-
- ret
- FillListView endp
-
-
- END start
-
-
- Conclusiom
- -------------------------------------------------------------------------------
-
- Well, that is about it for using COM with assembly language. Hopefully, my
- next article will go into how to define your own interfaces. As you can
- see, using COM is not difficult at all, and with it you can add a very
- powerful capability to your assembly language programs.
-