5.2 Icon Handler Interfaces

only for RuBoard - do not distribute or recompile

5.2 Icon Handler Interfaces

Now that you know how an icon handler works, let's discuss the interfaces involved in a little more detail.

5.2.1 IPersistFile

IPersistFile inherits one method, GetClassID , from IPersist . IPer-sistFile contains an additional five methods (see Table 5.1): IsDirty , Load , Save , SaveCompleted , and GetCurFile . Typically, IPersistFile is implemented when you want to read or write information from a file. There are many more scenarios. You are encouraged to learn more about this interface, because in the world of COM, this interface gets some major game time. In our case, however, we are only interested in one method, and that's Load .

We do have a small problem with IPersistFile . It's derived from IPersist , and VB does not like interfaces that are derived from anything other than IUnknown or IDispatch . This is because Microsoft believes that Visual Basic objects should always support late binding and, hence, should always be derived from IDispatch . So what do we do? Before addressing this question, let's look at the IDL in Example 5.1, which shows that IPersistFile is derived from IPersist . IPersistFile has inherited the IPersist method GetClassID .

Example 5.1. VB Will Not Accept This Definition of IPersistFile
 [
    uuid(0000010c-0000-0000-C000-000000000046),
    helpstring("IPersist Interface"),
   odl
]
interface IPersist : IUnknown
{
    HRESULT GetClassID([in, out] CLSID *lpClassID);
}


[
	uuid(0000010b-0000-0000-C000-000000000046),
	helpstring("IPersistFile Interface"),
	odl
]
interface IPersistFile : IPersist
{

    HRESULT IsDirty(  );
	
    HRESULT Load([in] LPCOLESTR pszFileName, 
                 [in] DWORD dwMode);

    HRESULT Save([in] LPCOLESTR pszFileName,
                 [in] BOOL fRemember);

    HRESULT SaveCompleted([in] LPCOLESTR pszFileName);

    HRESULT GetCurFile([in, out] LPOLESTR *ppszFileName);

} 

Fortunately, the solution to this problem is very easy. We will simulate the inheritance by deriving IPersistFile from IUnknown . Then we add the method from IPersist that would have been a part of the interface via inheritance directly to the definition listing for IPersistFile . In other words, we remove the inheritance. The resulting IDL file is shown in Example 5.2.

Example 5.2. IPersist Is Added to IPersistFile to Simulate Inheritance
 [
    uuid(0000010b-0000-0000-C000-000000000046),
    helpstring("IPersistFile Interface"),
    odl
]

// original - interface IPersistFile : IPersist

interface IPersistFile : IUnknown
{

    // IPersist is added to IPersistFile definition
    HRESULT GetClassID([in, out] CLSID *lpClassID);

    //IPersistFile starts here
    HRESULT IsDirty(  );
		
    HRESULT Load([in] LPCOLESTR pszFileName, 
                 [in] DWORD dwMode);

    HRESULT Save([in] LPCOLESTR pszFileName,
                 [in] BOOL fRemember);

    HRESULT SaveCompleted([in] LPCOLESTR pszFileName);

    HRESULT GetCurFile([in, out] LPOLESTR *ppszFileName);

} 

Table 5.1 shows the methods supported by IPersistFile . We'll look at only one of these, the Load method, in detail, since it's the only method that we'll actually have to write any code for.

Table5.1. IPersistFile

Method

Description

GetClassID

This method is inherited from IPersist . It should return the CLSID of an object.

IsDirty

Checks an object for changes since it was last saved.

Load

Opens the selected file and initializes the object.

Save

Saves the object into the selected file.

SaveCompleted

Notifies the object that it can write to its file.

GetCurFile

Gets the name of the file that is currently associated with the object.

5.2.1.1 Load

The Load method is invoked by the shell immediately after the icon handler is loaded. The Load method is responsible for providing the icon handler with the name of the file that is to be displayed.

The documentation for the Load method states that its purpose is to open a specified file and initialize an object from the file contents. This is an important distinction to remember. Load does not load a file. It loads an object based on the contents of a file. The how and the why is left to the implementor. This function is only used for initialization. It does not return the object to the caller. Its syntax is as follows :

 HRESULT Load(LPCOLESTR   pszFileName   , DWORD   dwMode   ); 

Its parameters are:

pszFileName

A pointer to the name of the file for which the shell is requesting an icon to display.

dwMode

The access mode (which is ignored in the case of icon handlers).

Because the first parameter actually comes to us as a 4-byte address, we will have to use the CopyMemory API and the undocumented StrPtr function to retrieve the actual string value of the filename. This will be discussed in more detail in the implementation section of this chapter.

5.2.2 IExtractIcon

IExtractIcon is actually IExtractIconA or IExtractIconW , depending upon the circumstance. The original definition of this interface is found in a header file named shlobj.h . For those of you with Visual C++ installations, take a look at the file. It contains most of the interfaces used by the shell with liberal commenting, making it a really good source of information. It also will show you how to define an interface in straight C++. That's right; there's no IDL in this file.

Preprocessor definitions in the file are used to determine whether IExtractIcon is being compiled for Windows 9x or Windows NT and Windows 2000. The appropriate interface, IExtractIconA or IExtractIconW , is then used. Typically, interface names ending in "A" denote the Windows 9x version and the interface names ending in "W" are for Windows NT and Windows 2000. We do not have the luxury of a preprocessor in VB. We will have to define both interfaces (each has a distinct GUID) and implement both interfaces, as well. The complete listing for IExtractIcon is shown in Example 5.3.

Example 5.3. IExtractIconA and IExtractIconW
 typedef [public] long HICON;
typedef [public] long LPSTRVB;
typedef [public] long UINT;

typedef enum {
    GIL_SIMULATEDOC  = 0x0001,      
    GIL_PERINSTANCE  = 0x0002,      
    GIL_PERCLASS     = 0x0004,      
    GIL_NOTFILENAME  = 0x0008,      
    GIL_DONTCACHE    = 0x0010      
} GETICONLOCATIONRETURN;

[
    uuid(000214eb-0000-0000-c000-000000000046),
    helpstring("IExtractIconA Interface"),
    odl
]
interface IExtractIconA : IUnknown
{
    HRESULT GetIconLocation([in] UINT uFlags,
                            [in] LPSTRVB szIconFile, 
                            [in] UINT cchMax,
                            [in,out] long *piIndex,
                            [in,out] GETICONLOCATIONRETURN *pwFlags);

    HRESULT Extract([in] LPCSTRVB pszFile,				
                    [in] UINT nIconIndex,			
                    [in,out] HICON *phiconLarge,		
                    [in,out] HICON *phiconSmall,		
                    [in] UINT nIconSize);			
}

[
    uuid(000214fa-0000-0000-c000-000000000046),
    helpstring("IExtractIconW"),
    odl
]
interface IExtractIconW : IUnknown
{
    HRESULT GetIconLocation([in] UINT uFlags,
                            [in] LPWSTRVB szIconFile, 
                            [in] UINT cchMax,
                            [in,out] long *piIndex,
                            [in,out] GETICONLOCATIONRETURN *pwFlags);

    HRESULT Extract([in] LPWSTRVB pszFile,				
                    [in] long nIconIndex,			
                    [in,out] HICON *phiconLarge,		
                    [in,out] HICON *phiconSmall,		
                    [in] UINT nIconSize);
} 

As its name indicates, IExtractIcon is concerned with retrieving the icon to be displayed by the context icon handler. The methods of this interface are shown in Table 5.2.

Table5.2. IExtractIcon

Method

Description

Extract

Specifies the location of an icon.

GetIconLocation

Retrieves the location and index of an icon.

5.2.2.1 GetIconLocation

GetIconLocation is used by the shell to retrieve the location and index of an icon from the icon handler. If the icon is in a DLL or EXE, then GetIconLocation returns the filename and the index of the icon as it resides in the resource section of that file; otherwise , the method returns a value of GIL_NOTFILENAME in the pwFlags parameter. Its syntax is:

 HRESULT GetIconLocation(UINT   uFlags   , LPSTR   szIconFile   , INT   cchMax   , 
LPINT   piIndex   , UINT *   pwFlags   ); 

Its parameters are the following:

uFlags

[in] Icon state flags. This value is supplied by the shell.

szIconFile

[in, out] The address that receives the name and location of the icon file from the icon handler. This is a null- terminated string.

cchMax

[in] Size of the buffer that receives the icon location. This is usually set to the value of MAX_PATH and defines the total number of characters that the icon handler can write to szIconFile .

piIndex

[in, out] The zero-based ordinal position of the icon in the file whose path and name are written to the szIconFile buffer. The icon handler provides the shell with this value if the icon is to be extracted from a file.

pwFlags

[in, out] A value from the GETICONLOCATIONRETURN enumeration. This parameter tells the shell how it should handle the icon file that is returned.

The first parameter, uFlags , is of no concern to us, so we can skip it for now. It will come into play later when we create namespace extensions (see Chapter 12).

The second parameter, szIconFile , is a pointer. If the icon handler uses G etIconLocation (as opposed to Extract ) to provide the icon file, szIconFile should point to a buffer that contains a valid filename upon successful completion. This is a long value. We can't assign a string directly to szIconFile . You should start getting used to the idea of using pointers now. Out of necessity, we will be using pointers to strings, rather than the strings themselves , for most of the book.

The third parameter, cchMax , is merely the size of the buffer that contains the icon filename.

Upon successful completion, the fourth parameter contains the index of the icon to be displayed. This is the index of the icon as it appears in the resource section of the file specified by szIconFile .

The icon handler should assign the fifth parameter one or more of the values from the GETICONLOCATIONRETURN enumeration defined in Table 5.3. These can be OR ed together.

Table5.3. The GETICONLOCATIONRETURN Enumeration

Constant

Description

GIL_DONTCACHE

The physical image bits of the icon should not be cached by the caller.

GIL_NOTFILENAME

The location of the icon is not a filename/index pair. If this flag is set when the method returns, the shell will then call the Extract method for the icon location.

GIL_PERCLASS

All objects of this class have the same icon. There is no need to use this flag, since it defeats the purpose of an icon handler.

GIL_PERINSTANCE

Each object of this class has its own icon.

GIL_SIMULATEDOC

The icon is the one registered for the file object's document type.

5.2.2.2 Extract

Extract is called by the shell after the icon handler supplies a value of GIL_NOTFILENAME for the pwFlags parameter of the GetIconLocation method and is used to provide the location of an icon (or the handle to an icon) that does not reside as a resource in a file. There are various reasons for returning a handle rather than the filename and index at which the icon can be found. For example, in Chapter 12, when we implement a namespace extension, the icons used will reside in an image list. This is for reasons of speed. Repeatedly opening and closing a file to retrieve an icon is very slow. If you have to access a file a few hundred times just to get an icon, you might consider using the Extract method instead. The syntax for Extract is as follows:

 HRESULT Extract( LPCSTR   pszFile   , UINT   nIconIndex   , HICON *   phiconLarge   , HICON *   phiconSmall   , UINT   nIconSize   ); 

Its parameters are:

pszFile

[in] The icon filename. This is the same value returned by the GetIconLocation method.

nIconIndex

[in] The icon's index. This is the same value returned by the GetIconLocation method.

phiconLarge

[in, out] Handle to the large icon.

phiconSmall

[in, out] Handle to the small icon.

nIconSize

[in] Size of the icon being requested by the shell. Icons are always square, so only one dimension needs to be specified.

pszFile and nIconIndex are the same values returned by GetIconLocation . If Extract returns S_FALSE (an OLE-defined error), then these values must contain a valid filename/index pair. Otherwise, phiconLarge and phiconSmall should contain valid handles to icons, such as from a call to the Win32 LoadIcon function or the ImageList_GetIcon function in COMCTL32.DLL .

only for RuBoard - do not distribute or recompile