Writing an Infotip shell extension in Delphi

By: Deepak Shenoy

Abstract: Hovering over a file in Explorer brings up a "hint" window. The Infotip shell extension allows you to customize the text displayed. Deepak Shenoy shows you how it's done.

Writing an Infotip shell extension in Delphi

By Deepak Shenoy

Windows 2000 (and Windows 98 with IE 5 desktop integration installed) gives us a new Shell Extension - the Infotip. This is a hint window that pops up when you hover over any file. The standard hint shows the name of the file and the size, but you can customize this, based on the extension of the file.

There is a default Infotip extension for Microsoft Word and Excel documents - you can see the name, author and title of the document in the infotip. Here's what a standard infotip looks like (Windows 2000):

What we will do now is make an infotip for Delphi form files (DFM files). Here's what we hope to achieve:

You'll find the full code for this article at http://codecentral.borland.com/codecentral/ccweb.exe/listing?id=15151 and at http://www.agnisoft.com/delphi/dfminfotip.zip.

An Infotip shell extension is a Windows DLL that:
1.Implements IQueryInfo and IPersistFile
2. Registers itself in the registry. This step is slightly different from other shell extensions.

Implementing IQueryInfo and IPersistFile

IQueryInfo gives the text of the Infotip that's shown in the hint window. IPersistFile is what the shell uses to give you information about which file the user is hovering over.

First we'll create a new automation object. Let's call it DFMInfoTip. Here's what the dialog looks like (after selecting File|New...|ActiveX|Automation Object):

This generates a simple type library and an implementation for the IDFMInfoTip interface that's automatically generated. In this file, let's support the other interfaces:

TDFMInfoTip= 

class(TAutoObject,IDFMInfoTip,IQueryInfo,IPersistFile,IPersist)

Note: We need to implement IPersist also because IPersistFile inherits from IPersist.

Now let's see some code. IPersistFile has a function called Load, which is called to give us the name of the file that the mouse is hovering on.


function TDFMInfoTip.Load(pszFileName: POleStr;dwMode: Integer): HResult;
begin
  FFile := pszFileName;
  Result := S_OK;
end;

For the rest of the functions in IPersistFile (and IPersist) we'll return E_NOTIMPL.

Now let's look at IQueryInfo, which has just two functions. GetInfo is called by the shell to retrieve the text of the infotip. Here's how I've implemented it:

function TDFMInfoTip.GetInfoTip(dwFlags: DWORD;
  var ppwszTip: PWideChar): HResult;
var szTip : string;
begin
  Result := S_OK;
  // The current file name is in FFile through IPersistFile
  szTip := GetDFMInfo;
  ppwszTip := pMalloc.Alloc( sizeof(WideChar)*Length(szTip)+1);
  if (ppwszTip <> nil) then
    StringToWideChar(szTip, ppwszTip, sizeof(WideChar)*Length(szTip)+1);
end;
pMalloc is an IMalloc instance, returned by ShGetMalloc in the constructor. This is used so that the allocated memory can be freed by the shell when it's done displaying the infotip.

The function GetDFMInfo extracts some information out of a DFM file. First, we figure out if it's a binary or text dfm:


  fStream := TFileStream.Create(FFile,fmOpenRead or fmShareDenyNone);
  slStrings := TStringList.Create;
  try
    fStream.Position := 0;
    pFirst := @First;
    fStream.Read(pFirst^, 1);
    fStream.Position := 0;
    if First = $FF then // binary DFM
    begin
      Result := Result + 'Type: Binary';
      inStream := TMemoryStream.Create;
      ObjectResourceToText( fStream, inStream );
      inStream.Position := 0;
      slStrings.LoadFromStream(inStream);
      inStream.Free;
    end
    else
    begin // Delphi 5's text DFM
       slStrings.LoadFromStream(fStream);
       Result := Result + 'Type: Text';
    end;
We now have the entire form as text in slStrings. Then let's extract fields in the DFM that are of interest to us. Here's a snippet that gets the caption:

    szText := szFullText;
    //get the caption
    iPos := Pos('  Caption = ''', szText);
    if iPos <> 0 then
    begin
      Inc(iPos, Length('  Caption = '''));
      iEnd := iPos;
      while true do
      begin
        if (szText[iEnd] = '''') then
          if (Copy(szText, iEnd+1, 4)='#39''') then
            Inc(iEnd, 4)
          else
            break;
        Inc(iEnd);
      end;
      Result := Result + #13#10 + 'Caption: ' + szText ;
    end;

The code included with this article gets the caption, width and height of the selected file.

Registering an Infotip Extension

To register an Infotip Extension, you must:

  1. Register the COM DLL - you can run regsvr32.exe in the Windows System directory passing the DLL file name as a parameter.
  2. Create an entry under HKEY_CLASSES_ROOT/.dfm like this:

The default value for this key must evaluate to the CLSID of the COM object implementing the shell extension. I've put in the CLSID of the DFMInfoTip object implemented.

Note: The CLSID {00021500-0000-0000-C000-000000000046} is the IID of IQueryInfo.

Of course, under Windows NT and 2000, all shell extensions must be "approved." This is obvious only if you log on as user other than administrator. The registry key you must create in order to "approve" a shell extension is:

HKEY_LOCAL_MACHINE
  SOFTWARE
    Microsoft
      Windows
        CurrentVersion
          Shell Extensions
            Approved

Under this key, create a new string value with the name of the shell extension CLSID (in this case, {A6614304-6DFB-4A31-8032-C4E0CCA42D81}) and assign it any description text.

That's about all you need to do. The code sample has two .REG files that you can run to import the correct registry entries. Why two? I found that the Windows 2000 regedit tool exports the registry as Unicode instead of ASCII - so there's one file for Windows 2000 and one for Windows 98.

Deepak Shenoy(shenoy@agnisoft.com) is a director at Agni Software, a software company in India that offers consultancy and offshore development solutions. Deepak is currently working with Windows 2000, COM+ and XML. In his spare time he tries to play the guitar, which is why he is not allowed too much spare time.

Server Response from: SC3