「フォルダを選択するダイアログ」。似たようなものをよく見かけます。ファイル検索での参照、関連づけされていないファイルをダブルクリックしたとき、IE3.0以上で「お気に入りの追加」を選んだとき、某有名解凍アプリケーションなど、様々なところで似たようなダイアログボックスが現れます。
っつーても、今はもはやFAQで、これを簡単に使うためのクラスや関数のライブラリが結構出回ってたりします。でも、この関数は逆にCOM関係を知るのには比較的適した例と言えます。 |
まずは下準備
というわけで、次の2行をStdAfx.hの下の方(「//{{AFX_INSERT_LOCATION}}」って書かれてる部分の上の行くらい)に追加してください。 |
#include <Shlobj.h>
#pragma comment(lib, "Ole32.lib")
COMインターフェイスを使用するときには、Shlobj.hをインクルードします(確か前は、これ以外にも必要なヘッダーファイルがあったような気がしたんだけど、今はいらないみたいです)。また、COM関係のAPIがエクスポートされているDLLのライブラリファイルOle32.libを、#pragmaを使って「ライブラリ検索リスト」に加えます。
あと、あらかじめ言っておきますが、今回はインターフェイスそのものについては触れません。今回はCOMについて軽く触れる、まぁ肩慣らしみたいなものです。 |
とりあえず関数だけ
というわけで、以下のような関数を作ってみてください。 |
CString CTestView::BrowseForFolder( HWND p_hWnd, CString p_cSetStr
, CString p_cRootStr, CString p_cCaptionStr, UINT p_uiFlags )
{
BOOL bRes;
char chPutFolder[MAX_PATH];
LPITEMIDLIST pidlRetFolder;
BROWSEINFO stBInfo;
CString cRetStr;
// 構造体を初期化します。
stBInfo.pidlRoot = NULL; //ルートフォルダです。
stBInfo.hwndOwner = p_hWnd; //表示するダイアログの親ウィンドウのハンドルです。
stBInfo.pszDisplayName = chPutFolder; //選択されているフォルダ名を受けます。
stBInfo.lpszTitle = p_cCaptionStr; //説明の文字列です。
stBInfo.ulFlags = p_uiFlags; //表示フラグです。
stBInfo.lpfn = NULL; //ダイアログプロシージャへのポインタです。
stBInfo.lParam = 0L; //プロシージャに送るパラメーターです。
// ダイアログボックスを表示します。
pidlRetFolder = ::SHBrowseForFolder( &stBInfo );
// pidlRetFolderにはそのフォルダを表すアイテムIDリストへのポインタが
// 入っています。chPutFolderには選択されたフォルダ名(非フルパス)だけ
// しか入っていないので、このポインタを利用します。
if( pidlRetFolder != NULL )
{
// フルパスを取得します。
bRes = ::SHGetPathFromIDList( pidlRetFolder, chPutFolder );
if( bRes != FALSE )
cRetStr = chPutFolder;
::CoTaskMemFree( pidlRetFolder );
}
return cRetStr;
}
見慣れないAPI、プレフィックスがSHの関数が2つにCoの関数がひとつあります。さらに、ただ取得しただけのポインタがクラスのように振る舞っていたり、わざわざ削除していたりとなんか怪しいです。では順に見ていきましょう。
|
BROWSEINFO
::SHBrowseForFolder()にはほとんど中身がありません。すべての鍵はBROWSEINFO構造体にあります。では、各メンバについて見ていきましょう。
HWND hwndOwnerには、これから表示するダイアログボックスのオーナーウィンドウのハンドルを入れます。この例ではCDialogクラスの関数として作製しているので、そのウィンドウハンドルを渡すのがいいでしょう。
LPCITEMIDLIST pidlRootには、ルートフォルダの「アイテムIDリスト」を入れます。って、そのアイテムIDリストってなんやねん? と思われるでしょう。このアイテムIDリストについては次回、さらにこのメンバについては次次回に説明します。
LPSTR pszDisplayNameには、フォルダ名を返すための文字列のポインタを入れておきます。ここから帰ってくるのはフォルダ名なので、フルパスを取得することはできません。フルパスの取得方法はあとで。
LPCSTR lpszTitleには、タイトルの下に書き込む文字列へのポインタを入れておきます。この名前から見ると、まるでダイアログのタイトルみたいなんですけどねー。
UINT ulFlagsには、表示についてのフラグを設定します。これが結構くせものです。 |
この部分に関して、「HRSさん」より情報を頂きました。以下に掲載します。情報、ありがとうございました!!
BIF_BROWSEFORCOMPUTER:このフラグを指定すると、ネットワークコンピュータ内のコンピュータのみが選択できるようです。当然、ワークグループやドメインを展開し、別ドメインに参加しているコンピュータを選択することも可能です。 |
BFFCALLBACK lpfnには、ダイアログからのメッセージを受け取るウィンドウプロシージャへのポインタを入れます。これは次次回説明します。
LPARAM lParamには、上で説明したウィンドウプロシージャで使うための任意の32ビット整数を入れられます。これも次次回に説明します。
int iImageには、選択されたアイテムのアイコンの、システムイメージリスト内のインデックスが返されます。これについては――説明しません(爆)。調査が済んだら説明するかも。 と、構造体については以上で終わり。今回説明しない部分には、上の例のようにNULLや0を入れておいてください。ぜーったい必要なのはpszDisplayNameとulFlagsだけですね。 |
::SHBrowseForFolder()
で、::SHBrowseForFolder()関数で、ダイアログを表示します。CDialog::DoModal()のように、ここで一度止まり、ダイアログが閉じてから再び進みます。 戻り値はアイテムIDリストというものです。あれ? さっき出てきましたねぇ。あれと同じです。このアイテムIDリストというもの、フォルダやファイルのことだと思ってください。でも、フルパスと違って、マイコンピュータやゴミ箱など、特殊なフォルダも取得できます。ダイアログボックスで「キャンセル」ボタンを押したときには、NULLが返ってきます。
ここで重要なのは、アイテムIDリストのポインタを取得しているということです。
ウィンドウやデバイスコンテキストを操作するときにはそれぞれHWNDやHDCといった「ハンドル」を使用します。これは一種のポインタで、「どっかにある実体」をこのハンドルを使って操作する仕組みになっています。
ここでアイテムIDリストを「ポインタ」として取得するということは、同じことを意味します(アイテムIDリストの場合、実は違う。それはのちほど)。これと同じように、他のCOMインターフェイスオブジェクトはすべてポインタとして取得し、それを元に操作します。このことを、頭の片隅にでも置いておいてください。 |
::SHGetPathFromIDList()
なんかSHの付く関数が続きます。この関数は、先ほど取得したアイテムIDリストから、フォルダのフルパスを取得することができます。BROWSEINFO構造体のpszDisplayNameに返ってくるのはフォルダ名だけなので、この時点でやっとフルパスを取得できるというわけです。 |
::CoTaskMemFree()
さて、当初の目的はここで達成しています。ところが、インターフェイスは色々と後始末をしなければならないのです。 ::SHBrowseForFolder()で取得したアイテムIDリスト、これを削除しなければなりません。この関数から得たポインタは、new演算子やmalloc()で動的に割り当てたメモリのため、これをプログラムの側で解放しなければならないのです。 このメモリ領域を開放するAPIは::CoTaskMemFree()です。プレフィックスのCoは、Com系の関数を意味しています。これをしないとメモリリークが発生してしまうので注意しましょう(MFCバージョンのnewみたいな検出機構も付いていないしね)。 |
まとめ
今回はインターフェイスは出てきませんでしたね。ちょっとシェル系関数とCOM系関数を使ってみただけでした。そんなに難しい部分はないと思います……今のところは。 次回は「フォルダ選択ダイアログ」から離れて、アイテムIDリストの取得方法や「インターフェイス」について見てみます。さらに次次回では、この後編で、「フォルダ選択ダイアログ」についてさらに突っ込んだ内容にしたいと思います。 |
(C)KAB-studio 1997, 1998 ALL RIGHTS RESERVED. |