-6

In Delphi, create a VCL Forms Application. Use the 64-bit Windows platform if you are on a 64-bit Windows.

Use the following code:

unit Unit1;

interface

uses
  CodeSiteLogging,
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    procedure GetControlPanelItems;
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses
  Winapi.ShlObj, Winapi.ShellAPI, System.Win.ComObj;

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  GetControlPanelItems;
end;

procedure TForm1.GetControlPanelItems;
var
  psfDeskTop: IShellFolder;
  psfControl: IShellFolder;
  pidControl: PITEMIDLIST;
  pidChild: PITEMIDLIST;
  pidAbsolute: PItemIdList;
  pEnumList: IEnumIDList;
  celtFetched: ULONG;
  FileInfo: SHFILEINFOW;
  ShExeInfo: SHELLEXECUTEINFO;
begin
  OleCheck(SHGetDesktopFolder(psfDeskTop));
  OleCheck(SHGetSpecialFolderLocation(0, CSIDL_CONTROLS, pidControl));
  OleCheck(psfDeskTop.BindToObject(pidControl, nil, IID_IShellFolder, psfControl));
  OleCheck(psfControl.EnumObjects(0, SHCONTF_NONFOLDERS or SHCONTF_INCLUDEHIDDEN or SHCONTF_FOLDERS, pEnumList));

  while pEnumList.Next(1, pidChild, celtFetched) = 0 do
  begin
    pidAbsolute := ILCombine(pidControl, pidChild);
    SHGetFileInfo(LPCTSTR(pidAbsolute), 0, FileInfo, SizeOf(FileInfo), SHGFI_PIDL or SHGFI_DISPLAYNAME or SHGFI_TYPENAME);

    CodeSite.Send('TForm1.GetControlPanelItems: szDisplayName', FileInfo.szDisplayName);

    // Exe-Info:
    ZeroMemory(@ShExeInfo, SizeOf(ShExeInfo));
    ShExeInfo.cbSize := SizeOf(ShExeInfo);
    ShExeInfo.lpVerb := 'Open';
    // control panel item's PIDL:
    ShExeInfo.lpIDList := pidAbsolute;
    ShExeInfo.nShow := SW_SHOWNORMAL;
    ShExeInfo.fMask := SEE_MASK_IDLIST;
    //ShExeInfo.lpFile := ???
    //ShExeInfo.lpDirectory := ???

    CodeSite.Send('TForm1.GetControlPanelItems: ShExeInfo.lpFile', ShExeInfo.lpFile);
    CodeSite.Send('TForm1.GetControlPanelItems: ShExeInfo.lpDirectory', ShExeInfo.lpDirectory);
  end;
end;

end.

This gets me the Display names of the control panels.

But how can I get the file-paths? (ShExeInfo.lpDirectory, ShExeInfo.lpFile)

user1580348
  • 5,721
  • 4
  • 43
  • 105
  • 1
    AFAIK, you can't. Not all of the Control Panel items are executables. Many are run using `rundll32.exe` and are not directly executable stand-alone. – Ken White Jan 20 '18 at 21:39
  • 1
    In addition, one executable could also contain more than one control panel applet. – Jerry Dodge Jan 20 '18 at 21:47
  • 3
    What us the underlying problem. You aren't interested in knowing their names I guess. You want to invoke them perhaps? – David Heffernan Jan 20 '18 at 22:21
  • I need to create shell link shortcuts (.LNK) to the control panel applets. So when I double-click the shortcut (.LNK) the control panel applet is opened. But I need the names too, of course, for that I use FileInfo.szDisplayName. And David, please stop downvoting my questions! – user1580348 Jan 21 '18 at 00:29
  • You know the `ITEMIDLIST`, so set it up to your created link (`IShellLink`) by the `SetIDList` method. – Victoria Jan 21 '18 at 01:39
  • P.S. for getting display name one might use `SHGetNameFromIDList` function. And ask proper `HRESULT` constant in your loop (or use e.g. `Succeeded` function). – Victoria Jan 21 '18 at 02:23
  • 1
    There aren't necessarily files names for each applet. So what you ask for can't be achieved. – David Heffernan Jan 21 '18 at 06:09
  • 2
    There are 5 downvotes. Not even I have that much power. I was responding to your comment where you insist on having filenames. I guess you now realise that you were mistaken in that regard. – David Heffernan Jan 21 '18 at 16:28
  • 1
    If you think your item identifiers are files, you can use SHGetPathFromIDList. – Sertac Akyuz Jan 22 '18 at 00:07

1 Answers1

1

As others mentioned here, it might be worthless to try getting file name of a certain applet binary as there can be more than one applet implemented in a single binary. For your overall task, dropped in your comment, creating shell shortcut links, simply use absolute ITEMIDLIST that you know in your loop, and set it to the created IShellLink object by the SetIDList method.

Victoria
  • 7,822
  • 2
  • 21
  • 44
  • Downvoter, care to comment, please? That is a correct approach. Even MSDN explains the `SetIDList` like _"This method is useful when an application needs to **set a Shell link to an object that is not a file, such as a Control Panel application**, a printer, or another computer."_ – Victoria Jan 21 '18 at 21:26