Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2010年12月11日] [更新 2013年07月02日] Delphi サンプルプログラム集
818_シェル名前空間と項目識別子
動作確認等 Windows 7 U64(SP1) + Delphi XE Pro
Delphi 2010 以降,Windows XP 以降専用
818_ShellNameSpace_PItemIDList.zip [2,690 KB] 2013年07月02日版 (EXE 同梱)



  • 2013年07月02日
  • 特殊フォルダの項目識別子の取得に KNOWNFOLDERID を使用するサンプルを追加




シェル名前空間と項目識別子 ( PIDL ) - デスクトップが大元のルート


本サイトには,ファイルのリストやフォルダツリーのサンプルがありますが,これらは,特殊フォルダは正常に表示されない場合があります.また,たとえ表示されても,エクスプローラでの表示とは違ったり,アイコンが表示されない場合もあります.
これらをエクスプローラのように表示するには,「シェル名前空間 (シェルネームスペース)」の機能を使用します.シェル名前空間に関する説明記事が [也工房] の以下のページにあります. シェル名前空間では,フォルダ,ファイルは,項目識別子 (PItemIDList 型) というオブジェクトと,シェル名前空間フォルダオブジェクト (以下,単にフォルダオブジェクト) 等で管理しています.
List という名前があるように,その中にフォルダとファイル名等,複数の情報が格納されています.
項目識別子は,アイテム ID または PIDL と記述されていることもあります.シェル名前空間では大元のルートフォルダは [デスクトップ] です.

このページのサンプルは,この項目識別子の操作の基本的な動作確認用です.




01_フォルダ・ファイルの項目識別子を求める


シェル名前空間でフォルダ,ファイルを扱うには,それらの項目識別子を取得する必要があります.そのテストです.
特殊フォルダについては,SHGetFolderLocation という専用の関数があります.また一般のフォルダ,ファイル (以下,ファイルシステムのフォルダ,ファイル) には ILCreateFromPath 関数があります.この ILCreateFromPath 関数は,仮想フォルダを GUID 文字列で与えれば項目識別子を取得できます.
シェル名前空間に関する関数 (メソッド) は,Delphi 2010 以降に実装されたものも多く,Delphi 2010 よりも前のバージョンで使用するには,利用者が定義しなければなりません.


図1
設計時画面

Windows 7 の「デスクトップ」の項目識別子を求め,その項目識別子からパス名を取得した結果です.
エクスプローラでの表示名は,デスクトップですが,ディレクトリもあります.そのディレクトリのフルパスが取得されています.


図2
Windows 7 の場合
  • エクスプローラの表示名とは違う

Windows XP の場合です.やはり実パス,ディレクトリ名が取得されていますが,この実パスをエクスプローラで確認してみると,実際の [デスクトップ] とは違っています.
取得した項目識別子を元に,この次のページの [819_シェル名前空間とフォルダビュー (ファイルリスト)] のサンプルコードでファイルをリストすると,実際のデスクトップと同じとなります.項目識別子には,そのような情報が含まれていることになります.


図3
Windows XP の場合
  • 項目識別子から取得した表示名は,Windows のバージョンによって異なる場合がある

[マイコンピュータ] の項目識別子を取得した後,その項目識別子からパス名を取得して表示した結果ですが, 空文字となっています.
仮想フォルダは,ディスク上に,実際のディレクトリは存在しません.そのため空文字となります.


図4
マイコンピュータ (仮想フォルダ)
  • 項目識別子から取得した表示名は空

このプロジェクトの EXE の項目識別子を求め,その項目識別子からフルパスを求めた結果です.
実在するフォルダやファイルは,実際のパス名を取得することができます.


図5
一般のファイル
  • このプロジェクトの EXE ファイル
  • 正しく取得できている

リスト1
フォルダやファイルの項目識別子を求める
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ShlObj;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses ActiveX;

{$R *.dfm}

//=============================================================================
//  特殊フォルダの項目識別子を求める
//  CSIDL値から取得
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  Buff : array[0..MAX_PATH - 1] of Char;
  pidl : PItemIDList;
begin
  //デスクトップの項目識別子を取得
  SHGetFolderLocation(0, CSIDL_DESKTOP, 0, 0, pidl);

  //確認のために取得した項目識別子からパス名を取得
  SHGetPathFromIDList(pidl, Buff);
  //取得したフルパス名を表示
  MessageBox(0, Buff, '結果', MB_OK);

  CoTaskMemFree(pidl);
end;

//=============================================================================
//  仮想フォルダの項目識別子を求める
//  GUID文字列から取得
//  ここでは[マイコンピュータ](表示名はWindowsのバージョンによって異なる)
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  Buff : array[0..MAX_PATH - 1] of Char;
  pidl : PItemIDList;
begin
  //項目識別子を取得
  pidl := ILCreateFromPath('::{20D04FE0-3AEA-1069-A2D8-08002B30309D}');

  //確認のために取得した項目識別子からパス名を取得
  SHGetPathFromIDList(pidl, Buff);
  //取得したフルパス名を表示
  //実際のファイルシステムにはないのでSHGetPathFromIDListでは取得できない
  MessageBox(0, Buff, '結果', MB_OK);

  CoTaskMemFree(pidl);
end;

//=============================================================================
//  一般の(ファイルシステムの)フォルダやファイルの項目識別子を求める
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  APath : String;
  Buff  : array[0..MAX_PATH - 1] of Char;
  pidl  : PItemIDList;
begin
  //このプロジェクトのEXE名の項目識別子を取得
  APath := ParamStr(0);
  pidl  := ILCreateFromPath(PChar(APath));

  //確認のために取得した項目識別子からパス名を取得
  SHGetPathFromIDList(pidl, Buff);
  //取得したフルパス名を表示
  MessageBox(0, Buff, '結果', MB_OK);

  CoTaskMemFree(pidl);
end;

end.




02_フォルダオブジェクトとバインド


前のテストでは仮想フォルダの場合,項目識別子からパス名を求めると,エクスプローラで表示される表示名と違っていたり,仮想フォルダは空文字になってしまいました.仮想フォルダはファイルシステム上に実際には存在しないフォルダのためです.エクスプローラで表示される表示名を取得するには専用の関数を使用します.この関数を使うにはフォルダオブジェクトというものを使用します.

項目識別子は,どこのフォルダの階層下であるかの情報を必要とします.上のテストプログラムではデスクトップがそのフォルダでした.あるフォルダ内のファイルリストを取得する場合は,各々のファイルの項目識別子は,このフォルダの階層下ということになります.このフォルダのシェル名前空間におけるオブジェクトがフォルダオブジェクトです.
インターフェイスとして,IShellFolder またはその継承インターフェイスを使用します.

フォルダオブジェクトを取得するには,対象となるフォルダの項目識別子をデスクトップのフォルダオブジェクトに対して「バインド」という操作を行います.この操作によって,デスクトップとそのフォルダとの関連付けができることになります.
フォルダオブジェクトを使用して,項目識別子を求めたり,逆に項目識別子から表示名を求める関数は,いくつかの引数の設定が必要ですので,以下のテストでは別の関数として定義しています.

テストの結果,仮想フォルダもエクスプローラと同じ表示名が取得できています.また,ファイルシステム上の元のフルパスも取得できています.このフルパス相当の値は,仮想フォルダの場合,GUID 文字列となります.
デスクトップをフォルダオブジェクトとして求めた項目識別子を,絶対値相当の項目識別子,あるいは完全項目識別子 (完全 PIDL) と言うことがあります.


図6
設計時画面
図7
[コンピュータ]
  • Windows XP では [マイコンピュータ]
  • エクスプローラと同じ表示名が取得できている
  • 仮想フォルダでは GUID 文字列が他のフォルダでのフルパスに相当する
図8
このプロジェクトの EXE 名
  • 項目識別子から表示名を取得する関数の引数に SHGDN_FORPARSING を指定するとフルパスが取得できる

リスト2
フォルダオブジェクトを使用したテスト
項目識別子を取得して,その項目識別子からフルパスまたは表示名を取得
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ShlObj;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses ActiveX;

{$R *.dfm}

//-----------------------------------------------------------------------------
//  相対項目識別子からフォルダまたはファイル名を求める
//  仮想フォルダはGUID文字列として求まる
//
//  第1引数  対象の項目識別子を取得する際に使用したシェルフォルダオブジェクト
//  第2引数  対象の項目識別子.ShFolder階層下の相対値
//  第3引数  フルパスを取得する場合は,SHGDN_FORPARSING
//           ファイル名だけ取得する場合は,SHGDN_NORMALまたはSHGDN_INFOLDER
//-----------------------------------------------------------------------------
function GetDispNameFromPItemID(ShFolder: IShellFolder; ItemID:
  PItemIDList;Flag: DWORD=SHGDN_NORMAL): String;
var
  StrRet : TStrRet;
begin
  Result := '';
  if ShFolder = nil then exit;
  if ItemID = nil then exit;

  FillChar(StrRet, SizeOf(StrRet), 0);
  if NOERROR = ShFolder.GetDisplayNameOf(ItemID, Flag, StrRet) then begin
    Result := StrRet.pOleStr;
  end;
end;

//-----------------------------------------------------------------------------
//  (ディレクトリ名を除いた)ファイル名を相対項目識別子に変換する関数
//
//  ShFolderにディレクトリ部分に相当するフォルダオブジェクト,Pahtにはディレク
//  トリ部分を除いたファイル名を指定する
//-----------------------------------------------------------------------------
function GetPItemIDFromFileName(ShFolder:IShellFolder; Path:String): PItemIDList;
var
  Len   : Cardinal;
  Flags : Cardinal;
begin
  Flags := 0;
  Len   := Length(Path);
  ShFolder.ParseDisplayName(Application.Handle,
                            nil,
                            PChar(StringToOleStr(Path)),
                            Len,
                            Result,
                            Flags);
end;

//=============================================================================
//  仮想フォルダの項目識別子からファイル名を取得
//  ここでは[マイコンピュータ]
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  pDesktopFolder : IShellFolder;
  APath          : String;
  pidlChild      : PItemIDList;
  DispName1      : String;
  DispName2      : String;
  DispName3      : String;
begin
  //[デスクトップ]のシェルフォルダオブジェクトを取得
  SHGetDesktopFolder(pDesktopFolder);

  //[マイコンピュータ]のGUIDパス名(表示名はWindowsのバージョンによって異なる)
  APath := '::{20D04FE0-3AEA-1069-A2D8-08002B30309D}';

  //そのシェルフォルダオブジェクト階層下として[マイコンピュータ]のPIDLを取得
  pidlChild := GetPItemIDFromFileName(pDesktopFolder, APath);

  //取得したPIDLからバス名(表示名)を取得して確認表示
  DispName1 := GetDispNameFromPItemID(pDesktopFolder, pidlChild, SHGDN_NORMAL);
  DispName2 := GetDispNameFromPItemID(pDesktopFolder, pidlChild, SHGDN_INFOLDER);
  DispName3 := GetDispNameFromPItemID(pDesktopFolder, pidlChild, SHGDN_FORPARSING);

  DispName1 := DispName1 + sLineBreak + DispName2 + sLineBreak + DispName3;
  MessageBox(0, PChar(DispName1), '結果', MB_OK);

  //メモリの解放
  CoTaskMemFree(pidlChild);
  pDesktopFolder := nil;
end;

//=============================================================================
//  一般のフォルダ・ファイルの項目識別子と,項目識別子からファイル名を取得
//
//  このコードは,Delphi 2007以前でコンパイルしても,Window 2000では例外発生
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  pDesktopFolder : IShellFolder;
  pShellFolder   : IShellFolder;
  AFolder        : String;
  APath          : String;
  pidlDesktop    : PItemIDList;
  pidlFolder     : PItemIDList;
  pidlChild      : PItemIDList;
  DispName1      : String;
  DispName2      : String;
  DispName3      : String;
begin
  //[デスクトップ]のシェルフォルダオブジェクトを取得
  SHGetDesktopFolder(pDesktopFolder);
  //デスクトップの項目識別子を取得
  SHGetFolderLocation(0, CSIDL_DESKTOP, 0, 0, pidlDesktop);

  //対象となるフォルダ
  AFolder := ExtractFileDir(Application.ExeName);
  //そのフォルダ内のファイル
  APath := ExtractFileName(Application.ExeName);

  //対象フォルダの項目識別子(デスクトップからの完全項目識別子(絶対値相当))
  //ここで求まるpidlFolderが,ファイルシステムのディレクトリに相当する
  pidlFolder := ILCreateFromPath(PChar(AFolder));

  //対象となるフォルダの項目識別子とデスクトップの項目識別子を比較
  //同じなら対象フォルダは[デスクトップ]フォルダ
  if pDesktopFolder.CompareIDs(0, pidlFolder, pidlDesktop) = 0 then begin
    pShellFolder := pDesktopFolder;
  end else begin
    //対象の項目識別子を[デスクトップ]のシェルフォルダオブジェクトにバインド
    //対象のシェルフォルダオブジェクトpShellFolderが求まる
    pDesktopFolder.BindToObject(pidlFolder, nil, IShellFolder, pShellFolder);
  end;

  pidlChild := nil;

  //対象ファイルの,対象フォルダオブジェクトに対する相対項目識別子を求める
  pidlChild := GetPItemIDFromFileName(pShellFolder, APath);

  //取得した項目識別子から表示名を取得して確認表示
  DispName1 := GetDispNameFromPItemID(pShellFolder, pidlChild, SHGDN_NORMAL);
  DispName2 := GetDispNameFromPItemID(pShellFolder, pidlChild, SHGDN_INFOLDER);
  DispName3 := GetDispNameFromPItemID(pShellFolder, pidlChild, SHGDN_FORPARSING);

  DispName1 := DispName1 + sLineBreak + DispName2 + sLineBreak + DispName3;
  MessageBox(0, PChar(DispName1), '結果', MB_OK);

  //メモリの解放
  CoTaskMemFree(pidlDesktop);
  CoTaskMemFree(pidlFolder);
  CoTaskMemFree(pidlChild);
  pDesktopFolder := nil;
  pShellFolder   := nil;
end;

end.




03_相対 PIDL からフルパス相当の完全 PIDL に変換


あるフォルダ内のファイルを扱うには,その相対項目識別子を使用しますが,場合によっては,そのファイルのフルパス相当の項目識別子 (完全項目識別子,完全 PIDL )が必要なことがあります.

この完全 PIDL を求めるには,前のテストのように,フォルダオブジェクトからも求めることができますが,フォルダの完全項目識別子と,そのファイルの相対項目識別子からも取得できます.そのテストです.このフォルダの完全項目識別子が,ファイルシステムでのディレクトリに,ファイルの相対項目識別子が (ディレクトリ名を除いた) ファイル名に相当することになります.


リスト3
ファイルの相対項目識別子から完全項目識別子を求めるテスト
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ShlObj;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses ActiveX;

{$R *.dfm}

//-----------------------------------------------------------------------------
//  (ディレクトリ名を除いた)ファイル名を項目識別子に変換する関数
//
//  ShFolderにディレクトリ部分に相当するフォルダオブジェクト,Pathにはディレク
//  トリ部分を除いたファイル名を指定する
//-----------------------------------------------------------------------------
function GetPItemIDFromFileName(ShFolder:IShellFolder; Path:String): PItemIDList;
var
  Len   : Cardinal;
  Flags : Cardinal;
begin
  Flags := 0;
  Len   := Length(Path);
  ShFolder.ParseDisplayName(Application.Handle,
                            nil,
                            PChar(StringToOleStr(Path)),
                            Len,
                            Result,
                            Flags);
end;

//=============================================================================
//  ファイルの項目識別子から,絶対値相当の項目識別子を求める
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  pDesktopFolder : IShellFolder;
  pShellFolder   : IShellFolder;
  AFolder        : String;
  AFileName      : String;
  pidlDesktop    : PItemIDList;
  pidlFolder     : PItemIDList;
  pidlChild      : PItemIDList;
  pidlFull       : PItemIDList;
  Buff           : array[0..MAX_PATH - 1] of Char;
begin
  //デスクトップの項目識別子とシェルフォルダオブジェクトを取得
  SHGetFolderLocation(0, CSIDL_DESKTOP, 0, 0, pidlDesktop);
  SHGetDesktopFolder(pDesktopFolder);

  //対象となるフォルダ
  AFolder := ExtractFileDir(Application.ExeName);
  //そのフォルダ内の対象2ァイル名
  AFileName := ExtractFileName(Application.ExeName);

  //対象フォルダの項目識別子
  pidlFolder := ILCreateFromPath(PChar(AFolder));

  //対象フォルダを[デスクトップ]のシェルフォルダオブジェクトにバインド
  //対象のフォルダのシェルフォルダオブジェクトが求まる
  pDesktopFolder.BindToObject(pidlFolder, nil, IShellFolder, pShellFolder);

  //対象ファイルの,対象フォルダオブジェクトに対する相対項目識別子を求める
  pidlChild := GetPItemIDFromFileName(pShellFolder, AFileName);

  //-------  ここまではテスト用の項目識別子の作成 ----------


  //絶対値相当の項目識別子(完全PIDL)に変換
  //pidlFolderはファイルシステムのディレクトリにpidlChildがファイル名に相当する
  //ILCombine関数は,それを結合する
  pidlFull := ILCombine(pidlFolder, pidlChild);

  //完全PIDLからフルパス名を求めて確認表示
  SHGetPathFromIDList(pidlFull, Buff);
  MessageBox(0, Buff, '結果', MB_OK);

  //メモリの解放
  CoTaskMemFree(pidlDesktop);
  CoTaskMemFree(pidlFolder);
  CoTaskMemFree(pidlChild);
  CoTaskMemFree(pidlFull);
end;

end.




04_ファイルシステムのフォルダ名から求めた項目識別子との比較


特殊フォルダを取得する関数に,SHGetFolderPath 関数があります.これはファイルシステム上の特殊フォルダ名を知ることができます.フォルダ名がわかれば項目識別子が求まるわけですが,このフォルダ名から取得した項目識別子と,シェル名前空間の SHGetFolderLocation 関数で求めた項目識別子を比較するテストです.
結果は Windows と Delphi のバージョンによって異なる場合もあります.シェル名前空間でフォルダやファイルを扱う場合,専用の関数類 (メソッド類) を使用した方が無難と言えます.


図9
設計時画面
図10
実行結果

リスト4
項目識別子を求める際に使用する関数による違い
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ShlObj;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses ActiveX;

{$R *.dfm}

//=============================================================================
//  項目識別子の取得方法の違い
//  シェル名前空間から取得した値と,ファイルシステムから取得した値を比較
//
//  結果はOSあるいはDelphiのバージョン,特殊フォルダによって異なる
//  項目識別子を使用したファイル関係の操作は,シェル名前空間で行う方が無難
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  Buff        : array[0..MAX_PATH - 1] of Char;// String;
  APath       : String;
  pidlVirtual : PItemIDList;
  pidlFileSys : PItemIDList;
begin
  //デスクトップの項目識別子をシェル名前空間から取得
  SHGetFolderLocation(0, CSIDL_DESKTOP, 0, 0, pidlVirtual);

  //デスクトップの項目識別子を通常のファイルシステムから取得
  SHGetFolderPath(0, CSIDL_DESKTOP, 0, 0, Buff);
  APath       := Buff;
  pidlFileSys := ILCreateFromPath(PChar(APath));

  //取得した両方の値が等しいかをチェック
  if (ILIsEqual(pidlVirtual, pidlFileSys)) then begin
    MessageBox(0, '項目識別子は同じです.', '結果', MB_OK);
  end else begin
    MessageBox(0, '項目識別子は一致しません.', '結果', MB_OK);
  end;

  CoTaskMemFree(pidlVirtual);
  CoTaskMemFree(pidlFileSys);
end;

end.




05_特殊フォルダの項目識別子取得に KNOWNFOLDERID を使用


マイクロソフトでは,特殊フォルダの取得に CSIDL を使用する方法は互換性のために残し,Vista 以降では,KNOWNFOLDERID を使用することを推奨しています.
この KNOWNFOLDERID には,Windows 7 から導入された [ライブラリ] も含まれています.

次のコードは,この KNOWNFOLDERID を使用して,項目識別子を求め,さらにこの項目識別子から特殊フォルダの GUID 値を IShellFolder の GetDisplayNameOf 関数で求めています.
この GUID 値を ShellExecute 関数の引数として使用して,エクスプローラでコントロールパネルを表示します.
KNOWNFOLDERID の値から項目識別子を取得する SHGetKnownFolderIDList 関数の第 2 引数は,検索のオプション,第 3 引数はアクセストークンの指定です.0 にした場合は,現在のユーザが対象となります.

ここではエクスプローラの表示に,ShellExecute 関数を使用していますが,ShellExecuteEx 関数を使用すれば,項目識別子を直接使用することができます.

仮想フォルダではない場合は,SHGetKnownFolderPath 関数でフォルダ名を取得することができます.
MSDN の KNOWNFOLDERID の記事の中で,Folder Type VIRTUAL とあるのは取得できません.
仮想フォルダ内のフォルダやファイルを列挙したり,操作するには,項目識別子を取得しなければなりませんが,単にエクスプローラで表示するだけであれば,シェルコマンドでも可能です.


図11
コントロールパネルを表示
図12
[ライブラリ] フォルダを表示
  • KNOWNFOLDERID の値を FOLDERID_Libraries にした結果

リスト5
特殊フォルダの項目識別子取得に KNOWNFOLDERID を使用
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses KnownFolders, ShlObj, ShellAPI, ActiveX;

{$R *.dfm}

//-----------------------------------------------------------------------------
//  相対項目識別子からフォルダまたはファイル名を求める
//  仮想フォルダはGUID文字列として求まる
//
//  第1引数  対象の項目識別子を取得する際に使用したシェルフォルダオブジェクト
//  第2引数  対象の項目識別子.ShFolder階層下の相対値
//  第3引数  フルパスを取得する場合は,SHGDN_FORPARSING
//           ファイル名だけ取得する場合は,SHGDN_NORMALまたはSHGDN_INFOLDER
//-----------------------------------------------------------------------------
function GetDispNameFromPItemID(ShFolder: IShellFolder; ItemID:
  PItemIDList;Flag: DWORD=SHGDN_NORMAL): String;
var
  StrRet : TStrRet;
begin
  Result := '';
  if ShFolder = nil then exit;
  if ItemID = nil then exit;

  FillChar(StrRet, SizeOf(StrRet), 0);
  if NOERROR = ShFolder.GetDisplayNameOf(ItemID, Flag, StrRet) then begin
    Result := StrRet.pOleStr;
  end;
end;

//=============================================================================
//  特殊フォルダの取得にFOLDERID_XXXを使用する例
//
//  FOLDERID_XXXの使用には,usesにKnownFoldersが必要
//  ShellExecuteの使用には,usesにShellAPIが必要
//  IShellFolder, PItemIDListの使用には,usesにShlObjが必要
//  CoTaskMemFreeの使用には,usesにActiveXが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  pDesktopFolder : IShellFolder;
  pidl           : PItemIDList;
  StrPath        : String;
  AFile          : String;
  hInstance      : Cardinal;
begin
  //[デスクトップ]のシェルフォルダオブジェクトを取得
  SHGetDesktopFolder(pDesktopFolder);
  //FOLDERID_ControlPanelFolderの項目識別子をpidlChildとして求める
  SHGetKnownFolderIDList(FOLDERID_ControlPanelFolder, 0, 0, pidl);

  //取得した項目識別子からバス名を取得
  //仮想フォルダの場合はGUIDの文字列となる
  StrPath    := GetDispNameFromPItemID(pDesktopFolder, pidl, SHGDN_FORPARSING);
  Edit1.Text := StrPath;

  AFile     := 'explorer.exe';
  hInstance := ShellExecute(Handle,
                            'open',
                            PChar(AFile),
                            PChar(StrPath),
                            nil,
                            SW_SHOW);
  if hInstance <= 32 then begin
    MessageBox(0, '起動に失敗しました', 'エラー', MB_ICONSTOP);
  end;

  CoTaskMemFree(pidl);
end;

end.