Creating Shortcuts in .NET

AID: 3664
  • Status: Published

8015 points

  • Byegl1044
  • TypeGeneral
  • Posted on2010-09-07 at 16:21:02
Awards
  • Community Pick
  • Experts Exchange Approved
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.

IShellLink
http://msdn.microsoft.com/en-us/library/bb774950(VS.85).aspx

The 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:
Activator.CreateInstance(Type.GetTypeFromCLSID(New Guid(CLSID_ShellLink)), True)

                                    
1:

Select allOpen in new window



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.
=====================================================================

Comments

Add your Comment

Please Sign up or Log in to comment on this article.

Join Experts Exchange Today

Gain Access to all our Tech Resources

Get personalized answers

Ask unlimited questions

Access Proven Solutions

Search 3.2 million solutions

Read In-Depth How-To Guides

1000+ articles, demos, & tips

Watch Step by Step Tutorials

Learn direct from top tech pros

And Much More!

Your complete tech resource

See Plans and Pricing

30-day free trial. Register in 60 seconds.

Loading Advertisement...

Top Visual Basic.NET Experts

  1. CodeCruiser

    1,541,075

    Genius

    8,400 points yesterday

    Profile
    Rank: Genius
  2. kaufmed

    303,871

    Wizard

    500 points yesterday

    Profile
    Rank: Genius
  3. Idle_Mind

    230,817

    Guru

    2,010 points yesterday

    Profile
    Rank: Savant
  4. nepaluz

    192,076

    Guru

    0 points yesterday

    Profile
    Rank: Sage
  5. PaulHews

    161,438

    Guru

    520 points yesterday

    Profile
    Rank: Genius
  6. BuggyCoder

    150,598

    Guru

    0 points yesterday

    Profile
    Rank: Sage
  7. JamesBurger

    123,179

    Master

    0 points yesterday

    Profile
    Rank: Sage
  8. emoreau

    112,211

    Master

    0 points yesterday

    Profile
    Rank: Genius
  9. Masteraco

    102,128

    Master

    0 points yesterday

    Profile
    Rank: Wizard
  10. TheLearnedOne

    80,982

    Master

    0 points yesterday

    Profile
    Rank: Savant
  11. Dhaest

    63,803

    Master

    2,000 points yesterday

    Profile
    Rank: Genius
  12. MlandaT

    53,803

    Master

    2,100 points yesterday

    Profile
    Rank: Genius
  13. wdosanjos

    53,796

    Master

    0 points yesterday

    Profile
    Rank: Genius
  14. mlmcc

    53,048

    Master

    0 points yesterday

    Profile
    Rank: Savant
  15. RolandDeschain

    41,679

    10 points yesterday

    Profile
    Rank: Sage
  16. srosebabu

    31,025

    2,000 points yesterday

    Profile
    Rank: Guru
  17. mas_oz2003

    28,400

    0 points yesterday

    Profile
    Rank: Genius
  18. sedgwick

    27,350

    0 points yesterday

    Profile
    Rank: Genius
  19. jacko72

    26,596

    0 points yesterday

    Profile
    Rank: Genius
  20. tommyBoy

    25,850

    0 points yesterday

    Profile
    Rank: Genius
  21. dlmille

    22,160

    0 points yesterday

    Profile
    Rank: Genius
  22. imnorie

    21,664

    1,600 points yesterday

    Profile
    Rank: Genius
  23. Cluskitt

    21,418

    0 points yesterday

    Profile
    Rank: Wizard
  24. robert_schutt

    20,440

    0 points yesterday

    Profile
    Rank: Guru
  25. navneethegde

    20,332

    0 points yesterday

    Profile
    Rank: Wizard

Hall Of Fame