The CShortcut Class


Earlier I mentioned a CShortCut server written in C++. Notice that the name of the Visual Basic version has a different case. Of course, the real difference between these components is their GUIDs. Their interfaces are actually very similar, and their implementations aren’t much different either. Aside from petty language differences such as curly braces, the difference is that dealing with Variants and Strings is a whole lot easier in Visual Basic. The C++ version is on the CD if you want to compare them.


The IShellLink interface seems to be designed specifically to cause trouble for Visual Basic programmers. On the one hand it ignores interface capabilities, such as properties, that would make access easy. Furthermore, it uses types, such as unsigned integers, that Visual Basic doesn’t understand. Like many API functions, it’s designed to enable every possible feature, not to be convenient or easy to use. In other words, it needs a Visual Basic–friendly wrapper:

Private link As New CShellLink

CShellLink wraps the IShellLink interface and link represents a particular shell link. The main purpose of the CShortcut class is to transfer the properties of the link object to and from a corresponding LNK file.

The Save method


CShortcut saves its link object to a file using the Save method:

‘ Link file parameter is Variant to accept any of these:
’ edstDesktop - Put on desktop
’ edstCommonDesktop - Put on shared desktop
’ edstPrograms - Put on programs menu
’ edstCommonPrograms - Put on shared programs menu
’ edstStartMenu - Put on start menu
’ edstCurrent - Put in current directory
’ edstPath - Put in same directory as target file
’ [directory] - Put in hardcoded path
’ [file.LNK] - Put in hardcoded file
Function Save(vLinkFile As Variant) As String
Dim sLink As String
‘ Convert constant or directory to full path
sLink = FixLocation(vLinkFile)
If sLink = sEmpty Then ErrRaise eeFileNotFound

‘ Save the object to disk
MCasts.IVBPersistFile(link).Save sLink, APITRUE
Save = sLink
End Function

Since the link file might be passed in as a constant representing a standard location, the method returns the real path in case the caller needs to locate the link file.

Interface casting


The Windows implementation of the IShellLink interface also implements the IPersistFile interface. You need to call the Save method of IPersistFile to save your changes to the link file. The Visual Basic way of doing this is to create a separate object variable for the persistent object:

Dim persist As IVBPersistFile
Set persist = link
persist.Save sLink, APITRUE

This technique is a little messy because it leaves unnecessary object variables lying around. Most object-oriented languages support a casting syntax so that you can typecast one object to another type with which it is polymorphic. The syntax varies depending on the language and on whether inheritance is used, but a compatible Visual Basic syntax might look like this:

IVBPersistFile(link).Save sLink, APITRUE

Or perhaps Visual Basic could have provided an operator and made the syntax look like this:

link@IVBPersistFile.Save sLink, APITRUE

Unfortunately, Visual Basic doesn’t support either of these syntaxes, but you can fake it by writing a typecasting function. The global module GCasts contains casting functions for all the interfaces provided by VBCore and by the Windows API type library. Here’s an example:

Function IVBPersistFile(obj As IVBPersistFile) As IVBPersistFile
Set IVBPersistFile = obj
End Function

All the other casting functions look exactly like this one except for the function name, which is also the name of the interface type being typecast. Since the CShortcut class resides in the same component as the casting functions, you must further qualify the casting function with the name of the MCasts object as explained in Chapter 5.

MCasts.IVBPersistFile(link).Save sLink, APITRUE

Your own references to casting functions won’t need this qualification.


By the way, APITRUE is a C-style True with a value of 1, indicating something that matters for certain uses of IPersistFile. It has no effect in the architecture of CShortcut.

The Resolve method


Resolve is the opposite of Save. You pass it the name of the LNK file you want to open. You can also pass optional parameters that control what happens if the LNK file you specify doesn’t contain a valid shortcut:

‘ Flags control behavior if LNK file reference can’t be resolved:
’ SLR_ANY_MATCH - Display a dialog (with hWnd parameter as parent
’ window) asking user whether to search for reference
’ SLR_NO_UI - Search the disk for the time period specified by
’ TimeOut parameter
Sub Resolve(sFileA As String, _
Optional Flags As ESLR = SLR_ANY_MATCH, _
Optional hWnd As Long = hNull, _
Optional TimeOut As Integer = 0)
‘ Load from LNK file and resolve
MCasts.IVBPersistFile(link).Load sFileA, STGM_DIRECT
If Flags = SLR_NO_UI And TimeOut > 0 Then
Flags = Flags Or MBytes.LShiftDWord(TimeOut, 16)
End If
link.Resolve hWnd, Flags
End Sub

Sometimes LNK files get out of date when a user moves the executable file that the shortcut references. The default flag, SLR_ANY_MATCH, specifies that a dialog box will ask the user what to do for unresolved shortcuts. The SLR_NO_UI specifies that an appropriate (we hope) action will be taken without user interaction.

CShortcut properties


The CShortcut properties work as you would expect, except that the underlying CShellLink uses API-style strings while the friendly versions have to use Visual Basic strings. The Arguments property is typical:

Property Get Arguments() As String
Dim s As String
s = String$(cMaxPath, 0)
link.GetArguments s, cMaxPath
Arguments = MUtility.StrZToStr(s)
End Property

Property Let Arguments(sArgumentsA As String)
link.SetArguments sArgumentsA
End Property

The Property Get works like any API string access function. You pass the underlying link object a string buffer and a maximum length. It fills the buffer with a null-terminated string, which you convert to a Visual Basic string. The other properties are similar. Some of them have a little more code to set defaults or validate input.

What’s next


If you can figure out how to use the IShellLink interface, you should be able to use other interfaces, starting with the new ones introduced by Windows 95 and now available in Unicode variations for Windows NT 4.0.


The most important interface behind what you see on your screen and in Windows Explorer is IShellFolder. It’s the manager behind the new object hierarchy starting on your desktop with My Computer and moving up to the directories and files on your system. Ideally, this new paradigm would be everywhere by now. All sorts of programs, not just Windows Explorer, would be organizing objects in similar hierarchies. You do see that in a few programs, but it certainly hasn’t become pervasive. My theory of why starts with the semi-random name assigned to this hierarchy—the shell namespace. Nobody understands what
it is, much less how to program it. How can you take something seriously
when its main data structures are named PIDL and SHITEMID. Furthermore, the samples for it are written in tortured C, not C++. As for Visual Basic, well, this is probably the most Visual Basic–hostile interface you’ll ever encounter, and frankly I don’t think it’s all that great for C++ either. Nevertheless, we’ll do a few things with IShellFolder in Chapter 11.


IShellFolder is intimately related to other new interfaces that let you create helpers for any new document types your programs create. For example, ICon­textMenu lets you access the context menus for registered file types. IShell­PropSheetExt lets you write property sheet extensions that display information about the file type when users right click on a file and select Properties from the context menu. Context menu handlers, drag-and-drop handlers, property sheet handlers, and file viewers are not the same as similar features in your application because they affect your documents, not your program. In fact, your program might not be running when the shell extension goes into action. The idea was that everybody who creates a new document type would automatically provide all the appropriate handlers. Unfortunately, they forgot to make it easy. I was hoping to get around to that for this book, but I guess I’ll leave it to you. Of course, by the time you’re ready to start, new interfaces associated with Internet Explorer and future versions of Windows might lure you away from the task. Let’s hope they make these new interfaces easier to program.


Next on my list is IStorage. The Visual Basic file I/O system is nothing to brag about. It carries a lot of compatibility baggage and lacks some fundamental features, such as the ability to commit changes without closing an open file. COM provides the IStorage interface and some related API functions as an enhanced I/O system that does everything normal file systems do plus a few other tricks you’ve never seen before. Even if it didn’t do that extra stuff, it would still be a better, more object-oriented system than the one built into Visual Basic. Alas, you’ll have to fight with COM to get it to work with Visual Basic. Too bad. But don’t let that stop you. You might even want to combine IStorage and IShell­Folder to create a complete object hierarcy representing all the system objects and all of their contents.


Something else you might think about is implementing ITypeInfo in order to create object browsing tools. Don’t bother. Visual Basic comes with the TypeLib Information component (TLBINF32.DLL), which already provides everything you need and more. This is the same component that provides all the information used by Visual Basic’s Object Browser and other browsing features. The only problem is figuring out how to use it. There’s no documentation. There’s no support. But all you have to do is load the DLL in the References dialog box. Then look at the members with the Object Browser. You’ll have to figure the object hierarchy from the names only. It’s not a project for the faint of heart, but I’d rather try to decipher this DLL than try to implement ITypeInfo from scratch.


Visual Basic comes with an ActiveX Control Interface Wizard that fakes inheritance of controls with delegation, but it doesn’t do anything about faking
inheritance of classes. All you need to do is use the TypeLib Information component to read in all the properties, methods, and enums of the public class you want to enhance. Use this information to create the delegated members of classes in the same way that the wizard does it for controls. It’s one more project I wanted to do for this book, but never got around to.


NOTE At this point you may be feeling annoyed. If I really know how
to do this stuff, why don’t I tell you the details? Why don’t I do
it myself? I have to plead guilty. In fact, I’m downright embarrassed. My feeble excuse is that half of the problem involves type libraries, and since this book is about Visual Basic, I don’t have to explain the foreign IDL and ODL languages in detail. Besides, I give you hints (if you study them long and hard enough) in the CShortcut example. But the real reason is that I ran out of time. Sorry.