Interface Type Libraries


Interface type libraries can be written either in Object Description Language (ODL) or in Interface Description Language (IDL). ODL is the language preferred by the Visual Basic group and they provide the MKTYPLIB compiler on the Visual Basic CD. IDL is the language preferred by the C++ group and they provide the MIDL compiler with their product. Both of these compilers are poorly documented and bedeviled by bugs, but I prefer the new bugs in MIDL to the old bugs in MKTYPLIB. Since this is a Visual Basic book, I won’t go very far into the details of IDL or ODL. If you have a little C background, you can learn a lot from examples and comments in the source to the Windows API type library, which is provided with this book. The CD also has an article about writing type libraries with ODL, but the emphasis is on API functions rather than interfaces.


To give you some idea of what I’m talking about, I’ll have to show part of the IDL source for IShellLink. You can see the whole thing in the SHLOBJ.IDL file on the CD:

[   odl,
helpstring(“Visual Basic version of IShellLink interface”),
#ifdef UNICODE
uuid(000214F9-0000-0000-C000-000000000046)
#else
uuid(000214EE-0000-0000-C000-000000000046)
#endif
]
interface IVBShellLink : IUnknown {

[ helpstring(“Retrieves the path and filename … “) ]
HRESULT GetPath([in, out] LPTSTR pszFile,
[in] int cchMaxPath,
[in, out] LPVOID pfd,
[in] ESLGP fFlags);

[ helpstring(“Retrieves the list of … identifiers”) ]
HRESULT GetIDList([out, retval] LPITEMIDLIST * ppidl);
[ helpstring(“Sets the list of shell link item identifiers”) ]
HRESULT SetIDList([in] LPCITEMIDLIST pidl);

[ helpstring(“Retrieves the … description string”) ]
HRESULT GetDescription([in, out] LPTSTR pszName,
[in] int cchMaxName);
[ helpstring(“Sets the shell link description string”) ]
HRESULT SetDescription([in] LPCTSTR pszName);

[ helpstring(“Retrieves the … working directory”) ]
HRESULT GetWorkingDirectory([in, out] LPTSTR pszDir,
[in] int cchMaxPath);
[ helpstring(“Sets the … working directory”) ]
HRESULT SetWorkingDirectory([in] LPCTSTR pszDir);

[ helpstring(“Retrieves the … command-line arguments”) ]
HRESULT GetArguments(LPTSTR pszArgs, int cchMaxPath);
[ helpstring(“Sets the … command-line arguments”) ]
HRESULT SetArguments([in] LPCTSTR pszArgs);

[ propget, helpstring(“Retrieves or sets the … hot key”) ]
HRESULT Hotkey([out, retval] WORD *pwHotkey);
[ propput ]
HRESULT Hotkey([in] WORD wHotkey);
§
[ helpstring(“Resolves a shell link … “) ]
HRESULT Resolve([in] HWND hwnd,
[in] ESLR fFlags);

[ helpstring(“Sets the … path and filename”) ]
HRESULT SetPath([in] LPCTSTR pszFile);
};

This shows a few key parts of the interface. In real life, there’s no such thing as part of an interface. Every method and property must be present with parame­ters of exactly the right size in exactly the right order. You must use the uuid attribute to provide the correct GUID, and you must derive the interface from either IUnknown or from an interface that derives from IUnknown. Don’t worry about what derive means; just use the syntax shown above. Once you meet those requirements, the rest can be lies.


My version of the interface lies about the method names and parameters. Just as we can lie with aliases in Declare statements, we can lie to improve interfaces. If you can get your hands on the original C header file SHLOBJ.H from which I created SHLOBJ.IDL, you can see what kind of whoppers I’m telling. For example, the official names of the Hotkey property Get and Let procedures are GetHotkey and SetHotkey, and normally they don’t have the propget and propset attributes because C and C++ don’t support properties. I’d like to turn GetDescription and SetDescription into properties, too, but because they use LPTSTR parameters for strings, the Get and Set must be different and can’t be converted to properties.


I’m also lying about some of the types. The WORD type used by the Hotkey property and the DWORD type used by the Resolve method are unsigned integers in the original interface, but I make them signed integers compatible with Visual Basic’s Integer and Long types by using alias features that you don’t really want to know about.


The IVBShellLink interface provides a template for defining the methods and properties of an imaginary class. A shell link file is just a binary file, and, theoretically, we could figure out its format and write the code to create and read such files. Also, we could use this code to implement the methods and properties of IVBShellLink. But Windows already implements this interface. We don’t need to reinvent the wheel; we just need some way to hook up the Windows implementation to our template. Here’s the type library syntax:

[    uuid(00021401-0000-0000-C000-000000000046),
helpstring(“Visual Basic CShellLink class”)
]
coclass CShellLink {
[default]
interface IVBShellLink;
};

COM uses the term coclass to describe what Visual Basic calls a class. The Windows developers who created the system implementation of IShellLink gave it a magic GUID number starting with 00021401, and we can reuse their implementation by declaring a coclass with the magic number.


There’s one more piece to the puzzle. The Windows implementation of IShell­Link depends on an interface called IPersistFile. You can look this one up in OAOBJ.IDL on the CD. We’ll only be dealing with its Load and Save methods.


CHALLENGE This should give you another piece of the COM language puzzle. When you create the CJumpStart class, Visual Basic
creates a default interface named _CJumpStart with all the
methods and properties you give your class. It creates a co­-
class named CJumpStart that implements the default interface.
It generates hidden GUIDs for both the interface and the
coclass and puts them in a hidden type library. Now you’re
ready to write your own COM language that competes with
Visual Basic.