Is it possible to 'Pin to start menu' using Inno Setup?
Asked Answered
M

3

10

I'm using the excellent Inno Setup installer and I notice that some Applications (often from Microsoft) get installed with their launch icon already highly visible ('pinned?') in the start menu (in Windows 7). Am I totally reliant on the most-recently-used algorithm for my icon to be 'large' in the start menu, or is there a way of promoting my application from the installer please?

Messner answered 27/10, 2009 at 8:22 Comment(0)
L
9

It is possible to pin programs, but not officially. Based on a code posted in this thread (which uses the same way as described in the article linked by @Mark Redman) I wrote the following:

[Code]
#ifdef UNICODE
  #define AW "W"
#else
  #define AW "A"
#endif

const
  // these constants are not defined in Windows
  SHELL32_STRING_ID_PIN_TO_TASKBAR = 5386;
  SHELL32_STRING_ID_PIN_TO_STARTMENU = 5381;
  SHELL32_STRING_ID_UNPIN_FROM_TASKBAR = 5387;
  SHELL32_STRING_ID_UNPIN_FROM_STARTMENU = 5382;

type
  HINSTANCE = THandle;
  HMODULE = HINSTANCE;

  TPinDest = (
    pdTaskbar,
    pdStartMenu
  );

function LoadLibrary(lpFileName: string): HMODULE;
  external 'LoadLibrary{#AW}@kernel32.dll stdcall';
function FreeLibrary(hModule: HMODULE): BOOL;
  external '[email protected] stdcall';
function LoadString(hInstance: HINSTANCE; uID: UINT;
  lpBuffer: string; nBufferMax: Integer): Integer;
  external 'LoadString{#AW}@user32.dll stdcall';

function TryGetVerbName(ID: UINT; out VerbName: string): Boolean;
var
  Buffer: string;
  BufLen: Integer;
  Handle: HMODULE;
begin
  Result := False;

  Handle := LoadLibrary(ExpandConstant('{sys}\Shell32.dll'));
  if Handle <> 0 then
  try
    SetLength(Buffer, 255);
    BufLen := LoadString(Handle, ID, Buffer, Length(Buffer));

    if BufLen <> 0 then
    begin
      Result := True;
      VerbName := Copy(Buffer, 1, BufLen);
    end;
  finally
    FreeLibrary(Handle);
  end;
end;

function ExecVerb(const FileName, VerbName: string): Boolean;
var
  I: Integer;
  Shell: Variant;
  Folder: Variant;
  FolderItem: Variant;
begin
  Result := False;

  Shell := CreateOleObject('Shell.Application');
  Folder := Shell.NameSpace(ExtractFilePath(FileName));
  FolderItem := Folder.ParseName(ExtractFileName(FileName));

  for I := 1 to FolderItem.Verbs.Count do
  begin
    if FolderItem.Verbs.Item(I).Name = VerbName then
    begin
      FolderItem.Verbs.Item(I).DoIt;
      Result := True;
      Exit;
    end;
  end;  
end;

function PinAppTo(const FileName: string; PinDest: TPinDest): Boolean;
var
  ResStrID: UINT;
  VerbName: string;
begin
  case PinDest of
    pdTaskbar: ResStrID := SHELL32_STRING_ID_PIN_TO_TASKBAR;
    pdStartMenu: ResStrID := SHELL32_STRING_ID_PIN_TO_STARTMENU;
  end;
  Result := TryGetVerbName(ResStrID, VerbName) and ExecVerb(FileName, VerbName);
end;

function UnpinAppFrom(const FileName: string; PinDest: TPinDest): Boolean;
var
  ResStrID: UINT;
  VerbName: string;
begin
  case PinDest of
    pdTaskbar: ResStrID := SHELL32_STRING_ID_UNPIN_FROM_TASKBAR;
    pdStartMenu: ResStrID := SHELL32_STRING_ID_UNPIN_FROM_STARTMENU;
  end;
  Result := TryGetVerbName(ResStrID, VerbName) and ExecVerb(FileName, VerbName);
end;

The above code first reads the caption of the menu item for pinning or unpinning applications from the string table of the Shell32.dll library. Then connects to the Windows Shell, and for the target app. path creates the Folder object, then obtains the FolderItem object and on this object iterates all the available verbs and checks if their name matches to the one read from the Shell32.dll library string table. If so, it invokes the verb item action by calling the DoIt method and exits the iteration.

Here is a possible usage of the above code, for pinning:

if PinAppTo(ExpandConstant('{sys}\calc.exe'), pdTaskbar) then
  MsgBox('Calc has been pinned to the taskbar.', mbInformation, MB_OK);
if PinAppTo(ExpandConstant('{sys}\calc.exe'), pdStartMenu) then
  MsgBox('Calc has been pinned to the start menu.', mbInformation, MB_OK);

And for unpinning:

if UnpinAppFrom(ExpandConstant('{sys}\calc.exe'), pdTaskbar) then
  MsgBox('Calc is not pinned to the taskbar anymore.', mbInformation, MB_OK);
if UnpinAppFrom(ExpandConstant('{sys}\calc.exe'), pdStartMenu) then
  MsgBox('Calc is not pinned to the start menu anymore.', mbInformation, MB_OK);

Please note that even though this code works on Windows 7 (and taskbar pinning also on Windows 8.1 where I've tested it), it is really hacky way, since there is no official way to programatically pin programs to taskbar, nor start menu. That's what the users should do by their own choice.

Lundberg answered 31/7, 2014 at 18:8 Comment(14)
OMG, how many hours did you try to figure that out? Thank you so much!Lublin
@tmighty, I'm glad it helped someone! And it took me less than hour; it's just a refactored code from the linked thread :-)Lundberg
Thank you! :-) Can you tell me your preferred way of executing your code? I mean the "if PinAppTo(ExpandConstant('{sys}\calc.exe'), pdTaskbar) then...". Where do you do that? Do you define it as a task or do you execute this code automatically? I never ran a code before except for installing the vcredist_x86 runtimes which I ran using [Run] "Filename: "{tmp}\vcredist_x86.exe"; Parameters: "/q"; Check: VCRedistNeedsInstall. This was just an exe that I had to run, not a function like yours.Lublin
@tmighty, I've never been asked to deploy an application that would pin itself somewhere. That's what the users should do by themselves in the first place. But if there would be such requirement, a task would be fair enough, I think (since tasks are usually used to confirm creating icons). For executing I would use post install step.Lundberg
Amazing! Attaching my app to the start menu works. Attaching it to the task bar does not work yet, but the start menu was my main goal. Thank you so much again! I will try to figure out what goes wrong at adding my app to the task bar.Lublin
The taskbar also worked. It merely took a few minutes to refresh, I think. Thank you so much!Lublin
On Windows 7 and Window 8, it works great. However on Windows XP, I get the error "Variant is null, cannot invoke". Do you have any idea how to fix this?Lublin
@tmighty, it's highly possible that on Windows XP those verb names are stored on different resource indices in the shell32.dll library. You can find those names if you open (a copy of) the shell32.dll library in some resource editor and find in its string resource table an item which matches to the popup menu item used to pin application to start menu (of course there is none for taskbar pinning since that's been added in Vista).Lundberg
I am a total newbie regarding Code in Inno Setup. Do you think you could edit your code so that I can see how to skip the taskbar pinning for pre-vista Windows?Lublin
@tmighty, you can use this helper function. It returns True if you're running Windows Vista or above, False otherwise.Lundberg
On "Windows 8.1 64bit with Media Center" it does not work. Have you by chance already found out how to do this on this OS?Lublin
I have investigated, and the string "Pin to ''Start''" is not to be found on "Windows 8.1 with MediaCenter" within user32.dll, kernel32.dll or shell32.dll. Hmmmm. I have no idea where else it could be found.Lublin
@tmighty, I'm afraid I can't help here. It's difficult to say for me that even if you find that menu item, there will be the same Windows Shell services as on classic desktop versions.Lundberg
@Lundberg how can I pin to taskbar with argument? Edit: I tried without argument now and in last page of installer it fails to pin in Windows 10. Verbs list not contains anything about pin to taskbar. But it have pin to start which I don't need.Piecedyed
D
6

There's a reason there's no programmatic way to pin things to the taskbar/start menu. In my experience, I have seen the start menu highlight newly-created shortcuts, and that's designed to handle exactly this situation. When you see a newly-installed program show up on the start menu, it's probably because of that algorithm and not because the installer placed it there.

That said, if a new shortcut does not appear highlighted, it may be because the installer extracts a pre-existing shortcut and preserves an old timestamp on it, rather than using the API function to create a shortcut in the start menu.

Dorise answered 27/10, 2009 at 8:27 Comment(0)
K
4

Have a look at: http://blogs.technet.com/deploymentguys/archive/2009/04/08/pin-items-to-the-start-menu-or-windows-7-taskbar-via-script.aspx

Kilowatt answered 27/10, 2009 at 8:27 Comment(1)
and as noted in that article, there is a reason that there is not an API for pinning directly. Please don't spam the desktop/taskbar/startmenu/etc.Centrist

© 2022 - 2024 — McMap. All rights reserved.