Mega Code Archive

 
Categories / Delphi / Examples
 

Shell Extension Infotips and IQueryInfo

Title: Shell Extension Infotips and IQueryInfo Question: How do I customize the infotips that are shown by Windows (IE 4.0 and up) when I hover over a file? Answer: Customising the content of the infotip window is done by writing an ActiveX object that implements the IPersistFile and IQueryInfo interfaces (in addition to the standard IUnknown interface). It's a very simple process, but the feature is strangely undocumented in even the latest Platform SDK from Microsoft. Once written, the CLSID of your ActiveX object must be entered into the Registry under the file extension which you wish to customise the infotip for, usually under HKEY_CLASSES_ROOT. Detailed below are the steps to create a basic infotip extension. The two key interfaces here are IPersistFile and IQueryInfo. IPersistFile allows the filename of the current file to be passed to the COM object, and IQueryInfo allows the object to pass back the tip to display. Firstly, create a new ActiveX library (File New ActiveX Library) and add a new unit to the project. You don't want to create the object via Delphi's type library editor, since it doesn't know about the interfaces you need, so you will have to do it manually. Put in the following Uses and Const clauses (I strongly urge you to generate your own Class ID for your object - but use mine if you're lazy): uses ComObj, ShlObj, ActiveX, Windows; const CLSID_InfoTip : TGUID = '{088FB88B-09E0-4a8d-BF9A-EDCD8041EA1E}'; Define a new type as below: TInfoTip = class(TComObject, IQueryInfo, IPersistFile) private fName : string; protected { IQueryInfo } function GetInfoTip(dwFlags: DWORD; var ppwszTip: PWideChar): HResult; stdcall; function GetInfoFlags(out pdwFlags: DWORD): HResult; stdcall; { IPersistFile } function IsDirty: HResult; stdcall; function Load(pszFileName: POleStr; dwMode: Longint): HResult; stdcall; function Save(pszFileName: POleStr; fRemember: BOOL): HResult; stdcall; function SaveCompleted(pszFileName: POleStr): HResult; stdcall; function GetCurFile(out pszFileName: POleStr): HResult; stdcall; function GetClassID(out classID: TCLSID): HResult; stdcall; end; implementation uses ComServ; For this extension, the only important method in IPersistFile is Load and all of the other IPersistFile methods should simply return E_NOTIMPL, so fill in the other IPersistFile methods something like this: function TInfoTip.Save(pszFileName: POleStr; fRemember: BOOL): HResult; begin Result := E_NOTIMPL; end; When the shell calls IPersistFile.Load it passes in the filename to work with as the pszFileName parameter, which we need to save for later use (note that type POleStr is the same as type PWideChar.) So implement IPersistFile.Load as below: function TInfoTip.Load(pszFileName: POleStr; dwMode: Integer): HResult; begin WideCharToStrVar(pszFileName, fName); Result := S_OK; end; Now we move onto IQueryInfo. As yet, IQueryInfo.GetInfoFlags is not yet used by the shell, so just return E_NOTIMPL as above. The heart of the whole extension is IQueryInfo.GetInfoTip. The dwFlags parameter is currently unused by the shell and can be ingnored. The ppwszTip parameter is used to return the tip we want to display (as a UNICODE string.) It's important to note that we have to allocate the memory for it, but the shell will deallocate it (see the Platform SDK for more details.) There's a really useful Delphi procedure, StringToOleStr, which takes a pascal-style string, allocates all the required memory, converts it to a WideChar string, and returns a PWideChar - beautiful. Try find that in a C library! The IQueryInfo.GetInfoTip method should look something like this - remember to return S_OK: function TInfoTip.GetInfoTip(dwFlags: DWORD; var ppwszTip: PWideChar): HResult; begin ppwszTip := StringToOleStr('this is my info tip for: ' + #13 + fName); Result := S_OK; end; Since we're not using the type library editor, we need to handle the ComFactory details, too, so at the bottom of your unit, put the following lines in. Note that you must have ComServ in your uses clause somewhere (usually in the implementation section.) initialization TComObjectFactory.Create(ComServer, TInfoTip, CLSID_InfoTip, '', 'Test Info Tip', ciMultiInstance, tmApartment); end. Finally, we need to associated the object with a file extension, so run RegEdit and add the following keys (alternatively, you could handle the registry modifications via the UpdateRegistry method of the TComObjectFactory type): HKEY_CLASSES_ROOT \.txt \ShellEx \{00021500-0000-0000-C000-000000000046} Make the default value under this key the CLSID of your object ({088FB88B-09E0-4a8d-BF9A-EDCD8041EA1E} or whatever) - note that {00021500-0000-0000-C000-000000000046} is the GUID of the IQueryInfo interface and mustn't be changed. Change .txt as appropriate. Don't forget to register your ActiveX server (Run Register ActiveX Server.) It's important to note that once the infotip has been shown, the DLL remains loaded by the explorer.exe process and you won't be able to recompile or unregister the library at all. To do so, you will need to kill explorer.exe via the task manager. To get the desktop and start bar back again, start explorer.exe again via the New Task option in the task manager. Note: To work properly under Windows NT and Windows 2000, shell extensions must be registered and approved by the system administrator. Many developers undervalue this point since they mostly log on to their Windows NT boxes as administrator. In this case, as well as under Windows 9x, there's no real need to have extensions approved. Regardless, here's the registry entry responsible for extension approval: 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 and assign it a description text. Voila! You've got yourself a customised info tip extension. Move your mouse over a .txt file and watch the magic happen. It's worthwhile noting that the shell automatically displays the filename in the info tip if the entire filename is not currently visible (ie. shown with an ellipsis at the end.) You also have to handle displaying the size of the file, its type, etc. Download: Shell_Extension_Infotips_demo_project.zip