EternalWindows
シェル名前空間 / アイテムIDリスト

シェル名前空間とはデスクトップをルートしたアイテムの階層であり、 通常のファイルシステムと複数の仮想フォルダを内包しています。 シェル名前空間を使用した最も身近なアプリケーションはエクスプローラーです。 エクスプローラーはシェル名前空間におけるデスクトップを表示し、 そこには通常のファイルやフォルダに加えて、ごみ箱やコントロールパネルなどの仮想フォルダも含まれています。 言い換えればシェル名前空間とは、こうしたユーザーにとって便利な仮想フォルダ(特殊なフォルダ)を作り出して提供する仕組です。 シェル名前空間の階層は、次に示すような図から確認することができます。

上図から分かるように、シェル名前空間の最上位に存在するのはデスクトップです。 その下にはごみ箱やコントロールパネル、コンピュータなどの仮想フォルダが並び、 コンピュータの下にはCドライブから始まるファイルシステムが存在します。 アプリケーションがファイルシステムの関数を使用する場合は、 仮想フォルダなどを列挙することはできませんが、 シェル名前空間の関数を使用すればこれは可能になります。

シェル名前空間のルートに存在するデスクトップは仮想フォルダであり、 ファイルシステムにおけるデスクトップと異なる点に注意してください。 たとえば、ファイルシステムにおけるデスクトップは、"C:\Users\username\Desktop"で表すことができますが、 実際にこのフォルダにアクセスしてもコントロールパネルなどの仮想フォルダは確認できないはずです。 なぜなら、仮想フォルダはファイルシステム上に存在しているわけではないからです。 しかし、そうなると、仮想フォルダはアプリケーション内でどのように識別されることになるのでしょうか。 ファイルシステムであれば、ファイルパスという一意の文字列でファイルやフォルダを識別できますが、 仮想フォルダではこのようなパスを使用することはできません。 よって、アイテム(ファイルやフォルダ)を一意に識別する別の仕組みが必要になりますが、 ここで使用されるのがアイテムIDリストです。

アイテムIDリストとは、1つ以上のアイテムIDが連結されて終端の2バイトが0で終了するデータのことを指します。 アイテムIDとは1つのアイテムを識別する値であり、 たとえば"C:\sample.txt"というパスのアイテムIDリストであれば、 C:を識別するアイテムIDとsample.txtを識別するアイテムIDが連結されています。 そして、終端の2バイトには0が格納されています。 もし、sample.txtを単一で識別することになった場合は1つのアイテムIDしか必要ありませんが、 そのような場合でもsample.txtはアイテムIDリストとして識別されることに注意してください。 つまり、その1つのアイテムIDの後に、2バイトの0が格納されることになります。 アプリケーション内では、アイテムを常にアイテムIDリストとして識別することになりますから、 アイテムIDという言葉は特に意識する必要はありません。

システムに既定で存在するフォルダのアイテムIDリストは、SHGetSpecialFolderLocationで取得することができます。

HRESULT SHGetSpecialFolderLocation(
  HWND hwndOwner,
  int nFolder,
  PIDLIST_ABSOLUTE *ppidl
);

hwndOwnerは、NULLを指定します。 nFolderは、フォルダを表すCSIDL型の定数を指定します。 たとえば、コントロールパネルのPIDLを取得したい場合はCSIDL_CONTROLSを指定します。 ppidlは、アイテムIDリストを受け取る変数のアドレスを指定します。 引数の型から分かるように、アイテムIDリストはPIDLIST_ABSOLUTE型で識別することができますが、 実際には他の型で識別することもよくあります。 不要になったアイテムIDリストは、CoTaskMemFreeで開放することになります。

SHGetSpecialFolderLocationの第3引数から分かるように、アイテムIDリストはPIDLと呼ぶこともあります。 よって、以後はPIDLと記述することにします。 PIDLから関連するファイルパスを取得するには、SHGetPathFromIDListを呼び出します。

BOOL SHGetPathFromIDList(
  PCIDLIST_ABSOLUTE pidl,
  LPTSTR pszPath
);

pidlは、PIDLを指定します。 仮想フォルダは関連するファイルパスを持たないため、仮想フォルダのPIDLを指定してはいけません。 pszPathは、ファイルパスを受け取るバッファを指定します。

次に、SHGetSpecialFolderLocationとSHGetPathFromIDListの使い方を示します。

#include <windows.h>
#include <shlobj.h>

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR            szPath[256]; 
	PIDLIST_ABSOLUTE pidl;

	SHGetSpecialFolderLocation(NULL, CSIDL_PROGRAM_FILES, &pidl);
	SHGetPathFromIDList(pidl, szPath);

	MessageBox(NULL, szPath, TEXT("OK"), MB_OK);

	CoTaskMemFree(pidl);

	return 0;
}

SHGetSpecialFolderLocationの第2引数にCSIDL_PROGRAM_FILESを指定した場合は、 C:\Program Filesを表すPIDLを取得することができます。 そして、これをSHGetPathFromIDListに指定した場合は、 当然ながらC:\Program Filesという文字列が返ることになります。 既に述べたように、SHGetPathFromIDListには仮想フォルダのPIDLは指定できませんが、 デスクトップに限っては例外的に取得することができます。

最後に、仮想フォルダとしてのデスクトップのPIDLが、 ファイルシステムにおけるデスクトップのPIDLと異なることを証明します。

#include <windows.h>
#include <shlobj.h>

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR            szPath[256]; 
	PIDLIST_ABSOLUTE pidlVirtual;
	PIDLIST_ABSOLUTE pidlFileSys;

	SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidlVirtual);

	SHGetSpecialFolderPath(NULL, szPath, CSIDL_DESKTOP, FALSE);
	pidlFileSys = ILCreateFromPath(szPath);

	if (ILIsEqual(pidlVirtual, pidlFileSys))
		MessageBox(NULL, TEXT("PIDLは一致します。"), TEXT("OK"), MB_OK);
	else
		MessageBox(NULL, TEXT("PIDLは一致しません。"), TEXT("OK"), MB_OK);

	CoTaskMemFree(pidlVirtual);
	CoTaskMemFree(pidlFileSys);

	return 0;
}

SHGetSpecialFolderLocationにCSIDL_DESKTOPを指定した場合は、 仮想フォルダとしてのデスクトップのPIDLを取得することができます。 SHGetSpecialFolderPathは、第3引数のCSIDLで識別されるフォルダのパスを取得する関数であり、 CSIDL_DESKTOPを指定した場合はデスクトップのファイルパスを取得することができます。 ILCreateFromPathはファイルパスからPIDLを取得する関数であり、 デスクトップのファイルパスを指定した場合は、 ファイルシステムにおけるデスクトップのPIDLが返ることになります。 ILIsEqualは2つのPIDLが一致するかを調べる関数であり、 上記コードの場合ではFALSEを返すことになります。 つまり、2つのPIDLは一致していないということになります。 なお、古いSDKにはIL関数やPIDLIST_ABSOLUTEの定義が存在しないことがある点に注意してください。


戻る