C++でエクスプローラを開いている時、そのパスを取得したい。

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
あらかた

C++でエクスプローラを開いている時、そのパスを取得したい。

#1

投稿記事 by あらかた » 12年前

あらかたと申します

現在COMの勉強中です
http://homepage1.nifty.com/MADIA/vb/vb_ ... 30011.html
を自分なりにC++のコードに書き直してみました。

int main() {
CoInitialize(0);
IShellFolderViewDual2 *fid=NULL;
Folder *fldr;
FolderItems *fis;
CoCreateInstance(CLSID_ShellFolderView,NULL,CLSCTX_INPROC_SERVER,IID_IShellFolderViewDual2,(LPVOID *)&fld);
fld->get_Folder(&fldr); //←ここでfldrがNULLになります ・・・・・・・・・☆
fldr->Items(&fis);
FolderItem *fi;
VARIANT vr;
VariantInit( &vr );
vr.vt = VT_UI4;
vr.uintVal = 0;
fis->Item(vr,&fi);
BSTR buf;
fi->get_Path(&buf);
fi->Release();
fis->Release();
fldr->Release();
fld->Release();
CoUninitialize();
return 0;
}

ここで質問がいくつかあります。
1.なぜ☆の行でNULLになるのか?
2.これだとひとつのフォルダパスしか取得できない(と思っています)が参考ページの
   For Each O In CreateObject("Shell.Application").Windows
 はどうやるのでしょうか?
その他、このコードよりも効率的な方法があれば教えてください。よろしくお願いします

あらかた

Re: C++でエクスプローラを開いている時、そのパスを取得したい。

#2

投稿記事 by あらかた » 12年前

再びすみません。開発環境書き忘れていました
OS:XP 32bit
IDE:Visual Studio 2008

あとコード貼り付けの規約を見落としていました。

コード:

int main() {
    CoInitialize(0);
    IShellFolderViewDual2 *fid=NULL;
    Folder *fldr;
    FolderItems *fis;
    CoCreateInstance(CLSID_ShellFolderView,NULL,CLSCTX_INPROC_SERVER,IID_IShellFolderViewDual2,(LPVOID *)&fld);
    fld->get_Folder(&fldr);	 //←ここでfldrがNULLになります	 ・・・・・・・・・☆
    fldr->Items(&fis);
    FolderItem *fi;    
    VARIANT vr;
    VariantInit( &vr );
    vr.vt = VT_UI4;
    vr.uintVal = 0;
    fis->Item(vr,&fi);
    BSTR buf;
    fi->get_Path(&buf);
    fi->Release();
    fis->Release();
    fldr->Release();
    fld->Release();
    CoUninitialize();
    return 0;
}

アバター
a5ua
記事: 199
登録日時: 13年前

Re: C++でエクスプローラを開いている時、そのパスを取得したい。

#3

投稿記事 by a5ua » 12年前

色々調べながら書いて見ました。以下のページを参考にしています。
戻り値によるエラー判定はコードが読みにくくなるので、エラーハンドリングで例外を使うようにアレンジしています。
http://www.sol.dti.ne.jp/~yoshinor/ni/ni0003.html

私も完全に理解しているわけではないので、正しいかどうかは怪しいですが、コードの説明をすると、

はじめに、IShellWindowsのインスタンスを作成します。(これで、シェルが管理しているウインドウが得られる?)
Countプロパティを取得し、ウインドウの数を得る。
Itemメソッドにより、InternetExplorerオブジェクトが得られるらしい。
IEオブジェクトのLocationURLプロパティでエクスプローラーのパスが得られる。
(ちなみに、ブラウザのIEで何らかのページを開いているときは、そのURLも表示されました。)

なお、以下のコードはVC2010で作成したものなので、コンパイルできないなどの問題がありましたら言ってください。

コード:

#pragma comment(lib, "comsuppw.lib")

#include <Windows.h>
#include <ShlObj.h>
#include <comutil.h>	// _variant_t
#include <iostream>
#include <vector>
#include <exception>


// メンバー名からディスパッチIDを得る
DISPID GetIDOfName(IDispatch *object, OLECHAR *member)
{
	DISPID id;
	if (FAILED(object->GetIDsOfNames(IID_NULL, &member, 1, LOCALE_USER_DEFAULT, &id))) {
		throw std::exception("failed: GetIDOfName");
	}
	return id;
}

// プロパティを取得
_variant_t GetProperty(IDispatch *object, OLECHAR *member)
{
	DISPID id = GetIDOfName(object, member);
	DISPPARAMS params = {NULL, NULL, 0, 0};

	VARIANT result;

	if (FAILED(object->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &params, &result, NULL, NULL))) {
		throw std::exception("failed: GetProperty");
	}
	return _variant_t(result);
}

// メソッドを呼ぶ
_variant_t CallMethod(IDispatch *object, OLECHAR *method, std::vector<_variant_t> &args)
{
	DISPID id = GetIDOfName(object, method);

	// DISPPARAMS の設定
	DISPPARAMS params = {NULL, NULL, 0, 0};
	if (!args.empty()) {
		params.rgvarg = &args[0];
		params.cArgs = args.size();
	}

	VARIANT result;

	if (FAILED(object->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, &result, NULL, NULL))) {
		throw std::exception("failed: CallMethod");
	}
	return _variant_t(result);
}

// メソッドを呼ぶ(1引数バージョン)
_variant_t CallMethod(IDispatch *object, OLECHAR *method, _variant_t arg1)
{
	std::vector<_variant_t> args;
	args.push_back(arg1);

	return CallMethod(object, method, args);
}

void main2()
{
	IShellWindows *sw;

	if (FAILED(CoCreateInstance(CLSID_ShellWindows, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&sw)))) {
		throw std::exception("failed: CoCreateInstance");
	}

	// このコメントは処理のイメージ
	// count = sw.Count;
	long count = GetProperty(sw, L"Count");

	for (long i = 0; i < count; ++i) {
		// ie = sw.Item(i);
		IDispatch *ie = CallMethod(sw, L"Item", _variant_t(i, VT_I4));

		// url = ie.LocationURL;
		_bstr_t url = GetProperty(ie, L"LocationURL");

		std::wcout << url.GetBSTR() << std::endl;
	}

	sw->Release();
}

int main()
{
	// 日本語表示のため
	std::wcout.imbue(std::locale("japanese"));

	CoInitialize(nullptr);

	try {
		main2();
	} catch (std::exception &e) {
		std::cerr << e.what() << std::endl;
	}

	CoUninitialize();

	return 0;
}

あらかた

Re: C++でエクスプローラを開いている時、そのパスを取得したい。

#4

投稿記事 by あらかた » 12年前

a5ua さん、誠にありがとうございます。
おかげ様で目的を果たせました。(しかもIEのURLも取得できるとは!)
IShellWindowsを使えばよかったのですね。
nullptrが定義されていない以外は問題なかったです。

IID_PPV_ARGSなど初見の文法もあり大変勉強になりました。
今後のためにもお聞きしたいのですが、
IShellWindowsに用意されているメソッドなどを直接呼び出さなかったのはなぜですか?
よろしければ返信ください

アバター
a5ua
記事: 199
登録日時: 13年前

Re: C++でエクスプローラを開いている時、そのパスを取得したい。

#5

投稿記事 by a5ua » 12年前

あらかた さんが書きました: IShellWindowsに用意されているメソッドなどを直接呼び出さなかったのはなぜですか?
もちろん、sw->get_Count(&count);のように直接呼んでもできます。
今回は、それ以降が、すべてIDispatchインターフェースを通した操作になっているので、統一してみたという程度のものです。(IShellWindowsはIDispatchを継承している)

ところで、前述のInternetExplorerオブジェクトは、C++ではIWebBrowserインターフェースだと思われます。
http://msdn.microsoft.com/en-us/library ... 85%29.aspx
IDispatch::QueryInterfaceなどで、インターフェースを取得すれば、直接プロパティの取得や、メソッド呼び出しができそうです。

アバター
tk-xleader
記事: 158
登録日時: 13年前
連絡を取る:

Re: C++でエクスプローラを開いている時、そのパスを取得したい。

#6

投稿記事 by tk-xleader » 12年前

エクスプローラー限定ですが、全てメソッドの直接呼出しでエクスプローラの表示パスを得るとすると、おおよそこんなコードになります。

コード:

#include<iostream>
#include<algorithm>
#include<windows.h>
#include<shlobj.h>
#include<shlwapi.h>

#ifdef UNICODE
#define tcout wcout
#else
#define tcout cout
#endif

namespace my{
	/**
	COM用のスマートポインタテンプレートクラス
	即席なので穴だらけ…
	*/
	template<typename T>
	class com_ptr{
		T* ptr;
	public:
		com_ptr():ptr(NULL){}
		explicit com_ptr(T* ptr_):ptr(_ptr){
			ptr->AddRef();
		}
		com_ptr(const com_ptr<T>& obj):ptr(obj.ptr){
			if(ptr)ptr->AddRef();
		}
		template<typename U>
		com_ptr(const com_ptr<U>& other):ptr(NULL){
			if(other->QueryInterface(ptr) != S_OK){
				ptr = NULL;
			}
		}
		~com_ptr(){
			this->release();
		}
		com_ptr& operator=(com_ptr other){
			this->swap(other);
			return *this;
		}
		void swap(com_ptr<T>& other)throw(){
			std::swap(this->ptr,other.ptr);
		}
		void release(){
			if(ptr){
				ptr->Release();
				ptr = NULL;
			}
		}
		bool operator==(const com_ptr&other){
			return this->ptr == other.ptr;
		}
		bool operator!=(const com_ptr&other){
			return !((*this) == other);
		}
		T* get()const{return ptr;}
		operator T*()const{return ptr;}
		T& operator*()const{return *ptr;}
		T* operator->()const{return ptr;}
		T** resetptr(){
			this->release();
			return &ptr;
		}
	};
}

int main(){
	std::wcout.imbue(std::locale(""));
	my::com_ptr<::IShellWindows>shellWindows;
	::CoInitialize(NULL);
	if(FAILED(::CoCreateInstance(::CLSID_ShellWindows,NULL,CLSCTX_ALL,IID_PPV_ARGS(shellWindows.resetptr())))){
		::CoUninitialize();
		return -1;
	}

	long count;
	shellWindows->get_Count(&count);

	::VARIANT index = {};
	index.vt = VT_I4;
	index.lVal = 0;
	for(; index.lVal < count; index.lVal++){
		my::com_ptr<::IDispatch>pDispatch;
		if(shellWindows->Item(index,pDispatch.resetptr()) != S_OK){
			continue;
		}
		my::com_ptr<::IShellBrowser>pShellBrowser;
		if(IUnknown_QueryService(pDispatch,SID_STopLevelBrowser,IID_PPV_ARGS(pShellBrowser.resetptr())) != S_OK){
			continue;
		}
		my::com_ptr<::IShellView>pShellView;
		pShellBrowser->QueryActiveShellView(pShellView.resetptr());
		my::com_ptr<::IFolderView>pFolderView;
		if(pShellView->QueryInterface(IID_PPV_ARGS(pFolderView.resetptr())) != S_OK){
			continue;
		}
		my::com_ptr<::IShellFolder>pShellFolder;
		if(pFolderView->GetFolder(IID_PPV_ARGS(pShellFolder.resetptr())) != S_OK){
			continue;
		}
		my::com_ptr<::IPersistFolder2>pPersistFolder;
		if(pShellFolder->QueryInterface(IID_PPV_ARGS(pPersistFolder.resetptr())) != S_OK){
			continue;
		}
		::LPITEMIDLIST pidl;
		pPersistFolder->GetCurFolder(&pidl);
		TCHAR pathbuf[MAX_PATH+10] = {};
		::SHGetPathFromIDList(pidl,pathbuf);
		std::tcout<<pathbuf<<std::endl;
	}
	::CoUninitialize();
}
IPersistFolder2 インターフェイスが GetCurFolder メソッドを持っていて、これを使えばエクスプローラーが表示しているフォルダのPIDLが得られます。そしてPIDLさえ取得してしまえば、SHGetPathFromIDList API によってパス文字列に変換できます。
訂正 : デバッグ用の余計なコードを削除

アバター
tk-xleader
記事: 158
登録日時: 13年前
連絡を取る:

Re: C++でエクスプローラを開いている時、そのパスを取得したい。

#7

投稿記事 by tk-xleader » 12年前

てかせっかくcom_ptrテンプレートに変換用コンストラクタがあるのだから、それを使わないと、何のために実装したのか?と言う話になりますね…

コード:

#include<iostream>
#include<algorithm>
#define NOMINMAX
#include<windows.h>
#include<shlobj.h>
#include<shlwapi.h>

#ifdef UNICODE
#define tcout wcout
#else
#define tcout cout
#endif

namespace my{
	/**
	COM用のスマートポインタテンプレートクラス
	即製なので穴だらけ…
	*/
	template<typename T>
	class com_ptr{
		T* ptr;
	public:
		com_ptr():ptr(NULL){}
		explicit com_ptr(T* ptr_):ptr(_ptr){
			ptr->AddRef();
		}
		com_ptr(const com_ptr<T>& obj):ptr(obj.ptr){
			if(ptr)ptr->AddRef();
		}
		template<typename U>
		com_ptr(const com_ptr<U>& other):ptr(NULL){
			if(other->QueryInterface(IID_PPV_ARGS(ptr)) != S_OK){
				ptr = NULL;
			}
		}
		~com_ptr(){
			this->release();
		}
		com_ptr& operator=(com_ptr other){
			this->swap(other);
			return *this;
		}
		void swap(com_ptr<T>& other)throw(){
			std::swap(this->ptr,other.ptr);
		}
		void release(){
			if(ptr){
				ptr->Release();
				ptr = NULL;
			}
		}
		bool operator==(const com_ptr&other){
			return this->ptr == other.ptr;
		}
		bool operator!=(const com_ptr&other){
			return !((*this) == other);
		}
		T* get(){return ptr;}
		operator T*(){return ptr;}
		bool operator!(){return !ptr;}
		T& operator*()const{return *ptr;}
		T* operator->()const{return ptr;}
		T** resetptr(){
			this->release();
			return &ptr;
		}
	};
}

int main(){
	std::wcout.imbue(std::locale(""));
	my::com_ptr<::IShellWindows>shellWindows;
	::CoInitialize(NULL);
	if(FAILED(::CoCreateInstance(::CLSID_ShellWindows,NULL,CLSCTX_ALL,IID_PPV_ARGS(shellWindows.resetptr())))){
		::CoUninitialize();
		return -1;
	}

	long count;
	shellWindows->get_Count(&count);

	::VARIANT index = {};
	index.vt = VT_I4;
	index.lVal = 0;
	for(; index.lVal < count; index.lVal++){
		my::com_ptr<::IDispatch>pDispatch;
		if(shellWindows->Item(index,pDispatch.resetptr()) != S_OK){
			continue;
		}
		my::com_ptr<::IShellBrowser>pShellBrowser;
		if(IUnknown_QueryService(pDispatch,SID_STopLevelBrowser,IID_PPV_ARGS(pShellBrowser.resetptr())) != S_OK){
			continue;
		}
		my::com_ptr<::IShellView>pShellView;
		pShellBrowser->QueryActiveShellView(pShellView.resetptr());
		my::com_ptr<::IFolderView>pFolderView(pShellView);
		if(!pFolderView){
			continue;
		}
		my::com_ptr<::IShellFolder>pShellFolder;
		if(pFolderView->GetFolder(IID_PPV_ARGS(pShellFolder.resetptr())) != S_OK){
			continue;
		}
		my::com_ptr<::IPersistFolder2>pPersistFolder(pShellFolder);
		if(!pPersistFolder){
			continue;
		}
		::LPITEMIDLIST pidl;
		pPersistFolder->GetCurFolder(&pidl);
		TCHAR pathbuf[MAX_PATH+10] = {};
		::SHGetPathFromIDList(pidl,pathbuf);
		std::tcout<<pathbuf<<std::endl;
	}
	::CoUninitialize();
}

あらかた

Re: C++でエクスプローラを開いている時、そのパスを取得したい。

#8

投稿記事 by あらかた » 12年前

a5ua さん、tkmakwins15さん
ありがとうございます。
それぞれ別の取得方法で参考になります。

この返信までには提示くださったコードなどを理解するつもりでしたが、
調べれば調べるほど様々なCOMオブジェクトが出てくるので整理が追いつきません。
理解できない部分が明確になってからまた質問したいと思います。
そのときはまたご教授ください。本当にありがとうございました。

閉鎖

“C言語何でも質問掲示板” へ戻る