none
SHFileOperationによるファイル操作を元に戻す

    質問

  •  SHFileOperationを使って、ファイルを削除し、それを元に戻す方法について、
    http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+200310/03100013.txt
    などを参考に実装してみました。

    ところが、Windowsの種類によって全く別の結果になってしまいます。
    Windows2000では、このやり方でうまくいくのですが、XP(Home)とVistaでは元に戻すことができません。

    まず、削除する機能を次のように実装しました。
        SHFILEOPSTRUCT fo;  
        ZeroMemory(&fo, sizeof(SHFILEOPSTRUCT));  
        fo.hwnd = GetSafeHwnd();  
        fo.wFunc = FO_DELETE;  
        fo.pFrom = tc; // ここには\0を2重につけたファイル名が入っています  
        fo.fFlags = FOF_ALLOWUNDO | FOF_SIMPLEPROGRESS;  
     
        int nRet = SHFileOperation(&fo);  
        if (nRet != 0)  
        {  
            // エラー処理  
        }  
     

    そして、元に戻す機能は次のような感じです。
        LPSHELLFOLDER   lpShellFolder;  
        LPSHELLVIEW     lpShellView;  
        LPCONTEXTMENU   lpContextMenu;  
        CMINVOKECOMMANDINFO cmi;  
     
        //IShellFolder取得  
        SHGetDesktopFolder(&lpShellFolder);  
     
        //IShellView取得  
        lpShellFolder->CreateViewObject(GetSafeHwnd(), 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 = (LPCSTR)MAKEINTRESOURCE(28700 - 1);  
        cmi.hwnd = GetSafeHwnd();  
        cmi.lpParameters = NULL;  
        cmi.lpDirectory = NULL;  
        cmi.nShow = SW_SHOWNORMAL;  
        cmi.dwHotKey = 0;  
        cmi.hIcon = NULL;  
        lpContextMenu->InvokeCommand(&cmi);  
     
        //解放  
        lpContextMenu->Release();  
        lpShellFolder->Release();  
     

    補足その1:
    InvokeCommand()で直接呼び出す代わりに、CMenuを作ってTrackPopupMenu()でポップアップを表示させて実際にコマンドを呼びだしてみましたが、Undoしてくれません。

    補足その2:
    Vistaの場合、削除の実行後エクスプローラの[編集]メニューを見ると、「元に戻す - 削除」というコマンドが有効になっており、これを実行すると、ちゃんと削除が「元に戻り」ます。ところが、XPの場合直後にエクスプローラの[編集]メニューを見ても該当する「元に戻す」コマンドが表示されません。(その前にエクスプローラで行った操作の「元に戻す」が表示されます。)

    OSによって違うので、UACなどの関係なのか、そもそも28700 というIDが違っているのかなどといろいろ調べてみたのですが、お手上げ状態です。

    どなたか、アドバイスをお願いいたします。
    2009年2月20日 2:08

回答

  • JANEQUIN さん の発言:

    「プロセス」「セッション」の意味が正しく理解できていないので違っているかもしれませんが、前掲のコードでは(とりあえず)自プロセス(?)で削除した処理を元に戻そうとしています。
    しかも、このコードで2000では動作して、XPとVistaではだめなんです。

    OSというより、シェルが提供している部分になるので、バージョン間で挙動が違うことはあり得ます。(IContextMenuでいじくるという手法は明文化されていないので、バージョン依存の場当たり的な対応になります)

    JANEQUIN さん の発言:

    エクスプローラと同じような機能を持たせたいので、名前の変更や貼り付けなど、すべてのファイル操作を元に戻せるようにしようと考えています。

    全てのファイル操作に対するUndoは、一般的な手法が提供されていません。
    自身で移動操作やら、リネーム操作やらを記憶しておいて、やるしかないんじゃないかなぁ。

    そう言う意味で、「Undoの実行までフォローしてくれない理不尽さに悩みます」とコメントしています。

    残念ながら、IFileOperationもUndo操作の一般的なやり方を提供してくれていません。


    解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。
    • 回答としてマーク JANEQUIN 2009年2月23日 23:41
    2009年2月23日 14:39
    モデレータ

すべての返信

  • 元に戻すって、「1つ直前のファイル操作を元に戻す」のか、「ゴミ箱のファイルを元(の場所)に戻す」のか、どちらなんでしょうか。
    いずれにしても、OSというより、エクスプローラ依存の動作になりますので、あんまりアテにしたくないところですが、そうも言ってられないんですよね…。

    ちなみに、FOF_ALLOWUNDOでの操作結果は、XPまではプロセスを越えて共有されていないはずです。
    別プロセスで実行されるようなことがあれば、このUndo情報は利用できないことになります。
    Vistaはセッションが同じだと共有されているらしいですね、今回初めて知りましたが…。
    http://msdn.microsoft.com/en-us/library/bb759795(VS.85).aspx


    # SHFileOperationやら、IFileOperationやらでUndoフラグを用意している割に、Undoの実行まではフォローしてくれない理不尽さに悩みます。

    ゴミ箱のファイルを元(の場所)に戻すという視点でいけば、ゴミ箱のIShellFolderにたどり着いて、IContextMenuを経て、元(の場所)に戻すを実行するということになります。
    http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+200901/09010004.txt

    解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。
    2009年2月20日 15:49
    モデレータ
  • pFromのファイル名はフルパスでなければならないそうですが、その点は大丈夫でしょうか?
    XP以前はこの操作を行ったプロセスのみ有効だそうです。
    VistaからはIFileOperation Interfaceを使うようにとのことですので、本来、別プロセスであるはずのExplorerからUndoが有効になるよう拡張されているのでしょうね。

    とはいえ、一般的なUndo方法は見つけることができませんでした。

    2009年2月21日 5:43
  • Azulean さん、

    いつもありがとうございます。

    元に戻すって、「1つ直前のファイル操作を元に戻す」のか、「ゴミ箱のファイルを元(の場所)に戻す」のか、どちらなんでしょうか。

    エクスプローラと同じような機能を持たせたいので、名前の変更や貼り付けなど、すべてのファイル操作を元に戻せるようにしようと考えています。

    ちなみに、FOF_ALLOWUNDOでの操作結果は、XPまではプロセスを越えて共有されていないはずです。
    別プロセスで実行されるようなことがあれば、このUndo情報は利用できないことになります。
    Vistaはセッションが同じだと共有されているらしいですね、今回初めて知りましたが…。
    http://msdn.microsoft.com/en-us/library/bb759795(VS.85).aspx


    「プロセス」「セッション」の意味が正しく理解できていないので違っているかもしれませんが、前掲のコードでは(とりあえず)自プロセス(?)で削除した処理を元に戻そうとしています。
    しかも、このコードで2000では動作して、XPとVistaではだめなんです。

    ゴミ箱のファイルを元(の場所)に戻すという視点でいけば、ゴミ箱のIShellFolderにたどり着いて、IContextMenuを経て、元(の場所)に戻すを実行するということになります。
    http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+200901/09010004.txt


    実験したコードはゴミ箱のIShellFolderではなく、デスクトップのIShellFolderのIContextMenuを使って戻すというものです。これならプロセスやセッションは関係ないのでしょうか?
    2009年2月23日 0:44
  • 佐祐理 さん、

    pFromのファイル名はフルパスでなければならないそうですが、その点は大丈夫でしょうか?


    ファイル名はフルパスです。

    XP以前はこの操作を行ったプロセスのみ有効だそうです。


    私の書いた「補足2」の現象がこのことを表しているのですね。

    VistaからはIFileOperation Interfaceを使うようにとのことですので、本来、別プロセスであるはずのExplorerからUndoが有効になるよう拡張されているのでしょうね。


    なるほど。まずこれについて調べてみることにします。

    ありがとうございました。
    2009年2月23日 0:56
  • JANEQUIN さん の発言:

    「プロセス」「セッション」の意味が正しく理解できていないので違っているかもしれませんが、前掲のコードでは(とりあえず)自プロセス(?)で削除した処理を元に戻そうとしています。
    しかも、このコードで2000では動作して、XPとVistaではだめなんです。

    OSというより、シェルが提供している部分になるので、バージョン間で挙動が違うことはあり得ます。(IContextMenuでいじくるという手法は明文化されていないので、バージョン依存の場当たり的な対応になります)

    JANEQUIN さん の発言:

    エクスプローラと同じような機能を持たせたいので、名前の変更や貼り付けなど、すべてのファイル操作を元に戻せるようにしようと考えています。

    全てのファイル操作に対するUndoは、一般的な手法が提供されていません。
    自身で移動操作やら、リネーム操作やらを記憶しておいて、やるしかないんじゃないかなぁ。

    そう言う意味で、「Undoの実行までフォローしてくれない理不尽さに悩みます」とコメントしています。

    残念ながら、IFileOperationもUndo操作の一般的なやり方を提供してくれていません。


    解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。
    • 回答としてマーク JANEQUIN 2009年2月23日 23:41
    2009年2月23日 14:39
    モデレータ
  • Azulean さん、

    自分なりに次のように結論付けました。

    ◆IContextMenuを使って呼び出す方法について
    たまたまうまくいくケースもあるが、手法が明文化されていないし、結果は不定。削除の場合にゴミ箱のIContextMenuを呼び出すのも同様。

    ◆IFileOperationについて
    Vista以上限定だし、そもそもUndoに関する機能は提供されていない。

    ◆結局は、Undoの処理は自前でやるしかない。

    いずれにしても、カット/コピー/ペーストの3つの操作に対して、SHFileOperationでは、MoveとCopyというどちらかというとDrag&Drop的な処理しかないので、この部分は自前でやらなければならないだろうなとは思っていました。あきらめて自前で記述するようにします(気が重いです)。

    ありがとうございました。
    2009年2月23日 23:31