// IShellLink descriptions code came from https://github.com/jrsoftware/issrc/blob/master/Examples/CodeAutomation2.iss

// procedure CreateShortcut(AtPath: string; ToPath: string; RunAsAdministrator: boolean);

const
  CLSID_ShellLink = '{00021401-0000-0000-C000-000000000046}';

const
  // IShellLinkDataList::GetFlags()/SetFlags()
  SLDF_HAS_ID_LIST         = $00000001;   // Shell link saved with ID list
  SLDF_HAS_LINK_INFO       = $00000002;   // Shell link saved with LinkInfo
  SLDF_HAS_NAME            = $00000004;
  SLDF_HAS_RELPATH         = $00000008;
  SLDF_HAS_WORKINGDIR      = $00000010;
  SLDF_HAS_ARGS            = $00000020;
  SLDF_HAS_ICONLOCATION    = $00000040;
  SLDF_UNICODE             = $00000080;   // the strings are unicode
  SLDF_FORCE_NO_LINKINFO   = $00000100;   // don't create a LINKINFO (make a dumb link)
  SLDF_HAS_EXP_SZ          = $00000200;   // the link contains expandable env strings
  SLDF_RUN_IN_SEPARATE     = $00000400;   // Run the 16-bit target exe in a separate VDM/WOW
  SLDF_HAS_LOGO3ID         = $00000800;   // this link is a special Logo3/MSICD link
  SLDF_HAS_DARWINID        = $00001000;   // this link is a special Darwin link
  SLDF_RUNAS_USER          = $00002000;   // Run this link as a different user
  SLDF_HAS_EXP_ICON_SZ     = $00004000;   // contains expandable env string for icon path
  SLDF_NO_PIDL_ALIAS       = $00008000;   // don't ever resolve to a logical location
  SLDF_FORCE_UNCNAME       = $00010000;   // make GetPath() prefer the UNC name to the local name
  SLDF_RUN_WITH_SHIMLAYER  = $00020000;   // Launch the target of this link w/ shim layer active
  SLDF_RESERVED            = $80000000;   // Reserved-- so we can use the low word as an index value in the future

type
  IShellLinkW = interface(IUnknown)
    '{000214F9-0000-0000-C000-000000000046}'
    procedure Dummy;
    procedure Dummy2;
    procedure Dummy3;
    function GetDescription(pszName: String; cchMaxName: Integer): HResult;
    function SetDescription(pszName: String): HResult;
    function GetWorkingDirectory(pszDir: String; cchMaxPath: Integer): HResult;
    function SetWorkingDirectory(pszDir: String): HResult;
    function GetArguments(pszArgs: String; cchMaxPath: Integer): HResult;
    function SetArguments(pszArgs: String): HResult;
    function GetHotkey(var pwHotkey: Word): HResult;
    function SetHotkey(wHotkey: Word): HResult;
    function GetShowCmd(out piShowCmd: Integer): HResult;
    function SetShowCmd(iShowCmd: Integer): HResult;
    function GetIconLocation(pszIconPath: String; cchIconPath: Integer; out piIcon: Integer): HResult;
    function SetIconLocation(pszIconPath: String; iIcon: Integer): HResult;
    function SetRelativePath(pszPathRel: String; dwReserved: DWORD): HResult;
    function Resolve(Wnd: HWND; fFlags: DWORD): HResult;
    function SetPath(pszFile: String): HResult;
  end;

  IShellLinkDataList = interface(IUnknown)
    '{45E2B4AE-B1C3-11D0-B92F-00A0C90312E1}'
    function AddDataBlock(pDataBlock: cardinal): HResult;
    function CopyDataBlock(dwSig: DWORD; var ppDataBlock: cardinal): HResult;
    function RemoveDataBlock(dwSig: DWORD): HResult;
    function GetFlags(var pdwFlags: DWORD): HResult;
    function SetFlags(dwFlags: DWORD): HResult;
  end;

  IPersist = interface(IUnknown)
    '{0000010C-0000-0000-C000-000000000046}'
    function GetClassID(var classID: TGUID): HResult;
  end;

  IPersistFile = interface(IPersist)
    '{0000010B-0000-0000-C000-000000000046}'
    function IsDirty: HResult;
    function Load(pszFileName: String; dwMode: Longint): HResult;
    function Save(pszFileName: String; fRemember: BOOL): HResult;
    function SaveCompleted(pszFileName: String): HResult;
    function GetCurFile(out pszFileName: String): HResult;
  end;

procedure CreateShortcut(AtPath, ToPath, IconPath, WorkingDirectoryPath: string; RunAsAdministrator: boolean);
var
  Obj: IUnknown;
  SL: IShellLinkW;
  PF: IPersistFile;
  DL: IShellLinkDataList;
  Flags: DWORD;
begin
  Obj := CreateComObject(StringToGuid(CLSID_ShellLink));

  SL := IShellLinkW(Obj);
  OleCheck(SL.SetPath(ToPath));
  OleCheck(SL.SetWorkingDirectory(WorkingDirectoryPath));
  OleCheck(Sl.SetIconLocation(IconPath, 0));

  if RunAsAdministrator then
  begin
    DL := IShellLinkDataList(Obj);
    OleCheck(DL.GetFlags(Flags));
    OleCheck(Dl.SetFlags(Flags or SLDF_RUNAS_USER));
  end;

  PF := IPersistFile(Obj);
  OleCheck(PF.Save(AtPath, True));
end;