IShellFolderインターフェイス

ActiveBasicでのプログラミングでわからないこと、困ったことなどがあったら、ここで質問してみましょう(質問を行う場合は、過去ログやWeb上であらかじめ問題を整理するようにしましょう☆)。
返信する
メッセージ
作成者
かえる

IShellFolderインターフェイス

#1 投稿記事 by かえる »

こんにちは。以前もお世話になりましたかえると申します。
さて今回、フォルダの中身を取得して列挙するというような機能を作っています。
IShellFolderインターフェイスの実装に挑戦しているのですが、どうしても途中で
アクセス違反が起こってしまいます。
COMコンポーネントに関する知識が全くなく、ネット上に公開されているサンプル等を
参考にコードを組みました。ちゃんと調べたつもりですがひょっとするとクラスやそのメソッドの定義や値、
また更に根本的なところに間違いがあるのかもしれません。

コードを以下に載せます。
なぜ上手くいかないのでしょうか。お手数掛けますがご教唆よろしくお願いします。

コード: 全て選択


Class IShellFolder
    Inherits IUnknown
Public
	Virtual Function BindToObject(ByRef pidl As *IShellFolder, ByRef pbc As VoidPtr, ByRef riid As GUID, ByRef ppvOut As VoidPtr) As Long
	Virtual Function BindToStorage(ByRef pidl As *IShellFolder, ByRef pbc As VoidPtr, ByRef riid As GUID, ByRef ppvOut As VoidPtr) As Long
	Virtual Function CompareIDs(lParam As LPARAM, ByRef pidl1 As *IShellFolder, ByRef pidl2 As *IShellFolder) As Long
	Virtual Function CreateViewObject(hwndOwner As HWND, ByRef riid As GUID, ByRef ppvOut As VoidPtr) As Long
	Virtual Function EnumObjects(hwndOwner As HWND, grfFlags As Long, ByRef ppenumIDList As *IEnumIDList) As Long
	Virtual Function GetAttributesOf(cidl As DWord, ByRef apidl As *IShellFolder, rgfInOut As VoidPtr) As Long
	Virtual Function GetDisplayNameOf(ByRef pidl As *IShellFolder, uFlags As Long, lpName As String) As Long
	Virtual Function GetUIObjectOf(hwndOwner As HWND, cidl As DWord, ByRef apidl As *IShellFolder, ByRef riid As GUID, rgfReserved As DWord, ByRef ppv As VoidPtr) As Long
	Virtual Function ParseDisplayName(hwnd As HWND, ByRef pbc As VoidPtr, pwszDisplayName As String, ByRef pchEaten As DWord, ByRef ppidl As *IShellFolder, ByRef pdwAttributes As VoidPtr) As Long
	Virtual Function SetNameOf(hwndOwner As HWND, ByRef pidl As *IShellFolder, lpszName As String, uFlags As Long, ByRef ppidlOut As *IShellFolder) As Long
End Class

Class IMalloc
	Inherits IUnknown
Public
	Virtual Function Alloc(cb As DWord) As VoidPtr
	Virtual Function Realloc(ByRef pv As VoidPtr, cb As DWord) As VoidPtr
	Virtual Function Free(ByRef pv As VoidPtr) as VoidPtr
	Virtual Function GetSize(ByRef pv As VoidPtr) As DWord
	Virtual Function DidAlloc(ByRef pv As VoidPtr) As Long
	Virtual Function HeapMinimize() As VoidPtr
End Class

Class IEnumIDList
	Inherits IUnknown    
Public
	Virtual Function Clone(ByRef ppenum As *IEnumIDList) As Long
	Virtual Function Next(celt As Long, ByRef rgelt As *IEnumIDList, pceltFetched As Long) As Long
	Virtual Function Reset() As Long
	Virtual Function Skip(celt As Long) As Long
End Class

Type SHFILEINFO
    hIcon As HICON
    iIcon As Long
    dwAttributes As DWord
    szDisplayName[MAX_PATH-1] As Byte
    szTypeName[80-1] As Byte
End Type

Declare Function SHGetSpecialFolderLocation Lib "Shell32.dll" Alias "SHGetSpecialFolderLocation" (ByVal hwnd As HWND, ByVal nFolder As Long, ByRef pidl As *IShellFolder)  As Long 
Declare Function SHGetMalloc Lib "shell32.dll" Alias "SHGetMalloc"(ByRef Malloc As *IMalloc) As Long 
Declare Function SHGetFileInfo Lib "Shell32" Alias "SHGetFileInfoA"(ByVal pszPath As *IEnumIDList, ByVal dwFileAttributes As Long, ByRef psfi As SHFILEINFO, ByVal cbFileInfo As Long, ByVal uFlags As Long) As Long

'IShellFolder::EnumObjects()
Const SHCONTF_FOLDERS = &H20
Const SHCONTF_NONFOLDERS = &H40
Const SHCONTF_INCLUDEHIDDEN = &H80

'SHGetSpecialFolderLocation()
Const CSIDL_DESKTOP = &H0&
Const CSIDL_DRIVES = &H11

'SHGetFileInfo()
Const SHGFI_PIDL = &H8
Const SHGFI_DISPLAYNAME = &H200&

Dim CLSID_Desktop = [
    &h00021400,
    0,
    0,
    [&hC0, 0, 0, 0, 0, 0, 0, &h46]
]As GUID

Dim IID_IShellFolder = [
    &h000214E6,
    0,
    0,
    [&hC0, 0, 0, 0, 0, 0, 0, &h46]
]As GUID

コード: 全て選択


	Dim m_pMalloc As *IMalloc
	Dim pFolder As *IShellFolder
	Dim pEnumIDList As *IEnumIDList
	Dim Sfi As SHFILEINFO
	Dim Ret As Long
	CoInitialize(NULL)
	CoCreateInstance(CLSID_Desktop, NULL, CLSCTX_INPROC, IID_IShellFolder, VarPtr(pFolder))
		SHGetMalloc(m_pMalloc)
		SHGetSpecialFolderLocation(hMainWnd, CSIDL_DESKTOP, pFolder)
		pFolder->EnumObjects(hMainWnd, SHCONTF_FOLDERS + SHCONTF_NONFOLDERS + SHCONTF_INCLUDEHIDDEN, pEnumIDList)
		While pEnumIDList->Next(1, pEnumIDList, Ret) = 0
			SHGetFileInfo(pEnumIDList, 0, Sfi, SizeOf(SHFILEINFO), SHGFI_PIDL + SHGFI_DISPLAYNAME)
			MessageBox(0, Sfi.szDisplayName, "", MB_OK)
		Wend
		pFolder->Release()
		pEnumIDList->Release()
		m_pMalloc->Release()
	CoUninitialize()
※最終的にはSHGetSpecialFolderLocation()でマイコンピュータのアイテムIDをとってその中身を列挙したいと思っています。
[/code]
イグトランス
記事: 899
登録日時: 2005年5月31日(火) 17:59
お住まい: 東京都
連絡する:

#2 投稿記事 by イグトランス »

SHGetSpecialFolderLocationの最後の引数が*IShellFolderへの参照になっていますが,正しくは*ITEMIDLISTへの参照です。
IShellFolderのBindToObjectを用いると,ITEMIDLISTから対応するフォルダを指すIShellFolderへのポインタを得られます。

尚,デスクトップを指すIShellFolderへのポインタはSHGetDesktopFolderを用いれば直接得られます。

コード: 全て選択

TypeDef HRESULT = Long
Declare Function SHGetDesktopFolder Lib "shell32.dll" (ByRef pshf As *IShellFolder) As HRESULT
かえる

#3 投稿記事 by かえる »

ご返信ありがとうございます。

SHGetSpecialFolderLocation()とSHGetDesktopFolder()を混同して、
前者でも直接*IShellFolderが取れると勘違いしていました。

ご指摘頂いたとおりSHGetDesktopFolderの定義を加え、
上記コードの
SHGetSpecialFolderLocation(hMainWnd, CSIDL_DESKTOP, pFolder)を
SHGetDesktopFolder(pFolder)に置き換えました所、今度はこの部分で
アクセス違反が起こってしまいました。するとやはりIShellFolderの定義
付近が間違っているのでしょうか。クラスの定義のあたりはあまり参考
となる資料が見付からず、適当に組み立ててしまったもので…

お手数掛けますが、再度よろしくお願いします。
かえる

#4 投稿記事 by かえる »

再び申し訳ありません。
上の記事の補足として、pidlと名の付くものすぺてを*IShellFolder参照としていたので

コード: 全て選択


Type ITEMIDLIST
    mkid As Long
End Type
を追記し、*IShellFolderを*ITEMIDLISTに修正しましたが、やはりアクセス違反が起こってしまいました。
Toshi
記事: 98
登録日時: 2005年7月19日(火) 19:47
お住まい: 山形県
連絡する:

#5 投稿記事 by Toshi »

コード: 全て選択

Class IShellFolder
    Inherits IUnknown
Public
    Virtual Function BindToObject(ByRef pidl As *IShellFolder, ByRef pbc As VoidPtr, ByRef riid As GUID, ByRef ppvOut As VoidPtr) As Long
    Virtual Function BindToStorage(ByRef pidl As *IShellFolder, ByRef pbc As VoidPtr, ByRef riid As GUID, ByRef ppvOut As VoidPtr) As Long
    Virtual Function CompareIDs(lParam As LPARAM, ByRef pidl1 As *IShellFolder, ByRef pidl2 As *IShellFolder) As Long
    Virtual Function CreateViewObject(hwndOwner As HWND, ByRef riid As GUID, ByRef ppvOut As VoidPtr) As Long
    Virtual Function EnumObjects(hwndOwner As HWND, grfFlags As Long, ByRef ppenumIDList As *IEnumIDList) As Long
    Virtual Function GetAttributesOf(cidl As DWord, ByRef apidl As *IShellFolder, rgfInOut As VoidPtr) As Long
    Virtual Function GetDisplayNameOf(ByRef pidl As *IShellFolder, uFlags As Long, lpName As String) As Long
    Virtual Function GetUIObjectOf(hwndOwner As HWND, cidl As DWord, ByRef apidl As *IShellFolder, ByRef riid As GUID, rgfReserved As DWord, ByRef ppv As VoidPtr) As Long
    Virtual Function ParseDisplayName(hwnd As HWND, ByRef pbc As VoidPtr, pwszDisplayName As String, ByRef pchEaten As DWord, ByRef ppidl As *IShellFolder, ByRef pdwAttributes As VoidPtr) As Long
    Virtual Function SetNameOf(hwndOwner As HWND, ByRef pidl As *IShellFolder, lpszName As String, uFlags As Long, ByRef ppidlOut As *IShellFolder) As Long
End Class
インターフェイスの定義はアルファベット順ではなく、明確に順番が決められています。
IShellFolder インターフェイスの場合は、

コード: 全て選択

Class IShellFolder
    Inherits IUnknown
Public
	Virtual Function ParseDisplayName(hwnd As HWND, ByRef pbc As VoidPtr, pwszDisplayName As String, ByRef pchEaten As DWord, ByRef ppidl As *IShellFolder, ByRef pdwAttributes As VoidPtr) As Long
	Virtual Function EnumObjects(hwndOwner As HWND, grfFlags As Long, ByRef ppenumIDList As *IEnumIDList) As Long
	Virtual Function BindToObject(ByRef pidl As *IShellFolder, ByRef pbc As VoidPtr, ByRef riid As GUID, ByRef ppvOut As VoidPtr) As Long
	Virtual Function BindToStorage(ByRef pidl As *IShellFolder, ByRef pbc As VoidPtr, ByRef riid As GUID, ByRef ppvOut As VoidPtr) As Long
	Virtual Function CompareIDs(lParam As LPARAM, ByRef pidl1 As *IShellFolder, ByRef pidl2 As *IShellFolder) As Long
	Virtual Function CreateViewObject(hwndOwner As HWND, ByRef riid As GUID, ByRef ppvOut As VoidPtr) As Long
	Virtual Function GetAttributesOf(cidl As DWord, ByRef apidl As *IShellFolder, rgfInOut As VoidPtr) As Long
	Virtual Function GetUIObjectOf(hwndOwner As HWND, cidl As DWord, ByRef apidl As *IShellFolder, ByRef riid As GUID, rgfReserved As DWord, ByRef ppv As VoidPtr) As Long
	Virtual Function GetDisplayNameOf(ByRef pidl As *IShellFolder, uFlags As Long, lpName As String) As Long
	Virtual Function SetNameOf(hwndOwner As HWND, ByRef pidl As *IShellFolder, lpszName As String, uFlags As Long, ByRef ppidlOut As *IShellFolder) As Long
End Class
このようになっています。
これを修正してみるとどうなるでしょうか。
かえる

#6 投稿記事 by かえる »

ご返信ありがとうございます。

まさか定義に順番が決められていたとは知りませんでした。
MSDNを参考にしたはずなのですが…
同様にIEnumIDListインターフェイスのほうも順番を並び替えたところ、
うまくアイテムIDリストを取得する事ができました。
ですが今度はその後のSHGetFileInfo()で引っかかってしまいます。
調べてみたところ更にアイテムIDリストをいじらないといけないようです。
GetDisplayNameOf()を使う手もありますが、そちらももう一捻り必要なようで。
と言う事でまだファイルを列挙できるには時間がかかりそうですね。。

イグトランスさん、Toshiさん。
今回は大変勉強になりました。本当にありがとうございました。
かえる

#7 投稿記事 by かえる »

再三申し訳ありません。

お二方のアドバイスで分かったつもりになっていたのですが、少し修正して試してみるとまた別の場所で
アクセス違反が起こってしまいました。やはりCOMコンポーネントは自分にはハードルが高すぎたでしょうか…
極力自分の力で解決したいと思っているのですが、資料が見つからなくていまいちよく分かりません。
本当に申し訳ないのですが再度質問させていただきます。
以下に自分の不明な点を整理し、コードを一緒に載せてみました。よろしくお願いします。

1.CoCreateInstanceなど、Co~関数にはそれぞれ何の意味があるのでしょうか。
2.IMallocインターフェイスには何か意味があったのでしょうか。
 更に、pMalloc->Free(pidl)は失敗してしまいます。
3.マイコンピュータのアイテム名を列挙するよう修正したつもりなのですが、BindToObjectがうまく
 いっていないのかpFolderが空のままで、EnumObjectsに失敗してしまいます。
4.GetDisplayNameOfメソッドで取得できるSTRRET構造体の定義の仕方が分かりません。
 unionとは共用体というものだそうですがいったいどのようにすればいいのでしょうか。
 typedef struct _STRRET
 {
  UINT uType; // One of the STRRET_* values
  union
  {
  LPWSTR pOleStr; // must be freed by caller of GetDisplayNameOf
  LPSTR pStr; // NOT USED
  UINT uOffset; // Offset into SHITEMID
  char cStr[MAX_PATH]; // Buffer to fill in (ANSI)
  } DUMMYUNIONNAME;
 } STRRET, *LPSTRRET;

イグトランス
記事: 899
登録日時: 2005年5月31日(火) 17:59
お住まい: 東京都
連絡する:

#8 投稿記事 by イグトランス »

まず元のコードをステップ実行させたり戻り値を見ていったりすると,IShellFolderのBindToObjectが失敗していることがわかりました。
引数が正しくないとのことです。

原因がさっぱりわからなかったので,次のようにしたところ,うまく動いてくれました。
まずメモリ解放がなっていなかったり,元のコードではCoCreateInstanceが無意味だったりすることを修正してC++へ移したものがうまく動くことを確認し,
それをもう再びABへ移植する(ただしその際インターフェイスの宣言などを書き直す)と言う手段を取りました。
そういうわけで,すみませんがかえるさんのコードがうまく動かない原因は不明のままです。 主な変更点は次のとおりです。
  • MSDNによるとSHGetMallocは使うなとあるので,CoGetMallocへ変更しました。CoGetMallocは1つめの引数を常に1とするほかはSHGetMallocと同じように使えます。
  • メモリ解放されていないものがいくつかあったので,そうならないよう修正しました。
  • BindToObjectでは新しいインスタンスを作り,それを最後の引数で返していますので,pFolderに対して事前にインスタンスを作る必要はありません。そこでCoCreateInstanceの行は削除しました。
最後に編集したユーザー イグトランス [ 2006年8月06日(日) 00:22 ], 累計 1 回
かえる

#9 投稿記事 by かえる »

イグトランスさんわざわざありがとうございます。
やっと思い通りの事ができるようになりました。本当すごいです。

矢張りCOMコンポーネントは難しいですね。しかし今回は定義の事など、身になる事がたくさんありました。
ご協力いただいた皆さん、本当にありがとうございました。
返信する