none
クリップボード内のデータが「コピー」or「切り取り」されたかを判断したい RRS feed

  • 質問

  • お世話になります。

     

    現在、ファイルを「コピー」「切り取り」してクリップボードに入れた後、

    クリップボードからIDataObjectを取得し、「コピー」「切り取り」の判断をしています。

     ※ファイルの「コピー」「切り取り」の処理はエクスプローラーで行なっています。

    しかし、zip内(仮想フォルダー内??)のファイルなどを「コピー」「切り取り」した場合、判断ができないようです。

    zip内のファイルなどをクリップボードに入れた場合でも、「コピー」「切り取り」の判断をする方法はないでしょうか?

     ※zip内のファイルとは、エクスプローラーなどで、ツリーやリストに展開されているファイルのことです

     

    簡単にはしていますが、現在、処理は以下のようにしています。

    IDataObject* pIDataObject = NULL;
    ::OleGetClipboard( &pIDataObject );

    FORMATETC   fmt = {0};
    fmt.cfFormat = (CLIPFORMAT)::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT);
    fmt.ptd      = NULL;
    fmt.dwAspect = DVASPECT_CONTENT;
    fmt.lindex   = -1;
    fmt.tymed    = TYMED_HGLOBAL;

    if ( S_OK ==  pIDataObject->QueryGetData( &fmt ) ) {
     STGMEDIUM        medium;
     pIDataObject->GetData( &fmt, &medium );
     DWORD* pdwEffect = (DWORD*)GlobalLock(medium.hGlobal);
     DWORD dwEffect = *pdwEffect;
     if ( dwEffect & DROPEFFECT_COPY ) {
     } else if ( dwEffect & DROPEFFECT_MOVE ) {
     }
    }

     

    通常のフォルダ内のファイルをクリップボードに入れた場合は、正常に判断できるのですが、

    zip内のファイルの場合、以下のところで「クリップボードのデータ形式が無効です」エラー番号が返ってきています。

    >>>> if ( S_OK ==  pIDataObject->QueryGetData( &fmt ) ) {

     

    環境は、XP,Vista VC++2010 です。

    2011年3月25日 4:23

回答

  • クリップボード・ドラッグドロップともに、これといってコピー・切り取りを判断できる独立情報はないみたいですね。

    FileDescriptor の内容はチェックしてみましたか?何かわかるかもしれません。ダメという可能性もありますが。。。外部からわかるのはその程度ですね。

    それで何をするか?にもよりますが、最終的にIShellFolder を取り出せるところに落とすのなら、任せてしまうという手もあります。

    IShellFolder::GetUIObject()メソッドを利用すればIDropTarget や IContextMenu を取得できます。これらにペースト操作をさせてしまえば、面倒なコピー処理をすべてまかなってくれます。

    ファイルを読み取る場合は、FileContents で Streamを引っ張れば圧縮ファイルの中身も読めますね。でもこっちだとコピー・カットは関係ない?

     


    わんくま同盟,Microsoft MVP for Visual C++(Oct 2005-) http://blogs.wankuma.com/tocchann/
    • 回答としてマーク target_3to4 2011年3月29日 7:59
    2011年3月25日 11:19
  • 登録されている情報から判断できないとすれば、それがバグであろうが、仕様であろうが判定は不可能。。。ということになるかと。

    なので、自分で処理することはできないと判断するのが妥当と思われます。

    もしかすると、FileContents の IStream を使ってファイルをすべて読み取るとその時点で自動的に削除されるなどはあるかもしれません。

    FileDescriptor(ファイルの名前などの情報がある。調べたようなのでわかると思います)とともに使えば、かなりいろんなことができます。これを機会に試してみるのもいいかもしれません。

    とはいえ、単に読み取っただけではダメ。。。という可能性もあるので、最終的には IShellFolder に任せてしまうのが一番無難かもしれませんが。。。

    ペースト(というよりメニュー処理)は、http://msdn.microsoft.com/en-us/library/cc144169(VS.85).aspx に詳細があります。

    もしかしたら、ZIPのエクステンションについても何か記述があるかもしれませんが、調査してないのでそれについてはわかりません。

     

     


    わんくま同盟,Microsoft MVP for Visual C++(Oct 2005-) http://blogs.wankuma.com/tocchann/
    • 回答としてマーク target_3to4 2011年3月29日 7:59
    2011年3月28日 10:19

すべての返信

  • ほかに試したことを書き忘れていましたので追記します。

    COleDataObject::IsDataAvailable で CFSTR_PREFERREDDROPEFFECT 

    が格納されているかも調べてみましたが、格納されていないようです。

     

    エクスプローラーのなどでは、CFSTR_PREFERREDDROPEFFECT が格納されてなくても

    「コピー」「切り取り」の判断をし、正しく処理されていますので、何か方法はあるのではないかと思っています。

    2011年3月25日 4:35
  • クリップボード・ドラッグドロップともに、これといってコピー・切り取りを判断できる独立情報はないみたいですね。

    FileDescriptor の内容はチェックしてみましたか?何かわかるかもしれません。ダメという可能性もありますが。。。外部からわかるのはその程度ですね。

    それで何をするか?にもよりますが、最終的にIShellFolder を取り出せるところに落とすのなら、任せてしまうという手もあります。

    IShellFolder::GetUIObject()メソッドを利用すればIDropTarget や IContextMenu を取得できます。これらにペースト操作をさせてしまえば、面倒なコピー処理をすべてまかなってくれます。

    ファイルを読み取る場合は、FileContents で Streamを引っ張れば圧縮ファイルの中身も読めますね。でもこっちだとコピー・カットは関係ない?

     


    わんくま同盟,Microsoft MVP for Visual C++(Oct 2005-) http://blogs.wankuma.com/tocchann/
    • 回答としてマーク target_3to4 2011年3月29日 7:59
    2011年3月25日 11:19
  • >zip内のファイルなどをクリップボードに入れた場合でも、「コピー」「切り取り」の判断をする方法はないでしょうか?
    調べてみましたが、方法は見つかりませんでした。

    >「コピー」「切り取り」の判断をし、正しく処理されていますので、何か方法はあるのではないかと思っています。
    Undocumentな方法で実現されている可能性もあります。

    しかし、ZIP FolderでCFSTR_PREFERREDDROPEFFECTが取得できないのは、不具合のようにも思えます。

    2011年3月25日 13:46
  • 回答ありがとうございます。

     

    とっちゃん様

    >>クリップボード・ドラッグドロップともに、これといってコピー・切り取りを判断できる独立情報はないみたいですね

    →これは、CFSTR_PREFERREDDROPEFFECT 以外に判断できる情報がないということでしょうか?

    >>FileDescriptor の内容はチェックしてみましたか?何かわかるかもしれません。ダメという可能性もありますが。。。外部からわかるのはその程度ですね。

    →FileDescriptor はクリップボードに含まれるデータ形式の一つだったでしょうか?

     前に見たことがあると思うのですが、すぐに見つかりませんでした…後で確認します。

     

    >>それで何をするか?にもよりますが、最終的にIShellFolder を取り出せるところに落とすのなら、任せてしまうという手もあります。IShellFolder::GetUIObject()メソッドを利用すればIDropTarget や IContextMenu を取得できます。これらにペースト操作をさせてしまえば、面倒なコピー処理をすべてまかなってくれます。

    →説明不足でした。特に特殊な処理をしたいわけではなく、クリップボードの内容を指定したフォルダーにペーストしたいだけです。「コピー」か「切り取り」かは、勝手に判断して、処理してもらっても問題はありません。エクスプローラーの貼り付けの動作を、自分のアプリで貼り付け先のフォルダーを指定して処理したいだけです。(非同期という問題もありますが)

    IContextMenuだとInvokeCommandでPasteを強制的に実行させる感じでしょうか?

    IDropTarget はドラッグ&ドロップで使うイメージだったので、Pasteに使えるとは思っていませんでした。調べてみます。

     

    kozz様

    >「コピー」「切り取り」の判断をし、正しく処理されていますので、何か方法はあるのではないかと思っています。
    Undocumentな方法で実現されている可能性もあります。

    しかし、ZIP FolderでCFSTR_PREFERREDDROPEFFECTが取得できないのは、不具合のようにも思えます。

    → もし Undocument だとすれば、仕方ないですね…

    できれば自分で判断して処理したかったのですが、とっちゃん様提示のIShellFolderから処理するほうが現実的かもしれません・

     

    >しかし、ZIP FolderでCFSTR_PREFERREDDROPEFFECTが取得できないのは、不具合のようにも思えます。

    →Zip内のファイルは特殊な扱いになっているのだと思います。当初、私も不具合と思ったのですが、エクスプローラーでは正しく処理されていたので、おそらくそういう仕様なのだと思います。

     

    2011年3月25日 15:50
  •  

    FileDescriptorについて調べてみました。

    http://msdn.microsoft.com/en-us/library/bb773288(v=vs.85).aspx

    http://msdn.microsoft.com/en-us/library/bb773290(v=vs.85).aspx

     

    クリップボードからFILEDESCRIPTOR構造体を取得できるみたいですが、

    調べた限りでは、「コピー」「切り取り」を判断する情報はなさそうでした。

     

    2011年3月28日 7:09
  • 登録されている情報から判断できないとすれば、それがバグであろうが、仕様であろうが判定は不可能。。。ということになるかと。

    なので、自分で処理することはできないと判断するのが妥当と思われます。

    もしかすると、FileContents の IStream を使ってファイルをすべて読み取るとその時点で自動的に削除されるなどはあるかもしれません。

    FileDescriptor(ファイルの名前などの情報がある。調べたようなのでわかると思います)とともに使えば、かなりいろんなことができます。これを機会に試してみるのもいいかもしれません。

    とはいえ、単に読み取っただけではダメ。。。という可能性もあるので、最終的には IShellFolder に任せてしまうのが一番無難かもしれませんが。。。

    ペースト(というよりメニュー処理)は、http://msdn.microsoft.com/en-us/library/cc144169(VS.85).aspx に詳細があります。

    もしかしたら、ZIPのエクステンションについても何か記述があるかもしれませんが、調査してないのでそれについてはわかりません。

     

     


    わんくま同盟,Microsoft MVP for Visual C++(Oct 2005-) http://blogs.wankuma.com/tocchann/
    • 回答としてマーク target_3to4 2011年3月29日 7:59
    2011年3月28日 10:19
  • タイトルだけ読むと一般論を尋ねているようにも読めます。しかし、Windows Explorer固有の話とも読めます。

    一般論として判断は無理なはずです。メモ帳で文字列をクリップボードに入れる際、切り取り or コピーの処理はクリップボードに入れる前に完了しています。後から貼り付けがされようがされなかろうが関係ありません。またクリップボードの内容は何回でも貼り付け可能です。

    これに対してWindows Explorerは独特な動きをします。切り取りを行ってもアイコンが薄くなるだけで実際のファイルシステムから切り取られているわけではありません。貼り付けを行わなければファイルは元通り残ります。貼り付けを行って初めて元ファイルは削除(というか移動)されます。また1回目の貼り付けでクリップボードはクリアされ、2回以上貼り付けることはできません。
    さらに、切り取りを行った状態で、ファイルを扱う別アプリケーション(例えばVisual Studioのソリューションエクスプローラー)に貼り付けを行うと、貼り付けはできますが、コピー動作となり元ファイルは削除されません。

    このことからわかるのは、Windows Explorerはクリップボード経由では切り取りは行っておらず、Windows Explorer→Windows Explorer間の場合のみ内部処理として切り取り(ファイルの移動)を行っている、と思われます。

    となると、質問に書かれているようなコード、つまり一般的なクリップボードAPIではなく、もしヒントがあるとするとWindows Explorerの内部情報、場合によってはCOMか何かでAPI公開されているかもしれません。

    # 具体的には何も探してないです。ゴメンナサイw

     

    --追記--

    そういえば、Windows Explorerに対してIDropSourceとなるアプリケーションを書いたことがありますが、Shell Clipboard Formatsに記載されていない大量のリクエストがIDataObject::GetData()で呼ばれてきたような記憶があります。その中に、MoveかCopyかを問うGetData()もあったかもしれません。多すぎて覚えてないので申し訳ない。ちなみにその時はIDataObject::QueryGetData()とかIEnumFORMATETC::Next()なんてまどろっこしいことはせず、いきなりGetData()が呼ばれていました。恐るべしWindows Explorer。

    ちなみに私自身は手抜きでCFSTR_FILEDESCRIPTORWとCFSTR_FILECONTENTSの2つしか実装しませんでした。
    2011年3月28日 22:46
  • 回答ありがとうございます。

     

    とっちゃん様

    >登録されている情報から判断できないとすれば、それがバグであろうが、仕様であろうが判定は不可能。。。ということになるかと。

    →そうですね…できれば判断して処理したかったのですが諦めます。

    今は、IContextMenuを試してます。今のところ問題なさそうです。

    IDropTargetの場合、DROPEFFECT_COPYやDROPEFFECT_COPYを

    結局指定しなければいけないようだったので試していません。

     

    >ペースト(というよりメニュー処理)は、http://msdn.microsoft.com/en-us/library/cc144169(VS.85).aspx に詳細があります。

    →ありがとうございます。参考にさせて頂きます。

     

     

    佐祐理様

     

    >一般論として判断は無理なはずです。

    →説明不足だったでしょうか。

    一番最初に記載したソースで、通常フォルダー内のファイルをコピー・切り取りした場合は、正常に判断できています。

    Zip内のファイル(Zipそのものではありません)をコピー・切り取りした場合、

    CFSTR_PREFERREDDROPEFFECTのデータが取得できず、判断できていません。

    >その中に、MoveかCopyかを問うGetData()もあったかもしれません。

    →おそらくCFSTR_PREFERREDDROPEFFECTではないかと思います。

     

     

    色々情報を頂いていますが、今のところ、「コピー」「切り取り」の判断をするのではなく

    IContextMenuのPasteに任せるのがいいように思います。

    まだ、確認中なので結果は後にご報告させて頂きます。

     

     

    2011年3月29日 0:45
  • 確認した結果、IShellFolder経由でIContextMenuを取得し、

    IContextMenuのInvokeCommandでPasteを指定して実行すれば

    希望の動作を実現することができました。

    今のところ問題はなさそうなので、この方法を使用したいと思います。

     

    Zip内のファイルの「コピー」「切り取り」が判定できていないのは残念ですが、

    一旦解決とさせて頂きます。

    もし方法があれば書き込んで頂けると助かります。

     

    とっちゃん様、kozz様、佐祐理様

    ありがとうございました。

     

     

    2011年3月29日 8:01