This article will discuss how to create and resolve shortcuts in your programs using Visual Studio .NET
If you haven't required the use of shortcuts yet, you might in the future. For instance, you might want to create a shortcut to your program, or a program-related data folder during installation. The .NET framework currently doesn't expose a method to generate shortcuts directly so you have to implement them yourself.
There are a couple of different ways to create shortcuts with already existing libraries such as the IWshShortcut Interface exposed by the "Windows Scripting Host" but this requires an unnecessary COM reference just to expose this Interface -- not very practical if your not going to use the other classes of the library. The IWshShortcut Interface is really just an exposed IShellLink Interface renamed... so why not skip the middleman?
It's more practical to implement the IShellLink Interface directly using the ComImport() attribute and expose the methods in managed code. This article will discuss just how to do that in .NET so you don't have to depend on the Windows Scripting Host.
IShellLinkhttp://msdn.microsoft.com/en-us/library/bb774950(VS.85).aspxThe first thing we need is a pointer to the IShellLink Interface. In C++ this is typically done by using CoCreateInstance() as in:
// Get a pointer to the IShellLink interface.
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,IID_IShellLink, (LPVOID*)&psl);
1:
2:
Select allOpen in new window
In VB .NET we can use the Activator Class to accomplish the same call:
Because we want a pointer to IShellLink Interface we must also use DirectCast. This internally calls QueryInterface() on the object. To get a pointer to the IShellLink Interface, the above example is changed to the following:
psl = DirectCast(Activator.CreateInstance(Type.GetTypeFromCLSID(New Guid(CLSID_ShellLink)), True), IShellLinkW)
1:
Select allOpen in new window
The Shortcut Class that we'll create below simply wraps the IShellLink Interface and can be used to create or resolve shortcuts rather than depending on the Windows Scripting Host library.
Add a new class file to your project named: Shortcut.vb
Add the following code:
Imports System.Runtime.InteropServices
Imports System.Runtime.InteropServices.ComTypes
Imports System.Text
Public Class Shortcut
' egl1044
Private Const CLSID_ShellLink As String = "00021401-0000-0000-C000-000000000046"
Private Const CLSID_FolderShortcut As String = "0AFACED1-E828-11D1-9187-B532F1E9575D"
Private dataBuffer As New StringBuilder(260)
Public Enum LinkType As Integer
File
Folder
End Enum
Public Enum WindowStyle As Integer
Normal = 1
Maximized = 3
ShowMinNoActive = 7
End Enum
Private psl As IShellLinkW = Nothing
Private ppf As IPersistFile = Nothing
Public Sub New(ByVal shortcutlinkType As LinkType)
' Get a pointer to the IShellLink interface.
Select Case shortcutlinkType
Case LinkType.File
psl = DirectCast(Activator.CreateInstance(Type.GetTypeFromCLSID(New Guid(CLSID_ShellLink)), True), IShellLinkW)
Case LinkType.Folder
psl = DirectCast(Activator.CreateInstance(Type.GetTypeFromCLSID(New Guid(CLSID_FolderShortcut)), True), IShellLinkW)
End Select
End Sub
Protected Overrides Sub Finalize()
Me.Release()
MyBase.Finalize()
End Sub
Public Sub Save(ByVal pszFileName As String)
' Get a pointer to the IPersistFile interface.
ppf = DirectCast(psl, IPersistFile)
ppf.Save(pszFileName, True)
End Sub
Public Sub Load(ByVal pszFileName As String)
' Get a pointer to the IPersistFile interface.
ppf = DirectCast(psl, IPersistFile)
ppf.Load(pszFileName, 0)
psl.Resolve(IntPtr.Zero, 0)
End Sub
Public Function SetPath(ByVal pszFile As String) As Integer
Return psl.SetPath(pszFile)
End Function
Public Function SetDescription(ByVal pszName As String) As Integer
Return psl.SetDescription(pszName)
End Function
Public Function SetWorkingDirectory(ByVal pszDir As String) As Integer
Return psl.SetWorkingDirectory(pszDir)
End Function
Public Function SetIconLocation(ByVal pszIconPath As String, ByVal iconIndex As Integer) As Integer
Return psl.SetIconLocation(pszIconPath, iconIndex)
End Function
Public Function SetArguments(ByVal pszArgs As String) As Integer
Return psl.SetArguments(pszArgs)
End Function
Public Function SetShowCmd(ByVal showCmd As WindowStyle) As Integer
Return psl.SetShowCmd(showCmd)
End Function
Public Function SetHotKey(ByVal wHotKey As Short) As Integer
Return psl.SetHotkey(wHotKey)
End Function
Public Function GetPath() As String
psl.GetPath(dataBuffer, dataBuffer.Capacity, IntPtr.Zero, 0)
Return dataBuffer.ToString
End Function
Public Function GetArguments() As String
psl.GetArguments(dataBuffer, dataBuffer.Capacity)
Return dataBuffer.ToString
End Function
Public Function GetDescription() As String
psl.GetDescription(dataBuffer, dataBuffer.Capacity)
Return dataBuffer.ToString
End Function
Public Function GetIconLocation() As ShortcutIconInfo
Dim iconIndex As Integer
psl.GetIconLocation(dataBuffer, dataBuffer.Capacity, iconIndex)
Return New ShortcutIconInfo(dataBuffer.ToString, iconIndex)
End Function
Public Function GetWorkingDirectory() As String
psl.GetWorkingDirectory(dataBuffer, dataBuffer.Capacity)
Return dataBuffer.ToString
End Function
Public Function GetShowCommand() As Integer
Dim pShowCmd As Integer
psl.GetShowCmd(pShowCmd)
Return pShowCmd
End Function
Public Function GetHotkey() As Short
Dim pHotKey As Short
psl.GetHotkey(pHotKey)
Return pHotKey
End Function
Public Sub Release()
If ppf IsNot Nothing Then
Marshal.FinalReleaseComObject(ppf)
ppf = Nothing
End If
If psl IsNot Nothing Then
Marshal.FinalReleaseComObject(psl)
psl = Nothing
End If
End Sub
Public Class ShortcutIconInfo
Private _iconLocation As String = String.Empty
Private _iconIndex As Integer = 0
Protected Friend Sub New(ByVal iconLocation As String, ByVal iconIndex As Integer)
Me._iconLocation = iconLocation
Me._iconIndex = iconIndex
End Sub
Public ReadOnly Property Location As String
Get
Return _iconLocation
End Get
End Property
Public ReadOnly Property Index As Integer
Get
Return _iconIndex
End Get
End Property
End Class
End Class
''' <summary>
''' IShellLinkW Interface
''' http://msdn.microsoft.com/en-us/library/bb774950(VS.85).aspx
''' </summary>
''' <remarks>This interface cannot be used to create a link to a URL.</remarks>
<ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), _
Guid("000214F9-0000-0000-C000-000000000046")> _
Public Interface IShellLinkW
<PreserveSig()> _
Function GetPath(<MarshalAs(UnmanagedType.LPWStr)> ByVal pszFile As StringBuilder, ByVal cchMaxPath As Integer, ByVal pfd As IntPtr, ByVal fFlags As Integer) As Integer
<PreserveSig()> _
Function GetIDList(ByRef ppidl As IntPtr) As Integer
<PreserveSig()> _
Function SetIDList(ByVal pidl As IntPtr) As Integer
<PreserveSig()> _
Function GetDescription(<MarshalAs(UnmanagedType.LPWStr)> ByVal pszName As StringBuilder, ByVal cchMaxName As Integer) As Integer
<PreserveSig()> _
Function SetDescription(<MarshalAs(UnmanagedType.LPWStr)> ByVal pszName As String) As Integer
<PreserveSig()> _
Function GetWorkingDirectory(<MarshalAs(UnmanagedType.LPWStr)> ByVal pszDir As StringBuilder, ByVal cchMaxPath As Integer) As Integer
<PreserveSig()> _
Function SetWorkingDirectory(<MarshalAs(UnmanagedType.LPWStr)> ByVal pszDir As String) As Integer
<PreserveSig()> _
Function GetArguments(<MarshalAs(UnmanagedType.LPWStr)> ByVal pszArgs As StringBuilder, ByVal cchMaxPath As Integer) As Integer
<PreserveSig()> _
Function SetArguments(<MarshalAs(UnmanagedType.LPWStr)> ByVal pszArgs As String) As Integer
<PreserveSig()> _
Function GetHotkey(ByRef pwHotkey As Short) As Integer
<PreserveSig()> _
Function SetHotkey(ByVal wHotkey As Short) As Integer
<PreserveSig()> _
Function GetShowCmd(ByRef piShowCmd As Integer) As Integer
<PreserveSig()> _
Function SetShowCmd(ByVal iShowCmd As Integer) As Integer
<PreserveSig()> _
Function GetIconLocation(<MarshalAs(UnmanagedType.LPWStr)> ByVal pszIconPath As StringBuilder, ByVal cchIconPath As Integer, ByRef piIcon As Integer) As Integer
<PreserveSig()> _
Function SetIconLocation(<MarshalAs(UnmanagedType.LPWStr)> ByVal pszIconPath As String, ByVal iIcon As Integer) As Integer
<PreserveSig()> _
Function SetRelativePath(<MarshalAs(UnmanagedType.LPWStr)> ByVal pszPathRel As String, ByVal dwReserved As Integer) As Integer
<PreserveSig()> _
Function Resolve(ByVal hWnd As IntPtr, ByVal fFlags As Integer) As Integer
<PreserveSig()> _
Function SetPath(<MarshalAs(UnmanagedType.LPWStr)> ByVal pszFile As String) As Integer
End Interface
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
Select allOpen in new window
Using the Shortcut Class
When creating a shortcut to a File or Folder you must always call the
SetPath() method. This is either the location of a file or the path to a folder that the shortcut will execute when the user double-clicks it (or will act as a drop-target when a user drags a file and drops it on the shortcut's icon).
In the constructor you tell the class you either want to create a shortcut to a File or Folder and call the other various methods afterwards. Finally, call the
Save() method specifying the
.LNK file to create.
Note: In the following examples, the .LNK file is arbitrarily saved into the root of drive G:. It would be more common to create .LNK files on the desktop or in a tools folder. The desktop of the current user is:
%USERPROFILE%\Desktop
which the system resolves to, for instance,
C:\Users\egl1044\Desktop
Creating a simple
file shortcut (it will execute Notepad.Exe):
Dim sc As New Shortcut(Shortcut.LinkType.File)
sc.SetPath("c:\windows\system32\notepad.exe")
sc.Save("g:\notepad.lnk")
1:
2:
3:
Select allOpen in new window
Creating a simple folder shortcut (it will open Windows Explorer on that folder):
Dim sc As New Shortcut(Shortcut.LinkType.Folder)
sc.SetPath("c:\windows\system32")
sc.Save("g:\system32")
1:
2:
3:
Select allOpen in new window
Creating a shortcut with a description (a tooltip: useful text displayed when the user hovers the mouse over it)
Dim sc As New Shortcut(Shortcut.LinkType.File)
sc.SetPath("c:\windows\system32\notepad.exe")
sc.SetDescription("notepad shortcut")
sc.Save("g:\notepad.lnk")
1:
2:
3:
4:
Select allOpen in new window
Creating a shortcut with command-line arguments (it starts Notepad and opens a text file):
Dim sc As New Shortcut(Shortcut.LinkType.File)
sc.SetPath("c:\windows\system32\notepad.exe")
sc.SetDescription("notepad shortcut")
sc.SetArguments("test.txt")' notepad executes with this argument
sc.Save("g:\notepad.lnk")
1:
2:
3:
4:
5:
Select allOpen in new window
Creating a shortcut and changing the icon:
Dim sc As New Shortcut(Shortcut.LinkType.File)
sc.SetPath("c:\windows\system32\notepad.exe")
sc.SetDescription("notepad shortcut")
sc.SetArguments("test.txt")
sc.SetIconLocation("c:\windows\system32\shell32.dll", 12)
sc.Save("g:\notepad.lnk")
1:
2:
3:
4:
5:
6:
Select allOpen in new window
Resolving Shortcuts
To resolve a shortcut (and learn what it will open) you must call the Load() method and specify the location to the shortcut file.
Dim sc As New Shortcut(Shortcut.LinkType.File)
sc.Load("g:\notepad.lnk")
Debug.Print(sc.GetPath())
Debug.Print(sc.GetDescription)
Debug.Print(sc.GetArguments)
Dim sci As Shortcut.ShortcutIconInfo = sc.GetIconLocation
Debug.Print(sci.Location)
Debug.Print(sci.Index)
1:
2:
3:
4:
5:
6:
7:
8:
Select allOpen in new window
By using the Shortcut Class wrapper you no longer should have to depend on the Windows Scripting Host. You may use this class "as is" or use the examples to create your own custom class to suit your requirements using the examples in this article. Enjoy!
=====================================================================
If you found this article helpful you can vote by clicking on "Yes" where it says
"Was this article helpful" just below this message.
=====================================================================