Click here to Skip to main content
15,884,472 members
Articles / Programming Languages / C++

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.8K   9.7K   54  
An article on using the Win32 API and COM to create short-cuts in existing and developmental languages.
/*
--------------------------------------------------------------------------------
File Name   : MAKELINK

Contains    : Console mode program (EXE file) to create a link file (short-cut).
              This is based on:
              
              (i) An example in MSDN: Platform SDK -> User Interface Services ->
              Shell and Common Controls -> Windows Shell API -> Shell links.
              Microsoft keep moving the articles in MSDN so it may be easier 
              to search for the example by its name (CreateLink).

              (ii) A public domain program on the web that uses the same
              technique as MSDN: http://www.metathink.com/shlink/shlink.c

Glossary    :

To do       :

Legal       : Copyright (c) 2005. All rights reserved.
--------------------------------------------------------------------------------
*/

/* Windows headers come first */
#define STRICT
#include <windows.h>

/* C run time library headers */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/* COM headers (requires shell32.lib, ole32.lib, uuid.lib) */
#include <objbase.h>
#include <shlobj.h>

/*
--------------------------------------------------------------------------------
Description:
  Show the help page using printf statements. Ideally this should not occupy 
  more than 25 lines.
--------------------------------------------------------------------------------
*/
static void ShowHelp(void)
{
  printf("\n");
  printf("MAKELINK - creates a short-cut (link) to a file. Usage:\n");
  printf("\n");
  printf("  MAKELINK -h\n");
  printf("  MAKELINK file args linkfile desc [show [curdir [iconfile iconindex] ] ]\n");
  printf("\n");
  printf("  -h, -?     ... Show help screen then quit.\n");
  printf("  file       ... File to which we are creating a short-cut.\n");
  printf("  args       ... Arguments of Targetfile, use \"\" if none.\n");
  printf("  linkfile   ... Name of the short-cut file, ending in 'lnk'.\n");
  printf("  desc       ... Description of Targetfile, use \"\" if none.\n");
  printf("  show       ... 1=Normal window(default), 3=Maximized, 7=Minimized (optional).\n");
  printf("  curdir     ... Current directory set when the link file runs (optional).\n");
  printf("  iconfile   ... DLL or ICO file containing icon used for the link (optional).\n");
  printf("  iconindex  ... Integer index of the icon used for the link (optional).\n");
  printf("\n");
  printf("The File and Args elements are always required (Args can equal \"\" if no\n");
  printf("arguments apply). Also the Linkfile and Desc elements are always required\n");
  printf("(Desc can equal \"\" if no description applies). The other elements are\n");
  printf("optional.\n");
  printf("\n");
}

/*
--------------------------------------------------------------------------------
Description:
  Show an error message using printf statements to stdout.

Parameters:
  pszError - Error text to show.
  hRes     - HRESULT values >= 0 indicate success, < 0 indicate failure.
--------------------------------------------------------------------------------
*/
static void ShowError(LPSTR pszError, HRESULT hRes)
{
  printf("%s %x \n", pszError, hRes);
}

/*
--------------------------------------------------------------------------------
Description:
  Get the command line arguments.

Parameters:
  pfHelp           - Returns TRUE if -H was used.
  pszTargetfile    - Returns the file name of the link's target.
  sizeTargetfile   - Sizeof the file name to avoid overflow.
  pszTargetargs    - Returns the command line arguments of the link's target.
  sizeTargetargs   - Sizeof the command line arguments buffer to avoid overflow.
  pszLinkfile      - Returns the file name of the actual link file.
  sizeLinkfile     - Sizeof the file name to avoid overflow.
  pzDescription    - Returns the description of the link's target.
  sizeDescription  - Sizeof the description string to avoid overflow.
  piShowmode       - Returns the ShowWindow() constant for the link's target.
  pszCurdir        - Returns the working directory of the active link.
  sizeCurdir       - Sizeof the working directory to avoid overflow.
  pszIconfile      - Returns the file name of the icon file used for the link.
  sizeIconfile     - Sizeof the file name to avoid overflow.
  piIconindex      - Returns the index of the icon in the icon file.
  pfUnknown        - Returns TRUE if an invalid parameter was used.
--------------------------------------------------------------------------------
*/
static void GetArguments(BOOL*       pfHelp,
                         LPSTR       pszTargetfile,
                         size_t      sizeTargetfile,
                         LPSTR       pszTargetargs,
                         size_t      sizeTargetargs,
                         LPSTR       pszLinkfile,
                         size_t      sizeLinkfile,
                         LPSTR       pszDescription,
                         size_t      sizeDescription,
                         int*        piShowmode,
                         LPSTR       pszCurdir,
                         size_t      sizeCurdir,
                         LPSTR       pszIconfile,
                         size_t      sizeIconfile,
                         int*        piIconindex,
                         BOOL*       pfUnknown)
{
  *pfHelp = FALSE;
  memset(pszTargetfile, 0, sizeTargetfile);
  memset(pszTargetargs, 0, sizeTargetargs);
  memset(pszLinkfile, 0, sizeLinkfile);
  memset(pszDescription, 0, sizeDescription);
  *piShowmode = 0;
  memset(pszCurdir, 0, sizeCurdir);
  memset(pszIconfile, 0, sizeIconfile);
  *piIconindex = 0;
  *pfUnknown = FALSE;

  /* Allow up to 8 command line arguments: 
       Targetfile, Targetargs, Linkfile, Description, Showmode, Curdir, Iconfile, Iconindex. 
     These are accessed __argv[1] through to __argv[8] since __argv[0] 
     is the actual program name which is not needed here. */
  if (
       (__argc == 2) && 
       ((stricmp(__argv[1],"-h") == 0) || (stricmp(__argv[1],"/h") == 0) || 
        (stricmp(__argv[1],"-?") == 0) || (stricmp(__argv[1],"/?") == 0))
     )
  {
    /* -H /H -? /? */
    *pfHelp = TRUE;
  }
  else if (__argc == 5 ||  /* progname, targetfile, targetargs, linkfile, description */
           __argc == 6 ||  /* progname, targetfile, targetargs, linkfile, description, showmode */
           __argc == 7 ||  /* progname, targetfile, targetargs, linkfile, description, showmode, curdir */
           __argc == 9)    /* progname, targetfile, targetargs, linkfile, description, showmode, curdir, iconfile, iconindex */
  {
    /* __argv[0] -->> (unused, program name)
       __argv[1] -->> Targetfile
       __argv[2] -->> Targetargs
       __argv[3] -->> Linkfile
       __argv[4] -->> Description
       __argv[5] -->> Showmode
       __argv[6] -->> Curdir
       __argv[7] -->> Iconfile
       __argv[8] -->> Iconindex */
    strncat(pszTargetfile, __argv[1], sizeTargetfile);
    strncat(pszTargetargs, __argv[2], sizeTargetargs);
    strncat(pszLinkfile, __argv[3], sizeLinkfile);
    strncat(pszDescription, __argv[4], sizeDescription);
    if (__argc >= 6)
    {
      *piShowmode = atoi(__argv[5]);
    }
    if (__argc >= 7)
    {
      strncat(pszCurdir, __argv[6], sizeCurdir);
    }
    if (__argc >= 9)
    {
      strncat(pszIconfile, __argv[7], sizeIconfile);
      *piIconindex = atoi(__argv[8]);
    }
  }
  else
  {
    /* Wrong number of arguments */
    *pfUnknown = TRUE;
  }
}

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

Parameters:
  pszTargetfile    - File name of the link's target, must be a non-empty
                     string.

  pszTargetargs    - Command line arguments passed to link's target, may
                     be an empty string.

  pszLinkfile      - File name of the actual link file, must be a non-empty
                     string.

  pszDescription   - Description of the linked item. If this is an empty
                     string the description is not set.

  iShowmode        - ShowWindow() constant for the link's target. Use one of:
                       1 (SW_SHOWNORMAL) = Normal window.
                       3 (SW_SHOWMAXIMIZED) = Maximized.
                       7 (SW_SHOWMINNOACTIVE) = Minimized.
                     If this is zero the showmode is not set.

  pszCurdir        - Working directory of the active link. If this is
                     an empty string the directory is not set.

  pszIconfile      - File name of the icon file used for the link.
                     If this is an empty string the icon is not set.

  iIconindex       - Index of the icon in the icon file. If this is
                     < 0 the icon is not set.

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);
}

/*
--------------------------------------------------------------------------------
Description:
  Get the command line arguments and create the link.

Returns:
  Program return code of zero if we succeeded, or non-zero otherwise. This
  is the usual convention for programs run from the command line.
--------------------------------------------------------------------------------
*/
int main()
{
  BOOL      fHelp;                     /* -H */
  char      szTargetfile[_MAX_PATH];   /* <Targetfile> */
  char      szTargetargs[_MAX_PATH];   /* <Targetargs> */
  char      szLinkfile[_MAX_PATH];     /* <Linkfile> */
  char      szDescription[_MAX_PATH];  /* <Description> */
  int       iShowmode;                 /* <Showmode> (optional) */
  char      szCurdir[_MAX_PATH];       /* <Curdir> (optional) */
  char      szIconfile[_MAX_PATH];     /* <Iconfile> (optional) */
  int       iIconindex;                /* <Iconindex> (optional) */
  BOOL      fUnknown;                  /* unrecognized argument */
  HRESULT   hRes;                      /* result of calling COM functions */
  int       iReturnCode;               /* program return code (0==OK, !0==error) */

  /* Get the command line parameters */
  iReturnCode = 0;
  GetArguments(&fHelp, 
               szTargetfile, sizeof(szTargetfile), 
               szTargetargs, sizeof(szTargetargs),
               szLinkfile, sizeof(szLinkfile),
               szDescription, sizeof(szDescription),
               &iShowmode,
               szCurdir, sizeof(szCurdir),
               szIconfile, sizeof(szIconfile),
               &iIconindex,
               &fUnknown);
  if (
       fHelp || 
       strlen(szTargetfile) == 0 || 
       strlen(szLinkfile) == 0 || 
       iShowmode < 0 ||
       iIconindex < 0 ||
       fUnknown
     )
  {
    /* Show the help page and quit if any of the following occurred:
       ... -H was used.
       ... A file name was missing.
       ... A negative showmode or icon index were used.
       ... An unknown command line argument was used.
    */
    ShowHelp();
  }
  else 
  {
    /* Call CoInitialize() and create the link if OK. */
    hRes = CoInitialize(NULL);
    if (SUCCEEDED(hRes))
    {
      hRes = CreateShortCut(szTargetfile,  /* Targetfile */
                            szTargetargs,  /* Target arguments */
                            szLinkfile,    /* Short-cut filename */
                            szDescription, /* Short-cut description */
                            iShowmode,     /* Showmode constant */
                            szCurdir,      /* Working directory for linked file */
                            szIconfile,    /* Icon file shown for the link */
                            iIconindex);   /* Index of icon in the file */
      if (!SUCCEEDED(hRes))
      {
        ShowError("CreateShortCut() failed, error: ", hRes);
        iReturnCode = 1;
      }
    }
    else
    {
      ShowError("CoInitialize() failed, error: ", hRes);
      iReturnCode = 1;
    }

    /* call CoUninitialize() and exit the program. */
    CoUninitialize();
  }
  return (iReturnCode);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

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