2011. március 2., szerda

Create a program icon on the Desktop or in a folder


Problem/Question/Abstract:

How do I create a Desktop icon for my application when it runs?

Answer:

Solve 1:

This is a unit to create a link in any folder you want, including the Windows Desktop:

unit ShellLink;

uses
  SysUtils, ShlObj, OLE2, Windows, Registry;

interface
procedure OLECheck(OleRetVal: HResult);
function GetShellLink: IShellLink;
function GetFolderLocation(const FolderType: string): string;
function ChangeFileExt(FileName, Ext: string): string;
function CreateLink(const AppName, LinkName, Desc, Dest: string): string;

implementation

procedure OLECheck(OleRetVal: HResult);
{Checks the HResult return value of an OLE function.  Raises an exception if value is
something other than S_OK. }
const
  OleErrStr = 'OLE function call failed. HResult is $%x. GetLastError is $%x';
begin
  if OleRetVal <> S_OK then
    raise EShellOleError.CreateFmt(OleErrStr, [OleRetVal, GetLastError]);
end;

function GetShellLink: IShellLink;
{ Returns reference to ISHellLink object }
begin
  OleCheck(CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_INPROC_SERVER,
    IID_IShellLink, Result));
end;

function GetFolderLocation(const FolderType: string): string;
{ Retrieves from registry path to folder indicated in FolderType }
var
  Reg: TRegistry;
begin
  Reg := TRegistry.Create;
  try
    with Reg do
    begin
      RootKey := HKEY_CURRENT_USER;
      if not OpenKey(SFolderKey, False) then
        { Open key where shell folder information is kept }
        raise ERegistryException.CreateFmt('Folder key "%s" not found', [SFolderKey]);
      { Get path for specified folder }
      Result := ReadString(FolderType);
      if Result = '' then
        raise ERegistryException.CreateFmt('"%s" item not found in registry',
          [FolderType]);
      CloseKey;
    end;
  finally
    Reg.Free;
  end;
end;

function ChangeFileExt(FileName, Ext: string): string;
var
  aFn: string;
begin
  aFn := ExtractFileName(FileName);
  delete(aFn, length(aFn) - 2, 3);
  aFn := aFn + Ext;
  Result := aFn;
end;

function CreateLink(const AppName, LinkName, Desc, Dest: string): string;
{ Creates a shell link for application or document specified in AppName with description Desc. Link will be located in folder specified by Dest, which is one of the string constants shown at the top of this unit. Returns the full path name of the link file. }
var
  SL: IShellLink;
  PF: IPersistFile;
  LnkName: string;
  WStr: array[0..MAX_PATH - 1] of WideChar;
begin
  SL := GetShellLink;
  try
    { The IShellLink Interface supports the IPersistFile interface. Get an interface pointer to it. }
    OleCheck(SL.QueryInterface(IID_IPersistFile, PF));
    try
      OleCheck(SL.SetPath(PChar(AppName))); {set link path to proper file}
      if Desc <> '' then
        OleCheck(SL.SetDescription(PChar(Desc))); {set description}
      { create a path location and filename for link file }
      LnkName := Dest + '\' + linkName;
      { If you want to create a link to"Desktop", you must call GetFolderLocation('Desktop') }
      { convert the link file pathname to a PWideChar }
      StringToWideChar(LnkName, WStr, MAX_PATH);
      PF.Save(WStr, True); {save link file}
    finally
      PF.Release;
    end;
  finally
    SL.Release;
  end;
  Result := LnkName;
end;
initialization
  OleInitialize(nil);
finalization
  OleUninitialize;

Example:

uses
  shellLink;

var
  lnkName: string;
  FileToInstall: string;
  LinkFile: string;
  comment: string;
  Dest: string; { destination's folder }
begin
  FileToInstall := 'Example.exe';
  LinkFile := 'Example.lnk';
  Comment := 'Link to Example.exe';
  Dest := GetFolderLocation('Desktop')
    lnkName := CreateLink(FileToInstall, LinkFile, Comment, Dest);
end;


Solve 2:

uses
  Registry, ShlObj, ActiveX, ComObj;

type
  ShortcutType = (_DESKTOP, _STARTMENU);

procedure CreateShortcut(FileName: string; Location: ShortcutType);
var
  MyObject: IUnknown;
  MySLink: IShellLink;
  MyPFile: IPersistFile;
  Directory, LinkName: string;
  WFileName: WideString;
  MyReg: TRegIniFile;
begin
  MyObject := CreateComObject(CLSID_ShellLink);
  MySLink := MyObject as IShellLink;
  MyPFile := MyObject as IPersistFile;
  MySLink.SetPath(PChar(FileName));
  MyReg := TRegIniFile.Create('Software\MicroSoft\Windows\CurrentVersion\Explorer');
  try
    LinkName := ChangeFileExt(FileName, '.lnk');
    LinkName := ExtractFileName(LinkName);
    if Location = _DESKTOP then
    begin
      {Use the next line of code to put the shortcut on your desktop}
      Directory := MyReg.ReadString('Shell Folders', 'Desktop', '');
      WFileName := Directory + '\' + LinkName;
      MyPFile.Save(PWChar(WFileName), False);
    end;
    if Location = _STARTMENU then
    begin
      {Use the next two lines to put the shortcut on your start menu}
      Directory := MyReg.ReadString('Shell Folders', 'Start Menu', '');
      CreateDir(Directory);
      WFileName := Directory + '\' + LinkName;
      MyPFile.Save(PWChar(WFileName), False);
    end;
  finally
    MyReg.Free;
  end;
end;


Solve 3:

{ uses ShlObj, ActiveX, ComObj, SysUtils, etc... }
{ "DestFolder" should be one of the CSIDL_ constants as declared in the ShlObj unit }

procedure CreateProgramShortcut(DestFolder: Integer; const Applic: string);
var
  SL: IShellLink;
  PF: IPersistFile;
  LnkName: WideString;
  FP: array[0..MAX_PATH * 2] of Char;
  IDL: PItemIDList;
begin
  CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_INPROC_SERVER, IShellLink, SL);
  PF := SL as IPersistFile;
  { Make shortcut point to Application.Exename }
  SL.SetPath(PChar(Applic));
  { Set input paramters (if any) }
  { Set default directory to exe-file's location }
  SL.SetWorkingDirectory(PChar(ExtractFilePath(Applic)));
  { Use the exe-file's icon }
  SL.SetIconLocation(PChar(Applic), 0);
  { Just leaving the following fields empty for now. Could be adjusted if the customer must have these set }
  SL.SetArguments('');
  SL.SetDescription('');
  SL.SetShowCmd(0);
  SL.SetHotKey(0);
  { Resolve the path-name of the special folder }
  if SHGetSpecialFolderLocation(0, DestFolder, IDL) = NOERROR then
    SHGetPathFromIDList(IDL, FP);
  { save the file as "Exename.lnk" }
  LnkName := WideString(FP) + '\' + ExtractFilename(ChangeFileExt(Applic, '.lnk'));
  PF.Save(PWideChar(LnkName), True);
end;

Now you can just paste this procedure into your project and then, the only thing to do is to call something like this:

{ Create a shortcut to current exe on the desktop }
CreateProgramShortcut(CSIDL_DESKTOP, ParamStr(0));
{ Create a shortcut to notepad in the start-menu }
CreateProgramShortcut(CSIDL_STARTMENU, 'c:\windwos\notepad.exe');


Solve 4:

Following code originates from a DragDrop demo of Angus Johnson and Anders Melander:

uses
  ActiveX, ShlObj, ComObj;

{Create a file link}

function CreateLink(SourceFile, ShortCutName: string): string;
var
  IUnk: IUnknown;
  ShellLink: IShellLink;
  IPFile: IPersistFile;
  tmpShortCutName: string;
  WideStr: WideString;
  i: integer;
begin
  IUnk := CreateComObject(CLSID_ShellLink);
  ShellLink := IUnk as IShellLink;
  IPFile := IUnk as IPersistFile;
  with ShellLink do
  begin
    SetPath(PChar(SourceFile));
    SetWorkingDirectory(PChar(ExtractFilePath(SourceFile)));
  end;
  ShortCutName := ChangeFileExt(ShortCutName, '.lnk');
  if FileExists(ShortCutName) then
  begin
    ShortCutName := Copy(ShortCutName, 1, Length(ShortCutName) - 4);
    i := 1;
    repeat
      tmpShortCutName := ShortCutName + '(' + IntToStr(i) + ').lnk';
      Inc(i);
    until
      not FileExists(tmpShortCutName);
    Result := tmpShortCutName;
  end
  else
    Result := ShortCutName;
  WideStr := Result;
  IPFile.Save(PWChar(WideStr), False);
end;

Usage is similar as with CopyFile but instead of actual copying it, it creates a shortcut to the sourcefile.

CreateLink('c:\apath\afile.ext', 'c:\anotherpath\afile.ext');


Solve 5:

{Shortcut Component for Delphi 2.0 by Elliott Shevin, Oak Park, Mich. USA
April, 1999
email: shevine@aol.com
version 1.1.

Includes the following corrections:
The Write method doesn't set a hot key if that property is not greater than space.
The correct values are used for ShowCmd.

This component incorporates the shortcut read function of TShellLink by Radek Voltr with shortcut creation code from Jordan Russell, who merits special thanks for reviewing and improving the code.

This is a freeware component. Use it any way you like, but please report errors and improvements to me, and acknowledge Radek and Jordan.}

unit ShortcutLink;

{$IFNDEF VER80}{$IFNDEF VER90}{$IFNDEF VER93}
{$DEFINE Delphi3orHigher}
{$ENDIF}{$ENDIF}{$ENDIF}

interface

uses
  Windows, Messages, SysUtils, Classes, Forms,
{$IFNDEF Delphi3orHigher}
  OLE2,
{$ELSE}
  ActiveX, ComObj,
{$ENDIF}
  ShellAPI, ShlObj, CommCtrl, StdCtrls;

const
  SLR_NO_UI = $0001;
  SLR_ANY_MATCH = $0002;
  SLR_UPDATE = $0004;
  SLGP_SHORTPATH = $0001;
  SLGP_UNCPRIORITY = $0002;
  Error_Message = 'Unable to create .lnk file';
{$IFDEF Delphi3orHigher}
  IID_IPersistFile: TGUID = (D1: $0000010B; D2: $0000; D3: $0000;
    D4: ($C0, $00, $00, $00, $00, $00, $00, $46));
{$ENDIF}

type
  EShortcutError = class(Exception);
  TShowCmd = (scShowMaximized, scShowMinimized, scShowNormal);

type
  TShortcutLink = class(TComponent)
  private
    { Private declarations }
  protected
    { Protected declarations }
    fShortcutFile, fTarget, fWorkingDir, fDescription, fArguments, fIconLocation:
      string;
    fIconNumber, fHotKey: Word;
    fShowCmd: integer;
    procedure fSetHotKey(c: string);
    function fGetHotKey: string;
    procedure fSetShowCmd(c: TShowCmd);
    function fGetShowCmd: TShowCmd;
    function fGetDesktopFolder: string;
    function fGetProgramsFolder: string;
    function fGetStartFolder: string;
    function fGetStartupFolder: string;
    function fGetSpecialFolder(nFolder: integer): string;
  public
    { Public declarations }
    procedure Read;
    procedure Write;
    property DesktopFolder: string read fGetDesktopFolder;
    property ProgramsFolder: string read fGetProgramsFolder;
    property StartFolder: string read fGetStartFolder;
    property StartupFolder: string read fGetStartupFolder;
  published
    { Published declarations }
    property ShortcutFile: string read fShortcutFile write fShortcutFile;
    property Target: string read fTarget write fTarget;
    property WorkingDir: string read fWorkingDir write fWorkingDir;
    property Description: string read fDescription write fDescription;
    property Arguments: string read fArguments write fArguments;
    property IconLocation: string read fIconLocation write fIconLocation;
    property HotKey: string read fGetHotKey write fSetHotKey;
    property ShowCmd: TShowCmd read fGetShowCmd write fSetShowCmd default
      scShowNormal;
    property IconNumber: Word read fIconNumber write fIconNumber;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Win95', [TShortcutLink]);
end;

{This is the Read method, which reads the link to which fShortcutFile points. It was SetSelfPath in Radek Voltr's TShellLink component, where setting the ShortcutFile property caused the shortcut file to be read immediately.}

procedure TShortcutLink.Read;
var
  X3: PChar;
  hresx: HResult;
  Psl: IShellLink;
  Ppf: IPersistFile;
  Saver: array[0..Max_Path] of WideChar;
  X1: array[0..MAX_PATH - 1] of Char;
  Data: TWin32FindData;
  I, Y: Integer;
  W: Word;
begin
  hresx := CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_INPROC_SERVER,
{$IFDEF Delphi3orHigher}IID_IShellLinkA
{$ELSE}IID_IShellLink
{$ENDIF}, psl);
  if hresx <> 0 then
    Exit;
  hresx := psl.QueryInterface(IID_IPersistFile, ppf);
  if hresx <> 0 then
    Exit;
  X3 := StrAlloc(MAX_PATH);
  StrPCopy(X3, fShortcutFile);
  MultiByteToWideChar(CP_ACP, 0, X3, -1, Saver, Max_Path);
  hresx := ppf.Load(Saver, STGM_READ);
  if hresx <> 0 then
  begin
    raise EShortcutError.Create('Unable to open link file');
    Exit;
  end;
  hresx := psl.Resolve(0, SLR_ANY_MATCH);
  if hresx <> 0 then
    Exit;
  hresx := psl.GetWorkingDirectory(@X1, MAX_PATH);
  if hresx <> 0 then
  begin
    raise EShortcutError.Create('Error in getting WorkingDir');
    Exit;
  end;
  fWorkingDir := StrPas(@X1);
  hresx := psl.GetPath(@X1, MAX_PATH, Data, SLGP_UNCPRIORITY);
  if hresx <> 0 then
  begin
    raise EShortcutError.Create('Error in getting Target');
    Exit;
  end;
  fTarget := StrPas(@X1);
  hresx := psl.GetIconLocation(@X1, MAX_PATH, I);
  if hresx <> 0 then
  begin
    raise EShortcutError.Create('Error in getting icon data');
    Exit;
  end;
  fIconLocation := StrPas(@X1);
  fIconNumber := I;
  hresx := psl.GetDescription(@X1, MAX_PATH);
  if hresx <> 0 then
  begin
    raise EShortcutError.Create('Error in get Description');
    Exit;
  end;
  fDescription := StrPas(@X1);
  Y := 0;
  hresx := psl.GetShowCmd(Y);
  if hresx <> 0 then
  begin
    raise EShortcutError.Create('Error in getting ShowCmd');
    Exit;
  end;
  fShowCmd := Y;
  W := 0;
  hresx := psl.GetHotKey(W);
  if hresx <> 0 then
  begin
    raise EShortcutError.Create('Error in geting HotKey');
    Exit;
  end;
  fHotKey := W;
  if fHotKey = 0 then
    HotKey := ' '
  else
    HotKey := chr(fHotKey);
  hresx := psl.GetArguments(@X1, MAX_PATH);
  if hresx <> 0 then
  begin
    raise EShortcutError.Create('Error in getting Arguments');
    Exit;
  end;
  fArguments := StrPas(@X1);
{$IFNDEF Delphi3orHigher}
  ppf.release;
  psl.release;
{$ENDIF}
  StrDispose(X3);
end;

{The Write method is adapted from code in Jordan Russell's Inno Setup.}

procedure TShortcutLink.Write;
var
  aISL: IShellLink;
  aIPF: IPersistFile;
{$IFNDEF Delphi3OrHigher}
  aPidl: PItemIDList;
  WideFilename: array[0..MAX_PATH - 1] of WideChar;
{$ELSE}
  Obj: IUnknown;
  WideFilename: WideString;
{$ENDIF}
begin
  {Get an IShellLink interface to make the shortcut. The methods differ between
  Delphi 2 and later releases.}

{$IFNDEF Delphi3OrHigher}
  if not SUCCEEDED(CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_INPROC_SERVER,
    IID_IShellLink, aISL)) then
    raise EShortcutError.Create(Error_Message);
{$ELSE}
  Obj := CreateComObject(CLSID_ShellLink);
  aiSL := Obj as IShellLink;
{$ENDIF}

  try
    {Now we have an IShellLink interface, so we can set it up as we like. Set the target.}
    aISL.SetPath(Pchar(fTarget));
    {Set the working directory ("Start in")}
    aISL.SetWorkingDirectory(PChar(fWorkingDir));
    {Set the command-line params}
    aISL.SetArguments(Pchar(fArguments));
    {Set the description}
    aISL.SetDescription(Pchar(fDescription));
    {Set the show command}
    aISL.SetShowCmd(fShowCmd);
    {Set the hotkey}
    {Vers. 1.1 avoids this command if fHotKey isn't greater than a space.}
    if fHotKey > ord(' ') then
      aISL.SetHotKey(((HOTKEYF_ALT or HOTKEYF_CONTROL) shl 8) or fHotKey);
    {Set the icon location}
    aISL.SetIconLocation(Pchar(fIconLocation), fIconNumber);
    {The shortcut IShellLink is now all set up. We get an IPersistFile interface from it, and use it to save the link. Delphi 2 differs from later releases.}

{$IFNDEF Delphi3OrHigher}
    if aISL.QueryInterface(IID_IPersistFile, aIPF) <> S_OK then
      raise EShortcutError.Create(Error_Message)
    else
      MultiByteToWideChar(CP_ACP, 0, PChar(fShortcutFile), -1, WideFilename,
        MAX_PATH);
{$ELSE}
    aiPF := Obj as IPersistFile;
    WideFilename := fShortcutFile;
{$ENDIF}

    try
{$IFNDEF Delphi3OrHigher}
      if aIPF.Save(WideFilename, True) <> S_OK
{$ELSE}
      if aIPF.Save(PWideChar(WideFilename), True) <> S_OK
{$ENDIF} then
        raise EShortcutError.Create(Error_Message);
    finally
{$IFNDEF Delphi3OrHigher}
      aIPF.Release; {Only needed for D2--later releases do this implicitly.}
{$ENDIF}
    end;

  finally
{$IFNDEF Delphi3OrHigher}
    aISL.Release; {Only needed for D2--later releases do this implicitly.}
{$ENDIF}
  end;
end;

function TShortcutLink.fGetDesktopFolder: string;
begin
  result := fGetSpecialFolder(CSIDL_DESKTOPDIRECTORY);
end;

function TShortcutLink.fGetProgramsFolder: string;
begin
  result := fGetSpecialFolder(CSIDL_PROGRAMS);
end;

function TShortcutLink.fGetStartFolder: string;
begin
  result := fGetSpecialFolder(CSIDL_STARTMENU);
end;

function TShortcutLink.fGetStartupFolder: string;
begin
  result := fGetSpecialFolder(CSIDL_STARTUP);
end;

function TShortcutLink.fGetSpecialFolder(nFolder: integer): string;
var
  aPidl: PItemIDList;
  handle: THandle;
  TC: TComponent;
  fLinkDir: string;
begin
  {Get the folder location (as a PItemIDList)}
  TC := self.owner;
  handle := (TC as TForm).handle;
  if SUCCEEDED(SHGetSpecialFolderLocation(handle, nFolder, aPidl)) then
  begin
    {Get the actual path of the desktop directory from the PItemIDList}
    SetLength(fLinkDir, MAX_PATH); {SHGetPathFromIDList assumes MAX_PATH buffer}
    SHGetPathFromIDList(aPidl, PChar(fLinkDir)); {Do it}
    SetLength(fLinkDir, StrLen(PChar(fLinkDir)));
    result := fLinkDir;
  end;
end;

procedure TShortcutLink.fSetHotKey(c: string);
var
  s: string[1];
  c2: char;
begin
  s := c;
  if length(c) < 1 then
    s := ' ';
  s := uppercase(s);
  c2 := s[1];
  if ord(c2) < ord(' ') then
    c2 := ' ';
  fHotKey := ord(c2);
end;

function TShortcutLink.fGetHotKey: string;
begin
  if fHotKey = 0 then
    fHotKey := ord(' ');
  result := chr(fHotKey);
end;

procedure TShortcutLink.fSetShowCmd(c: TShowCmd);
begin
  case c of
    scSHOWMAXIMIZED: fShowCmd := SW_Maximize;
    scSHOWMINIMIZED: fShowCmd := SW_ShowMinNoActive;
    scSHOWNORMAL: fShowCmd := SW_Restore;
  end;
end;

function TShortcutLink.fGetShowCmd: TShowCmd;
begin
  case fShowCmd of
    SW_MAXIMIZE: result := scShowMaximized;
    SW_SHOWMINNOACTIVE: result := scShowMinimized;
    SW_RESTORE: result := scShowNormal;
  else
    result := scShowNormal;
  end;
end;

initialization
  CoInitialize(nil); {Must initialize COM or CoCreateInstance won't work}
finalization
  CoUninitialize; {Symmetric uninitialize}

end.


Solve 6:

function CreateShellLink(sEintrag, sExeFile, sParams, sIconFile: string; iIconNr:
  Integer;
  const sDescription: string): HRESULT;
{create ShellLink, overwrite if already exist}
var
  hrInit: HRESULT;
  pIShellLink: IShellLink;
  pIPersistFile: IPersistFile;
begin
  result := E_FAIL;
  {they should be NIL}
  Assert((nil = pIShellLink) and (nil = pIPersistFile));
  {parameter test}
  Assert((sEintrag <> '') and (sExeFile <> ''));
  if (sEintrag = '') or (sExeFile = '') then
    Exit;
  {action}
  hrInit := CoInitialize(nil);
  try
    result := hrInit;
    if FAILED(result) then
      Exit;
    {Get a pointer to the IShellLink interface}
    result := CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_INPROC_SERVER,
      IID_IShellLinkA, pIShellLink);
    if FAILED(result) then
      Exit;
    {Set the path to the shortcut target and the params}
    pIShellLink.SetPath(PChar(sExeFile));
    pIShellLink.SetArguments(PChar(sParams));
    {add description and icon}
    if sDescription <> '' then
      pIShellLink.SetDescription(PChar(sDescription));
    if sIconFile <> '' then
      pIShellLink.SetIconLocation(PChar(sIconFile), iIconNr);
    {Query IShellLink for the IPersistFile interface for saving the shortcut in persistent storage}
    result := pIShellLink.QueryInterface(IID_IPersistFile, pIPersistFile);
    if FAILED(result) then
      Exit;
    {Ensure that the string is OLECHAR
    Ensure that the new link has the .LNK extension
    Save the link by calling IPersistFile::Save.}
    if CompareText(ExtractFileExt(sEintrag), '.lnk') <> 0 then
      sEintrag := sEintrag + '.lnk';
    ForceDirectories(ExtractFilePath(sEintrag));
    result := pIPersistFile.Save(PWideChar(WideString(sEintrag)), True);
  finally
    pIShellLink := nil;
    pIPersistFile := nil;
    if SUCCEEDED(hrInit) then
      CoUninitialize;
  end;
end;


Solve 7:

uses
  ComObj, ShlObj, ActiveX;

procedure CreateShortcut(AFileName: string; ALocation: string);
var
  MyObject: IUnknown;
  MySLink: IShellLink;
  MyPFile: IPersistFile;
  WFileName: WideString;
begin
  MyObject := CreateComObject(CLSID_ShellLink);
  MySLink := MyObject as IShellLink;
  MyPFile := MyObject as IPersistFile;
  MySLink.SetPath(PChar(AFileName));
  MySLink.SetWorkingDirectory(PChar(ExtractFilePath(AFileName)));
  WFileName := ALocation;
  MyPFile.Save(PWChar(WFileName), False);
end;

function GetSpecialFolder(AFolderID: Integer): string;
var
  AInfo: PItemIdList;
  Buffer: array[0..MAX_PATH] of Char;
begin
  if (SHGetspecialFolderLocation(Application.Handle, AFolderID, aInfo) = NOERROR)
    and SHGetPathFromIDList(aInfo, Buffer) then
    Result := StrPas(Buffer);
end;

procedure MakeShortcut;
begin
  CreateShortcut(Application.ExeName, GetSpecialFolder(CSIDL_COMMON_STARTUP)
    + '\shortcutname.lnk');
end;

procedure DeleteShortCut;
  DeleteFile(PChar(GetSpecialFolder(CSIDL_COMMON_STARTUP) + '\shortcutname.lnk'));
end;

Nincsenek megjegyzések:

Megjegyzés küldése