none
ActiveXからDragDropイベントでクライアント側にBSTRの配列を渡したい RRS feed

  • 質問

  • いつもお世話になっております。

    今MFC ActiveX ControlWizardでActiveXコントロールを作っています。
    標準ではDrag&Dropイベントが用意されていないようなので、自分でDragDropというイベントをつくり、ファイルがドロップされたタイミングで発行しようとしています。
    それでドロップされたファイルが複数の可能性があるため、文字列の配列をクライアント側に渡したく重い、SafeArrayを作ってVARIANT型として渡そうとしているのですが、SafeArrayもVARIANTも初めて使うので勝手がよく分からず、うまくいかず悩んでいます。

    次のようにコーディングしました。
    [xxxx.odl]
    [id(4)] void DragDropEx(VARIANT Files);

    [xxxxCtrl.cpp]
    xxxxCtrl::OnDropFiles(HDROP hDropInfo) 
    {
    UINT fileCount = ::DragQueryFile(hDropInfo, (UINT)-1, NULL, 0);  // ドロップされたファイル数を取得
    if (fileCount < 1)
    return;

    SAFEARRAY *psa;
    SAFEARRAYBOUND rgb[1];

    rgb[0].cElements = fileCount; // 作成するSAFEARRAYの要素数 = 32
    rgb[0].lLbound = 0;           // 作成するSAFEARRAYの添字の下限値 = 0
    psa = ::SafeArrayCreate(VT_BSTR, 1, rgb); // SAFEARRAY作成
    if(psa == NULL)
    return;
    BSTR* data;
        ::SafeArrayAccessData(psa, (void**)&data);
    TCHAR szFileName[_MAX_PATH];
    for (UINT i = 0; i < fileCount; i++)
    {
    ::DragQueryFile(hDropInfo, i, szFileName, _MAX_PATH);
    data[i] = szFileName;
    }
    ::SafeArrayUnaccessData(psa); // SAFEARRAYアンロック

    VARIANT filesData;
    filesData.vt = VT_BYREF;
    filesData.pparray = &psa;
    FireDragDropEx(filesData);
    }

    SafeArrayをVARIANTにするところが特にこれで良いのか不安です。
    ビルドしてActiveX Test ContainerでテストするとD&Dしたとき次のようなメッセージが出ます。
    XXXXl Control: DragDropEx {Files=}

    次にC#のフォームで作ったテストクライアントアプリで試すとD&Dしたとき、Main()のApplication.Run(new Form1())で止まり、
    「マネージデバッグアシスタント 'Invalid Variant'では・・・・.exeに問題を検出しました。 追加情報:アンマネージVARIANTからマネージオブジェクトへの変換中に、無効なVARIANTが検出されました。」
    というエラーダイアログが表示されます。

    どうも無効なVARIANTと認識されてしまっているようです。
    どなたか、どこがおかしいかご指摘いただけないでしょうか?







    2010年1月29日 6:27

回答

すべての返信

  • >data[i] = szFileName;
    TCHARはUnicodeですよね。
    BSTRは文字コードはUnicodeですが、通常のUnicode文字列とは構造が異なります。

    APIを使用する場合SysAllocStringを使用して、通常のUnicode文字列からBSTRに変換します。

    まずはここを直してみてください。

    [String Manipulation Functions]
      http://msdn.microsoft.com/en-us/library/ms221105.aspx
      BSTRがどういう構造になっているか、上記を読めばわかります。
     
    [BSTR 用のメモリの割り当てと解放]
      http://msdn.microsoft.com/ja-jp/library/xda6xzx7.aspx

    [SysAllocString]
      http://msdn.microsoft.com/en-us/library/ms221458.aspx

    [CStringT::AllocSysString]
      http://msdn.microsoft.com/ja-jp/library/za1865s1(VS.80).aspx

    [_bstr_t Class]
      http://msdn.microsoft.com/ja-jp/library/zthfhkd6(VS.80).aspx

    [ConvertStringToBSTR]
      http://msdn.microsoft.com/ja-jp/library/t58y75c0(VS.80).aspx

    多々手段はありますが、要件に応じて使い分けてください。
    • 編集済み kozz 2010年1月29日 8:32 typo
    • 回答としてマーク 高橋 春樹 2010年2月4日 4:58
    2010年1月29日 8:30
  • kozzさん、こんにちは。
    なんとか解決しました。
    ありがとうございました。

    BSTR* data;
        ::SafeArrayAccessData(psa, (void**)&data);

    TCHAR szFileName[_MAX_PATH];
    CStringArray sa; 
    for (UINT i = 0; i < fileCount; i++)
    {
    ::DragQueryFile(hDropInfo, i, szFileName, _MAX_PATH);
    sa.Add(szFileName);
    data[i] = sa[i].AllocSysString();
    }
    ::SafeArrayUnaccessData(psa); // SAFEARRAYアンロック

    VARIANT vrt;
    ::VariantInit(&vrt);
    vrt.vt = VT_ARRAY|VT_BSTR;
    vrt.parray = psa;
    FireDragDrop(vrt);
    COleControl::OnDropFiles(hDropInfo);



    2010年1月29日 12:12
  • ちょっと待った。

    CStringArray sa; 
    for (UINT i = 0; i < fileCount; i++)
    {
    ::DragQueryFile(hDropInfo, i, szFileName, _MAX_PATH);
    sa.Add(szFileName);
    data[i] = sa[i].AllocSysString();
    }

    そこで、CStringArray である必要はありますか?
    よく考え直してください。

    主に AllocSysString が何をするものか、CStringArray は何のためのものかを理解して頂きたいと思っています。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2010年1月29日 12:54
    モデレータ
  • こんにちは。フォーラムオペレーターの高橋春樹です。

    kozzさん、Azuleanさん、いつもお世話になっております。

    hiro-taさん、MSDNフォーラムのご利用有難うございます。
    Azuleanさんから、ご指摘を頂いたと思うのですが、確認して頂けたでしょうか?

    今回、kozzさんとAzuleanさんからの投稿が、フォーラムを閲覧している方にとっても
    有用な情報になるのではないか、と思いましたので、勝手ながら回答マークを付けさせてもらいました。

    今後ともMSDNフォーラムを宜しくお願いします(^_^)


    マイクロソフト株式会社 フォーラム オペレーター 高橋春樹
    2010年2月4日 4:59
  • 分かりました。

    //sa.Add(szFileName);
    //data[i] = sa[i].AllocSysString();
    data[i] = CString(szFileName).AllocSysString();

    ということですよね?
    それと後始末も必要でした。

    FireDragDrop(vrt);
    for (i = 0; i < fileCount; i++)
    SysFreeString(data[i]);
    COleControl::OnDropFiles(hDropInfo);

    2010年2月5日 11:53
  • そこで解放処理が必要ということは、
    SafeArrayCreateで作成したArray descriptorの解放が必要だと思います。
    どうでしょうか。

    [SafeArrayDestroy]
      http://msdn.microsoft.com/en-us/library/ms221702.aspx
      Remarksに注意してください。

    • 編集済み kozz 2010年2月5日 15:56 typo
    • 回答としてマーク ひろ太 2010年2月8日 1:24
    2010年2月5日 15:53
  • >そこで解放処理が必要ということは、
    >SafeArrayCreateで作成したArray descriptorの解放が必要だと思います。
    >どうでしょうか。
    >

    その通り、必要でした。
    kozzさん、ありがとうございました。
    2010年2月8日 1:25