Reputation: 612
I'm writing an app where I'd like to provide users with the option to pin it to the Windows Taskbar programmatically. Is there a way to achieve this?
I've searched online, but the information seems to be scattered, outdated, or not related to Delphi. Can someone guide me on how to accomplish this task?
I have an FMX app, but I can just wrap the code in a Windows compiler directive? So code for VCL or FMX is fine.
Upvotes: 0
Views: 328
Reputation: 839
As mentioned by Remy, there is no public API available, so you have to implement by yourself.
However, on new Windows updates, Windows 11 now asks the user permission for the application to be pinned using a notification prompt, which is fair.
Here is how I did for some experiments, this pins and unpins to the taskbar of Windows 10/11, but on new Windows 11 updates, it won't bypass the confirmation prompt, as this was planned for Windows in order not to abuse and let all applications to indiscriminately pin without user's permission.
unit TaskbarPinner;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls,
ShlObj, ActiveX, ShellApi, ComObj, RegChangeThread, DWMApi;
const
SID_IShellDispatch6 = '{286e6f1b-7113-4355-9562-96b7e9d64c54}';
IID_IShellDispatch5: TGUID = SID_IShellDispatch6;
const
CLSID_TaskbandPin: TGUID = '{90AA3A4E-1CBA-4233-B8BB-535773D48449}';
CLSID_TrayNotify: TGUID = '{25DEAD04-1EAC-4911-9E3A-AD0A4AB560FD}';
IID_IFlexibleTaskbarPinnedList: TGUID = '{60274FA2-611F-4B8A-A293-F27BF103D148}';
IID_IUnknown: TGUID = '{00000000-0000-0000-C000-000000000046}';
IID_IFlexibleTaskbarPinnedList0: TGUID = '{53d51c3c-d7e0-4fec-b4c6-33b4f8a41c64}';
IID_IFlexibleTaskbarPinnedList1: TGUID = '{60274FA2-611F-4B8A-A293-F27BF103D148}';
IID_IFlexibleTaskbarPinnedList2: TGUID = '{BBD20037-BC0E-42F1-913F-E2936BB0EA0C}';
IID_IFlexibleTaskbarPinnedList3: TGUID = '{C3C6EB6D-C837-4EAE-B172-5FEC52A2A4FD}';
IID_IPinnedList3: TGUID = '{0DD79AE2-D156-45D4-9EEB-3B549769E940}';
SID_ITrayNotify = '{FB852B2C-6BAD-4605-9551-F15F87830935}';
IID_ITrayNotify: TGUID = '{FB852B2C-6BAD-4605-9551-F15F87830935}';
SID_INotificationCB = '{D782CCBA-AFB0-43F1-94DB-FDA3779EACCB}';
IID_INotificationCB: TGUID = '{D782CCBA-AFB0-43F1-94DB-FDA3779EACCB}';
type
PLMC =(PLMC_EXPLORER = 4);
IFlexibleTaskbarPinnedList = interface(IUnknown)
['{60274FA2-611F-4B8A-A293-F27BF103D148}']
// function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
// function _AddRef: Integer; stdcall;
// function _Release: Integer; stdcall;
function EnumObjects(var p2: IEnumFullIDList): HRESULT; stdcall;
function GetPinnableinfo: HRESULT; stdcall;
function IsPinnable: HRESULT; stdcall;
function Resolve: HRESULT; stdcall;
function LeaveFolder: HRESULT; stdcall;
function GetChangeCount: HRESULT; stdcall;
function IsPinned: HRESULT; stdcall;
function GetPinnedItem: HRESULT; stdcall;
function GetAppIDForPinnedItem: HRESULT; stdcall;
function ItemChangeNotify: HRESULT; stdcall;
function UpdateForRemovedItemsAsNecessary: HRESULT; stdcall;
function PinShellLink: HRESULT; stdcall;
function GetPinnedItemForAppID: HRESULT; stdcall;
function ApplyPrependDefaultTaskbarLayour: HRESULT; stdcall;
function ApplyInPlaceTaskbarLayout: HRESULT; stdcall;
function ApplyReorderTaskbarLayout: HRESULT; stdcall;
function IsEmpty: HRESULT; stdcall;
end;
// Windows Vista
IPinnedList = interface(IUnknown)
['{C3C6EB6D-C837-4EAE-B172-5FEC52A2A4FD}']
function EnumObjects: HRESULT; stdcall; // $18
function Modify: HRESULT; stdcall; // $20
function GetChangeCount: HRESULT; stdcall;// $28
function IsPinnable: HRESULT; stdcall; // $30
function Resolve: HRESULT; stdcall; // $38
function IsPinned: HRESULT; stdcall; // $40
end;
// Windows 7, 8, 8.1
IPinnedList2 = interface(IUnknown)
['{BBD20037-BC0E-42F1-913F-E2936BB0EA0C}']
function EnumObjects: HRESULT; stdcall; // $18
function Modify: HRESULT; stdcall; // $20
function GetChangeCount: HRESULT; stdcall;// $28
function GetPinnableInfo: HRESULT; stdcall;// $30
function IsPinnable: HRESULT; stdcall; // $38
function Resolve: HRESULT; stdcall; // $40
function IsPinned: HRESULT; stdcall; // $48
function GetPinnedItem: HRESULT; stdcall; // $50
function GetAppIDForPinnedItem: HRESULT; stdcall; // $58
function ItemChangeNotify: HRESULT; stdcall; // $60
function UpdateForRemovedItemsAsNecessary: HRESULT; stdcall; // $68
end;
// Windows 10 build 1809+
IPinnedList3 = interface(IUnknown)
['{0DD79AE2-D156-45D4-9EEB-3B549769E940}']
function EnumObjects(var ppv: IEnumFullIDList): HRESULT; stdcall; // $18
function GetPinnableInfo(ido: IDataObject; pinnableFlag: Integer; var isi, isi2: IShellItem; var us: USHORT; i: integer): HRESULT; stdcall;// $20
function IsPinnable(pn: IDataObject; pinableFlag: Integer): HRESULT; stdcall; // $28
function Resolve(hWnd: HWND; l: ULONG; pidl: PCIDLIST_ABSOLUTE; var pidlo: PCIDLIST_ABSOLUTE): HRESULT; stdcall; // $30
function LegacyModify(unpin: PCIDLIST_ABSOLUTE; pin: PCIDLIST_ABSOLUTE): HRESULT; stdcall; // $38
//Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Taskband", L"FavoritesChanges
function GetChangeCount(var FavoritesChanges: ULONG): HRESULT; stdcall;// $40
function IsPinned(pin: PCIDLIST_ABSOLUTE): HRESULT; stdcall; // $48 S_OK = pinned S_FALSE = not pinned
function GetPinnedItem(pidl: PCIDLIST_ABSOLUTE; var pidlI: PCIDLIST_ABSOLUTE): HRESULT; stdcall; // $50
function GetAppIDForPinnedItem(pidl: PCIDLIST_ABSOLUTE; var apID: USHORT): HRESULT; stdcall; // $58
function ItemChangeNotify(pidl1: PCIDLIST_ABSOLUTE; pidl2: PCIDLIST_ABSOLUTE): HRESULT; stdcall; // $60
function UpdateForRemovedItemsAsNecessary: HRESULT; stdcall; // $68
function PinShellLink(us: USHORT; var ish: IShellLinkW): HRESULT; stdcall; // $70
function GetPinnedItemForAppID(apID: USHORT; var pidl: PCIDLIST_ABSOLUTE): HRESULT; stdcall; // $78
function Modify(unpin: PCIDLIST_ABSOLUTE; pin: PCIDLIST_ABSOLUTE; caller: PLMC): HRESULT; stdcall; // $80
end;
LPNOTIFYITEM = ^tagNOTIFYITEM;
tagNOTIFYITEM = record
pszExeName: LPWSTR;
pszIconText: LPWSTR;
hIcon: HICON;
hWnd: HWND;
dwUserPref: DWORD;
uID: UINT;
guitItem: TGUID;
end;
NOTIFYITEM = tagNOTIFYITEM;
INotificationCB = interface(IUnknown)
[SID_INotificationCB]
function Notify(dwMessage: DWORD; var pNotifyItem: NotifyItem): HRESULT; stdcall;
end;
ITrayNotify = interface(IUnknown)
[SID_ITrayNotify]
function RegisterCallback(var pNotifyCB: INotificationCB): HRESULT; stdcall;
function SetPreference(pNotifyItem: LPNOTIFYITEM): HRESULT; stdcall;
function EnableAutoTray(bTraySetting: BOOL): HRESULT; stdcall;
end;
{ interface IShellDispatch6 Windows 8+ }
IShellDispatch6 = interface(IShellDispatch5)
[SID_IShellDispatch6]
function SearchCommand: HRESULT; stdcall; { [helpstring] }
end;
type
TOnTaskbarPinChange = procedure(Sender: TObject) of Object;
TTaskbarPinner = class (TObject)
private
FOnTaskbarPinChange: TOnTaskbarPinChange;
procedure SetOnTaskbarPinChange(const Value: TOnTaskbarPinChange);
function GetPinnedList: TStrings;
public
published
property OnTaskbarPinChange: TOnTaskbarPinChange read FOnTaskbarPinChange write SetOnTaskbarPinChange;
property Items: TStrings read GetPinnedList;
end;
implementation
{ TTaskbarPinner }
function TTaskbarPinner.GetPinnedList: TStrings;
begin
end;
procedure TTaskbarPinner.SetOnTaskbarPinChange(
const Value: TOnTaskbarPinChange);
begin
FOnTaskbarPinChange := Value;
end;
end.
Using IPinnedList3 Modify will pin it or unpin it.
procedure TForm2.btnPinUnpinClick(Sender: TObject);
var
hr: HRESULT;
pl: IPinnedList3;
pidl: PItemIDList; // PItemIDList = PCIDLIST_ABSOLUTE
buffer: array[0..MAX_PATH] of WideChar;
begin
CoInitialize(nil);
hr := CoCreateInstance(CLSID_TaskbandPin, nil, CLSCTX_INPROC_SERVER, IID_IPinnedList3, pl);
if Succeeded(hr) then
begin
StringToWideChar(ParamStr(0), buffer, (High(buffer) - Low(buffer)+1));
pidl := ILCreateFromPath(@buffer);
hr := pl.IsPinned(PCIDLIST_ABSOLUTE(pidl));
if hr = S_OK then // is already pinned, let's unpin
hr := pl.Modify(PCIDLIST_ABSOLUTE(pidl), nil, PLMC_EXPLORER)
else // this is the undocumented call to pin the pidl
hr := pl.Modify(nil, PCIDLIST_ABSOLUTE(pidl), PLMC_EXPLORER);
OleCheck(hr);
ILFree(pidl);
end;
CoUninitialize;
end;
If you want the "complete" demo experiment, here https://github.com/vhanla/DelphiExperiments/blob/main/VCL/Undocumented/Pin2Taskbar/TaskbarPinner.pas
But keep in mind, that it still needs to check Windows versions, maybe update IDL changes, handling the async answer for the new permission prompt, etc.
Upvotes: 1
Reputation: 595295
There is no public API available which allows an app to directly pin itself programmatically to the Taskbar. Pinning is strictly a user-controlled action.
But, there is a public API available which allows an app to ask the user to pin the app to the Taskbar on the app's behalf. Details are on Microsoft's site:
That being said, there are a number of requirements before you can use this feature. For instance, you have to register to get an unlock code from Microsoft first to use the API. And then you have to make sure your app is running in the foreground, and use the APIs to query if the Taskbar is available, if it allows pinning, and if your app is not already pinned, before you can then ask the user to pin your app.
Upvotes: 2