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

前端 未结 3 1854
轮回少年
轮回少年 2020-12-06 07:04

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 (\'pinne

3条回答
  •  無奈伤痛
    2020-12-06 07:20

    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 'FreeLibrary@kernel32.dll 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.

提交回复
热议问题