Click here to Skip to main content
15,880,796 members
Articles / Programming Languages / C++
Article

How to create short-cuts (link files)

Rate me:
Please Sign up or sign in to vote.
4.55/5 (18 votes)
28 Aug 200511 min read 224.7K   9.7K   54   25
An article on using the Win32 API and COM to create short-cuts in existing and developmental languages.

Introduction

This topic describes how to use the Windows API and COM library to create short-cut files (link files). These files have the extension .lnk and are used for linking to EXE files, document files and other types of file. They store the name of the target file, the logged folder to use when the target file is run, command line arguments passed to the target file, and other information. Short-cuts have many uses:

  • They can be created with path names and arguments of commonly-run programs and documents. The short-cut can be double clicked or run at a later time, making it possible to start the program or document without re-entering the full path name and arguments.
  • They are used in the menus shown when Start - Programs is clicked, since each menu item is actually a short-cut file. To add a new menu item, create a short-cut to the target file, then copy the short-cut to the Start menu\programs folder or sub-folders. This causes the short-cut to be added to the menus, and if the new short-cut is selected from the menu, the target file is run.
  • They can be added to the Windows Desktop folder to make the short-cut available on the desktop.
  • They can be added to the Windows Start menu\programs\startup folder. This makes the target program run whenever the computer starts.
  • When programs are installed on a new computer, short-cut files can also be installed on the new computer, in the Windows Start menu\programs folder or sub-folders. This makes the installed program available from the Start button on the new computer.

Short-cut files are small binary files (very) approximately 1000 bytes in size. They are stored using a Microsoft proprietary binary format, and are created and modified using Windows API COM (Component Object Model) functions.

The remainder of this topic describes the fields in the short-cut, how to create a short-cut, and a sample program that creates short-cuts. Because this is a Windows API article, this topic concludes by discussing how to create short-cuts in other languages that don't offer direct COM support. Short-cut creation is discussed using Visual Basic and Ubercode - these discussions are relevant because they show that COM and the Windows API are truly language-independent concepts, and can be used by new and existing computer languages.

Anatomy of a short-cut

It's easy to understand a short-cut by viewing its properties from Windows Explorer. Use Windows Explorer to find a short-cut file (e.g. by searching for files with the extension .lnk), click the right mouse button on the short-cut file, choose Properties from the menu, and then click on Shortcut in the Properties dialog. The short-cut properties look like this (the exact details vary with different versions of Windows):

Image 1

The short-cut dialog shows the most important fields, but bear in mind short-cuts have extra fields that are not shown in the dialog. Here is the complete list of fields:

  • Target type - This is the type of the target file, and is implied by the target file's extension. The most common types of target file will be .exe (application), .doc (document), .txt (text), however many others are possible. Short-cuts can also be created to folders, disk drives and network drives.
  • Target location - This is the folder containing the target file.
  • Target - This is the file name of the target of the short-cut. This should be a full path name, and the file should exist. The target file may be followed by optional command line arguments enclosed in double quotes. If the target file does not exist, Windows will attempt to find it when the short-cut is run.
  • Start in - When the short-cut is run, this folder is used as the working directory of the target file. If this is an empty string the initial directory is not defined.
  • Shortcut key - This is a hot-key combination that may be set up under Windows to run the target file directly. This is normally left at none.
  • Run - This determines whether the target file runs as a normal size window, or as a maximized or minimized window. This is actually the ShowWindow() constant used when the target file is executed, and should be one of:
    • Normal window = SW_SHOWNORMAL (1).
    • Maximized = SW_SHOWMAXIMIZED (3).
    • Minimized = SW_SHOWMINNOACTIVE (7).

    Other values are ignored by Windows. Also note that some target files ignore this information because they define their preferred window size when they start.

  • Comment - This is the description of the target file. This is not shown in all versions of Windows, and the description is optional.
  • Icon - If the Change icon button is clicked, you can select the file name of the icon file used for the link. Typically this is a Windows .ico (icon) file, or a .dll (dynamic link library) file. A .dll file may contain multiple icons, so an icon index is also required when displaying an icon from the DLL. The short-cut icon file customizes the appearance of the short-cut when shown on the desktop or on the start menu.

Most of the fields in this list can be set by code when the short-cut file is created. This is shown next.

Using the code

Short-cuts are created using the Windows COM (Component Object Model) library. Functions in the COM library are available to programs written in C, C++, VBScript, JavaScript and other languages. The sample code here is written in C and is based on an MSDN article and sample code from this site. The short-cut is created using the following steps:

  1. Use CoInitialize() to initialize the COM library.
  2. Use CoCreateInstance() to create an IShellLink object. This object represents the short-cut stored in memory, and its fields are then set as required.
  3. Use QueryInterface() to create an IPersistFile object, required for saving the short-cut to disk. After the IPersistFile object is created, it is used for saving the short-cut to a file.
  4. After saving the short-cut, use Release() to close the two objects.
  5. Finally, call CoUninitialize() to close the COM library.

The most important steps are 2, 3 and 4 given above. These are shown in the CreateShortCut() function shown next:

/*
-------------------------------------------------------------------
Description:
  Creates the actual 'lnk' file (assumes COM has been initialized).

Parameters:
  pszTargetfile    - File name of the link's target.
  pszTargetargs    - Command line arguments passed to link's target.
  pszLinkfile      - File name of the actual link file being created.
  pszDescription   - Description of the linked item.
  iShowmode        - ShowWindow() constant for the link's target.
  pszCurdir        - Working directory of the active link.
  pszIconfile      - File name of the icon file used for the link.
  iIconindex       - Index of the icon in the icon file.

Returns:
  HRESULT value >= 0 for success, < 0 for failure.
--------------------------------------------------------------------
*/
static HRESULT CreateShortCut(LPSTR pszTargetfile, LPSTR pszTargetargs,
                              LPSTR pszLinkfile, LPSTR pszDescription,
                              int iShowmode, LPSTR pszCurdir,
                              LPSTR pszIconfile, int iIconindex)
{
  HRESULT       hRes;                  /* Returned COM result code */
  IShellLink*   pShellLink;            /* IShellLink object pointer */
  IPersistFile* pPersistFile;          /* IPersistFile object pointer */
  WORD          wszLinkfile[MAX_PATH]; /* pszLinkfile as Unicode
                                          string */
  int           iWideCharsWritten;     /* Number of wide characters
                                          written */

  hRes = E_INVALIDARG;
  if (
       (pszTargetfile != NULL) && (strlen(pszTargetfile) > 0) &&
       (pszTargetargs != NULL) &&
       (pszLinkfile != NULL) && (strlen(pszLinkfile) > 0) &&
       (pszDescription != NULL) &&
       (iShowmode >= 0) &&
       (pszCurdir != NULL) &&
       (pszIconfile != NULL) &&
       (iIconindex >= 0)
     )
  {
    hRes = CoCreateInstance(
      &CLSID_ShellLink,     /* pre-defined CLSID of the IShellLink
                               object */
      NULL,                 /* pointer to parent interface if part of
                               aggregate */
      CLSCTX_INPROC_SERVER, /* caller and called code are in same
                               process */
      &IID_IShellLink,      /* pre-defined interface of the
                               IShellLink object */
      &pShellLink);         /* Returns a pointer to the IShellLink
                               object */
    if (SUCCEEDED(hRes))
    {
      /* Set the fields in the IShellLink object */
      hRes = pShellLink->lpVtbl->SetPath(pShellLink,
                                           pszTargetfile);
      hRes = pShellLink->lpVtbl->SetArguments(pShellLink,
                                            pszTargetargs);
      if (strlen(pszDescription) > 0)
      {
        hRes = pShellLink->lpVtbl->SetDescription(pShellLink,
                                                pszDescription);
      }
      if (iShowmode > 0)
      {
        hRes = pShellLink->lpVtbl->SetShowCmd(pShellLink,
                                                     iShowmode);
      }
      if (strlen(pszCurdir) > 0)
      {
        hRes = pShellLink->lpVtbl->SetWorkingDirectory(pShellLink,
                                                        pszCurdir);
      }
      if (strlen(pszIconfile) > 0 && iIconindex >= 0)
      {
        hRes = pShellLink->lpVtbl->SetIconLocation(pShellLink,
                                          pszIconfile, iIconindex);
      }

      /* Use the IPersistFile object to save the shell link */
      hRes = pShellLink->lpVtbl->QueryInterface(
        pShellLink,                /* existing IShellLink object */
        &IID_IPersistFile,         /* pre-defined interface of the
                                      IPersistFile object */
        &pPersistFile);            /* returns a pointer to the
                                      IPersistFile object */
      if (SUCCEEDED(hRes))
      {
        iWideCharsWritten = MultiByteToWideChar(CP_ACP, 0,
                                             pszLinkfile, -1,
                                             wszLinkfile, MAX_PATH);
        hRes = pPersistFile->lpVtbl->Save(pPersistFile,
                                                 wszLinkfile, TRUE);
        pPersistFile->lpVtbl->Release(pPersistFile);
      }
      pShellLink->lpVtbl->Release(pShellLink);
    }

  }
  return (hRes);
}

The code works as follows. Firstly the inputs are validated, to avoid program errors caused by NULL pointers or missing arguments. Assuming the inputs are valid, CoCreateInstance() is called to create the IShellLink object.

If IShellLink was successfully created, the fields in the short-cut can be set. This is done by SetPath(), SetArguments(), SetDescription(), SetShowCmd(), SetWorkingDirectory(), and SetIconLocation(). Some of these fields are optional, for example if no description was passed into the function, SetDescription() is not called.

After setting the fields, the short-cut exists only in memory. The next step is to use QueryInterface() to create the IPersistFile object. The call to QueryInterface() (instead of CoCreateInstance()) connects IPersistFile to the IShellLink object. This means IPersistFile knows what to save when it is told to write to a file.

After IPersistFile was created, the name of the short-cut file (pszLinkfile) is converted to a Unicode string. Then IPersistFile's Save() method is called, which saves the short-cut to an actual file. The end result is the short-cut which now exists as a .lnk file on the disk.

Before the function returns, both objects are tidied up by calling their Release() functions. This frees any memory used and closes any files. The function returns the HRESULT value produced by the COM functions - this is a positive integer value or zero indicating success, or a negative value if the functions have failed. HRESULT values can be tested with the SUCCEEDED macro.

Sample program

This topic also includes a sample program for creating short-cuts, provided as C source code. The file has no external dependencies other than the Windows API, so it can be compiled by creating a new console-mode project, adding the source file and compiling it. The compiled program makelink.exe should run under any version of Windows, from Windows 95 onwards. When the sample program is run without any parameters, it shows a help screen as follows:

Image 2

The help screen shows how the program is called up. For example if you type:

makelink c:\windows\notepad.exe "" mylink.lnk ""

this creates a short-cut file mylink.lnk in the current directory. The empty strings "" represent the arguments and description which are not required. When mylink.lnk is double-clicked, it runs the Notepad program. As another example if you type:

makelink c:\windows\write.exe c:\windows\tips.txt mylink.lnk ""

this creates a short-cut file mylink.lnk in the current directory. When activated, the short-cut will run the Windows write.exe program and make it open the Windows tips file.

If you read through the source code of the sample program, you will see the following functions:

  • ShowHelp() - shows the command line arguments.
  • ShowError() - this is called if an error occurs. It prints the error details to the console window.
  • GetArguments() - this parses the command line arguments passed to the program.
  • CreateShortCut() - function that creates the short-cut file, as shown previously.
  • main() - checks the command line arguments. If -h was used, or if the arguments are invalid, this shows the help page and quits. If the arguments are valid, this calls CreateShortCut() to create the short-cut file.

Short-cuts in other languages - Ubercode

It is interesting to see how short-cut are created using other computer languages. The main complication is that not all languages allow direct calls to the COM library. Programs in these languages must either provide link creation code as an inbuilt function in the language, or must use other techniques to access the COM library.

For example Ubercode is a modern computer language with ease-of-use as its main objective. Ubercode syntax is as simple as possible, consistent with being useful for real-world applications, and consistent with being compilable to Windows EXE files and allowing simple distribution to other computers. To implement short-cuts, Ubercode includes built-in library functions that directly permit the creation of short-cuts. Here is a sample program:

Ubercode 1 class MakeShortCut  

  public function main()
  var
    linkfile:string[*]
    result:integer(0:MAXINT)
  code
    linkfile <- "mylink.lnk"
    result <- FileCreateShortcut("c:\windows\notepad.exe", 
                                             "", linkfile, "")
    if SUCCEEDED(result) then
      call Msgbox("MakeShortCut",
                  "Return code = " + Str(result) + NL +
                  "Successfully created " + linkfile)
    else
      call Sound("")
      call Msgbox("MakeShortCut",
                  "Return code = " + Str(result) + NL +
                  "Could not create " + linkfile)
    end if
  end function

end class

As can be seen, Ubercode syntax is similar to JavaScript and to C#. The program consists of a single class MakeShortCut containing a single function main. The class is therefore a main class that compiles to an EXE file. The code in function main works as follows:

First the name of the short-cut file is copied to a string variable, then FileCreateShortcut() is called. This is an inbuilt library function that takes the same argument as the makelink program shown previously (Ubercode allows functions to have optional arguments). After FileCreateShortcut() returns, the result of the call is checked, and a success or failure message is shown. When the program terminates, the short-cut file will exist on disk.

If you want to experiment more with this code and with the Ubercode Developer Environment, it can be downloaded from the Ubercode website.

Short-cuts in other languages - Visual Basic

Most versions of Microsoft Visual Basic provide direct access to the COM library, so short-cuts can be created using Visual Basic. Here is a program that creates a short-cut. This example is loosely based on the Microsoft Windows Script Technologies help file v5.6, under the topic Creating a shortcut. This example has been tested with VBScript v5.6, and should also run under Visual Basic version 5 and version 6:

VBScript
' Make sure variables are declared.
option explicit

' Routine to create "mylink.lnk" on the Windows desktop.
sub CreateShortCut()
  dim objShell, strDesktopPath, objLink
  set objShell = CreateObject("WScript.Shell")
  strDesktopPath = objShell.SpecialFolders("Desktop")
  set objLink =
    objShell.CreateShortcut(strDesktopPath & "\mylink.lnk")
  objLink.Arguments = "c:\windows\tips.txt"
  objLink.Description = "Shortcut to Notepad.exe"
  objLink.TargetPath = "c:\windows\notepad.exe"
  objLink.WindowStyle = 1
  objLink.WorkingDirectory = "c:\windows"
  objLink.Save
end sub

' Program starts running here.
call CreateShortCut()

This works as follows. The program starts running at the first line of module-level code (i.e. code not in a function or subroutine), which is the line of code call CreateShortCut(). The CreateShortCut() routine declares its variables, then creates the COM object objShell using "WScript.Shell". The string variable strDesktopPath is initialized with the full path to the Windows desktop. The call objShell.CreateShortcut creates an actual IShellLink object which is stored in the objLink variable. The fields of objLink are set, and these fields have the same meanings and values as in the short-cut properties dialog shown previously. Refer to the section Anatomy of a short-cut for more details on the fields.

After setting the fields, objLink.Save uses the IPersistFile object to save the short-cut file to disk. Visual Basic automatically cleans up COM objects, so it is not necessary to call the Release method for the objects before the subroutine returns.

To run the example, save the code to a file with the extension .vbs, for example makelink.vbs. Open a command window and type wscript makelink.vbs to run it. After the Visual Basic file finishes running, the short-cut mylink.lnk should exist on the Windows desktop. If double-clicked, the short-cut will run Windows Notepad, and open the Windows tips file.

Conclusion

This article discussed the uses of short-cuts and their internal structure. It showed a C function for creating a short-cut and provided C source code for a command-line program that creates short-cuts. The article then discussed short-cut creation using other languages. The purpose of the discussion was to show COM functions are available in the same way as other Windows API functions - there is nothing special about COM that requires it to be called from C++ or from Microsoft scripting languages.

I hope you enjoyed the article. Any suggestions or feedback is welcome.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United Kingdom United Kingdom
I am a developer with over 10 years experience, primarily using C, Microsoft Visual Studio, the Win32 API, Borland Delphi, Pascal dialects such as Virtual Pascal and FPC Pascal, and several versions of Basic.

Areas that interest me most are language design, VHLL (very high level languages), design for ease-of-use, and design of languages for beginner programmers. I have contributed to the Ubercode website (http://www.ubercode.com) and the Visual Fred site (http://www.visualfred.com), both experimental languages designed for beginners.

Comments and Discussions

 
SuggestionSome changes for supporting UNICODE and default parameters values Pin
Shmuel Zang7-Jan-15 6:44
Shmuel Zang7-Jan-15 6:44 

I've made some changes to the CreateShortCut function for: support UNICODE, remove the use of the virtual-table's pointer and, enable default values for some of the parameters. I've changed the order of the second and the third parameter, since the most important parameters are the files' names. So that, we can call the function only with the files' names and, get default values for the other parameters.


Here is the changed function:


C++
HRESULT CreateShortCut(LPCTSTR pszTargetfile, LPCTSTR pszLinkfile,
                       LPCTSTR pszTargetargs, LPCTSTR pszDescription, 
                       int iShowmode, LPCTSTR pszCurdir, 
                       LPCTSTR pszIconfile, int iIconindex)
{
  HRESULT       hRes;         /* Returned COM result code */
  IShellLink*   pShellLink;   /* IShellLink object pointer */
  IPersistFile* pPersistFile; /* IPersistFile object pointer */

  hRes = E_INVALIDARG;
  if (
       (pszTargetfile != NULL) && (_tcslen(pszTargetfile) > 0) &&
       (pszLinkfile != NULL) && (_tcslen(pszLinkfile) > 0) &&
       (iShowmode >= 0) &&
       (iIconindex >= 0)
     )
  {
    hRes = CoCreateInstance(
      CLSID_ShellLink,      /* pre-defined CLSID of the IShellLink object */
      NULL,                 /* pointer to parent interface if part of aggregate */
      CLSCTX_INPROC_SERVER, /* caller and called code are in same process */
      IID_IShellLink,       /* pre-defined interface of the IShellLink object */
      (LPVOID*)&pShellLink); /* Returns a pointer to the IShellLink object */
    if (SUCCEEDED(hRes))
    {
      /* Set the fields in the IShellLink object */
      hRes = pShellLink->SetPath(pszTargetfile);
	  hRes = pShellLink->SetArguments(pszTargetargs != NULL ? pszTargetargs : _T(""));
      if (pszDescription != NULL && _tcslen(pszDescription) > 0)
      {
        hRes = pShellLink->SetDescription(pszDescription);
      }
      if (iShowmode > 0)
      {
        hRes = pShellLink->SetShowCmd(iShowmode);
      }
      if (pszCurdir != NULL && _tcslen(pszCurdir) > 0)
      {
        hRes = pShellLink->SetWorkingDirectory(pszCurdir);
      }
      if (pszIconfile != NULL && _tcslen(pszIconfile) > 0 && iIconindex >= 0)
      {
        hRes = pShellLink->SetIconLocation(pszIconfile, iIconindex);
      }

      /* Use the IPersistFile object to save the shell link */
      hRes = pShellLink->QueryInterface(
        IID_IPersistFile, /* pre-defined interface of the IPersistFile object */
        (void**)&pPersistFile); /* returns a pointer to the IPersistFile object */
      if (SUCCEEDED(hRes))
      {
        hRes = pPersistFile->Save(pszLinkfile, TRUE);
        pPersistFile->Release();
      }
      pShellLink->Release();
	}

  }
  return (hRes);
}

For the header file, we can add the following forward declaration:


C++
HRESULT CreateShortCut(LPCTSTR pszTargetfile, LPCTSTR pszLinkfile,
                       LPCTSTR pszTargetargs = NULL, LPCTSTR pszDescription = NULL, 
                       int iShowmode = SW_SHOWNORMAL, LPCTSTR pszCurdir = NULL, 
                       LPCTSTR pszIconfile = NULL, int iIconindex = 0);

GeneralRe: Some changes for supporting UNICODE and default parameters values Pin
Member 113718949-Aug-17 1:44
Member 113718949-Aug-17 1:44 
GeneralMy vote of 5 Pin
Shmuel Zang7-Jan-15 6:29
Shmuel Zang7-Jan-15 6:29 
GeneralMy vote of 5 Pin
zvx20-Mar-13 2:30
zvx20-Mar-13 2:30 
QuestionUser Privileges Pin
Member 865541717-Feb-12 5:58
Member 865541717-Feb-12 5:58 
GeneralUseful reference Pin
RCohn18-Feb-10 9:50
RCohn18-Feb-10 9:50 
GeneralWhy copying MSDN doc ?!! Pin
kilt20-Aug-09 1:27
kilt20-Aug-09 1:27 
GeneralRe: Why copying MSDN doc ?!! Pin
scott.leckie28-Oct-10 0:16
scott.leckie28-Oct-10 0:16 
GeneralThanks a lot! Pin
Usef_7418-Sep-06 17:18
Usef_7418-Sep-06 17:18 
GeneralTraget type Pin
Jian12345678-Jun-06 2:01
Jian12345678-Jun-06 2:01 
GeneralRe: Traget type Pin
mehdi raouf26-Dec-06 0:00
mehdi raouf26-Dec-06 0:00 
Questionpath length problem Pin
antonix9-Feb-06 14:03
antonix9-Feb-06 14:03 
AnswerRe: path length problem Pin
William Rayer10-Feb-06 3:03
William Rayer10-Feb-06 3:03 
GeneralWindows 2000. Pin
dragos_ciulica13-Sep-05 4:15
dragos_ciulica13-Sep-05 4:15 
GeneralRe: Windows 2000. Pin
William Rayer21-Sep-05 7:19
William Rayer21-Sep-05 7:19 
GeneralI know the download isn't working, click 'Link to source files' below Pin
William Rayer7-Sep-05 9:07
William Rayer7-Sep-05 9:07 
QuestionCode question Pin
Geert van Horrik4-Sep-05 22:25
Geert van Horrik4-Sep-05 22:25 
AnswerRe: Code question Pin
William Rayer7-Sep-05 9:16
William Rayer7-Sep-05 9:16 
GeneralDownload Not Working the zip Pin
alejorb4-Sep-05 10:38
alejorb4-Sep-05 10:38 
QuestionRemove items Pin
Geert van Horrik3-Sep-05 23:45
Geert van Horrik3-Sep-05 23:45 
AnswerRe: Remove items Pin
William Rayer4-Sep-05 5:02
William Rayer4-Sep-05 5:02 
GeneralRe: Remove items Pin
Geert van Horrik4-Sep-05 21:12
Geert van Horrik4-Sep-05 21:12 
GeneralLink to source files Pin
William Rayer31-Aug-05 7:32
William Rayer31-Aug-05 7:32 
GeneralDownload not working... Pin
Geert van Horrik31-Aug-05 0:42
Geert van Horrik31-Aug-05 0:42 
GeneralImage files Pin
William Rayer30-Aug-05 2:35
William Rayer30-Aug-05 2:35 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.