Visual Basic Win32 Shell Routines
SHAddToRecentDocs: Create an App Shortcut (Workaround - Pre-XP)
Posted:   Sunday January 26, 1997
Updated:   Monday December 26, 2011
Applies to:   VB4-32, VB5, VB6
Developed with:   VB4-32, Windows 95
OS restrictions:   None
Author:   VBnet - Randy Birch


SHBrowseForFolder: Browse for Folders Overview
SHBrowseForFolder: Browse for Folders Dialog
SHFileOperation: Add Recycle Bin Functionality
SHFileOperation: Copy, Move or Delete Files
SHFileOperation: Copy or Move an Entire Directory
SHAddToRecentDocs: Add Entries to Recent Documents List
Windows 9x through Windows 2000.

Note: Windows XP does not allow shortcuts to exes to be created in the Recent Files folder, which this workaround relies on.

Or maybe hack is a better description of how this method achieves the desired results.

Various code pages show how devs can utilize the SHGetSpecialFolderLocation, SHGetPathFromIDList, SHFileOperation and SHAddToRecentDocs shell APIs to manipulate the files and directories normally exposed through the iShell interface. On pre-XP machines the principles exposed by these methods (plus a little sneakery) you can do the one thing that has eluded VB programmers thus far - creating shortcuts and placing them wherever desired!

Before I get a flood of email, Yes!, I know that the setup kit uses a DLL routine to create start menu folders.  But one of its huge limitations is its inability to traverse drives. Coupled with the requirement of using relative paths rather than absolute paths in providing the navigation path back from the default Start Menu folder to the (hopefully) final resting place of the setup DLL-created shortcuts, the setup DLL method becomes quite burdensome, not to mention prone to path errors where the user's Windows or application installation is non-typical.

The method shown here is actually quite simple. SHAddToRecentDocs creates a shortcut on the fly and adds it to the user's recent files (Documents) listing on the Start menu. SHFileOperation API will move and rename files or folders across drives, and SHGetSpecialFolderLocation will provide the current user's "special" system paths including the Start Menu and Recent Files path, among others.

Therefore, the code below uses SHAddToRecentDocs to create the shortcuts for us, SHGetSpecialFolderLocation to find the appropriate folders for the given system, and SHFileOperation to create a folder, move, and then rename those shortcuts. The result is you can now create any shortcut you want, to any file you want, and place it anywhere on the user's system.

Although any folder or drive could have been specified, I chose to demo this as a means for installing three "fake" program files (the executable, the help file, and a readme) into a fake Start Menu\Programs\MyApp folder. This is a transparent routine with no interface -- the form illustrated simply shows the output of the debug text as the routine executes. Therefore, to create this project, simply start a new project, add a command button (Command1) to a form, and add an empty BAS module to hold the API declare code.

 BAS Module Code
Place the following API declare code into the general declarations area of a bas module. If this is a one-form project, the declares below could be placed into the general declaration section of the form instead, with all Public references changed to Private.

Option Explicit
' Copyright 1996-2011 VBnet/Randy Birch, All Rights Reserved.
' Some pages may also contain other copyrights by the author.
' Distribution: You can freely use this code in your own
'               applications, but you may not reproduce 
'               or publish this code on any web site,
'               online service, or distribute as source 
'               on any media without express permission.
   hWnd        As Long
   wFunc       As Long
   pFrom       As String
   pTo         As String
   fFlags      As Integer
   fAborted    As Boolean
   hNameMaps   As Long
   sProgress   As String
 End Type
Public Const FO_MOVE = &H1
Public Const FO_RENAME = &H4

Public Const FOF_SILENT = &H4
Public Const FOF_FILESONLY = &H80

Public Const SHARD_PATH = &H2& 

Public Declare Function SHAddToRecentDocs Lib "shell32" _
  (ByVal dwFlags As Long, _
   ByVal dwData As String) As Long

Public Declare Function SHFileOperation Lib "shell32" _
   Alias "SHFileOperationA" _
  (lpFileOp As SHFILEOPSTRUCT) As Long

Public Declare Function SHGetPathFromIDList Lib "shell32" _
   Alias "SHGetPathFromIDListA" _
  (ByVal pidl As Long, _
   ByVal pszPath As String) As Long
Public Declare Function SHGetSpecialFolderLocation Lib "shell32" _
  (ByVal hwndOwner As Long, _
   ByVal nFolder As Long, _
     pidl As Long) As Long
 Form Code
Paste the following code into the general declarations section of Form1 containing a command button:

Option Explicit

Private Sub Command1_Click()

   Dim i As Integer
   Dim FolderPath As String
   Dim StartMenuPath As String
   Dim fNameOld As String
   Dim fNameNew As String
   Dim fNames() As String
   ReDim fNames(1 To 3) As String
   Const CSIDL_RECENT = &H8

  'Lets do some preparation before actually
  'doing any disk activities.
  'We'll need 2 things....
  '- The path to the user's start menu folder.  This is
  '  needed because different users may use different
  '  paths for the current menu (i.e. on my Win95 system,
  '  my Windows folder is c:\win); Win NT4 uses profiles
  '  so each user has their own. Therefore, call the
  '  SHGetSpecialFolderLocation which is encapsulated
  '  in the GetRecentFolders() function.
  '- We also need the user's Start Menu folder, for the
  '  same reasons.  
   FolderPath = GetSpecialFolder(CSIDL_RECENT)
   StartMenuPath = GetSpecialFolder(CSIDL_STARTMENU)
   If FolderPath = "" Or StartMenuPath = "" Then
      MsgBox "We canne' do it Captain: Error retrieving folder paths."
      Exit Sub
   End If
  'for debugging - print the resulting strings  
   Print FolderPath
   Print StartMenuPath
  'set up the StartMenuPath now to reflect the folder
  'we'll be installing the shortcuts into a bit later  
   StartMenuPath = StartMenuPath & "Programs\MyApp\"
  'These are the three fictional files to create shortcuts
  'for. They represent typical files you might
  'provide with your app. To make things even more
  'complicated, point to a drive that doesn't host Windows.
  'Note that SHAddToRecentDocs doesn't care if the file actually 
  'exists at this point; it is not resolved until the shortcut 
  'is accessed.  
   fNames(1) = "d:\MyApp\Thunder.exe"
   fNames(2) = "d:\MyApp\Thunder Help.hlp"
   fNames(3) = "d:\MyApp\Thunder Readme.txt"
  'The first step is to get these files added to the
  'Recent Files list.  The SHAddToRecentDocs API
  'makes this a simple task.  
   Call SHAddToRecentDocs(SHARD_PATH, fNames(1))
   Call SHAddToRecentDocs(SHARD_PATH, fNames(2))
   Call SHAddToRecentDocs(SHARD_PATH, fNames(3))
  'At this point, if you check the Start\Documents menu, 
  'the 3 files above will be listed.

  'Since we now know the shortcuts exist in the documents
  'folder, and the path to both that folder and the users
  'start menu, we can move the shortcuts from the recent
  'folder into our new application folder using the
  'SHFileOperation API.  Conveniently, SHFileOperation
  'will even create out new folder for us if it doesn't
  'exist!  And by specifying the flags FOF_SILENT and
  'FOF_NOCONFIRMATION, messages and dialogs indicating
  'this is happening will be suppressed.
  'Because the shortcuts now reside in the Recent folder,
  'we need to modify the file array to include the Recent
  'folder path. In addition, the extension now has .lnk 
  'appended to the original filename.  
   fNames(1) = FolderPath & "Thunder.exe.lnk"
   fNames(2) = FolderPath & "Thunder Help.hlp.lnk"
   fNames(3) = FolderPath & "Thunder Readme.txt.lnk"
  'for debugging - do a DIR against the shortcuts
  'to confirm they do exist  
   For i = 1 To 3
      Print Dir(fNames(i))
  'call SHFileOperation to move the shortcuts  
   ShellMoveFiles fNames(), StartMenuPath
  'Now, if desired, we can rename the links to
  'something more traditional for a start menu item.
  'The FO_RENAME flag in ShellRenameFile requires
  'that we do this one file at a time.  
   fNameOld = StartMenuPath & "Thunder.exe.lnk"
   fNameNew = StartMenuPath & "Whoh! Baby! Thunder.lnk"
   ShellRenameFile fNameOld, fNameNew
   fNameOld = StartMenuPath & "Thunder Help.hlp.lnk"
   fNameNew = StartMenuPath & "MyApp Thunder Help.lnk"
   ShellRenameFile fNameOld, fNameNew
   fNameOld = StartMenuPath & "Thunder Readme.txt.lnk"
   fNameNew = StartMenuPath & "MyApp Readme.lnk"
   ShellRenameFile fNameOld, fNameNew
End Sub

Private Sub ShellMoveFiles(sFileArray() As String, sDestination As String)
  'set working variables  
   Dim i As Integer
   Dim sFiles As String
  'create a single string of files from the passed file array,
  'each separated by Chr$(0)  
   For i = LBound(sFileArray) To UBound(sFileArray)
      sFiles = sFiles & sFileArray(i) & Chr$(0)
  'add a final terminating null  
   sFiles = sFiles & Chr$(0)
  'for debugging - print the resulting strings  
   Print sFiles
  'set up the options  
   With SHFileOp
      .wFunc = FO_MOVE
      .pFrom = sFiles
      .pTo = sDestination
   End With
  'and perform the move.  Because the folder specified 
  'doesn't exist, SHFileOperation will create it. FOF_SILENT 
  'above instructs the API to suppress displaying the "flying 
  'folders" dialog during the move.  FOF_NOCONFIRMATION suppresses 
  'prompting to move the files ... the "Are you sure you want to
  'move etc..." dialog. FOF_NOCONFIRMMKDIR instructs it to 
  'create the folder without prompting if it is OK.
   Call SHFileOperation(SHFileOp)

End Sub

Private Function GetSpecialFolder(CSIDL As Long) As String
  'a few local variables needed  
   Dim sPath As String
   Dim pidl As Long
   Const ERROR_SUCCESS = 0
   Const MAX_LENGTH = 260
  'fill pidl with the specified folder item   
   If SHGetSpecialFolderLocation(Me.hWnd, CSIDL, pidl)= ERROR_SUCCESS Then
     'Of the structure is filled, initialize and
     'retrieve the path from the id list, and return
     'the folder with a trailing slash appended.  
      sPath = Space$(MAX_LENGTH)
      If SHGetPathFromIDList(ByVal pidl, ByVal sPath) Then
         GetSpecialFolder = Left$(sPath, _
                            InStr(sPath, Chr$(0)) - 1) & "\"
      End If
   End If

End Function

Private Sub ShellRenameFile(sOldName As String, sNewName As String)
  'set working variables  
  'add a pair of terminating nulls to each string  
   sOldName = sOldName & Chr$(0) & Chr$(0)
   sNewName = sNewName & Chr$(0) & Chr$(0)
  'for debugging - print the resulting strings  
   Print sOldName
   Print sNewName
  'set up the options  
   With SHFileOp
      .wFunc = FO_RENAME
      .pFrom = sOldName
      .pTo = sNewName
   End With
  'and rename the file  
   Call SHFileOperation(SHFileOp)

End Sub
Open two copies of explorer; one at the Recent files folder, the other at the Start Menu\Programs folder. Run the project, hit the command button and enjoy! Once you understand the mechanics of what's going on, try creating shortcuts elsewhere using this technique. See the Browse For Folders Overview for a complete list of the special folders you can obtain with the GetSpecialFolder() routine.

NOTE: with the introduction of Windows XP exe files can no longer be assigned to SHAddToRecentDocs. No error will occur, but the shortcut to the exe will not be created.

And do NOT even think of pointing the shortcuts to any of Windows' "virtual" folder locations - the recycle bin (CSIDL_BITBUCKET), the Desktop (CSIDL_DESKTOP - use CSIDL_DESKTOPDIRECTORY instead), the control panel (CSIDL_CONTROLS), printers (CSIDL_PRINTERS), the network (CSIDL_NETWORK) or the fonts folder (CSIDL_FONTS) using this routine.


PayPal Link
Make payments with PayPal - it's fast, free and secure!


Copyright 1996-2011 VBnet and Randy Birch. All Rights Reserved.
Terms of Use  |  Your Privacy


Hit Counter