The Other Way To Use OLE

by Jim Kane

Published 1999-07-20    Printer-friendly version

One day a long time ago when CompuServe and not the newsgroups reined supreme, someone asked how to create a shortcut using Clarion. After a quick trip to my trusty MSDN (Microsoft Developer Network) CD, it became apparent to me that to create a shortcut I had to call an OLE interface called IShellLink. Unfortunately, while Clarion 5 supports the Topspeed, Pascal, and C calling conventions, it does not natively support calling OLE interfaces.

There are two general methods of calling OLE objects: late binding and early binding. Most OCX and OLE code Clarion programmers are use to seeing is late -binding, where the address of each method is looked up via the Idispatch API call before each call. No compile-time knowledge of the order the methods are in is needed for late binding; only the method name is required.

The early binding discussed here a more efficient approach, although it requires that you know the number of methods and the order in which they appear in the OLE interface. The address of the area which contains the method entry points is determined once and stored, and as all of the methods are at a fixed offset from this point they can subsequently be called based on that offset.

While calling a method based on its offset from a known address is slightly less efficient than the normal way of calling an API function directly, the extra step is quick and saves the trouble of storing the address for each method separately.

The Keys To The Kingdom

I posted an answer to the original question about creating a shell link that in effect that could have been summed up in two words: "No Way." Never liking to accept limits, when I next had some free time (about a year later), I read all about how to call OLE interfaces. I realize that with a little straightforward assembler work I could write one procedure (or a family of procedures) that would handle all the dirty work for me. Once you understand this one little ugly piece and how to use it, you'll be able to do anything Windows can do. Sorry, but no more excuses then!

My reading showed that a call to the API CoCreateInstance procedure returned the keys to the kingdom, so to speak, or at least a pointer to a pointer to the entrance to the OLE world. If only I could take that pointer to a pointer and call the address at the end of the chain, OLE would be mine.

Actually what CoCreateInstance returns is a pointer to a pointer to a table of addresses, called a vtable. A vtable is a simple list of addresses of all the methods in an OLE interface. For those not using OOP think of a method as a procedure. An OLE Interface can be thought of, for this discussion, as a collection of methods or procedures that when called does something. It's actually a whole lot more but that will do for now.

Finding The vTable

Calling an OLE interface is a two step process. First you navigate the pointer chain to get to the vtable, and then you determine where in the vtable the procedure to be called is located.

I'll take a common OLE interface called Iunknown (which also pretty well sums what I've said about OLE at this point!) with three methods: QueryInterface, Addref, Release. For now don't worry about what the methods do. Listing 1 shows what the mystical vtable would look like in Clarion syntax:

Listing 1. A vtable declared in Clarion code.
Vtable_IUnknown Group
AddressofIUnknown_QueryInterface    long
AddressofIUnknown_Addref        long
AddressofIUnknown_release       long
                end

Now I'll diagram the chain of pointers to arrive at a vtable so it's not so abstract. I'll store the pointer to a pointer in a Clarion long variable called ppVtable (pp has nothing to do with the bathroom in this case, it's a shorthand for pointer to a pointer) and the offset into the vtable in a long variable called OfsMethod. Figure 1 uses some arbitrary addresses to demonstrate how to get to the code for the Iunknown.AddRef method. Since Addref is one down the list from the top and each long is four bytes, it's address is stored at the start of the vtable plus an offset of 4H.

Figure  1. The vtable data.
Arbitrary Address Variable Name/Description Contents
120H OfsMethod 124H
124H ppVTable 58204H
58204H pVTable - pointer to the vtable 7F800H
7F800H Vtable - Address of 1st method AddressofIUnknown_QueryInterface) 9AB00H
7F804H Vtable - Address of 2nd Method (AddressofIunknown_AddRef) 9C000H

At address 9AB00H sits the executable code for Iunknown.QueryInterface.

At address 9C000H sits the executable code for Iunknown_AddRef.

So given a pointer to a pointer for a vtable (in this case the value 58204H), if you look in memory at that location you get the pointer to the vtable (or pVTable) at 7F800H . If you look in memory at 7F800H you find (at long last!) the start of the vtable. Jump down to an offset of four bytes and look in memory at 7F804H and you're all set: 9C000H is the correct target address. This means lots of peeks and jumping around but it's a relatively simple chain to follow. The chain navigation code only has to be written once, and after that's done hopefully you can get a lot of good use out of it.

Five Steps To OLE

Thinking further, the problem of calling an OLE interface (after obtaining a pointer to a vtable) really has five parts (one of which I've kept a secret until now):

  1. Put the parameters for the OLE method, if any, on the stack (I'll explain the stack in a moment).
  2. Put the pointer to a pointer to the vtable on the stack - a requirement of calling an OLE Interface (this is the new thing I held back on previously).
  3. Put the return address to your Clarion code on the stack.
  4. Navigate the pointer to a pointer bit to arrive at the vtable.
  5. Once at the vtable, apply the offset for the method you want and jump/call to that address.

I'm visually oriented so I'll turn that into a picture. A stack is an area of memory, just like a stack of books where each "book" is 32 bits. Every time you push something onto the stack it goes on top. Every time you pop something off the stack, a book goes away and exposes the book underneath. A stack grows from high memory to low memory. Here's what the stack looks like in a simple procedure call (not an OLE interface ) and diagram:

Module('SomeDll.Dll')
  AddTwoNumbers(long p1,  short p2),long,pascal
End
Code to call:
Result = AddTwoNumbers(2,3)
  !After call to AddTwoNumbers
  Message('Result is:' & Result,'Result')
Procedure code:
AddTwoNumbers procedure(long p1, short p2)
Result Long
  Code
  !Start of procedure
  Result = p1 + p2
  Return Result

Here's what the stack looks like at the comment !Start of procedure (the absolute address values are arbitrary):

Address Stack Contents
1F4H   ReturnAddress - address of Message statement
1F8H    p1=2
1FCH    p2=3

Notice that although P2 is a short, on the stack it takes up 32 bits from 1F8H to 1FBH. In fact, all parameters on the stack take up 32 bits no matter if the value being passed is a byte, short or long! If a parameter is longer than 32 bytes, such as a cstring, then rather than trying to put the entire cstring on the stack, just its address is placed on the stack.

After the execution of the Return Result statement, the three values shown above are removed from the stack. This is how the Pascal calling convention works. All OLE interfaces use the Pascal calling convention as do almost all API calls.

To complete the picture look at the assembler code to call AddTwoNumbers. It's not very complex:

Mov  eax, p2       ! move p2 into the eax register
Push eax        ! Push  a long onto the stack in this case 
            !  p2 with a value of 3
Mov   eax, p1       ! put p1 into eax, want to guess where its 
            !  going next?
Push eax        ! p1 on the stack
Call AddTwoNumbers  ! puts the return address on the stack 
            ! and jumps to the start of the procedure
            !  AddTwoNumbers.

After that entertaining jaunt into the world of assembler and the stack lets get back to the problem at hand. Say you have a pointer to a pointer to a vtable for an OLE interface call IshellLink. You'll store that value in a long called ppVtable_IshellLink. Further, say IshellLink has a method call SetPath that can be called with one parameter, the address of a cstring. The purpose of the method is to set the path and file name for the target of a shortcut, i.e. the program that should be run when a shortcut is clicked. If Clarion directly supported OLE, you might think of this as calling a class method and would prototype it something like this:

IshellLink.SetPath(*cstring szPath),Pascal

and call it (if it worked!) to take a step to creating a shortcut to notepad like this:

SzPath = 'C:\Windows\NotePad.Exe'
IshellLink.SetPath(szPath)

Since *cstring is a pointer to a cstring, or in other words the memory address of the cstring, you could also prototype and call the above like this with the same result:

IshellLink.SetPath(long pszPath),Pascal

And use the address() function to get the address:

IshellLink.SetPath(address(szPath))

Keep in mind Clarion does not support this calling convention so the above is hypothetical.

If you review the requirements for calling an OLE interface listed above, the picture you need on the stack for this to work is:

Figure 2. Stack data for calling OLE interface
Arbitrary Address Value or Description
200H Return Address to Clarion code after the call to the OLE Interface
1FCH ppVtable
1F8H address(szPath)

The other piece of information you need to be able to jump to the OLE method of choice is the offset to the SetPath method. You'll need to put that on the stack as well.

It's looking like to call an OLE interface with one parameter what you need is a magic function called

ICall1P(long ppVtable, long OfsMethod, long p1),long, pascal,proc

that can create a stack like the one shown above and jump to the correct vtable entry.

Now you can write the Clarion code part so you can picture it better, diagram the stack this magic procedure will start with, and lastly present the assembler code to accomplish what you need.

Module(ICall.a)
  ICall1P(long ppVtable, long OfsMethod, long p1),long,cr.gif (850 bytes)
        pascal,proc,Name('ICall')
End
Data:
SzPath      cstring('C:\Windows\Notepad.exe')
PpVtable    long(58204H)    !pointer to a pointer for the 
                ! vtable for IshellLink.
OfsMethod   long(50H)       !offset to SetPath
Hr      Long,Auto   !Ole Return code, < 0 = error

Code:
Hr = ICall1P(ppVtable, OfsMethod, Address(szPath))
If Hr<0 then 
  Message('Call Failed, blame Bill') 
else 
  Message('Call Worked, blame Jim Kane)
end

After the calling ICall1P this is what the stack will look like:

ReturnAddress = address of  If Hr<0 after ICall
PpVtable
OfsMethod
P1

If you compare that to the desired stack frame above, you will see the extra OfsMethod in there. Also there are some mundane details of preserving registers. You can think of a register in the CPU as if it's a 32 bit variable. The registers you will deal with are eax, ebx, ecx, edx. The code to get the stack from the starting point just above to the desired status prior to turning things over the OLE and Microsoft is as follows:

Public ICall:
 (*The parameters, how ever many there were are on the stack*)
 (*code to save ebx,ecx,edx-omitted for clarity *)
   Pop ecx      (* pop the return address off the stack *)
            (* into ecx *)
   Mov Save_ret,ecx (* save the return address for later *)
   Pop eax      (* eax = ppVtable*)
   Pop ebx      (* ebx = offset into vtable*)
   Push eax     (* put the ppVtable back onto the stack *)
            (* now that the offset is out of the way*)
   Mov ecx,[eax]    (* ecx = contents of memory at eax = *)
            (* pVtable and NOT ppvtable any more *)
   Add ecx,ebx      (* add in the offset down the vtable*)
            (* ecx now points to the address you *)
            (* want to call*)
   call dword [ecx] (*put the return address on the stack*)
            (* and go to the address pointed to by ecx*)
    (* upon return eax = the return value so leave eax alone! *)
    (* the return from the OLE method also took the *)
    (* parameters p1...pn off the stack.*)
   mov ecx, Save_ret
   push ecx     (* put the return address to the Clarion *)
            (* calling point back on the stack *)
    (* code to restore ebx,ecx,edx- omitted for clarity*)
   ret 0        (* I love it when a plan comes together!*)

You're Doing OLE!

So there you have it. Add those few lines of assembler to your code and OLE away just like the big boys. Actually just choose Project from the main menu and add Icall.a to the external source module section, add the prototypes for Icall1P to the global map. The project system will take it from there - the assembler will be called to assemble Icall.a and the Clarion compiler will compile the rest. The project system recognizes the portion that needs to be assembled by the .a extension.

The nice thing is regardless of how many parameters the interface method has, the code above should handle the details of calling the interface. It requires just two inputs: the ppVtable available from CoCreateInstance and the offset into the vtable available form OLE header files or in some cases from a free Microsoft utility called OLEView. Now you have reduced the barrier to COM in Clarion to finding the needed constants and preparing the interface method parameters. Next time I'll take on those challenges and show how to create a shortcut the OLE way.

So if you catch me on the newsgroups, feel free to ask a question. Just remember it may take a year or more to get an answer!

Download the source code

Read Part 2


Jim Kane was not born any where near a log cabin. In fact he was born in New York City. After attending college at New York University, he went on to dental school at Harvard University. Troubled by vast numbers of unpaid bills, he accepted a U.S. Air Force Scholarship for dental school, and after graduating served in the US Air Force. He is now retired from the Air Force and writing software for ProDoc Inc., developer of legal document automation systems. In his spare time, he runs a computer consulting service, Productive Software Solutions. He is married to the former Jane Callahan of Cando, North Dakota. Jim and Jane have two children, Thomas and Amy.

Printer-friendly version

Reader Comments

To add a comment to this article you must log in.

 
 

Search

 

Advanced Search
Topical Index

Related Articles

Subscribe to
ClarionMag

One year: $189

(includes all back issues since '99)

Renewals from $139

Two years: $289

Renewals from $239

More Info

Subscribe Now!

ClarionMag Blog

RSS Feeds

Updates via Email

Enter your Email


Powered by FeedBlitz

Quick Links