|
Delphi Programming / Object Pascal
[掲載 2005年02月11日] [更新 2020年10月21日] Delphi サンプルプログラム集 |
460_特殊フォルダのフルパスを取得 |
動作確認等 |
Windows 7 U64(SP1) + Delphi XE Pro
Windows Vista 以降専用,Windows 7 以降,Delphi XE5 以降専用サンプルあり |
|
460_SpecialFolderPath.zip 6.4 MB] 2020年10月21日版 (EXE 同梱) |
- 2013年07月06日 (過去の履歴は省略)
- 関数別のサンプルにして,記事の構成を全面的に書き直し,解説的な内容も追加
- 2014年01月25日
- GetEnvironmentVariable 関数と TPath 関係の関数のサンプルを追加
|
シェル関係の関数類を使用して,特殊フォルダのパス名を取得するサンプルです.
特殊フォルダのパスを取得する関数には下表のようなものがあります.1 つは従来からある関数類で,CSIDL という値を使用して特殊フォルダのパス名または項目識別子を取得します.もう 1 つは Vista 以降で使用可能になった KNOWNFOLDERID (または FOLDERID) と呼ばれる値を使用して取得します.
SHFolder.pas (SHfolder.dll) にも SHGetFolderPath 関数がありますが,これは Windows 9X, 2000 初期の時代のものです (Internet Explorer 5.0 以前の環境用).互換用に残されています.現在は ShlObj.pas (Shell32.dll) 内にある同名の関数を使用することになります.
全ての特殊フォルダのパス名あるいは項目識別子が取得できるわけではありません.使用しているコンピュータに実装されていないフォルダや操作できないフォルダの取得は失敗します.また,ファイルシステムのパス名しか取得できない関数では,GUID で表わされる仮想フォルダは取得できません.
添付の CSIDL と FOLDERID のリストは,Delphi XE で定義されているものですが,2013年07月05日現在,MSDN オンラインの記事にあるリストと同じで,全ての定数値が含まれています. |
関数名 |
説明 |
SHGetFolderPath |
CSIDL の値から特殊フォルダのパスを取得
SHGetSpecialFolderPath 関数の機能拡張版
仮想フォルダの取得は失敗する
Vista 以降では,内部で SHGetKnownFolderPath を実行する
つまり,SHGetKnownFolderPath のラッパー関数 |
SHGetSpecialFolderPath |
CSIDL の値からフォルダのパスを取得
仮想フォルダの取得は失敗する
サポート外.代わりに ShGetFolderPath を使用する |
SHGetFolderLocation |
CSIDL の値からフォルダの項目識別子を取得
SHGetSpecialFolderLocation 関数の機能拡張版
Vista 以降では,内部で SHGetKnownFolderIDList を実行する
つまり,SHGetKnownFolderIDList のラッパー関数 |
SHGetSpecialFolderLocation |
CSIDL の値からフォルダの項目識別子を取得
サポート外.代わりに SHGetFolderLocation を使用する |
SHGetKnownFolderPath |
Windows Vista 以降用.Delphi 2010 以降で使用可能
KNOWNFOLDERID の値からフォルダのパスを取得 |
SHGetKnownFolderIDList |
Windows Vista 以降用.Delphi 2010 以降で使用可能
KNOWNFOLDERID の値からフォルダの項目識別子を取得 |
SHGetKnownFolderItem |
Windows 7 以降用.Delphi 2010 以降で使用可能
KNOWNFOLDERID の値から IShellItem オブジェクトを取得
IShellItem は Windows XP(SP1) から使用可能 |
[備考]
ネット上に,SHGetFolderPath よりも,サポート外の SHGetSpecialFolderPath の方が新しい,という記事があります.
記事の掲載日や環境の記載がない記事もあり,情報の判断が困難な場合がありますが,おそらく,歴史的には次のように言えるようです.
- Windows 9X の時代に SHGetFolderPath が SHFolder.dll で提供された
- その後,Windows 2000 の Shell32.dll に SHGetSpecialFolderPath が実装された
(Shell32.dll version 4.71 で追加)
- その後 SHGetSpecialFolderPath を拡張した SHGetFolderPath が実装された
(Shell32.dll version 5.0 で追加)
- 互換性のために SHFolder.dll を現在も提供している
古いアプリケーションの中には,SHFolder.dll がないためにエラーとなるものがあるようです.
Internet Explorer 5.0 以前の環境では,SHFolder.dll を使用することになります. |
上の表の一部を除いた関数の簡単なテストです.[Program Files] または [Program Files (x86)] のフォルダのフルパスを取得します.
特殊フォルダのパスを取得する関数の使い方としては,これだけで終わりですが,これだけではサンプルプログラム集として寂しいので,これ以降は,各々の関数で,指定した特殊フォルダのパス名がどんな値になるか,あるいは,どんなファイルがあるのかを確認できるように,TOpenDialog で,あるいはエクスプローラで表示するようにしています.
エクスプローラでは表示できても,TOpenDialog では表示できないフォルダもあります.例えばコントロールパネルが該当します.これは,TOpenDialog の目的から考えても当然と言えます.そのようなことも確認,テストするためのサンプルです. |
|
図1
32 ビットの EXE を作成して実行した結果
- CSIDL_PROGRAM_FILES のパスを取得した場合
- どのボタンでも結果は同じ
|
|
図2
64 ビットの EXE を作成して実行した結果
- CSIDL_PROGRAM_FILES のパスを取得した場合
- どのボタンでも結果は同じ
- 32 ビット版の Windows ではこの結果となる
|
リスト1
特殊フォルダのフルパスを取得する関数類の簡単なテスト |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
uses
ShlObj,
KnownFolders,
ActiveX;
//=============================================================================
// フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
if not CheckWin32Version(6, 0) then begin
Button3.Enabled := False;
Button4.Enabled := False;
end;
end;
//=============================================================================
// CSIDL値からフォルダのパス名を取得
//
// SHGetFolderPath の使用には,uses に ShlObj が必要
// CSIDL_XXX の使用には,usesにShlObj が必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LBuff : array [0..MAX_PATH-1] of Char;
LiRet : HRESULT;
LDir : string;
begin
FillChar(LBuff, SizeOf(LBuff), #0);
LiRet := SHGetFolderPath(0, CSIDL_PROGRAM_FILES, 0, SHGFP_TYPE_CURRENT, @LBuff);
if LiRet = S_OK then begin
LDir := LBuff;
MessageBox(0, PChar(LDir), '結果', MB_OK);
end;
end;
//=============================================================================
// CSIDL 値からフォルダの項目識別子を取得
//
// SHGetFolderLocation の使用には,uses に ShlObj が必要
// CSIDL_XXXの使用には,uses に ShlObj が必要
// CoTaskMemFree の使用には,uses に ActiveX が必要
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LItemID : PItemIDList;
LBuff : array [0..MAX_PATH-1] of Char;
LDir : string;
begin
// 最初に nil にしておくと,解放の際に nil がどうかで判定が可能
LItemID := nil;
if SHGetFolderLocation(0, CSIDL_PROGRAM_FILES, 0, 0, LItemID) = S_OK then begin
//項目識別子からパス名を取得
FillChar(LBuff, SizeOf(LBuff), #0);
SHGetPathFromIDList(LItemID, @LBuff);
LDir := LBuff;
end;
// 項目識別子のメモリアロケートの解放
if LItemID <> nil then CoTaskMemFree(LItemID);
MessageBox(0, PChar(LDir), '結果', MB_OK);
end;
//=============================================================================
// FOLDERID からフォルダのパス名を取得
// Windows Vista 以降用
//
// SHGetKnownFolderPath の使用には,uses に ShlObj が必要
// FOLDERID_XXX の使用には,uses に KnownFolders が必要
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
LpFolderPath : PChar;
LiRet : HRESULT;
FolderPath : string;
begin
LiRet := SHGetKnownFolderPath(FOLDERID_ProgramFiles, 0, 0, LpFolderPath);
if LiRet = S_OK then begin
FolderPath := LpFolderPath;
MessageBox(0, PChar(FolderPath), '結果', MB_OK);
end;
end;
//=============================================================================
// FOLDERID からフォルダの項目識別子を取得
// Windows Vista 以降用
//
// SHGetKnownFolderIDList の使用には,uses に ShlObj が必要
// FOLDERID_XXX の使用には,uses に KnownFolders が必要
// CoTaskMemFree の使用には,uses に ActiveX が必要
//=============================================================================
procedure TForm1.Button4Click(Sender: TObject);
var
LItemID : PItemIDList;
LBuff : array [0..MAX_PATH-1] of Char;
LDir : string;
begin
// 最初に ni lにしておくと,解放の際にnilがどうかで判定が可能
LItemID := nil;
if SHGetKnownFolderIDList(FOLDERID_ProgramFiles, 0, 0, LItemID) = S_OK then begin
// 項目識別子からパス名を取得
FillChar(LBuff, SizeOf(LBuff), #0);
SHGetPathFromIDList(LItemID, @LBuff);
LDir := LBuff;
end;
// 項目識別子のメモリアロケートの解放
if LItemID <> nil then CoTaskMemFree(LItemID);
MessageBox(0, PChar(LDir), '結果', MB_OK);
end;
end.
02_LPTSTR と PWSTR 型の文字列の扱い |
マイクロソフト SDK や MSDN の関数類の定義をみると,文字列型には,プリフィックスが LP と P で始まるものがあります.
上のサンプルでは,SHGetFolderPath 関数の引数が LPTSTR, SHGetKnownFolderPath 関数が PWSTR となっています.プリフィックスの LP は Long Pointer の意味で,主に Windows 9X の 16 ビットの時代に 32 ビット値を処理するための仕組みです.比較的新しい関数類の文字列の多くは,PWSTR のように,プリフィックスがポインタを意味する P となっているようです.
プリフィックスが LP の文字列型を扱う場合,例えば次のように,Char 型の配列を用意して文字列の領域を確保してから,その先頭アドレスを関数の引数に渡します.後は Delphi がうまく処理してくれます. |
LBuff : array [0..MAX_PATH-1] of Char; |
引数に PChar 型を渡す場合は,例えば次のようにしてメモリ領域を確保します.[670_他のアプリへ文字列送信と取得] にも参考コードがあります. |
procedure TForm1.Button1Click(Sender: TObject);
var
LpFolderPath : PChar;
LFolderPath : String;
begin
LpFolderPath := StrAlloc(MAX_PATH + 1);
try
if SHGetFolderPath(0, CSIDL_PROGRAM_FILES, 0, 0, LpFolderPath) = S_OK then begin
LFolderPath := LpFolderPath;
MessageBox(0, PChar(LFolderPath), '結果', MB_OK);
end;
finally
StrDispose(LpFolderPath);
end;
end;
引数に String 型を渡す場合は,例えば次のように,文字列の長さ分のメモリ領域を確保します.これも [670_他のアプリへ文字列送信と取得] に参考コードがあります.
Windows API の中には,指定の引数の値を 0 や -1 にすると,必要なバッファサイズを取得できる関数がありますが,長さ制限のあるパス名の取得関数類にはないようです. |
procedure TForm1.Button2Click(Sender: TObject);
var
LFolderPath : String;
begin
SetLength(LFolderPath, MAX_PATH);
if SHGetFolderPath(0, CSIDL_PROGRAM_FILES, 0, 0, PChar(LFolderPath)) = S_OK then begin
MessageBox(0, PChar(LFolderPath), '結果', MB_OK);
end;
end;
上のコードの処理をしないと,実行後に,下図のような,「アドレス... でアドレス... に対する読み取り違反がおきました」という,メモリへのアクセス関係エラーが発生する場合があります. |
|
図3
メモリアクセス関係エラー |
プリフィックスが P の文字列型の場合は,特にメモリ領域を確保しなくても,筆者が現在確認したところでは,Delphi がうまく処理してくれるようです.上の Button3Click イベントのコードのように取得できるようです.動作を確実にするためには,プリフィックスが LP の文字列型と同じようにしておいた方が無難かも知れません.DLL 内でどのように処理が行われているかは,その関数によるかも知れません.
文字列型の扱いは,ネット上の C++ のサンプルコードを見ても,疑問に思うコードがあり,その使用方法に迷う方もいるようです.その点,Windows API の関数類を使用しないのであれば,Delphi の文字列型の扱い方は単純明快です. |
03_SHGFP_TYPE 定数と KNOWN_FOLDER_FLAG |
SHGetFolderPath と SHGetFolderLocation 関数の第 4 引数には下表のどちらかの値を指定するようになっています.特殊フォルダを別の場所に設定した場合等の判定に利用できます.
SHGetKnownFolderPath と SHGetKnownFolderIDList では,同じような指定に,KNOWN_FOLDER_FLAG 列挙型の値を使用します. |
定数名 |
値 |
説明 |
SHGFP_TYPE_CURRENT |
0 |
実際のパスを取得する場合に指定 |
SHGFP_TYPE_DEFAULT |
1 |
デフォルトのパスを取得する場合に指定 |
04_項目識別子のメモリアロケート ( 割り当て ) 処理 |
項目識別子を使用する関数では,関数の利用後に,アプリケーション側で,関数が割り当てた項目識別子のメモリアロケートを解放する必要がある場合があります.
メモリアロケーションの解放には CoTaskMemFree(LItemID) を使用します.
実際に必要かどうかは,各々の関数の説明で確認することになります. |
if LItemID <> nil then CoTaskMemFree(LItemID); |
シェル関数類の説明には,項目識別子のメモリアロケーションの解放に ILFree を使用するように書いてあるものがあります.
ところが,ILFree の説明を読むと,Windows 2000 以降ではむしろ CoTaskMemFree を使用すべきとあります.そこで,本ページのサンプルでは,CoTaskMemFree を使用しています.
項目識別子のメモリアロケーションの解放を確実にするには,次のように,使用する前に nil を代入しておくと便利です.これは,コンポーネントやクラス類を実行時に生成する場合にも利用できます. |
SHGetFolderPath 関数を使用した特殊フォルダのパス名を取得するサンプルです.
取得可能な特殊フォルダはファイルシステムだけです.仮想フォルダは取得できません. |
|
図4
設計時画面
- 以下を配置
CSIDL を一覧表示する TListBox
フルパスを表示する TEdit
|
|
図5
実行時画面
- ListBox1 に CSIDL 値をリスト表示
- 項目ダブルクリックまたは [ENTER] で該当フォルダ内を表示
- TEditに フルパス表示
|
|
図6
TOpenDialog でデスクトップを表示 |
リスト2
SHGetFolderPath 関数で特殊フォルダのフルパスを取得 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Edit1: TEdit;
OpenDialog1: TOpenDialog;
RadioGroup1: TRadioGroup;
procedure FormCreate(Sender: TObject);
procedure ListBox1Click(Sender: TObject);
procedure ListBox1DblClick(Sender: TObject);
procedure ListBox1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure RadioGroup1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
uses
ShlObj,
ShellAPI;
//=============================================================================
// Form 生成時の処理
// ディスクから CSIDL 値を格納したファイルを読み込み
// ListBox1 の各 Item に AddObject で表示する文字列と各 CSIDL 値をセット
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
LStrList : TStringList;
LTempStrList : TStringList;
LIndex : Integer;
begin
LStrList := TStringList.Create;
LTempStrList := TStringList.Create;
try
LStrList.LoadFromFile('../CSIDL_List.csv');
LStrList.Sort;
// CSV を操作するのでデリミタを設定
LTempStrList.Delimiter := ',';
LTempStrList.StrictDelimiter := True;
ListBox1.Items.BeginUpdate;
for LIndex := 0 to LStrList.Count - 1 do begin
LTempStrList.DelimitedText := LStrList[LIndex];
ListBox1.Items.AddObject(LTempStrList[0], TObject(StrToInt(LTempStrList[1])));
end;
ListBox1.Items.EndUpdate;
finally
FreeAndNil(LStrList);
FreeAndNil(LTempStrList);
end;
end;
//=============================================================================
// ListBox の項目をダブルクリックしたらその CSIDL に相当するのフォルダ名を取得
// そのフォルダを [ファイルを開く] ダイアログまたはエクスプローラで開く
//=============================================================================
procedure TForm1.ListBox1DblClick(Sender: TObject);
var
LIndex : Integer;
LCSIDL : Integer;
LBuff : array [0..MAX_PATH] of Char;
LDir : string;
LExeFile : string;
LhInst : Cardinal;
begin
Edit1.Text := '';
// ListBox1 の Index を取得してその TObject で定義した値を取出す
LIndex := ListBox1.ItemIndex;
LCSIDL := Integer(ListBox1.Items.Objects[LIndex]);
FillChar(LBuff, SizeOf(LBuff), #0);
if SHGetFolderPath(0, LCSIDL, 0, 0, @LBuff) = S_OK then begin
LDir := LBuff;
Edit1.Text := LDir;
// 以下,取得した値の確認用
if (Sender <> nil) and (LDir <> '') then begin
if RadioGroup1.ItemIndex = 0 then begin
OpenDialog1.InitialDir := LDir;
OpenDialog1.Execute;
Update;
end else begin
// 実際には起動アプリが空でもエクスプローラは起動する
// あるいは,起動ファイルにパス名を指定すれば起動する
LExeFile := 'explorer.exe';
LhInst := ShellExecute(Handle,
'open',
PChar(LExeFile),
PChar(LDir),
nil,
SW_SHOW);
if LhInst <= 32 then begin
MessageBox(Handle, '起動に失敗しました.', '情報', MB_ICONINFORMATION);
end;
end;
end;
end;
end;
//=============================================================================
// ListBox1 の項目を選択したら,パス名を取得して Edit1 に表示
//=============================================================================
procedure TForm1.ListBox1Click(Sender: TObject);
begin
ListBox1DblClick(nil);
end;
//=============================================================================
// ListBox1 で [Enter] キーを押したら,ダブルクリックと同じ動作をさせる
//=============================================================================
procedure TForm1.ListBox1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = VK_RETURN then begin
ListBox1DblClick(Sender);
end;
end;
//=============================================================================
// RaditoGroup1 の OnClick イベント処理
// ListBox1 にフォーカス移動して [Enter] キー操作可能にする
//=============================================================================
procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
ListBox1.SetFocus;
end;
end.
06_SHGetFolderLocation 関数 |
SHGetFolderLocation 関数を使用して,特殊フォルダの項目識別子を取得するサンプルです.
項目識別子では,TOpenDialog や ShellExecute 関数でフォルダを表示できません.そこで,[818_シェル名前空間と項目識別子] のサンプルと同じように,デスクトップの IShellFolder を求め,そのメソッド GetDisplayNameOf でフォルダのパス名とエクスプローラで表示される表示名を取得します.
この時取得できるパス名は,仮想フォルダの場合,GUID の文字列となります.これを使用します.
本ページの基本的なコードで使用している SHGetPathFromIDList 関数でも,項目識別子からパス名を取得できますが,仮想フォルダは取得できません. |
|
図7
実行例
- 以下を配置
CSIDL を一覧表示する ListBox1 (TListBox)
フルパスを表示する Edit1 (TEdit)
表示名を表示する Edit2 (TEdit)
|
|
図8
実行
|
|
図9
CSIDL_CONTROLS をエクスプローラで表示 |
リスト3
SHGetFolderLocation 関数で特殊フォルダのフルパスを取得 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Edit1: TEdit;
OpenDialog1: TOpenDialog;
RadioGroup1: TRadioGroup;
Edit2: TEdit;
procedure FormCreate(Sender: TObject);
procedure ListBox1Click(Sender: TObject);
procedure ListBox1DblClick(Sender: TObject);
procedure ListBox1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure RadioGroup1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
uses
ShlObj,
ActiveX,
ShellAPI;
//=============================================================================
// Form 生成時の処理
// ディスクから CSIDL 値を格納したファイルを読み込み
// ListBox1 の各 Item に AddObject で表示する文字列と各 CSIDL 値をセット
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
LStrList : TStringList;
LTempStrList : TStringList;
LIndex : Integer;
begin
LStrList := TStringList.Create;
LTempStrList := TStringList.Create;
try
LStrList.LoadFromFile('../CSIDL_List.csv');
LStrList.Sort;
// CSV を操作するのでデリミタを設定
LTempStrList.Delimiter := ',';
LTempStrList.StrictDelimiter := True;
ListBox1.Items.BeginUpdate;
for LIndex := 0 to LStrList.Count - 1 do begin
LTempStrList.DelimitedText := LStrList[LIndex];
ListBox1.Items.AddObject(LTempStrList[0], TObject(StrToInt(LTempStrList[1])));
end;
ListBox1.Items.EndUpdate;
finally
FreeAndNil(LStrList);
FreeAndNil(LTempStrList);
end;
end;
//-----------------------------------------------------------------------------
// 相対項目識別子からフォルダまたはファイル名を求める
// 仮想フォルダは GUID 文字列として求まる
//
// 第 1 引数 対象の項目識別子を取得する際に使用したシェルフォルダオブジェクト
// 第 2 引数 対象の項目識別子.ShFolder 階層下の相対値
// 第 3 引数 フルパスを取得する場合は,SHGDN_FORPARSING
// ファイル名だけ取得する場合は,SHGDN_NORMAL または SHGDN_INFOLDER
//-----------------------------------------------------------------------------
function GetDispNameFromPItemID(ShFolder: IShellFolder; ItemID:
PItemIDList; Flag: DWORD=SHGDN_NORMAL): string;
var
LStrRet : TStrRet;
begin
Result := '';
if ShFolder = nil then exit;
if ItemID = nil then exit;
FillChar(LStrRet, SizeOf(LStrRet), #0);
if NOERROR = ShFolder.GetDisplayNameOf(ItemID, Flag, LStrRet) then begin
Result := LStrRet.pOleStr;
end;
end;
//=============================================================================
// TListBox の項目をダブルクリックしたらその CSIDL に相当する項目識別子を取得
// その項目識別子に相当するフォルダを
// [ファイルを開く]ダイアログまたはエクスプローラで開く
//=============================================================================
procedure TForm1.ListBox1DblClick(Sender: TObject);
var
LIndex : Integer;
LCSIDL : Integer;
LItemID : PItemIDList;
LpDesktopFolder : IShellFolder;
LDisplayName : string;
LFolderPath : string;
LExeFile : string;
LhInst : Cardinal;
begin
Edit1.Text := '';
Edit2.Text := '';
LItemID := nil;
// ListBox1 の Index を取得してその TObject で定義した値を取出す
LIndex := ListBox1.ItemIndex;
LCSIDL := Integer(ListBox1.Items.Objects[LIndex]);
// CSIDL に相当する項目識別子を取得
if SHGetFolderLocation(0, LCSIDL, 0, 0, LItemID) = S_OK then begin
// [デスクトップ] のシェルフォルダオブジェクトを取得
SHGetDesktopFolder(LpDesktopFolder);
// 取得した項目識別子から表示名とバス名を取得
LDisplayName := GetDispNameFromPItemID(LpDesktopFolder, LItemID, SHGDN_INFOLDER);
LFolderPath := GetDispNameFromPItemID(LpDesktopFolder, LItemID, SHGDN_FORPARSING);
Edit1.Text := LFolderPath;
Edit2.Text := LDisplayName;
// 以下,取得した値の確認用
if (Sender <> nil) and (LFolderPath <> '') then begin
if RadioGroup1.ItemIndex = 0 then begin
OpenDialog1.InitialDir := LFolderPath;
OpenDialog1.Execute;
Update;
end else begin
// 実際には起動アプリが空でもエクスプローラは起動する
// あるいは,起動ファイルにパス名を指定すれば起動する
LExeFile := 'explorer.exe';
LhInst := ShellExecute(Handle,
'open',
PChar(LExeFile),
PChar(LFolderPath),
nil,
SW_SHOW);
if LhInst <= 32 then begin
MessageBox(Handle, '起動に失敗しました.', '情報', MB_ICONINFORMATION);
end;
end;
end;
end;
// 項目識別子のメモリアロケートの解放
if LItemID <> nil then CoTaskMemFree(LItemID);
end;
//=============================================================================
// ListBox1 の項目を選択したら,パス名を取得して Edit1 に表示
//=============================================================================
procedure TForm1.ListBox1Click(Sender: TObject);
begin
ListBox1DblClick(nil);
end;
//=============================================================================
// ListBox1 で [Enter] キーを押したら,ダブルクリックと同じ動作をさせる
//=============================================================================
procedure TForm1.ListBox1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = VK_RETURN then begin
ListBox1DblClick(Sender);
end;
end;
//=============================================================================
// RaditoGroup1 の OnClick イベント処理
// ListBox1 にフォーカス移動して [Enter] キー操作可能にする
//=============================================================================
procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
ListBox1.SetFocus;
end;
end.
07_SHGetFolderLocation 関数と ShellExecuteEx |
SHGetFolderLocation 関数を使用して,特殊フォルダの項目識別子を取得するサンプルです.
ShellExecuteEX 関数は,項目識別子を指定してフォルダをエクスプローラで表示できます. |
リスト4
SHGetFolderLocation 関数で特殊フォルダの項目識別子を取得
取得したフォルダを,ShellExecuteEx 関数を使用してエクスプローラで表示する場合 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Edit1: TEdit;
procedure FormCreate(Sender: TObject);
procedure ListBox1Click(Sender: TObject);
procedure ListBox1DblClick(Sender: TObject);
procedure ListBox1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
uses
ShlObj,
ShellAPI,
ActiveX;
//=============================================================================
// Form生成時の処理
// ディスクから CSIDL 値を格納したファイルを読み込み
// ListBox1 の各 Item に AddObject で表示する文字列と各 CSIDL 値をセット
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
LStrList : TStringList;
LTempStrList : TStringList;
LIndex : Integer;
begin
LStrList := TStringList.Create;
LTempStrList := TStringList.Create;
try
LStrList.LoadFromFile('../CSIDL_List.csv');
LStrList.Sort;
// CSV を操作するのでデリミタを設定
LTempStrList.Delimiter := ',';
LTempStrList.StrictDelimiter := True;
ListBox1.Items.BeginUpdate;
for LIndex := 0 to LStrList.Count - 1 do begin
LTempStrList.DelimitedText := LStrList[LIndex];
ListBox1.Items.AddObject(LTempStrList[0], TObject(StrToInt(LTempStrList[1])));
end;
ListBox1.Items.EndUpdate;
finally
FreeAndNil(LStrList);
FreeAndNil(LTempStrList);
end;
end;
//-----------------------------------------------------------------------------
// ShellExecuteEx で指定ファイルを実行
//
// SEE_MASK_NOCLOSEPROCESS
// ShellExcuteEx 実行後,プロセスを維持 Process にプロセスのハンドルを返す
// SEE_MASK_FLAG_NO_UI
// エラー発生時にメッセージBoxを表示しない
// SEE_MASK_IDLIST
// 項目識別子を使用する
//-----------------------------------------------------------------------------
function RunExeFilePIDL(hWnd: HWND; AFile: String; pidlFile: PItemIDList;
Verb: string): Cardinal;
var
LInfo : TShellExecuteInfo;
LRet : DWORD;
begin
FillChar(LInfo, SizeOf(LInfo), 0);
LInfo.cbSize := SizeOf(LInfo);
LInfo.Wnd := hWnd;
LInfo.fMask := SEE_MASK_NOCLOSEPROCESS or SEE_MASK_FLAG_NO_UI or SEE_MASK_IDLIST;
LInfo.lpVerb := PChar(Verb);
LInfo.lpFile := PChar(AFile);
LInfo.lpIDList := pidlFile;
LInfo.nShow := SW_SHOW;
ShellExecuteEx(@LInfo);
// ShellExecuteEx は成功すると hInstApp に 33 以上の値を返す
if LInfo.hInstApp >= 33 then
begin
repeat
LRet := WaitForInputIdle(LInfo.hProcess, 50);
Application.ProcessMessages;
until LRet <> WAIT_TIMEOUT;
end;
CloseHandle(LInfo.hProcess);
Result := LInfo.hInstApp;
end;
//=============================================================================
// TListBox の項目をダブルクリックしたらその CSIDL に相当する項目識別子を取得
// その項目識別子のフォルダをエクスプローラで表示
//=============================================================================
procedure TForm1.ListBox1DblClick(Sender: TObject);
var
LIndex : Integer;
LCSIDL : Integer;
LItemID : PItemIDList;
LBuff : array [0..MAX_PATH] of Char;
LExeFile : string;
LiRet : Cardinal;
begin
Edit1.Text := '';
LItemID := nil;
// ListBox1 の Index を取得してその TObject で定義した値を取出す
LIndex := ListBox1.ItemIndex;
LCSIDL := Integer(ListBox1.Items.Objects[LIndex]);
// LCSIDL に相当する項目識別子を取得
if SHGetFolderLocation(0, LCSIDL, 0, 0, LItemID) = S_OK then begin
// 項目識別子からパス名を取得
// 仮想フォルダは取得できない
FillChar(LBuff, SizeOf(LBuff), #0);
SHGetPathFromIDList(LItemID, LBuff);
Edit1.Text := LBuff;
// 以下,取得した値の確認用
// 実際には起動アプリが空でもエクスプローラは起動する
// あるいは,起動ファイルにパス名を指定すれば起動する
LExeFile := 'explorer.exe';
if Sender <> nil then begin
if LItemID <> nil then begin
LiRet := RunExeFilePIDL(0, LExeFile, LItemID, '');
if LiRet <= 32 then begin
MessageBox(Handle, '起動に失敗しました.', '情報', MB_ICONINFORMATION);
end;
end;
end;
end;
// 項目識別子のメモリアロケートの解放
if LItemID <> nil then CoTaskMemFree(LItemID);
end;
//=============================================================================
// ListBox1 の項目を選択したら,パス名を取得して Edit1 に表示
//=============================================================================
procedure TForm1.ListBox1Click(Sender: TObject);
begin
ListBox1DblClick(nil);
end;
//=============================================================================
// ListBox1 で [Enter] キーを押したら,ダブルクリックと同じ動作をさせる
//=============================================================================
procedure TForm1.ListBox1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = VK_RETURN then begin
ListBox1DblClick(Sender);
end;
end;
end.
08_SHGetKnownFolderPath 関数 |
Windows Vista 以降専用の関数です.
SHGetKnownFolderPath 関数を使用した特殊フォルダのパス名を取得するサンプルです. |
|
図10
実行例
- 以下を配置
FOLDERID を一覧表示する ListBox1 (TListBox)
フルパスを表示する Edit1 (TEdit)
|
|
図11
実行 |
|
図12
FOLDERID_Documents を表示
|
リスト5
SHGetKnownFolderPath 関数で特殊フォルダのフルパスを取得 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Edit1: TEdit;
OpenDialog1: TOpenDialog;
RadioGroup1: TRadioGroup;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure ListBox1Click(Sender: TObject);
procedure ListBox1DblClick(Sender: TObject);
procedure ListBox1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure RadioGroup1Click(Sender: TObject);
private
{ Private 宣言 }
FStrList : TStringList;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
uses
ShlObj,
ShellAPI;
//=============================================================================
// Form生成時の処理
// ディスクからファイルを読み込み
// ListBox1 の各 Item に表示する FOLDERID の文字列をセット
// 実際に使用する FOLDERID の TGUIDは 文字列として TStringList に残す
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
LTempStrList : TStringList;
LIndex : Integer;
begin
FStrList := TStringList.Create;
LTempStrList := TStringList.Create;
try
FStrList.LoadFromFile('../FOLDERID_List.csv');
FStrList.Sort;
// CSV を操作するのでデリミタを設定
LTempStrList.Delimiter := ',';
LTempStrList.StrictDelimiter := True;
for LIndex := 0 to FStrList.Count - 1 do begin
LTempStrList.DelimitedText := FStrList[LIndex];
ListBox1.Items.Add(Trim(LTempStrList[0]));
FStrList[LIndex] := Trim(LTempStrList[1]);
end;
finally
FreeAndNil(LTempStrList);
end;
end;
//=============================================================================
// 終了時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
FreeAndNil(FStrList);
end;
//=============================================================================
// TListBox の項目をダブルクリックしたらその FOLDERID に相当するフォルダ名を取得
// そのフォルダで[ファイル選択]ダイアログまたはエクスプローラで表示
//=============================================================================
procedure TForm1.ListBox1DblClick(Sender: TObject);
var
LIndex : Integer;
LFolderID : TGUID;
LpFolderPath : PChar;
LFolderPath : string;
LExeFile : string;
LhInst : Cardinal;
begin
Edit1.Text := '';
// FOLDERIDのTGUID の値を取得
LIndex := ListBox1.ItemIndex;
LFolderID := StringToGUID(FStrList[LIndex]);
// フォルダのパス名を取得
if SHGetKnownFolderPath(LFolderID, 0, 0, LpFolderPath) = S_OK then begin
LFolderPath := LpFolderPath;
Edit1.Text := LFolderPath;
// 以下,取得した値の確認用
if (Sender <> nil) and (LFolderPath <> '') then begin
if RadioGroup1.ItemIndex = 0 then begin
OpenDialog1.InitialDir := LFolderPath;
OpenDialog1.Execute;
Update;
end else begin
// 実際には起動アプリが空でもエクスプローラは起動する
// あるいは,起動ファイルにパス名を指定すれば起動する
LExeFile := 'explorer.exe';
LhInst := ShellExecute(Handle,
'open',
PChar(LExeFile),
PChar(LFolderPath),
nil,
SW_SHOW);
if LhInst <= 32 then begin
MessageBox(Handle, '起動に失敗しました.', '情報', MB_ICONINFORMATION);
end;
end;
end;
end;
end;
//=============================================================================
// ListBox1 の項目を選択したら,パス名を取得して Edit1 に表示
//=============================================================================
procedure TForm1.ListBox1Click(Sender: TObject);
begin
ListBox1DblClick(nil);
end;
//=============================================================================
// ListBox1 で [Enter] キーを押したら,ダブルクリックと同じ動作をさせる
//=============================================================================
procedure TForm1.ListBox1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = VK_RETURN then begin
ListBox1DblClick(Sender);
end;
end;
//=============================================================================
// RaditoGroup1 の OnClick イベント処理
// ListBox1にフォーカス移動して [Enter] キー操作可能にする
//=============================================================================
procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
ListBox1.SetFocus;
end;
end.
09_SHGetKnownFolderIDList 関数 |
Windows Vista 以降専用の関数です.
SHGetKnownFolderIDList 関数を使用して,特殊フォルダの項目識別子を取得するサンプルです.
項目識別子では,TOpenDialog や ShellExecute 関数でフォルダを表示できません.そこで,[818_シェル名前空間と項目識別子] のサンプルと同じように,デスクトップの IShellFolder を求め,そのメソッド GetDisplayNameOf でフォルダのパス名とエクスプローラで表示される表示名を取得します.
この時取得できるパス名は,仮想フォルダの場合,GUID の文字列となります.これを使用します.
本ページの基本的なコードで使用している SHGetPathFromIDList 関数でも,項目識別子からパス名を取得できますが,仮想フォルダは取得できません. |
|
図13
実行例
- 以下を配置
FOLDERID を一覧表示する ListBox1 (TListBox)
フルパスを表示する Edit1 (TEdit)
表示名を表示する Edit2 (TEdit)
|
|
図14
実行
|
|
図15
FOLDERID_Libraries を表示
- TOpenDialog で表示した画像
この [ライブラリ] は Windows 7 で追加されたフォルダ
|
リスト6
SHGetKnownFolderIDList 関数で特殊フォルダのフルパスを取得 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Edit1: TEdit;
OpenDialog1: TOpenDialog;
RadioGroup1: TRadioGroup;
Edit2: TEdit;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure ListBox1Click(Sender: TObject);
procedure ListBox1DblClick(Sender: TObject);
procedure ListBox1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure RadioGroup1Click(Sender: TObject);
private
{ Private 宣言 }
FStrList : TStringList;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
uses
ShlObj,
ActiveX,
ShellAPI;
//=============================================================================
// Form 生成時の処理
// ディスクからファイルを読み込み
// ListBox1 の各 Item に表示する FOLDERID の文字列をセット
// 実際に使用する FOLDERID の TGUID は文字列として TStringList に残す
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
LTempStrList : TStringList;
LIndex : Integer;
begin
FStrList := TStringList.Create;
LTempStrList := TStringList.Create;
try
FStrList.LoadFromFile('../FOLDERID_List.csv');
FStrList.Sort;
// CSV を操作するのでデリミタを設定
LTempStrList.Delimiter := ',';
LTempStrList.StrictDelimiter := True;
for LIndex := 0 to FStrList.Count - 1 do begin
LTempStrList.DelimitedText := FStrList[LIndex];
ListBox1.Items.Add(Trim(LTempStrList[0]));
FStrList[LIndex] := Trim(LTempStrList[1]);
end;
finally
FreeAndNil(LTempStrList);
end;
end;
//=============================================================================
// 終了時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
FreeAndNil(FStrList);
end;
//-----------------------------------------------------------------------------
// 相対項目識別子からフォルダまたはファイル名を求める
// 仮想フォルダは GUID 文字列として求まる
//
// 第 1 引数 対象の項目識別子を取得する際に使用したシェルフォルダオブジェクト
// 第 2 引数 対象の項目識別子.ShFolder 階層下の相対値
// 第 3 引数 フルパスを取得する場合は,SHGDN_FORPARSING
// ファイル名だけ取得する場合は,SHGDN_NORMAL または SHGDN_INFOLDER
//-----------------------------------------------------------------------------
function GetDispNameFromPItemID(ShFolder: IShellFolder; ItemID:
PItemIDList; Flag: DWORD=SHGDN_NORMAL): string;
var
LStrRet : TStrRet;
begin
Result := '';
if ShFolder = nil then exit;
if ItemID = nil then exit;
FillChar(LStrRet, SizeOf(LStrRet), #0);
if NOERROR = ShFolder.GetDisplayNameOf(ItemID, Flag, LStrRet) then begin
Result := LStrRet.pOleStr;
end;
end;
//=============================================================================
// TListBox の項目をダブルクリックしたらその FOLDERID に相当する項目識別子を取得
// その項目識別子に相当するフォルダで
// [ファイル選択] ダイアログまたはエクスプローラを開く
//=============================================================================
procedure TForm1.ListBox1DblClick(Sender: TObject);
var
LIndex : Integer;
LFolderID : TGUID;
LItemID : PItemIDList;
LpDesktopFolder : IShellFolder;
LDisplayName : string;
LFolderPath : string;
LExeFile : string;
LhInst : Cardinal;
begin
Edit1.Text := '';
Edit2.Text := '';
LItemID := nil;
// FOLDERID の TGUID の値を取得
LIndex := ListBox1.ItemIndex;
LFolderID := StringToGUID(FStrList[LIndex]);
// FOLDERID の TGUID に相当する項目識別子を取得
if SHGetKnownFolderIDList(LFolderID, 0, 0, LItemID) = S_OK then begin
// [デスクトップ] のシェルフォルダオブジェクトを取得
SHGetDesktopFolder(LpDesktopFolder);
// 取得した項目識別子から表示名とバス名を取得
LDisplayName := GetDispNameFromPItemID(LpDesktopFolder, LItemID, SHGDN_INFOLDER);
LFolderPath := GetDispNameFromPItemID(LpDesktopFolder, LItemID, SHGDN_FORPARSING);
Edit1.Text := LFolderPath;
Edit2.Text := LDisplayName;
// 以下,取得した値の確認用
if (Sender <> nil) and (LFolderPath <> '') then begin
if RadioGroup1.ItemIndex = 0 then begin
OpenDialog1.InitialDir := LFolderPath;
OpenDialog1.Execute;
Update;
end else begin
// 実際には起動アプリが空でもエクスプローラは起動する
// あるいは,起動ファイルにパス名を指定すれば起動する
LExeFile := 'explorer.exe';
LhInst := ShellExecute(Handle,
'open',
PChar(LExeFile),
PChar(LFolderPath),
nil,
SW_SHOW);
if LhInst <= 32 then begin
MessageBox(Handle, '起動に失敗しました.', '情報', MB_ICONINFORMATION);
end;
end;
end;
end;
// 項目識別子のメモリアロケートの解放
if LItemID <> nil then CoTaskMemFree(LItemID);
end;
//=============================================================================
// ListBox1 の項目を選択したら,パス名を取得して Edit1 に表示
//=============================================================================
procedure TForm1.ListBox1Click(Sender: TObject);
begin
ListBox1DblClick(nil);
end;
//=============================================================================
// ListBox1 で [Enter] キーを押したら,ダブルクリックと同じ動作をさせる
//=============================================================================
procedure TForm1.ListBox1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = VK_RETURN then begin
ListBox1DblClick(Sender);
end;
end;
//=============================================================================
// RaditoGroup1 の OnClick イベント処理
// ListBox1 にフォーカス移動して [Enter] キー操作可能にする
//=============================================================================
procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
ListBox1.SetFocus;
end;
end.
10_SHGetKnownFolderIDList 関数と ShellExecuteEx |
SHGetKnownFolderIDList 関数を使用して,特殊フォルダの項目識別子を取得するサンプルです. ShellExecuteEX 関数は,項目識別子を指定してフォルダをエクスプローラで表示できます.
この関数は,Windows Vista 以降で使用可能な関数です. |
リスト7
SHGetKnownFolderIDList 関数で特殊フォルダの項目識別子を取得
取得したフォルダを,ShellExecuteEx 関数を使用してエクスプローラで表示する場合 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Edit1: TEdit;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure ListBox1Click(Sender: TObject);
procedure ListBox1DblClick(Sender: TObject);
procedure ListBox1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
private
{ Private 宣言 }
FStrList : TStringList;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
uses
ShlObj,
ShellAPI,
ActiveX;
//=============================================================================
// Form 生成時の処理
// ディスクからファイルを読み込み
// ListBox1 の各 Item に表示する FOLDERID の文字列をセット
// 実際に使用する FOLDERID の TGUID は文字列として TStringList に残す
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
LTempStrList : TStringList;
LIndex : Integer;
begin
FStrList := TStringList.Create;
LTempStrList := TStringList.Create;
try
FStrList.LoadFromFile('../FOLDERID_List.csv');
FStrList.Sort;
// CSV を操作するのでデリミタを設定
LTempStrList.Delimiter := ',';
LTempStrList.StrictDelimiter := True;
for LIndex := 0 to FStrList.Count - 1 do begin
LTempStrList.DelimitedText := FStrList[LIndex];
ListBox1.Items.Add(Trim(LTempStrList[0]));
FStrList[LIndex] := Trim(LTempStrList[1]);
end;
finally
FreeAndNil(LTempStrList);
end;
end;
//=============================================================================
// 終了時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
FreeAndNil(FStrList);
end;
//-----------------------------------------------------------------------------
// ShellExecuteEx で指定ファイルを実行
//
// SEE_MASK_NOCLOSEPROCESS
// ShellExcuteEx 実行後,プロセスを維持Processにプロセスのハンドルを返す
// SEE_MASK_FLAG_NO_UI
// エラー発生時にメッセージ Box を表示しない
// SEE_MASK_IDLIST
// 項目識別子を使用する
//-----------------------------------------------------------------------------
function RunExeFilePIDL(hWnd: HWND; AFile: String; pidlFile: PItemIDList;
Verb: string): Cardinal;
var
LInfo : TShellExecuteInfo;
LRet : DWORD;
begin
FillChar(LInfo, SizeOf(LInfo), 0);
LInfo.cbSize := SizeOf(LInfo);
LInfo.Wnd := hWnd;
LInfo.fMask := SEE_MASK_NOCLOSEPROCESS or SEE_MASK_FLAG_NO_UI or SEE_MASK_IDLIST;
LInfo.lpVerb := PChar(Verb);
LInfo.lpFile := PChar(AFile);
LInfo.lpIDList := pidlFile;
LInfo.nShow := SW_SHOW;
ShellExecuteEx(@LInfo);
// ShellExecuteEx は成功すると hInstAppに 33 以上の値を返す
if LInfo.hInstApp >= 33 then
begin
repeat
LRet := WaitForInputIdle(LInfo.hProcess, 50);
Application.ProcessMessages;
until LRet <> WAIT_TIMEOUT;
end;
CloseHandle(LInfo.hProcess);
Result := LInfo.hInstApp;
end;
//=============================================================================
// TListBox の項目をダブルクリックしたらその FOLDERID に相当する項目識別子を取得
// その項目識別子に相当するフォルダをエクスプローラで開く
//=============================================================================
procedure TForm1.ListBox1DblClick(Sender: TObject);
var
LIndex : Integer;
LFolderID : TGUID;
LItemID : PItemIDList;
LBuff : array [0..MAX_PATH] of Char;
LExeFile : string;
LiRet : Cardinal;
begin
Edit1.Text := '';
LItemID := nil;
// LFolderID の TGUID の値を取得
LIndex := ListBox1.ItemIndex;
LFolderID := StringToGUID(FStrList[LIndex]);
// LFolderID の TGUID に相当する項目識別子を取得
if SHGetKnownFolderIDList(LFolderID, 0, 0, LItemID) = S_OK then begin
// 項目識別子からパス名を取得
// 仮想フォルダは取得できない
FillChar(LBuff, SizeOf(LBuff), #0);
SHGetPathFromIDList(LItemID, LBuff);
Edit1.Text := LBuff;
// 以下,取得した値の確認用
// 実際には起動アプリが空でもエクスプローラは起動する
// あるいは,起動ファイルにパス名を指定すれば起動する
LExeFile := 'explorer.exe';
if Sender <> nil then begin
if LItemID <> nil then begin
LiRet := RunExeFilePIDL(0, LExeFile, LItemID, '');
if LiRet <= 32 then begin
MessageBox(Handle, '起動に失敗しました.', '情報', MB_ICONINFORMATION);
end;
end;
end;
end;
// 項目識別子のメモリアロケートの解放
if LItemID <> nil then CoTaskMemFree(LItemID);
end;
//=============================================================================
// ListBox1 の項目を選択したら,パス名を取得して Edit1 に表示
//=============================================================================
procedure TForm1.ListBox1Click(Sender: TObject);
begin
ListBox1DblClick(nil);
end;
//=============================================================================
// ListBox1 で [Enter] キーを押したら,ダブルクリックと同じ動作をさせる
//=============================================================================
procedure TForm1.ListBox1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = VK_RETURN then begin
ListBox1DblClick(Sender);
end;
end;
end.
11_SHGetKnownFolderItem 関数 |
SHGetKnownFolderItem 関数を使用して,特殊フォルダのパス名を取得するサンプルです.
この関数は,IShellItem オブジェクトを取得します.
サンプルでは,このIShellItem の GetDisplayName メソッドで,フルパス名とエクスプローラで表示する名前を取得しています.項目識別子からパス名を取得するよりもコードが簡単になるのですが,この関数は Windows 7 以降でないと使用できません. |
|
図16
実行例
- 以下を配置
FOLDERID を一覧表示する ListBox1 (TListBox)
フルパスを表示する Edit1 (TEdit)
表示名を表示する Edit2 (TEdit)
|
|
図17
実行
|
|
図18
FOLDERID_ComputerFolder を表示
|
リスト8
SHGetKnownFolderItem 関数で特殊フォルダのフルパスを取得 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Edit1: TEdit;
RadioGroup1: TRadioGroup;
OpenDialog1: TOpenDialog;
Edit2: TEdit;
procedure FormCreate(Sender: TObject);
procedure ListBox1DblClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure ListBox1Click(Sender: TObject);
procedure ListBox1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure RadioGroup1Click(Sender: TObject);
private
{ Private 宣言 }
FStrList : TStringList;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
uses
ShlObj,
ShellAPI,
ActiveX;
//=============================================================================
// Form 生成時の処理
// ディスクからファイルを読み込み
// ListBox1 の各 Item に表示する FOLDERID の文字列をセット
// 実際に使用する FOLDERID の TGUID は文字列として TStringList に残す
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
LTempStrList : TStringList;
LIndex : Integer;
begin
FStrList := TStringList.Create;
LTempStrList := TStringList.Create;
try
FStrList.LoadFromFile('../FOLDERID_List.csv');
FStrList.Sort;
// CSV を操作するのでデリミタを設定
LTempStrList.Delimiter := ',';
LTempStrList.StrictDelimiter := True;
for LIndex := 0 to FStrList.Count - 1 do begin
LTempStrList.DelimitedText := FStrList[LIndex];
ListBox1.Items.Add(Trim(LTempStrList[0]));
FStrList[LIndex] := Trim(LTempStrList[1]);
end;
finally
FreeAndNil(LTempStrList);
end;
end;
//=============================================================================
// 終了時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
FreeAndNil(FStrList);
end;
//=============================================================================
// ダブルクリックした らFOLDERID に相当する IShellItem オブジェクトを取得
// IShellItem の GetDisplayName メソッドでフォルダのパス名と表示名を取得
// そのフォルダを [ファイルを開く] ダイアログまはたエクスプローラで表示
//=============================================================================
procedure TForm1.ListBox1DblClick(Sender: TObject);
var
LIndex : Integer;
LFolderID : TGUID;
LpItem : IShellItem;
LiRet : HRESULT;
LpFilePath : PChar;
LpDisplayName : PChar;
LExeFile : string;
LhInst : Cardinal;
begin
Edit1.Text := '';
Edit2.Text := '';
LpFilePath := nil;
LpDisplayName := nil;
// ListBox1 の Index を取得してその TObject で定義した値を取出す
LIndex := ListBox1.ItemIndex;
LFolderID := StringToGUID(FStrList[LIndex]);
// FOLDERID に相当する IShellItem オブジェクトを取得
LiRet := SHGetKnownFolderItem(LFolderID, 0, 0, IID_IShellItem, Pointer(LpItem));
if LiRet = S_OK then begin
//GetDisplayNameメソッドでパス名と表示名を取得
// SIGDN_FILESYSPATH にすると仮想フォルダ等の GUID 文字列は取得できない
LpItem.GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, LpFilePath);
LpItem.GetDisplayName(SIGDN_NORMALDISPLAY, LpDisplayName);
Edit1.Text := LpFilePath;
Edit2.Text := LpDisplayName;
// 以下,取得した値の確認用
if (Sender <> nil) and (LpFilePath <> nil) then begin
if RadioGroup1.ItemIndex = 0 then begin
OpenDialog1.InitialDir := LpFilePath;
OpenDialog1.Execute;
Update;
end else begin
// 実際には起動アプリが空でもエクスプローラは起動する
// あるいは,起動ファイルにパス名を指定すれば起動する
LExeFile := 'explorer.exe';
LhInst := ShellExecute(Handle,
'open',
PChar(LExeFile),
LpFilePath,
nil,
SW_SHOW);
if LhInst <= 32 then begin
MessageBox(Handle, '起動に失敗しました.', '情報', MB_ICONINFORMATION);
end;
end;
end;
end;
if LpFilePath <> nil then CoTaskMemFree(LpFilePath);
if LpDisplayName <> nil then CoTaskMemFree(LpDisplayName);
end;
//=============================================================================
// ListBox1 の項目を選択したら,パス名を取得し てEdit1 に表示
//=============================================================================
procedure TForm1.ListBox1Click(Sender: TObject);
begin
ListBox1DblClick(nil);
end;
//=============================================================================
// ListBox1 で [Enter] キーを押したら,ダブルクリックと同じ動作をさせる
//=============================================================================
procedure TForm1.ListBox1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = VK_RETURN then begin
ListBox1DblClick(Sender);
end;
end;
//=============================================================================
// RaditoGroup1 の OnClick イベント処理
// ListBox1 にフォーカス移動して [Enter] キー操作可能にする
//=============================================================================
procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
ListBox1.SetFocus;
end;
end.
Windows Vista であれば,IKnownFolderManager インターフェイスから IShellItem オブジェクトを取得できます.
まず,IKnownFolderManager インターフェイスのインスタンスを生成します.その GetFolder メソッドで IKnownFolde オブジェクトを取得します.この IKnownFolde の GetShellItem で IShellItem のオブジェクトが取得できます.後は上のサンプルと同じです. |
リスト9
IKnownFolderManager インターフェイスから IShellItem オブジェクトを取得する方法 |
procedure TForm1.ListBox1DblClick(Sender: TObject);
var
LIndex : Integer;
LFolderID : TGUID;
LpKonw : IKnownFolderManager;
LpKonwFolder : IKnownFolder;
LpItem : IShellItem;
LpFilePath : PChar;
LpDisplayName : PChar;
begin
Edit1.Text := '';
Edit2.Text := '';
LpFilePath := nil;
LpDisplayName := nil;
LIndex := ListBox1.ItemIndex;
LFolderID := StringToGUID(SL[LIndex]);
if Failed(CoCreateInstance(CLSID_KnownFolderManager,
nil,
CLSCTX_INPROC_SERVER,
IKnownFolderManager,
LpKonw)) then exit;
if LpKonw.GetFolder(LFolderID, LpKonwFolder) = S_OK then begin
if LpKonwFolder.GetShellItem(0, IID_IShellItem, LpItem) = S_OK then begin
LpItem.GetDisplayName(SIGDN_DESKTOPABSOLUTEEDITING, LpFilePath);
LpItem.GetDisplayName(SIGDN_NORMALDISPLAY, LpDisplayName);
Edit1.Text := LpFilePath;
Edit2.Text := LpDisplayName;
end;
end;
if LpFilePath <> nil then CoTaskMemFree(LpFilePath);
if LpDisplayName <> nil then CoTaskMemFree(LpDisplayName);
end;
12_特定フォルダ専用の関数と GetHomePath 関数 |
Windows API の中には,下表のような,特定のフォルダのパス名を取得する関数があります.
Delphi には GetHomePath という,CSIDL_APPDATA のパス名を取得する専用の関数があります. |
|
図19
実行例 |
リスト10
特定フォルダ専用の関数と GetHomePath 関数 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Edit1: TEdit;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Button5: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
//=============================================================================
// GetSystemDirectory 関数
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LBuff : array[0..MAX_PATH-1] of Char;
LSize : Cardinal;
begin
Edit1.Text := '';
FillChar(LBuff, SizeOf(LBuff), #0);
LSize := Windows.GetSystemDirectory(@LBuff, SizeOf(LBuff));
if LSize > 0 then begin
Edit1.Text := LBuff;
end;
end;
//=============================================================================
// GetSystemDirectory関数
// String 型で取得する場合
// Windows API の関数の中には必要なバッファサイズを取得できるものもある
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LPathName : string;
LSize : Cardinal;
begin
Edit1.Text := '';
// 必要なバッファサイズを取得
LSize := GetSystemDirectory(PChar(LPathName), 0);
if LSize > 0 then begin
// 受取るバッファサイズを設定
SetLength(LPathName, LSize);
Windows.GetSystemDirectory(PChar(LPathName), LSize);
Edit1.Text := LPathName;
end;
end;
//=============================================================================
// GetWindowsDirectory 関数
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
LBuff : array[0..MAX_PATH-1] of Char;
LSize : Cardinal;
begin
Edit1.Text := '';
FillChar(LBuff, SizeOf(LBuff), #0);
LSize := Windows.GetWindowsDirectory(@LBuff, SizeOf(LBuff));
if LSize > 0 then begin
Edit1.Text := LBuff;
end;
end;
//=============================================================================
// GetCurrentDirectory 関数
//=============================================================================
procedure TForm1.Button4Click(Sender: TObject);
var
LBuff : array[0..MAX_PATH-1] of Char;
LSize : Cardinal;
begin
Edit1.Text := '';
FillChar(LBuff, SizeOf(LBuff), #0);
LSize := Windows.GetCurrentDirectory(SizeOf(LBuff), @LBuff);
if LSize > 0 then begin
Edit1.Text := LBuff;
end;
end;
//=============================================================================
// GetHomePath 関数
//=============================================================================
procedure TForm1.Button5Click(Sender: TObject);
begin
Edit1.Text := SysUtils.GetHomePath;
end;
end.
13_GetEnvironmentVariable 関数 - 環境変数とその値 |
システムの環境変数には特殊フォルダの変数があります.
環境変数で定義されている値は,GetEnvironmentVariable 関数を使用すると取得できます.その確認用のサンプルです.
本サンプルは,GetEnvironmentStrings 関数を使用して,現在のプロセスで使用可能な環境変数の全て取得してリストしています.環境変数には特殊フォルダ以外の変数もあります.
|
|
図20
環境変数とその値
- IDE 上の [F9] で実行した場合
- BDS, DELPHI, EMBARCA 等が含まれている
- Windows 7 であるが [OS] は Windows NT となる
- 環境変数には特殊フォルダ以外も含まれている
|
上の図は IDE 上で実行した場合ですが,デバッグ時に有効な Delphi 専用の環境変数である BDS 等も含まれています.
下図は EXE を直接実行した場合です.BDS 等は除かれています. |
|
図21
環境変数とその値
- EXE を直接実行した場合
- BDS, DELPHI, EMBARCA 等は含まれていない
|
リスト11
GetEnvironmentVariable 関数を使用して,環境変数から特殊フォルダのフルパスを取得 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Edit1: TEdit;
ListBox1: TListBox;
procedure FormShow(Sender: TObject);
procedure ListBox1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses
StrUtils;
{$R *.DFM}
//=============================================================================
// フォーム表示開始時の処理
// 環境変数のリストを作成
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
var
LEnvList : TStringList;
LpEnvVars : PChar;
LpEnvEntry : PChar;
LEnvString : string;
LPos : Integer;
LIndex : Integer;
begin
ListBox1.Items.Clear;
LEnvList := TStringList.Create;
try
// 環境変数の値のリストを取得
LpEnvVars := GetEnvironmentStrings;
if LpEnvVars <> nil then begin
// 取得した PChar 型の文字列は #0 で区切られている.終端は #0#0
// これを順番に取り出す
// 先頭のポインタを退避
LpEnvEntry := LpEnvVars;
try
while LpEnvEntry^ <> #0 do begin
LEnvList.Add(LpEnvEntry);
Inc(LpEnvEntry, StrLen(LpEnvEntry) + 1);
end;
finally
// メモリブロックを破棄
Windows.FreeEnvironmentStrings(LpEnvVars);
end;
end;
// TListBox に結果を追加していく
for LIndex := 0 to LEnvList.Count - 1 do begin
LEnvString := Trim(LEnvList[LIndex]);
LPos := Pos('=', LEnvString);
LEnvString := StrUtils.AnsiLeftStr(LEnvString, LPos - 1);
// 空だけにしないと IDE からの実行時に自分自身が含まれる
if LEnvString <> '' then begin
ListBox1.Items.Add(LEnvString);
end;
end;
finally
FreeAndNil(LEnvList);
end;
end;
//=============================================================================
// ListBox1 の OnClick イベント処理
//=============================================================================
procedure TForm1.ListBox1Click(Sender: TObject);
var
LIndex : Integer;
LEnviroPath : string;
begin
Edit1.Text := '';
LIndex := ListBox1.ItemIndex;
if LIndex < 0 then exit;
LEnviroPath := SysUtils.GetEnvironmentVariable(Trim(ListBox1.Items[LIndex]));
Edit1.Text := LEnviroPath;
end;
end.
14_TPath の特殊フォルダのパス名取得関数 - Delphi XE5 以降 |
Delphi XE5 の TPath は特殊フォルダのパス名取得関数類が追加されています.
これらの関数は,Windows, OS X, iOS, Android で使用可能なマルチプラットフォーム対応の関数類となっています.
ここまでのサンプルではファイル選択ダイアログやエクスプローラでフォルダを表示していましたが,ここでは Delphi 付属のシェルコンポーネントを使用してみました.特殊フォルダをルートフォルダとしてツリー表示します. |
|
図22
設計時画面
|
|
図23
実行例
- GetSharedDocumentsPath の選択結果
|
リスト12
TPath の特殊フォルダのパス名取得関数 - Delphi XE5 以降 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls, ComCtrls, ShellCtrls;
type
TForm1 = class(TForm)
Edit1: TEdit;
Panel1: TPanel;
Splitter1: TSplitter;
ListBox1: TListBox;
ShellTreeView1: TShellTreeView;
procedure FormShow(Sender: TObject);
procedure ListBox1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses IOUtils;
{$R *.DFM}
//=============================================================================
// フォーム表示開始時の処理
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
ListBox1.Items.Clear;
ListBox1.Items.Add('TPath.GetHomePath');
ListBox1.Items.Add('TPath.GetTempPath');
ListBox1.Items.Add('TPath.GetLibraryPath');
ListBox1.Items.Add('TPath.GetDocumentsPath');
ListBox1.Items.Add('TPath.GetSharedDocumentsPath');
ListBox1.Items.Add('TPath.GetCachePath');
ListBox1.Items.Add('TPath.GetPicturesPath');
ListBox1.Items.Add('TPath.GetSharedPicturesPath');
ListBox1.Items.Add('TPath.GetPublicPath');
ListBox1.Items.Add('TPath.GetCameraPath');
ListBox1.Items.Add('TPath.GetSharedCameraPath');
ListBox1.Items.Add('TPath.GetMusicPath');
ListBox1.Items.Add('TPath.GetSharedMusicPath');
ListBox1.Items.Add('TPath.GetMoviesPath');
ListBox1.Items.Add('TPath.GetSharedMoviesPath');
ListBox1.Items.Add('TPath.GetAlarmsPath');
ListBox1.Items.Add('TPath.GetSharedAlarmsPath');
ListBox1.Items.Add('TPath.GetRingtonesPath ');
ListBox1.Items.Add('TPath.GetSharedRingTonesPath');
ListBox1.Items.Add('TPath.GetDownloadsPath');
ListBox1.Items.Add('TPath.GetSharedDownloadsPath');
end;
//=============================================================================
// ListBox1 の OnClick イベント処理
//=============================================================================
procedure TForm1.ListBox1Click(Sender: TObject);
var
LIndex : Integer;
LFolderPath : string;
begin
Edit1.Text := '';
LIndex := ListBox1.ItemIndex;
if LIndex < 0 then exit;
if LIndex = 0 then LFolderPath := TPath.GetHomePath;
if LIndex = 1 then LFolderPath := TPath.GetTempPath;
if LIndex = 2 then LFolderPath := TPath.GetLibraryPath;
if LIndex = 3 then LFolderPath := TPath.GetDocumentsPath;
if LIndex = 4 then LFolderPath := TPath.GetSharedDocumentsPath;
if LIndex = 5 then LFolderPath := TPath.GetCachePath;
if LIndex = 6 then LFolderPath := TPath.GetPicturesPath;
if LIndex = 7 then LFolderPath := TPath.GetSharedPicturesPath;
if LIndex = 8 then LFolderPath := TPath.GetPublicPath;
if LIndex = 9 then LFolderPath := TPath.GetCameraPath;
if LIndex = 10 then LFolderPath := TPath.GetSharedCameraPath;
if LIndex = 11 then LFolderPath := TPath.GetMusicPath;
if LIndex = 12 then LFolderPath := TPath.GetSharedMusicPath;
if LIndex = 13 then LFolderPath := TPath.GetMoviesPath;
if LIndex = 14 then LFolderPath := TPath.GetSharedMoviesPath;
if LIndex = 15 then LFolderPath := TPath.GetAlarmsPath;
if LIndex = 16 then LFolderPath := TPath.GetSharedAlarmsPath;
if LIndex = 17 then LFolderPath := TPath.GetRingtonesPath;
if LIndex = 18 then LFolderPath := TPath.GetSharedRingTonesPath;
if LIndex = 19 then LFolderPath := TPath.GetDownloadsPath;
if LIndex = 20 then LFolderPath := TPath.GetSharedDownloadsPath;
Edit1.Text := LFolderPath;
ShellTreeView1.Root := LFolderPath;
end;
end.
|