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
Shell API
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. 

1   IShellLinkA = interface(IUnknown) { sl. }
2     [SID_IShellLinkA]
3     function GetPath(pszFile: PAnsiChar; cchMaxPath: Integer;
4       var pfd: TWin32FindData; fFlags: DWORD): HResult;
5       stdcall;
6     function GetIDList(var ppidl: PItemIDList): HResult;
7       stdcall;
8     function SetIDList(pidl: PItemIDList): HResult; stdcall;
9     function GetDescription(pszName: PAnsiChar;
10      cchMaxName: Integer): HResult; stdcall;
11    function SetDescription(pszName: PAnsiChar): HResult;
12      stdcall;
13    function GetWorkingDirectory(pszDir: PAnsiChar;
14      cchMaxPath: Integer): HResult; stdcall;
15    function SetWorkingDirectory(pszDir: PAnsiChar): HResult;
16      stdcall;
17    function GetArguments(pszArgs: PAnsiChar;
18      cchMaxPath: Integer): HResult; stdcall;
19    function SetArguments(pszArgs: PAnsiChar): HResult;
20      stdcall;
21    function GetHotkey(var pwHotkey: Word): HResult; stdcall;
22    function SetHotkey(wHotkey: Word): HResult; stdcall;
23    function GetShowCmd(out piShowCmd: Integer): HResult;
24      stdcall;
25    function SetShowCmd(iShowCmd: Integer): HResult; stdcall;
26    function GetIconLocation(pszIconPath: PAnsiChar;
27      cchIconPath: Integer; out piIcon: Integer): HResult;
28      stdcall;
29    function SetIconLocation(pszIconPath: PAnsiChar;
30      iIcon: Integer): HResult; stdcall;
31    function SetRelativePath(pszPathRel: PAnsiChar;
32      dwReserved: DWORD): HResult; stdcall;
33    function Resolve(Wnd: HWND; fFlags: DWORD): HResult;
34      stdcall;
35    function SetPath(pszFile: PAnsiChar): HResult; stdcall;
36  end;
37  //Figure 1: The IShellLink interface. 
39  IPersistFile = interface(IPersist)
40    ['{ 0000010B-0000-0000-C000-000000000046 }']
41    function IsDirty: HResult; stdcall;
42    function Load(pszFileName: POleStr; dwMode: Longint):
43      HResult; stdcall;
44    function Save(pszFileName: POleStr; fRemember: BOOL):
45      HResult; stdcall;
46    function SaveCompleted(pszFileName: POleStr): HResult;
47      stdcall;
48    function GetCurFile(out pszFileName: POleStr): HResult;
49      stdcall;
50  end;
51  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: 

FShellLink := CreateComObject(CLSID_ShellLink) as IShellLink;
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: 


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