none
DLLからバイナリデータのバッファを返す RRS feed

  • 質問

  • OSはVistaです。
    VSEEは2008です。

    明示的にリンクしたDLL内の関数から返したいのですが、上位のEXEが確保した領域にデータを入れようとすると以下のエラーになります。
    「'System.ExecutionEngineException' のハンドルされていない例外が 不明なモジュールです。 で発生しました。」
    メモリマップ(?)が違うと思うので、ダメだとは思ったのですが他にどうしていいかわからず、教えていただけたらと思います。

    EXE側での、DLLのロードと関数ポインタの取得は成功しています。
    EXE側のコードは以下のようなものです。

    char    caA[ 10 ];

    caA[ 0 ] = 1;
    caA[ 1 ] = 2;
    lpFuncDll( caA );
    ※lpFuncDll・・・DLLから取得した関数のポインタ

    DLL側のコードは以下のようなものです。

    void f1( char *pcA ){
        *( pcA + 2 ) = *( pcA + 0 ) + *( pcA + 1 );
    }
    2010年8月26日 11:03

回答

  • 利用側プロジェクト(CLR Windows フォーム アプリケーション)のプロジェクトのプロパティで、構成プロパティ - 全般にある、共通言語ランタイムのサポートを「共通言語ランタイム サポート (/clr)」に変えてみてはいかがでしょうか。
    (デフォルトは「純粋 MSIL 共通言語ランタイム サポート (/clr:pure)」)
    # 詳しくないので理由までは説明できません。

    スタートアッププロジェクトは CLR プロジェクトなので、デフォルトではマネージデバッガになります。
    このため、ネイティブで書いた DLL はデバッグできません。
    これを改善するには、プロジェクトのプロパティで、構成プロパティ - デバッグにある、デバッガのタイプを「混合」にしてください。
    これで、マネージとネイティブの両方をデバッグできます。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク K6963 2010年8月27日 16:19
    2010年8月27日 14:12
    モデレータ

すべての返信

  • lpFuncDllの宣言が間違っているのだと思います。lpFuncDllとf1の宣言内容を示してくれたらわかるかも。

    あと関係ないですが「明示的にリンクした」ではなく逆で、「明示的にリンクしていない」ですよね?

    2010年8月26日 11:38
  • レスありがとです。

    例のように修正するのが手間なので、実コードを示します(^^;
    (症状は同じです。)
    EXE側

    typedef int ( *LPFNDLLFUNC2 )( unsigned char *, unsigned char * );

    private: System::Void button3_Click(System::Object^  sender, System::EventArgs^  e) {
        unsigned char    ucaPlain[ 10 ];
        unsigned char    ucaEncode[ 10 ];
        int    iR;
        DWORD    E;
        HMODULE    HM;
        LPFNDLLFUNC2    lpfnDllFunc2;
        FARPROC    F;

        strcpy( ( char * )ucaPlain, "ABC" );
        HM = LoadLibrary( _T( "dll3" ) );
        //
        if ( HM == NULL ){
            //
            E = GetLastError();
            return;
        }
        lpfnDllFunc2 = (LPFNDLLFUNC2)GetProcAddress(HM, "fn_k" );
        if ( lpfnDllFunc2 == NULL )
        {
            E = GetLastError();
            return;
        }
        iR = lpfnDllFunc2( ucaPlain, ucaEncode );
    }

    DLL側

    ヘッダ
    #ifdef DLL3_EXPORTS
    #define DLL3_API extern "C" __declspec(dllexport)
    #else
    #define DLL3_API __declspec(dllimport)
    #endif
    DLL3_API int fn_k( unsigned char *pucPlain, unsigned char *pucEncode );

    定義
    LIBRARY DLLK
    EXPORTS
    fndll3
    fn_k

    ソース
    DLL3_API int fn_k( unsigned char *pucPlain, unsigned char *pucEncode )
    {
        strcpy( ( char * )pucEncode, ( char * )pucPlain );
        strcat( ( char * )pucEncode, "_Z" );
        return 0;
    }

    「明示的」については、このヘルプ、
    http://msdn.microsoft.com/ja-jp/library/784bt7z7.aspx
    で、「明示的にリンクする」という言葉と使っているので、この方法を使っているのでこのように書きました。
    2010年8月26日 11:57
  • まず用語についてはすみませんでした。逆の意味を指すものと勘違いしていました。

    それから本題についても特に疑問を抱くような点はなく、原因はわかりませんでした。他の人に任せようかな…。

    別アプローチとしてEXE側に

    using namespace System::Runtime::InteropServices;
    [DllImport("dll3")]
    extern int fn_k(unsigned char *pucPlain, unsigned char *pucEncode);

    と書くとLoadLibrary()もGetProcAddress()もせずにfn_k()が呼び出せるようになります。

    最初calling convention が異なるのかとも考えましたがそうでもないようで…うーん。

    2010年8月26日 22:16
  • 正しい方法とはとても思えませんが、落ちない方法がありました。

    EXE側

    private: System::Void button3_Click(System::Object^  sender, System::EventArgs^  e) {
        unsigned char    ucaPlain[ 10 ];
        unsigned char    ucaEncode[ 10 ];
        array<unsigned char>^    UCA;
        pin_ptr<unsigned char>    pucA;
        int    iR;
        DWORD    E;
        HMODULE    HM;
        LPFNDLLFUNC2    lpfnDllFunc2;

        HM = LoadLibrary( _T( "dll3" ) );
        //
        if ( HM == NULL ){
            //
            E = GetLastError();
            return;
        }
        lpfnDllFunc2 = (LPFNDLLFUNC2)GetProcAddress(HM, "fn_k" );
        if ( lpfnDllFunc2 == NULL )
        {
            E = GetLastError();
            return;
        }
        UCA = gcnew array<unsigned char>( 10 );
        pucA = ( unsigned char * )malloc( 10 );
        //
        if ( ( unsigned char * )pucA == NULL ){
            //
            return;
        }
        ucaPlain[ 0 ] = 1;
        ucaPlain[ 1 ] = 2;
        iR = lpfnDllFunc2( ucaPlain, pucA );
        ucaEncode[ 0 ] = *pucA;
    }

    DLL側

    DLL3_API int fn_k( unsigned char *pucPlain, unsigned char *pucEncode )
    {
        unsigned char    ucTmp;

        ucTmp = *( pucPlain + 0 ) + *( pucPlain + 1 );
        *pucEncode = ucTmp;
        return 0;
    }

    ただ、この方法の場合、DLL関数内で落ちることはないのですが、結果は正しいものではありません。

    憶測ですが、渡す領域の性質が原因なのではないかと思っています。

    ので、領域の確保の仕方や渡し方を変えればできるのではないか、と。

     

    DLLを明示的にリンクする場合、デバッガが機能しないのですが、これは仕様なのでしょうか?

    DLLをデバッグする場合のヘルプで、プロジェクトのデバッグプロパティのコマンドには上位のEXEを記述しています。

    上位のブレークポイントは機能しますし、コード追跡はできるのですが、DLL内は追えません。

    ま、関数ポイントにより動的に呼び出すものを追えないのは当然な気もしますが。。。

     

    ちなみに、最終的にやりたいことは、開発環境に依存しない(例えば、Borland下で作ったものも使える)プラグイン形式のアプリケーションを作ることです。

     

    2010年8月27日 9:53
  • 利用側プロジェクト(CLR Windows フォーム アプリケーション)のプロジェクトのプロパティで、構成プロパティ - 全般にある、共通言語ランタイムのサポートを「共通言語ランタイム サポート (/clr)」に変えてみてはいかがでしょうか。
    (デフォルトは「純粋 MSIL 共通言語ランタイム サポート (/clr:pure)」)
    # 詳しくないので理由までは説明できません。

    スタートアッププロジェクトは CLR プロジェクトなので、デフォルトではマネージデバッガになります。
    このため、ネイティブで書いた DLL はデバッグできません。
    これを改善するには、プロジェクトのプロパティで、構成プロパティ - デバッグにある、デバッガのタイプを「混合」にしてください。
    これで、マネージとネイティブの両方をデバッグできます。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク K6963 2010年8月27日 16:19
    2010年8月27日 14:12
    モデレータ
  • できました!

    というか、またこれでしたか(^^;;;

    私もネット上でサンプルコードを探していて、正常に動作するものが見つかりました。

    が、コマンドラインアプリのプロジェクトでした。

    で、そのコードをそのままフォームアプリに載せると、やはり異常。

    プロジェクトプロパティを見ると、コマンドラインの方はデフォルトで/clrでした。。。

    前にも同じ方法による回答をもらった覚えがあり、自分の質問をさかのぼったところ、やはりありました。

    C++/CLIが未だにしっかり理解することができないため、質問を繰り返すことになっています。

    少しずつ理解は深まっていますが、まだ完全ではないのが現状です。

    また質問するいことになるかと思いますが、よろしくお願いします。

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

     

    2010年8月27日 16:19