Articles   Members Online:
-Article/Tip Search
-News Group Search over 21 Million news group articles.
Member Area
-Account Center
-Top 10 NEW!!
-Submit Article/Tip
-Forums Upgraded!!
-My Articles
-Edit Information
-Become a Member
-Why sign up!
-Chat Online!
-Indexes NEW!!
-Build your resume
-Find a job
-Post a job
-Resume Search
-Link to us
Visit Embarcadero
Embarcadero Community
A Quick Way to Shortcuts Turn on/off line numbers in source code. Switch to Orginial background IDE or DSP color Comment or reply to this aritlce/tip for discussion. Bookmark this article to my favorite article(s). Print this article
Delphi 5.x
User Rating
No Votes
# Votes
DSP, Administrator
Reference URL:
			 Author: Bill Todd

A Component for Creating and Modifying Shortcuts


Windows shortcuts provide a way to have as many links to a file as you need - in as 
many folders as you want. Shortcuts are also the tool for adding a file to the 
Windows Start menu. 

In Windows 3.x, creating shortcuts was easy. You had to learn a couple of simple 
DDE calls, and that was it. In 32-bit Windows, working with shortcuts is more 
complex, and requires the use of COM and interfaces. This article will look at 
working with shortcuts in detail, and show you how to build a custom component you 
can use to create and modify shortcuts in any folder. 

The Interfaces

Shortcuts, or links as they are sometimes called, are actually binary files stored 
on your hard disk with the .lnk extension. The Windows shell includes a COM object 
named ShellLink for working with shortcuts. The ShellLink object implements two 
interfaces, IShellLink and IPersistFile, that define the methods for working with 
shortcuts. Figure 1 shows the declaration of IShellLink from SHLOBJ.PAS, and Figure 
2 shows the declaration of IPersistFile from ACTIVEX.PAS. 
2   IShellLinkA = interface(IUnknown) { sl. }
3     [SID_IShellLinkA]
4     function GetPath(pszFile: PAnsiChar; cchMaxPath: Integer;
5       var pfd: TWin32FindData; fFlags: DWORD): HResult;
6       stdcall;
7     function GetIDList(var ppidl: PItemIDList): HResult;
8       stdcall;
9     function SetIDList(pidl: PItemIDList): HResult; stdcall;
10    function GetDescription(pszName: PAnsiChar;
11      cchMaxName: Integer): HResult; stdcall;
12    function SetDescription(pszName: PAnsiChar): HResult;
13      stdcall;
14    function GetWorkingDirectory(pszDir: PAnsiChar;
15      cchMaxPath: Integer): HResult; stdcall;
16    function SetWorkingDirectory(pszDir: PAnsiChar): HResult;
17      stdcall;
18    function GetArguments(pszArgs: PAnsiChar;
19      cchMaxPath: Integer): HResult; stdcall;
20    function SetArguments(pszArgs: PAnsiChar): HResult;
21      stdcall;
22    function GetHotkey(var pwHotkey: Word): HResult; stdcall;
23    function SetHotkey(wHotkey: Word): HResult; stdcall;
24    function GetShowCmd(out piShowCmd: Integer): HResult;
25      stdcall;
26    function SetShowCmd(iShowCmd: Integer): HResult; stdcall;
27    function GetIconLocation(pszIconPath: PAnsiChar;
28      cchIconPath: Integer; out piIcon: Integer): HResult;
29      stdcall;
30    function SetIconLocation(pszIconPath: PAnsiChar;
31      iIcon: Integer): HResult; stdcall;
32    function SetRelativePath(pszPathRel: PAnsiChar;
33      dwReserved: DWORD): HResult; stdcall;
34    function Resolve(Wnd: HWND; fFlags: DWORD): HResult;
35      stdcall;
36    function SetPath(pszFile: PAnsiChar): HResult; stdcall;
37  end;
38  //Figure 1: The IShellLink interface. 
40  IPersistFile = interface(IPersist)
41    ['{ 0000010B-0000-0000-C000-000000000046 }']
42    function IsDirty: HResult; stdcall;
43    function Load(pszFileName: POleStr; dwMode: Longint):
44      HResult; stdcall;
45    function Save(pszFileName: POleStr; fRemember: BOOL):
46      HResult; stdcall;
47    function SaveCompleted(pszFileName: POleStr): HResult;
48      stdcall;
49    function GetCurFile(out pszFileName: POleStr): HResult;
50      stdcall;
51  end;

Figure 2: The IPersistFile interface. 

Into the TWinShortcut Component 

The shell of the TWinShortcut custom component was created with the Component 
Wizard in the Object Repository, using TComponent as its ancestor Listing One shows 
the finished component. To make things easier to find, the properties and their 
private member variables are in alphabetical order. In the implementation section, 
the methods are divided into three groups: constructor and destructor, property 
getter and setter, and custom methods. Within each of these groups, the methods are 
in alphabetical order. The constructor is overridden to automatically create an 
instance of the ShellLink object using the following code: 
53  FShellLink := CreateComObject(CLSID_ShellLink) as IShellLink;
54  FPersistFile := FShellLink as IPersistFile;

The first statement creates the ShellLink object by calling CreateCOMObject and 
passing the ShellLink object's class ID as the parameter. The return value is cast 
to type IShellLink to provide a reference to the IShellLink interface and its 
methods. FShellLink is a protected member variable of type IShellLink. FPersistFile 
is also a protected member variable and is of type IPersistFile. Casting FShellLink 
to IPersistFile provides an interface reference to the IPersistFile methods 
implemented by the ShellLink object. TWinShortcut's destructor is overridden, and 
both FShellLink and FPersistFile are set to nil to destroy the ShellLink object. 
Because COM objects are reference counted, both variables must be set to nil before 
the ShellLink object will be destroyed. 

You must be able to specify the name and location of the shortcut file you want to 
work with, and that capability is provided by three properties: ShortcutFileName, 
ShortcutPath, and SpecialFolderLocation. One big problem in working with shortcuts 
is figuring out where to create them. For example, if you want to create a shortcut 
on the user's desktop, you have to know the path to the desktop folder, and that is 
different for different versions of Windows. 

The solution is a Windows API function named SHGetSpecialFolderLocation, which 
takes three parameters. The first is a window handle, which can be set to zero. The 
second is a constant that identifies the folder you want. To find a partial list of 
constants, click Start | Programs | Borland Delphi 5 | Help | MS SDK Help Files | 
Win32 Programmers Reference and search for SHGetSpecialFolderLocation. If you have 
the MSDN Library CD, search for SHGetSpecialFolderLocation and you'll find a list 
of over 40 folder constants. The Win32 Programmers Reference Help file also 
contains detailed information about IShellLink and IPersistFile and their methods. 
The third parameter is a variable of type PItemIdList. 

After calling SHGetSpecialFolderLocation, you will call SHGetPathFromIdList to 
extract the actual path from the PItemIdList parameter. The SpecialFolderLocation 
property of TWinShortcut is of type Word and corresponds to the second parameter, 
the folder number, of SHGetSpecialFolderLocation. This lets you specify the 
location of the shortcut by setting the value of the SpecialFolderLocation 
property, or by providing a path in the ShortcutPath property. 

TWinShortcut has a public OpenShortcut method that's used to open an existing 
shortcut. This method is only three statements long. The first statement is a call 
to the protected method GetFullShortcutPath. GetFullShortcutPath returns the full 
path and filename of the shortcut. The second statement, shown here, actually opens 
the file by calling the IPersistFile interface's Load method: 
56  OleCheck(FPersistFile.Load(PWideChar(WideString(FullPath)),

Load's two parameters are the name of the file and the mode. Because this function 
is Unicode-compatible, the path must be a null-terminated string of wide chars. 
Because the call to GetFullShortcutPath returns a Pascal ANSI string, the path 
variable FullPath is first cast to type WideString, and then cast to type PWideChar 
to match the type of the parameter. Note that Load is called as a parameter to the 
OleCheck procedure. OleCheck examines the value returned by 
SHGetSpecialFolderLocation, and if that value indicates an error, OleCheck raises 
an EOleSysError exception. This technique is used for all of the interface method 
calls in this example, so normal Delphi exception handling can be used to trap 
errors that occur when using this component. The last line of the LoadShortcut 
method calls the custom method GetPropertiesFromShortcut, which calls each of the 
get methods of the IShellLink interface and assigns the returned value to the 
corresponding property of TWinShortcut.

Before continuing, let's look at the GetFullShortcutPath and 
GetPropertiesFromShortcut methods used by OpenShortcut. If the ShortcutPath 
property is null, GetFullShortcutPath calls GetSpecialFolderPath. Otherwise, it 
assigns the value of the ShortcutPath property to Result. It then adds the 
ShortcutFileName property to the end of the string. This is safe because 
GetSpecialFolderPath always returns a path that ends with a backslash, and the 
write method for the ShortcutPath property ensures that the property value always 
ends with a backslash. The write method for the ShortcutFileName property ensures 
that the filename always includes the .lnk extension. 

GetSpecialFolderPath calls SHGetSpecialFolderLocation and passes the value of the 
SpecialFolderLocation property as the second parameter. This call loads the 
ItemIdList variable passed as the third parameter. Next, GetSpecialFolderPath calls 
SHGetPathFromIdList, passing two parameters. The first is the ItemIdList variable 
initialized by the call to SHGetSpecialFolderLocation, and the second is a char 
array, CharStr, into which the path will be placed. Finally, CharStr is assigned to 
the Result variable and a backslash is appended to the path. 

The final step in the OpenShortcut method is the call to GetPropertiesFromShortcut. 
This method calls each of the get methods in the IShellLink interface and assigns 
the returned value to the corresponding property of TWinShortcut. For example, the 
first call is to the IShellLink GetPath method, which returns the path to the 
target file, i.e. the file to which the shortcut points. These calls are 
straightforward with two exceptions. If you create a shortcut manually in Windows, 
and the shortcut is to a program that requires command-line arguments, you type 
them in the Target edit box following the path to the EXE file. However, the 
command-line arguments are stored separately in the shortcut file and are retrieved 
with a separate call, GetArguments.

The call to GetHotkey returns the hotkey information in a single parameter of type 
Word. The virtual key code is stored in the low byte, and the modifier flags that 
indicate which shift keys were pressed are stored in the high-order byte. If you 
want to display the hotkey as text, or give users the ability to enter a hotkey, 
the easy way is to use the THotkey component from the Win32 page of the Component 
palette. The problem is that the THotkey component stores the virtual key code in 
its HotKey property, and the modifier flags in its Modifiers property. To make 
things worse, the values used to represent the [Ctrl], [Alt], [Shift], and extended 
keys in the high byte of the value returned by GetHotkey, aren't the same as the 
values used to represent the same keys in the Modifiers property of THotkey. 

(Note: The extended-key flag indicates whether the keystroke message originated 
from one of the additional keys on the enhanced keyboard. The extended keys consist 
of the [Alt] and [Ctrl] keys on the right-hand side of the keyboard; the [Ins], 
[Del], [Home], [End], [PageUp], [PageDown], and the arrow keys to the left of the 
numeric keypad; the [NumLock] key; the k key; the [PrintScreen] key; and the divide 
(/) and [Enter] keys in the numeric keypad. The extended-key flag is set if the key 
is an extended key.) 

To make life easier for anyone using TWinShortcut, it has two properties, Hotkey 
and HotkeyModifiers, that are assignment-compatible with the properties of THotkey. 
The code following the call to GetHotkey converts the modifier flags from the form 
used by GetHotkey to the form used by the HotkeyModifiers property and by the 
THotkey component. The modifier constants used with GetHotkey and SetHotkey 
CommCtrl.pas unit. The constants used with the THotkey component's Modifiers 
property (hkAlt, hkCtrl, hkShift, and hkExt) are declared in the ComCtrls.pas unit. 

Creating or saving a modified shortcut is handled by the TWinShortcut's public 
SaveShortcut method. SaveShortcut begins by calling PutPropertiesToShortcut. This 
method calls the IShellLink put method for each property to assign the current 
value of the TWinShortcut properties to the corresponding shortcut properties. The 
only part of this process that is complex is converting the HotkeyModifiers 
property to the form required by the SetHotkey method. The series of if statements 
set the appropriate bits in the byte variable HotKeyMods. SetHotkey is called with 
a single-word parameter that's constructed by shifting HotKeyMods left eight bits 
to place it in the high-order byte of the word and adding the value of the HotKey 
property. Next, a call to GetFullShortcutPath returns the path to the link file. 
Finally, the IPersistFile Save method is called with the full path to the link file 
as a parameter. Again, the path must be cast first to a WideString, and then to a 


You can create and modify any Windows shortcut using the methods of the IShellLink 
and IPersistFile interfaces implemented by the ShellLink object. Although this 
article doesn't cover every method in detail, it should give you everything you 
need for most shortcut operations. For more detailed information about the 
interfaces or any of their methods, consult the Win32 Programmers Reference online 
help file that's installed with Delphi 5. 

Listing One - TWinShortcut
58  unit WinShortcut;
60  interface
62  uses
63    Windows, Messages, SysUtils, Classes, Graphics, Controls,
64    Forms, Dialogs, ComObj, ShlObj, ShellAPI, ActiveX, Menus,
65    ComCtrls;
67  type
68    TWinShortcut = class(TComponent)
69    private
70      { Private declarations. }
71      FArguments: string;
72      FDescription: string;
73      FHotkey: Word;
74      FHotKeyModifiers: THKModifiers;
75      FIconFile: string;
76      FIconIndex: Integer;
77      FShortcutFileName: string;
78      FShortcutPath: string;
79      FRunWindow: Integer;
80      FSpecialFolder: Integer;
81      FTarget: string;
82      FWorkingDirectory: string;
83    protected
84      { Protected declarations. }
85      FPersistFile: IPersistFile;
86      FShellLink: IShellLink;
87      function GetFullShortcutPath: string;
88      procedure GetPropertiesFromShortcut;
89      function GetSpecialFolderPath: string;
90      procedure PutPropertiesToShortcut;
91      procedure SetShortcutFileName(Value: string);
92      procedure SetShortcutPath(Value: string);
93      procedure SetSpecialFolder(Value: Integer);
94    public
95      { Public declarations. }
96      constructor Create(AOwner: TComponent); override;
97      destructor Destroy; override;
98      procedure OpenShortcut;
99      procedure SaveShortcut;
100   published
101     { Published declarations. }
102     property Arguments: string
103       read FArguments write FArguments;
104     property Description: string
105       read FDescription write FDescription;
106     property HotKey: Word read FHotkey write FHotkey;
107     property HotKeyModifiers: THKModifiers
108       read FHotKeyModifiers write FHotKeyModifiers;
109     property IconFile: string
110       read FIconFile write FIconFile;
111     property IconIndex: Integer
112       read FIconIndex write FIconIndex;
113     property RunWindow: Integer
114       read FRunWindow write FRunWindow;
115     property Target: string read FTarget write FTarget;
116     property ShortcutFileName: string
117       read FShortcutFileName write SetShortcutFileName;
118     property ShortcutPath: string
119       read FShortcutPath write SetShortcutPath;
120     property SpecialFolder: Integer
121       read FSpecialFolder write SetSpecialFolder;
122     property WorkingDirectory: string
123       read FWorkingDirectory write FWorkingDirectory;
124   end;
126 procedure register;
128 implementation
130 uses CommCtrl;
132 const
133   Backslash = '\';
134   LinkExtension = '.LNK';
136   { ********* Constructor and Destructor ********** }
138 constructor TWinShortcut.Create(AOwner: TComponent);
139 begin
140   { Create the ShellLink object and get an IShellLink and
141    an IPersistFile reference to it. }
142   inherited;
143   FShellLink :=
144     CreateComObject(CLSID_ShellLink) as IShellLink;
145   FPersistFile := FShellLink as IPersistFile;
146 end;
148 destructor TWinShortcut.Destroy;
149 begin
150   { Free the ShellLink object. }
151   FShellLink := nil;
152   FPersistFile := nil;
153   inherited;
154 end;
156 { ********* Property Getter and Setter Methods ********** }
158 procedure TWinShortcut.SetShortcutFileName(Value: string);
159 begin
160   FShortcutFileName := Value;
161   { If the file name does not end with the .LNK extension,
162    add the extension. }
163   if CompareText(ExtractFileExt(FShortcutFileName),
164     LinkExtension) <> 0 then
165     FShortcutFileName := FShortcutFileName + LinkExtension;
166 end;
168 procedure TWinShortcut.SetShortcutPath(Value: string);
169 begin
170   FShortcutPath := Value;
171   { Make sure the path ends with a backslash. }
172   if Copy(FShortcutPath,
173     Length(FShortcutPath), 1) <> Backslash then
174     FShortcutPath := FShortcutPath + Backslash;
175 end;
177 procedure TWinShortcut.SetSpecialFolder(Value: Integer);
178 begin
179   FSpecialFolder := Value;
180   { Clear the ShortcutPath when a value is assinged to the
181    SpecialFolder property. The SpecialFolder property will
182    not be used to get the path to the link file if the
183    ShortcutPath property is not null. }
184   FShortcutPath := '';
185 end;
187 { ********* Custom Methods ********** }
189 function TWinShortcut.GetFullShortcutPath: string;
190 { Gets the path to the shortcut file. If the ShortcutPath
191   property is null, the path comes from the SpecialFolder
192   property. }
193 begin
194   if FShortcutPath = '' then
195     Result := GetSpecialFolderPath
196   else
197     Result := FShortcutPath;
198   Result := Result + FShortcutFileName;
199 end;
201 procedure TWinShortcut.GetPropertiesFromShortcut;
202 { Calls the appropriate IShellLink method to get the value
203   of each property of the link and assign that value to the
204   corresponding property of this component. }
205 var
206   CharStr: array[0..MAX_PATH] of Char;
207   WinFindData: TWin32FindData;
208   RunWin: Integer;
209   HotKeyWord: Word;
210   HotKeyMod: Byte;
211 begin
212   OleCheck(FShellLink.GetPath(CharStr, MAX_PATH,
213     WinFindData, SLGP_UNCPRIORITY));
214   Target := CharStr;
215   OleCheck(FShellLink.GetArguments(CharStr, MAX_PATH));
216   Arguments := CharStr;
217   OleCheck(FShellLink.GetDescription(CharStr, MAX_PATH));
218   Description := CharStr;
219   OleCheck(
220     FShellLink.GetWorkingDirectory(CharStr, MAX_PATH));
221   WorkingDirectory := CharStr;
222   OleCheck(FShellLink.GetIconLocation(CharStr, MAX_PATH,
223     FIconIndex));
224   IconFile := CharStr;
225   OleCheck(FShellLink.GetShowCmd(RunWin));
226   RunWindow := RunWin;
227   OleCheck(FShellLink.GetHotkey(HotKeyWord));
228   { Extract the HotKey and Modifier properties. }
229   HotKey := HotKeyWord;
230   HotKeyMod := Hi(HotKeyWord);
231   if (HotKeyMod and HOTKEYF_ALT) = HOTKEYF_ALT then
232     Include(FHotKeyModifiers, hkAlt);
233   if (HotKeyMod and HOTKEYF_CONTROL) = HOTKEYF_CONTROL then
234     Include(FHotKeyModifiers, hkCtrl);
235   if (HotKeyMod and HOTKEYF_SHIFT) = HOTKEYF_SHIFT then
236     Include(FHotKeyModifiers, hkShift);
237   if (HotKeyMod and HOTKEYF_EXT) = HOTKEYF_EXT then
238     Include(FHotKeyModifiers, hkExt);
239 end;
241 function TWinShortcut.GetSpecialFolderPath: string;
242 { Returns the full path to the special folder specified in
243   the SpecialFolder property. A backslash is appended to
244   the path. }
245 var
246   ItemIdList: PItemIdList;
247   CharStr: array[0..MAX_PATH] of Char;
248 begin
249   OleCheck(ShGetSpecialFolderLocation(0, FSpecialFolder,
250     ItemIdList));
251   if ShGetPathFromIdList(ItemIdList, CharStr) then
252   begin
253     Result := CharStr;
254     Result := Result + Backslash;
255   end; // if
256 end;
258 procedure TWinShortcut.OpenShortcut;
259 { Opens the shortcut and loads its properties into the
260   component properties. }
261 var
262   FullPath: string;
263 begin
264   FullPath := GetFullShortcutPath;
265   OleCheck(FPersistFile.Load(PWideChar(WideString(
266     FullPath)), STGM_READWRITE));
267   GetPropertiesFromShortcut;
268 end;
270 procedure TWinShortcut.PutPropertiesToShortcut;
271 { Calls the appropriate IShellLink method to assign the
272   value of each of the components properties to the
273   corresponding property of the shortcut. }
274 var
275   HotKeyMods: Byte;
276 begin
277   HotKeyMods := 0;
278   OleCheck(FShellLink.SetPath(PChar(FTarget)));
279   OleCheck(FShellLink.SetIconLocation(PChar(FIconFile),
280     FIconIndex));
281   OleCheck(FShellLink.SetDescription(PChar(FDescription)));
282   OleCheck(FShellLink.SetWorkingDirectory(PChar(
283     FWorkingDirectory)));
284   OleCheck(FShellLink.SetArguments(PChar(FArguments)));
285   OleCheck(FShellLink.SetShowCmd(FRunWindow));
286   if hkShift in FHotKeyModifiers then
287     HotKeyMods := HotKeyMods or HOTKEYF_SHIFT;
288   if hkAlt in FHotKeyModifiers then
289     HotKeyMods := HotKeyMods or HOTKEYF_ALT;
290   if hkCtrl in FHotKeyModifiers then
291     HotKeyMods := HotKeyMods or HOTKEYF_CONTROL;
292   if hkExt in FHotKeyModifiers then
293     HotKeyMods := HotKeyMods or HOTKEYF_EXT;
294   OleCheck(
295     FShellLink.SetHotkey((HotKeyMods shl 8) + HotKey));
296 end;
298 procedure TWinShortcut.SaveShortcut;
299 { Copies the component properties to the shortcut
300   and saves it. }
301 var
302   FullPath: string;
303 begin
304   PutPropertiesToShortcut;
305   FullPath := GetFullShortcutPath;
306   OleCheck(FPersistFile.Save(PWideChar(
307     WideString(FullPath)), True));
308 end;
310 procedure register;
311 begin
312   RegisterComponents('DI', [TWinShortcut]);
313 end;
315 end.

End Listing One

Component Download:

Vote: How useful do you find this Article/Tip?
Bad Excellent
1 2 3 4 5 6 7 8 9 10


Share this page
Download from Google

Copyright © Mendozi Enterprises LLC