Windows は、シェルの管理領域であるシェルネームスペース と呼ばれるものを提供しています。
このシェルネームスペースは、ファイルとディレクトリで構成されるディスクシステムの(ある意味ハードウェア的な)概念に対して、ファイル、ディレクトリ、そのほかの仮想フォルダと呼ばれるWindowsオブジェクトを含むシステムの(ソフトウェア的な)概念を持ちます。Windows は、これらのすべてをオブジェクトとして、シェルネームスペースのデスクトップを基点としたフォルダツリー形式の階層構造で管理しています。
シェルネームスペース内の個々のオブジェクトは、アイテムIDとアイテムIDリストという識別子を持ちます。
アイテムID(item ID)は、オブジェクトの所属する親フォルダ内でのみ一意の識別子です。したがって、アイテムIDだけではシェルネームスペース上で1つのオブジェクトを識別することはできません。
そこで、オブジェクトにはもう1つの識別子であるアイテムIDリスト(item ID list)が与えられます。このアイテムIDリストは、複数のアイテムIDで構成されるリストのことです。これによって、シェルネームスペース上で一意の識別子となります。
Windows シェルは、シェルネームスペース領域内部のオブジェクトを、PIDL(アイテムIDリストへのポインタ)を通じて管理しています。アイテムIDリストは実際には ITEMIDLIST 構造体に格納されているので、PIDL は、この構造体のアドレスということになります。
シェルネームスペースにより、通常のファイル・ディレクトリ操作では行なうことのできないことを実現できます。その1つに、「デスクトップ」フォルダや「マイ コンピュータ」フォルダなどといった特殊フォルダを取得するということがあります。今回はそれを行なってみましょう。
手順は以下のようになります
まず最初に、 SHGetMalloc 関数でシェルの IMalloc インターフェースを取得しなければなりません。なぜなら、あとで使用する SHGetSpecialFolderLocation 関数によって割り当てられるメモリを解放するのに、シェルが提供するメモリアロケータ(メモリ管理を行なうオブジェクト)の機能を使わなければならないためです。
SHGetMalloc 関数は、シェルのメモリアロケータオブジェクトを作成し、その IMalloc インターフェースを返します。
HRESULT SHGetMalloc( LPMALLOC *ppMalloc );
ppMalloc パラメータには、インターフェースポインタを格納するための変数のアドレスを指定します。これにより、この変数に IMalloc インターフェースポインタが格納されます。この関数は HRESULT 型の戻り値を返しており、戻り値が負の場合はエラーが発生したことを示します。
#include "llmod.as"
#include "rrmod/com/lollipop.as"
; IMalloc インターフェース取得
getptr pm, pMalloc
dllproc "SHGetMalloc", pm, 1, D_SHELL
次に、SHGetSpecialFolderLocation 関数を呼び出して、フォルダのPIDLを取得します。
HRESULT SHGetSpecialFolderLocation( HWND hwndOwner, int nFolder, LPITEMIDLIST *ppidl );
hwndOwner は、ダイアログなど表示させるときのオーナーウィンドウのハンドルを指定しますが、 0 (NULL) を指定して問題ありません。 nFolder には、どの特殊フォルダを取得するのかを指定します。例えば、「プログラム」フォルダを取得したい場合は 0x0002 (CSIDL_PROGRAMS) を指定します。 ppidl には、取得したPIDLを格納する変数のアドレスを指定します。
このとき、実際にアイテムIDリストが格納されるバッファはシェルによって確保されます。そして、そのバッファにアイテムIDリストが格納され、そのバッファのアドレスがPIDLとして ppidl で指定した変数に格納されます。
; PIDL取得 pm.0 = 0 pm.1 = 0x0002 ; CSIDL_PROGRAMS (「プログラム」フォルダ) getptr pm.2, pidl ; PIDL を格納する変数 dllproc "SHGetSpecialFolderLocation", pm, 3, D_SHELL
PIDL からパス名を取得するには、SHGetPathFromIDList 関数を使います。
BOOL SHGetPathFromIDListA( LPCITEMIDLIST pidl, LPSTR pszPath );
pidl には、フォルダのPIDLを、 pszPath にはパス名を格納する文字列変数のアドレスを指定します。
; PIDL からパス名取得 sdim pathname, 260 ; パス名を格納する変数 pm.0 = pidl ; PIDL getptr pm.1, pathname dllproc "SHGetPathFromIDListA", pm, 2, D_SHELL mes "「プログラム」フォルダのパスは" + pathname
アイテムIDリストを格納するためにシェルによって確保されたメモリブロックは、アプリケーション側で解放しなければなりません。これには、はじめに取得した IMalloc インターフェースの Free メソッドを使用します。
void Free( void* pv );
pv は、解放するメモリブロックのアドレスです。このパラメータには PIDL をそのまま渡します。このメソッドのインデックスは 5 なので、 icall 実行時にこの値を指定します。
最後に、 release 命令で IMalloc インターフェースを解放します。
; IMalloc::Free メソッド pm = pidl ; PIDL icall pMalloc, 5, pm, 1 ; インターフェース解放 release pMalloc
#include "llmod.as" #include "rrmod/com/lollipop.as" ; IMalloc インターフェース取得 getptr pm, pMalloc dllproc "SHGetMalloc", pm, 1, D_SHELL if (stat < 0)|(pMalloc == 0){ ; 戻り値は HRESULT 型(負のときエラー) dialog "IMalloc インターフェースを取得できませんでした", 1 end } ; PIDL取得 pm.0 = 0 pm.1 = 0x0002 ; CSIDL_PROGRAMS (「プログラム」フォルダ) getptr pm.2, pidl ; PIDLを格納する変数 dllproc "SHGetSpecialFolderLocation", pm, 4, D_SHELL if stat < 0 { ; 戻り値は HRESULT 型(負のときエラー) dialog "PIDL を取得できませんでした", 1 goto *lb_free } ; PIDL からパス名取得 sdim pathname, 260 ; パス名を格納する変数 pm.0 = pidl ; PIDL getptr pm.1, pathname dllproc "SHGetPathFromIDListA", pm, 2, D_SHELL if stat == 0 { ; 戻り値は BOOL 型(0 のときエラー) dialog "PIDL からパス名を取得できませんでした", 1 goto *lb_free } dialog "「プログラム」フォルダのパスは[" + pathname + "]です" *lb_free if pMalloc { ; IMalloc->Free(pidl); pm = pidl ; PIDL icall pMalloc, 5, pm, 1 ; インターフェース解放 release pMalloc } end