ファイルパスからアイテムIDを取得する

 さて、今回はアイテムIDについて見ていきたいと思います。
 アイテムIDとは、正確にはITEMIDLIST構造体のことです。この構造体を使用すると、普通のファイルやフォルダと同じように、マイコンピュータやプリンタのフォルダなどを扱うことができます。また、前回紹介した::SHBrowseForFolder()といった一部の関数は、このアイテムIDでないと受け付けてくれません。
 というわけで、今回はそのアイテムIDを取得する方法を紹介しましょう。

とりあえず関数だけ
 とりあえず関数だけ紹介します。できれば前回と同じクラスに作製してください。
(注:今回、戻り値にLPITEMIDLIST型を使用しているので、shlobj.hのインクルードはStdafx.hの中で行ってください)


LPITEMIDLIST CTestView::GetItemIDList( CString p_cFileStr )
{
	if( p_cFileStr.IsEmpty() )
		return NULL;

	LPITEMIDLIST pIDL;
	LPSHELLFOLDER pDesktopFolder; 

	if( ::SHGetDesktopFolder( &pDesktopFolder ) != NOERROR )
		return NULL;

	OLECHAR       ochPath[MAX_PATH];
	ULONG         chEaten;	//文字列のサイズを受け取ります。
	ULONG         dwAttributes;	//属性を受け取ります。
	HRESULT       hRes;

	// これをしないとインターフェイスはダメなのです。
	::MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, p_cFileStr, -1, ochPath, MAX_PATH );

	// 実際にITEMIDLISTを取得します。
	hRes = pDesktopFolder->ParseDisplayName( NULL, NULL, ochPath, &chEaten, &pIDL, &dwAttributes);
    
	if( hRes != NOERROR )
		pIDL = NULL;

	pDesktopFolder->Release();

	return pIDL;
}
	

 またもや見慣れないAPIがたくさんあります。順に見ていきましょう。

IShellFolderインターフェイス
 IShellFolderインターフェイスを、さながら前回のIMallocインターフェイスを取得したときのように::SHGetDesktopFolder()を使用して取得しています。繰り返しになりますが、このインターフェイスは新しく作製され、そのポインタだけが返ってきているので、あとで削除しなければなりません。このことだけは憶えておいてください。
 で、このIShellFolderインターフェイスは、ファイルやフォルダを操作するためのインターフェイスです。このインターフェイスを使って、ファイルのパスからアイテムIDを取得します。

::MultiByteToWideChar()
 またもや見慣れない関数です。が、プレフィックスにSHが付いていません。この関数は、実際にはOLEとは関係のない関数ですが、必要不可欠な関数のひとつです。
 インターフェイス関係の関数には、受け取る文字列がユニコードでなければならないものがあります。すぐあとに説明するIShellFolder::ParseDisplayName()もそのひとつです。::MultiByteToWideChar()は、普通の文字をユニコードへと変換してくれる関数なのです。
 まぁ、この関数はそれほど重要ではないので、必要なんだなくらいに思っておいてください。第3引数に元の文字列、第5引数に送り先の文字列を入れます。送り先はOLECHAR型です。

IShellFolder::ParseDisplayName()
 では、本日のメインイベント、ファイルのパスからアイテムIDを取得してみましょう。といっても、それほど難しいことはないんです。
 ファイルのパスは第3引数に入れます。これは先ほどユニコードに変換したものを使用します。第5引数の、アイテムIDへのポインタへのポインタ(ややこしい)に、これから使用するアイテムIDが返ってきます。あとは特に必要ではないので、コードそのままにしておいてください。
 ちなみに、逆にここで取得したアイテムIDからファイルのパスを取得する場合には::SHGetPathFromIDList()を使用します。これについては前回を参考にしてください。

あとしまつ
 前回のIMallocと同じように、IShellFolderも、自分自身を削除しなければなりません。そのため、IShellFolder::Release()を呼び出しています(本当のことを言えば、削除しているわけではないんですけれども)。
 また、絶対に忘れてはならないのがIShellFolder::ParseDisplayName()で取得したアイテムIDの削除です。このアイテムIDも、メモリのどこかに作製され、そのポインタだけが返ってきています。そして、このポインタを戻り値として関数の外に出しているのです。
 そのため、この関数を使用し、戻り値のアイテムIDを使用したあと、削除しなければなりません。具体的には、次のようにします。


	LPMALLOC pMalloc;
	LPITEMIDLIST pRootIDL;

	// IMallocインターフェイスへのポインタを取得します。
	if( ::SHGetMalloc( &pMalloc ) != NOERROR )
		return _T( "" );

	// アイテムIDを取得します。
	pRootIDL = GetItemIDList( "C:\\Windows\\System" );

	// アイテムIDを使って色々します。

	// リリースします。
	if( pRootIDL != NULL )
		pMalloc->Free( pRootIDL );

	pMalloc->Release();
	

 このように、まずIMallocへのポインタをあらかじめ取得しておき、関数の最後でIMalloc::Free()を使用してアイテムIDを削除しておきます。このことを忘れないでおいてください。もしあなたが作製した関数のライブラリを他の方が使う可能性があるのなら、必ずこのことを明記しておくべきでしょう。

まとめ
 かなり口うるさく言っていますが、さらに言わせてもらえれば、インターフェイスはメモリのどこかに動的に作製されるものであり、あとで削除しなければならないものです。この原則さえ守れば、インターフェイスは結構つきあいやすい相手……というわけでもありませんね。知らない型やAPIがどんどん現れ、しかもサンプルは少ないと、きつい条件が重なっています。
 ですが、インターフェイスを使用したファイル操作は、今までの限界を取り払ってくれるでしょう。ファイラーの作製などを目指している方の手助けになればと思います。

 さて、次回は「フォルダ選択ダイアログ(後編)」です。まだ説明していなかったルートフォルダの設定方法、最初に開いたときに特定のフォルダが選択されているようにする方法など、::SHBrowseForFolder()を使いこなしてみましょう。逆に言えば、それだけ使いがいのある珍しいAPIとも言えますね(汗)。

(C)KAB-studio 1997 ALL RIGHTS RESERVED.