直前のファイル操作を元に戻すには?

[戻る]
rew 2003/10/04(土) 23:41:21
はじめまして、rewと言います。
いろいろ調べてはみたのですが分からないことがあったので、質問させて頂きます。

エクスプローラでは、ファイルの移動や削除などの直前に行った操作を
元に戻すメニューがありますが、これをプログラム上から行えないでしょうか。
SHFileOperationでFOF_ALLOWUNDOフラグを指定してファイルの移動などを行う
プログラムなのですが、これに「元に戻す」ボタンを付けたいのです。

環境はWin98SE+VC6で、MFCは使ってません。
どなたかご存じの方がいましたら、よろしくお願いします。

tetuo 2003/10/05(日) 11:13:22
実行した操作を記憶しておく。
「元に戻す」ボタンが押されてたら記憶していた操作と逆の操作をする。

これだけのこと。

rew 2003/10/05(日) 13:47:30
tetuoさん、どうもレスありがとうございます。

>実行した操作を記憶しておく。
>「元に戻す」ボタンが押されてたら記憶していた操作と逆の操作をする。

MoveFileやDeleteFileなどで行った操作なら、tetuoさんのおっしゃる方法で
やるしかないのかなと思います。
が、SHFileOperationでFOF_ALLOWUNDOフラグを指定してプログラム上で
移動などを行うと、別にプログラム側で操作を記憶しておかなくても
エクスプローラ上で「元に戻す」ボタンを押すとアンドゥが出来ますよね。
つまり、どこかに勝手にアンドゥ情報を保持してくれているわけです。

エクスプローラーを使った移動やごみ箱への削除はすべて「元に戻せる」わけで、
SHFileOperationでFOF_ALLOWUNDOを指定して行った操作なら一発で元に戻せる
ような命令があってもおかしくないのではと考えたのですが。

やはりそのような命令はないのでしょうか。
なければないで自前で実装したいと思いますので、ご存じの方よろしくお願いします。

kazuma 2003/10/05(日) 14:35:15
始めにお断りしておきますが,ひょっとしたらこれで出来るかな程度のアイデアです。
もっといい方法があるかもしれません。

1. まず適当なフォルダ(デスクトップフォルダなど)の IShellFolder を取得します。
   (SHGetDesktopFolder)
2. IShellFolder::GetUIObjectOf を使って IContextMenu を取得します。
3. IContextMenu::InvokeCommand で,「元に戻す」コマンドを実行させます。

kazuma 2003/10/05(日) 14:43:37
2. は IShellFolder::GetUIObjectOf のかわりに IShellFolder::CreateViewObject がい
いかもしれません。

kazuma 2003/10/05(日) 18:57:59
kazuma 2003/10/05(日) 14:35:15 についてです。

少し試してみたんですが,この方法では出来ないかもしれないです。

とりあえず,デスクトップとマイコンピュータの IContextMenu を取得して,
IContextMenu::QueryContextMenu を呼んでみたのですが,
得られたメニュー項目に「元に戻す」がないんですよね。

# IShellFolder::GetUIObjectOf, IShellFolder::CreateViewObject のいずれの場合もで
す。

rew 2003/10/05(日) 20:00:25
kazumaさん、ご助言ありがとうございます。
夕方頃にkazumaさんのレスを拝見して、それを参考に今まで組んでいました。
今見てみると「これでは無理かも」とのことですが、お教え頂いた方法で
なんとか元に戻すことに成功しました。以下にコードをのせておきます。
拡張シェルやインターフェースを扱うのはほとんど初めてで、かなり試行錯誤しながら
組んだものなので、何かおかしなところがあればぜひご指摘下さい。

void TestFunc(HWND hWnd)
{
    LPSHELLFOLDER        lpShellFolder;
    LPSHELLVIEW            lpShellView;
    LPCONTEXTMENU        lpContextMenu;
    CMINVOKECOMMANDINFO cmi;

    //IShellFolder取得
    SHGetDesktopFolder(&lpShellFolder);

    //IShellView取得
    lpShellFolder->CreateViewObject(hWnd, IID_IShellView, (LPVOID *)
&lpShellView);

    //IContextMenu取得(ビュー背景のコンテキストメニュー)
    lpShellView->GetItemObject(SVGIO_BACKGROUND, IID_IContextMenu, 
(LPVOID *)&lpContextMenu);

    //メニューのリスト取得
    lpContextMenu->QueryContextMenu(NULL, 0, 1, 0x7FFF, CMF_NORMAL);

    //元に戻す(28700)を実行
    ZeroMemory(&cmi, sizeof(CMINVOKECOMMANDINFO));
    cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
    cmi.fMask = 0;
    cmi.lpVerb = MAKEINTRESOURCE(28700 - 1);
    cmi.hwnd = hWnd;
    cmi.lpParameters = NULL;
    cmi.lpDirectory = NULL;
    cmi.nShow = SW_SHOWNORMAL;
    cmi.dwHotKey = 0;
    cmi.hIcon = NULL;
    lpContextMenu->InvokeCommand(&cmi);

    //解放
    lpContextMenu->Release();
    lpShellFolder->Release();

    return;
}


kazumaさん、どうもありがとうございました。

rew 2003/10/05(日) 20:06:50
[[解決]]
失礼しました。解決にチェックするのを忘れていました。

それとちょっと書き忘れていたのですが、「元に戻す」のメニュー項目IDは、
例えばIDM_UNDOのような感じでどこかに定義されてないのでしょうか?
受け取ったIDから自前で28700と調べてはみたのですが、なんとも格好悪い
気がするので、ご存じの方がいましたらよろしくお願いします。
また、もっと簡単に元に戻す方法をご存じの方がいましたら、そちらもよろしくお願いします。

それでは。

tetuo 2003/10/05(日) 21:01:04
エクスプローラーの元に戻すをエミュレートしたかったのか。
すっかり勘違いしてた。ごめんなさい。

rew 2003/10/05(日) 21:12:33
>エクスプローラーの元に戻すをエミュレートしたかったのか。

はい、つまりはそう言うことです。最初からそう書けばわかりやすかったですね。
色々考えて書いたのですが回りくどい表現になってしまい、申し訳ないです。。。

kazuma 2003/10/06(月) 14:09:32
rew さんのソースを試してみましたが,
私の環境(Windows 2000 SP4, IE6 SP1)では動きませんでした。

試しに下のようなコードで,メニューを表示させてみましたが,
メニュー項目に「元に戻す」がありません。

# もちろんエクスプローラで右クリックした場合は,「元に戻す」があります。

OS 等の環境によって動作が違うのかもしれませんね。
ご確認ください。

//メニューのリスト取得
CMenu menu;
POINT pos;

menu.CreatePopupMenu();
lpContextMenu->QueryContextMenu(menu, 0, 0, 0x7FFF, 0);
::GetCursorPos(&pos);
menu.TrackPopupMenu(0, pos.x, pos.y, CWnd::FromHandle(hWnd));

渋木宏明(ひどり) [E-Mail] [HomePage] 2003/10/06(月) 15:39:52
>私の環境(Windows 2000 SP4, IE6 SP1)では動きませんでした。

SHFileOperation() のヘルプかどっかにコソっと書いてあったけど、UNDO 操作が有効な
のは同じプロセス内だけかららしいです。

なので、例えば、個々のフォルダウィンドウを別プロセスで起動する設定だと、↑の方
法はうまくいかないかもしれませんね。

rew 2003/10/06(月) 18:05:46
>試しに下のようなコードで,メニューを表示させてみましたが,
>メニュー項目に「元に戻す」がありません。

メニューIDを調べるときに、kazumaさんと同じような方法でメニューを表示し、
「元に戻す」を選択して返ってきた値を取得・・・という方法をとりました。
私の環境ではその時に「元に戻す」がメニューにあったわけですが、環境によっては
動かないというようだと、ちょっと困りものですよね。。。


>UNDO 操作が有効なのは同じプロセス内だけかららしいです。
>なので、例えば、個々のフォルダウィンドウを別プロセスで起動する設定だと、↑の方
>法はうまくいかないかもしれませんね。

この設定は初耳だったのでちょっと調べてみたのですが、どうやら98にはないようですね。
お手数ですがこの設定を確認して頂けないでしょうか。
もしこの設定をしてるようなら、解除したときは動くのか教えて頂けるとありがたいです。

UNDOは同じプロセス内からだけ、ということなら、別プロセスで起動する設定の場合には
動かないのもやむをえないのかな、という気もします。
しかし同じプロセス内ということは、エクスプローラで操作した内容は戻せなくても、
プログラム内でSHFileOperationで操作した内容については元に戻せるのでしょうか?
それなら自分にとっては問題なし(むしろ好都合)なのですが・・・。

#プロセスとかに関する知識がないので、見当違いなことを言ってたらご指摘下さい。

kazuma 2003/10/07(火) 00:48:09
フォルダオプションの,
「別のプロセスでフォルダウィンドウを開く」
にはチェックを入れていません。

が,エクスプローラと私が作ったテスト用アプリとは当然別プロセスで動いてますから。

> しかし同じプロセス内ということは、エクスプローラで操作した内容は戻せなくても、
> プログラム内でSHFileOperationで操作した内容については元に戻せるのでしょうか?

テスト用のアプリで SHFileOperation を使ってファイルを削除してみたところ,
rew さんのコードで元に戻すことが出来ました。

rew 2003/10/07(火) 16:52:50
>テスト用のアプリで SHFileOperation を使ってファイルを削除してみたところ,
>rew さんのコードで元に戻すことが出来ました。

kazumaさん、どうもありがとうございました。
これで安心して使うことが出来ます。

古いスレッドには追加発言できません。