none
仮想フォルダのドラッグ&ドロップ RRS feed

  • 質問

  • こんにちは

    CListCtrlにエクスプローラなどからドラッグ&ドロップでファイルのパス名などを登録するソフトを作っています。(Visual Studio 2008 C++/MFC)

    しかし、マイコンピュータや、マイネットワーク、コントロールパネルの項目など特殊はフォルダ(?)をどうやって登録すればいいのか調べても全くわかりません。

    どなたかご教授くださいますでしょうか。

    ちなみに、普通のファイル・フォルダはCOleDropTargetクラスを継承して、OnDropをオーバーロードすることで、次のようにしています。

    BOOL CDropTarget:SurprisenDrop(CWnd *pWnd, COleDataObject *pDataObject,  DROPEFFECT dropEffect, CPoint point)
    {
        if(pDataObject->IsDataAvailable(CF_HDROP)){

            FORMATETC fmtetc =  { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
            STGMEDIUM stgmed;

            if(pDataObject->GetData(CF_HDROP, &stgmed, &fmtetc)) {
                HDROP hdrop = (HDROP)GlobalLock(stgmed.hGlobal);

                if (NULL != hdrop){
                    m_dlg->dropFiles(hdrop);
                    GlobalUnlock(stgmed.hGlobal);
                }
            }

    マイコンピュータなどは、

      if(pDataObject->IsDataAvailable(RegisterClipboardFormat(CFSTR_SHELLIDLIST))){
                 ・・・・
          }

    で真となることまではわかっていますが、この後がわかりません。


    不足する情報などあればご指摘いただければ幸いです。

    よろしくお願いいたします。
    2008年8月20日 19:19

回答

  • ポイントだけ。

     

    ・CFSTR_SHELLIDLISTは0番目に親のフォルダを、1番目から要素の配列を構成します。1番目からの要素は0番目の要素からの相対ITEMIDLISTになります。使うためには0番目とITEMIDLISTの連結が必要です。

    ・ITEMIDLISTの連結にはILCombineを使います。但し、この戻り値は解放が必要なのでILFreeを使って下さい。

    ・連結したITEMIDLISTで開くことで開けるコントロールパネルが出てきますが、一部は開けません。全てに対応するにはlpVerbをNULLにする必要があります。

    ・このソースコードで取得されるlpcItemIdListは解放してはいけません。ReleaseStgMedium関数でSTGMEDIUM構造体ベースで解放する必要があります。

    ・GlobalUnlockを忘れていませんか?

     

    このソースコードをベースに手元で編集した結果、一通り開けることは確認しています。

    2008年8月23日 2:20
    モデレータ

すべての返信

  •  kokuchin さんからの引用

    しかし、マイコンピュータや、マイネットワーク、コントロールパネルの項目など特殊はフォルダ(?)をどうやって登録すればいいのか調べても全くわかりません。

    何が欲しいのですか?

    「マイコンピュータ」という文字列が欲しいのですか?物理パス("C:\"とか)が欲しいのですか?

    大抵の仮想フォルダの場合、物理パスは得られませんけれども…。


     kokuchin さんからの引用

    マイコンピュータなどは、

      if(pDataObject->IsDataAvailable(RegisterClipboardFormat(CFSTR_SHELLIDLIST))){
                 ・・・・
          }

    表示名が欲しいのなら、ITEMIDLISTからIShellFolderなりを駆使すればなんとか。

     

    CFSTR_SHELLIDLISTについては http://msdn.microsoft.com/en-us/library/bb776902(VS.85).aspx#CFSTR_SHELLIDLIST で。

    2008年8月21日 15:21
    モデレータ



  • しかし、マイコンピュータや、マイネットワーク、コントロールパネルの項目など特殊はフォルダ(?)をどうやって登録すればいいのか調べても全くわかりません。

    >>> 何が欲しいのですか?大抵の仮想フォルダの場合、物理パスは得られませんけれども…。


    回答ありがとうございます。

    ほしいのは、とりあえずは、ドロップされた仮想フォルダ(?)のCSIDL値です。

    2008年8月21日 21:09
  •  kokuchin さんからの引用

    ほしいのは、とりあえずは、ドロップされた仮想フォルダ(?)のCSIDL値です。

    少なくともPIDLからCSIDLを得る関数は見当たりません。

    CSIDLからPIDLを得て比較するのも、一致する保証はありませんし…。

     

     

    PIDLからCSIDLが欲しいと語っている方がいましたが、解決されていないようです。

    http://discuss.joelonsoftware.com/default.asp?joel.3.312308.2

    2008年8月21日 22:06
    モデレータ
  • ありがとうございます。
    CSIDLを取得できない件は理解しました。このやり方はあきらめます。

    ご紹介いただいたmsdnのドキュメントを参考にしつつ、試験的に、特殊フォルダを、とあるダイアログにドロップしたら、開くことができるか、次のようなコードで試してみました。
    マイコンピュータ、マイネットワーク、ゴミ箱については、問題なく表示されたのですが、コントロールパネル内の項目(プログラムの追加と削除など)をドロップすると、「パラメータが間違っています」というエラーが発生してうまく表示することができません。

    どこが悪いのかいろいろググってみたのですが、どうしてもわかりません。SHELLEXECUTEINFOに設定不足の項目があるのでしょうか?

    よろしくお願いいたします。

    BOOL CDropTarget:SurprisenDrop(CWnd *pWnd, COleDataObject *pDataObject,
         DROPEFFECT dropEffect, CPoint point)
    {
        if(pDataObject->IsDataAvailable(CF_HDROP)){

        <略>   
     
        }else if(pDataObject->IsDataAvailable(RegisterClipboardFormat(CFSTR_SHELLIDLIST))){

            LPMALLOC pMalloc;
            if (FAILED(:Tongue TiedHGetMalloc(&pMalloc))){
          return FALSE;
        }

            FORMATETC   fmtetc
          = {(CLIPFORMAT)RegisterClipboardFormat(CFSTR_SHELLIDLIST), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
            STGMEDIUM stgmed;
     
            pDataObject->GetData(RegisterClipboardFormat(CFSTR_SHELLIDLIST), &stgmed, &fmtetc);

            LPIDA   toppos = reinterpret_cast<LPIDA>(::GlobalLock(stgmed.hGlobal));

            LPITEMIDLIST lpcItemIdList = reinterpret_cast<LPITEMIDLIST>(reinterpret_cast<BYTE*>(toppos) + toppos->aoffset[1]);

            SHELLEXECUTEINFO  sei;            //
        memset( &sei, 0, sizeof( sei ) );
            sei.cbSize       = sizeof( sei );
            sei.fMask        = SEE_MASK_IDLIST; // |_MASK_FLAG_NO_UI;
            sei.hwnd         = HWND_DESKTOP;
            sei.lpIDList     = lpcItemIdList;
            sei.lpVerb       = "open";
            sei.nShow        = SW_SHOWNORMAL;
            sei.lpFile       = NULL;    <---- これがおかしい????
            sei.lpParameters = NULL;
            sei.lpDirectory  = NULL;
            ShellExecuteEx( &sei );

            DWORD err = GetLastError();
            TRACE("%d\n", err);

            pMalloc->Free(lpcItemIdList);
            pMalloc->Release();

    2008年8月22日 18:38
  • ポイントだけ。

     

    ・CFSTR_SHELLIDLISTは0番目に親のフォルダを、1番目から要素の配列を構成します。1番目からの要素は0番目の要素からの相対ITEMIDLISTになります。使うためには0番目とITEMIDLISTの連結が必要です。

    ・ITEMIDLISTの連結にはILCombineを使います。但し、この戻り値は解放が必要なのでILFreeを使って下さい。

    ・連結したITEMIDLISTで開くことで開けるコントロールパネルが出てきますが、一部は開けません。全てに対応するにはlpVerbをNULLにする必要があります。

    ・このソースコードで取得されるlpcItemIdListは解放してはいけません。ReleaseStgMedium関数でSTGMEDIUM構造体ベースで解放する必要があります。

    ・GlobalUnlockを忘れていませんか?

     

    このソースコードをベースに手元で編集した結果、一通り開けることは確認しています。

    2008年8月23日 2:20
    モデレータ
  • ありがとうございました。ご指導のとおり、調べながら記述したら思い通りの動作を確認できました。 今後ともよろしくお願いいたします。
    2008年8月24日 20:06