none
DLLからどうやって上位アプリ中の関数をコールするんですか? RRS feed

  • 質問

  • VCで作ったPCI装置のDLLですが、PCI割り込みが発生した時に、上位アプリの関数を呼び出す問題です。  以前はVC 6で作ったアプリであれば、アプリの関数アドレスをDLLに渡して、割り込みが発生した際に、DLLからアプリの関数を呼び出せましたが、 VC .Netで作ったアプリは同じ方法でやったら、コンパイルは正常に通りますが、関数呼び出す時にエラーになります。   これはなぜですか? 解決方法はありますか?  もし上位アプリはC#で作るとした場合、この方法は使えますか?  教えていただきたいです。  もしなんか資料があれば、頂きたいです。


    cyo
    2011年7月27日 6:07

回答

  • 皆さん、問題を解決しました。 VCでも、OKでした。  こちらのサンプルコードを参照していました。 http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx

    教えてくれた資料を見ていました、自分の知識不足をよく感じました。 沢山勉強できて、うれしいです。 皆さんが助けてくれたことは心から感謝しています。

    VC++ WindowsFormApplicationの初心者のために、関係あるコードを残しておきます。

    。。。。。。。。。。

    public delegate void INTSUB(void);

     

    [DllImport("asxxxx.dll")]

     extern void AxIntStart(AXCB_AP *p, INTSUB^ func);

    。。。。。。。。。。。

    namespace HSVC2020 {

    using ..........................

     

     

    public ref class Form1 : public System::Windows::Forms::Form

     {

     public:

             INTSUB^ myDelegate;

     

     Form1(void)

     {

     InitializeComponent();

     //

     //TODO: ここにコンストラクター コードを追加します

    myDelegate = gcnew INTSUB(this, &HSVC2020::Form1::INTXMoveEnd);

    }

    ........................................

    .......................................

     

    private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {

    AxIntStart(&X, myDelegate);

    }

     

    public: void INTXMoveEnd(void){

    MessageBox::Show("X軸移動完了割り込み発生しました。", "X-MoveEnd-Int",MessageBoxButtons::OK, MessageBoxIcon::Stop);

    AxIntStop(&X); // 割り込み解除

     }

         }

    }

     

     

     


    cyo
    • 回答としてマーク AK46.5 2011年8月1日 7:04
    2011年8月1日 7:02

すべての返信

  • >これはなぜですか?

    どんなコードを書いているのかすらも明らかではないので、分かりません。

    コードかコンパイラオプションなどの組み合わせがまずかったのか、はたまた元々の DLL に何か問題があったのか。可能性は無限に広がっています。

    適切なアドバイスを得るためには、可能性を絞り込むための情報を提供してください。

    例えば、「エラーになる」とのことですが、なにかエラーメッセージやエラーコードなどは表示されていませんか?

    >解決方法はありますか?

    あるはずです。

    一応稼働実績があるということなので、問題点を見つけ出して、それを修正しましょう。

    >もし上位アプリはC#で作るとした場合、この方法は使えますか?

    C# でも、原理原則はほぼ同じです。

    「デリゲート(≒クラスメソッドの参照)を DLL 関数に引き渡す」という認識で問題ありません。

    2011年7月27日 6:34
  • 返事有難うございます。 今から、問題の内容をちゃんと整理してから、もう一度聞かせて頂きます。


    cyo
    2011年7月27日 7:25
  • 私の問題に関する情報を用意してきました。


     開発環境 Windows7 32BitOS VC2010

    DLLの内部割り込み関係のハンドル定義

    typedef void (*INT_HANDLER)(void); // 関数呼び出すハンドル定義

    INT_HANDLER IntFuncX1; //割り込み発生時、上位関数を呼び出す用ハンドルの宣言

     

    DLL内部  割り込みハンドルに上位アプリ関数を設定する部分

    void STDCALL AxIntStart(AX_AP FAR *p, void *func)

    {

    ……

                  IntFuncX1 = (INT_HANDLER)func;

     

    ③ 割り込み発生時、上位アプリ関数を呼びだすコード*/

    static void X1MVPE_IntOpe(Device_HANDLE hndl, INT no)

    {

           if(IntFuncX1)IntFuncX1)(); //ここでエラーが発生します。

    }

     

    DLLの割り込み設定宣言部分、上位アプリ関数のアドレスは2番目の引数でDllに渡す

    [DllImport("asXXXX.dll")]

    extern void AxIntStart(AX_AP *p, void *func);


    ⑤ アプリ 割り込み関数設定

    private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {

                             AxIntStart(&X, INTXMoveEnd);

             }

     

    ⑥ アプリの割り込み 処理関数 定義

    static void INTXMoveEnd(void){

                                              MessageBox::Show("X動作完了割り込み発生。", "X-MoveEnd-Int", MessageBoxButtons::OK, MessageBoxIcon::Stop);

                                              AxIntStop(&X); // 割り込み解除

                                 }

     

    割り込み設定/呼び出すに関するコードは以上です。

    割り込み発生時、上位アプリの処理関数を呼び出すとエラー発生します。 デバッグ中のエラー内容は:

    HSTEST-VC2020.exe  0x0064086a でハンドルされていない例外が発生しました: 0xC0000005: 場所0x0000000c を読み込み中にアクセス違反が発生しました。

     

    コンパイルできた実行ファイルを実行時のエラー内容は以下です。

    問題の署名:

      問題イベント名:                          APPCRASH

      アプリケーション名:                     HSTEST-VC2020.exe

      アプリケーションのバージョン:        0.0.0.0

      アプリケーションのタイムスタンプ:   4e30c288

      障害モジュールの名前:                StackHash_0a9e

      障害モジュールのバージョン:        0.0.0.0

      障害モジュールのタイムスタンプ:    00000000

      例外コード:                               c0000005

      例外オフセット:                          0032082a

      OS バージョン:                          6.1.7601.2.1.0.256.1

      ロケール ID:                             1041

      追加情報 1:                              0a9e

      追加情報 2:                              0a9e372d3b4ad19135b953a78882e789

      追加情報 3:                              0a9e

      追加情報 4:                        0a9e372d3b4ad19135b953a78882e789


    デバッグの時に、上位関数を呼び出すところで確認しまして、アプリ処理関数のアドレスが正しく INT_FuncX1に入っていました。何でエラーが発生したのか?どうやって解決? お願いいたします!

     


    cyo
    2011年7月28日 4:54
  • >開発環境 Windows732BitOS VC2010

    VC++ のカバー範囲は広いです。
    C++/CLI と C (と C++ も?)の混在利用ですか?

    >(1)DLLの内部割り込み関係のハンドル定義

    細かいことですが

    ○ハンドラ
    ×ハンドル

    ですね。

    >typedef void (*INT_HANDLER)(void); // 関数呼び出すハンドル定義

    コールバック関数の定義が↑であるなら、何故、コールバック関数の設定を行う関数の定義が

    >void STDCALL AxIntStart(AX_AP FAR *p, void *func)

    なんでしょう?

    .NET 相互運用する場合、型の精密な定義は重要です。

    ↑の宣言を受けて書かれた
     
    >[DllImport("asXXXX.dll")]
    >extern void AxIntStart(AX_AP *p, void *func);

    は、マーシャラに対する情報が不足しています。

    第2引数はコールバック関数の関数ポインタ型を正しく反映したデリゲート型であるべきです。

    >アプリ処理関数のアドレスが正しく INT_FuncX1に入っていました。

    「正しい」ことはどのように確認しましたか?

    ↑の P/Invoke の定義は、第二引数は「正体不明のアドレス値」となっています。
    なので、第二引数で引き渡されたアドレス値が「マネージメソッドを正しく呼び出せるデリゲートへの参照に接続すること」は非常に確認しづらいように思います。

    結果、「正しい」アドレス値が引き渡されていないからアプリがクラッシュしているんではないかと。

    2011年7月28日 5:59
  • ご返事有難うございます。

    >VC++ のカバー範囲は広いです。
    >C++/CLI と C (と C++ も?)の混在利用ですか?

    そうです。DLL内部では、CとC++を混在利用しています。DLLは何年前に、会社の先輩が作った物です。 今は新しいOSと開発環境に合うように、修正しているところです。

    私はVC++とCの経験が少ないものですので、ご返事内容についての理解はちょっと難しいです。  とりあえず、SetTimer関数のコードをまねして、DLL内部のコードを直しました。

    typedef void (CALLBACK* INT_HANDLER)(void);

    void STDCALL AxIntStart(AX_AP FAR *p, INT_HANDLER func){。。。。。}

    これで良いかどうかをちょっと教えて欲しいです。

     

    >[DllImport("asXXXX.dll")]
    >extern void AxIntStart(AX_AP *p, void *func);

    >は、マーシャラに対する情報が不足しています。   第2引数はコールバック関数の関数ポインタ型を正しく反映したデリゲート型であるべきです。

    今は、デリゲートに関する知識を勉強中です。 いろいろテストしたけど、コンパイル通りませんでした。 どうやって書けばいいのかまだ分からない状態です。 とりあえず、私が書いたコードを見てもらって、変なところを教えていただきたいです。

    delegate void INTSUB(void);

    [DllImport("asXXXX.dll")]
      extern void AxIntStart(AX_AP *p, INTSUB func);


    cyo
    2011年7月28日 11:43
  • 適当にいじくって動いたら OK みたいなやり方はやめてください。
    特に、C/C++ ではコンパイルは通るものの、実行時に必ず落ちる、ときたま落ちるといったような症状が出て、解決に時間がかかります。
    きちんと基礎的なところやどのようにして動くのかを押さえてから、設計・実装するようにしてください。

    今回の事例であれば、関数ポインタとか、コールバック関数とか、C/C++ でのやり方をまず身につけてください。
    それができない内から、C# との連携を考えても、手戻りが続くだけです。

    ところで、なぜ、C# で AX_AP* なんて書いているのですか?
    AX_AP を C# できちんと定義できているのですか?DllImport で書く場合の構造体やクラスの渡し方は調べていますか?
    基本をすっ飛ばしてやってもうまくいきませんので、きちんと調べてください。
    http://msdn.microsoft.com/ja-jp/library/eadtsekz.aspx


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2011年7月28日 13:59
    モデレータ
  • >void STDCALL AxIntStart(AX_AP FAR *p, INT_HANDLER func){。。。。。}

    は正しいですが

    >typedef void (CALLBACK* INT_HANDLER)(void);

    が正しいかどうかは自分で決めることです。

    CALLLBACK マクロは実質 Windows API と同じ呼び出し規約を使用することを明示するためのものなので、呼び出し規約がそれで目的に合っているかどうかを確認してください。

    >delegate void INTSUB(void);

    >[DllImport("asXXXX.dll")]
    >extern void AxIntStart(AX_AP *p, INTSUB func);

    第二引数はあってそうです。

    具体的に、どんなコンパイラエラーが出ていますか?

    何度も言うようですが、「エラーが出ます」程度の情報で解決できる問題なんて、そう滅多にあるもんじゃありません。

    ひょっとしtら、コンパイルエラーに関しては第一引数の記述に問題があったりしませんか?

     

    2011年7月28日 14:08
  • Azuleanさん、ご指摘有難うございました。  そうですね、VCは基礎が無いと、使えないものですね。 特に、VBに慣れてしまった人に対しては難しいです。 教えてくれた資料に基づいて、きちんと勉強していきますので、またよろしくお願いします。

    とりあえず、説明不足のところを補充します。  今回のテストをしやすいために、上位アプリはVC2010でWindowsFormアプリを作っていました。 上に書いたアプリのコード全部はVCのコードです。C#のコードではありません。ですので、ポインタを使っています。 AX_APはPCIデバイス用の構造体です。  VCでのアプリ開発は私に対して難しいから、今後はVBとC#を使うことを考えているので、C#のことを質問しました。


    cyo
    2011年7月29日 3:09
  • 書かれている内容を読む限りでは、そもそもVC++を使った経験が殆どない状態で文法に関する知識も足りないのではないかと感じました。
    VBとVC++ではかなり文法も違いますから、入門書を購入して順序だてて勉強されることをお勧めします。
    VBと言うのがVB6以前の話なのでしたら、クラスの概念等に関しても勉強が必要になると思います。

    あと、VisualC++でWindowsFormアプリを使おうと考えたのはなぜでしょう?
    WindowsFormアプリだとC++言語ではなくてC++/CLIという別の言語になってしまいます。
    C++/CLIの勉強をしてでもC++/CLIを使いこなしたいという話で無いのであれば、あまりお勧めしません。

    多分、違いが理解できていない状態で選択していまい、ハマってしまっている様な気がするので
    一度、仕切りなおしてC++言語で行くのかC++/CLI言語で行くのか等々方針を定めて
    勉強する所からやり直した方が今後の為には良いのではないかと思います。
    結論を急ぐあまり踏むべき段階をすっ飛ばしてしまっているように感じます。


    解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。
    2011年7月29日 4:27
  • 渋木さんご返事有難うございます。    分からないことが沢山です、調べに時間かかりました。

    まず、コンパイルエラーの内容です。

    エラー①    [DllImport("asXXXX.dll")]   extern void AxIntStart(AX_AP *p, INTSUB func); //ここでエラー

     

    内容:  error C3149: 'INTSUB' : トップレベルの '^' なしに、この型をここに使用することはできません。

    エラー② AxIntStart(&X, (INTSUB)INTXMoveEnd); //ここでエラー

    内容:  error C3149: 'INTSUB' : トップレベルの '^' なしに、この型をここに使用することはできません。
    エラー③    エラー②のコードを変えて、 AxIntStart(&X, INTXMoveEnd); //ここでエラー
    error C2664: 'AxIntStart' : 2 番目の引数を 'void (__clrcall *)(void)' から 'INTSUB ^' に変換できません。(新しい機能 ; ヘルプを参照) 
    デリゲートの使い方ですが、ネットで調べた資料 http://msdn.microsoft.com/ja-jp/library/system.delegate(v=vs.80).aspx 中のサンプルコードを見ましたけど、コードの構造はWindowsFormアプリの構造とぜんぜん違いますので、どこで、どう書けばいいのかを分かりません。  なんかいい資料があれば、教えて頂きたいです。

     


    cyo


    2011年7月29日 5:34
  • ご返事有難うございます。

    「C++とC++/CLIは別言語」ということは初めて聞きました。 有難うございます。 確かに、その違いは分かりません。

    私は今まで作ったアプリ、殆どはVB.NetとC#のWindowsFormアプリですので、自然にVC++のWindowsFormアプリを選択していました。 言語はC++だと思ってしまったので、深く考えていませんでした。

    PATIOさんが言っていたことはすごく重要だと思っています。 有難うございます。


    cyo
    2011年7月29日 5:50
  • C++とC++/CLIは別言語、ですがもう少し詳しく説明すると、

    1. C++言語を理解し、宣言と定義の違いなどが分かるようになっていて、
    2. C#言語を理解しdelegateやDllImportなどを自由自在に扱えるようになり、
    3. その上で、C#言語での記述がC++/CLIでどう対応付けられるかを理解する

    必要があると、私は考えています。1. 2.ができていない状態で3.に手を出しても、エラーメッセージに出てくる固有名詞すら理解できないでしょう。

    2011年7月29日 12:16
  • 既存のコードで試行錯誤するのではなく、「DLL 関数にマネージなコールバック関数を渡す」という課題だけにしぼりこんだプロジェクトを作成して検討をおこなってみてはどーでしょう?

     


    2011年7月30日 14:28
  • 以前似たような質問がありましたので、紹介しておきますね。

    [コールバック関数の作成]
      http://social.msdn.microsoft.com/Forums/ja-JP/vcgeneralja/thread/2037c420-51dd-4027-b8ec-84b838fa3adc
      ※Native(unmanaged) codeでの実装方法です。

    以下はC++/CLIによる実装例です。
    [Quick C++/CLI - Learn C++/CLI in less than 10 minutes]
      http://www.codeproject.com/KB/mcpp/quickcppcli.aspx
      ※9.Wrapping Around C Callbacksを参考にしてください。

    [How to: Marshal Callbacks and Delegates Using C++ Interop]
      http://msdn.microsoft.com/en-us/library/367eeye0(VS.80).aspx

    C++/CLIといってもpureとmixed(C++ Interopが利用できる)とsafeのcompiler optionがあります。
    sampleによってはpure(safe)だったりmixedだったりします。

    [Pure and Verifiable Code]
      http://msdn.microsoft.com/en-us/library/85344whh(v=VS.80).aspx

    [/clr (共通言語ランタイムのコンパイル)]
      http://msdn.microsoft.com/ja-jp/library/k8d11d4s(v=vs.80).aspx

    C++ InteropはC++などnative(unmanaged) codeを混在して利用することが出来ます。
    簡潔に表現すれば、.NET Framework向けのcodeとWindows APIなどを利用したcodeを混在して記述することが出来ます。
    これはC++/CLIの利点の一つですね。

    C++ Interopは以下を参考にしてください。
    [Using C++ Interop (Implicit PInvoke)]
      http://msdn.microsoft.com/en-us/library/2x8kf7zx(v=VS.80).aspx
      このpage下部のsampleはpureで記述されています。

    先に紹介した[How to: Marshal Callbacks and Delegates Using C++ Interop]のsampleはmixedです。

    というわけで、楽をするならmixedで作るのが良いです。
    C#の作法を真似たいのならば、safe(pure)で作るのが良いでしょう。
    もちろんC++ nativeで作るのも良いです。

    2011年7月31日 9:41
  • 皆さん、沢山の返事 有難うございます。

    教えてくれたことを漏れなくしっかり勉強していきます。

    とりあえず、今の状況を報告します。 今日C#で、テストアプリを作っていまして、上位アプリの関数をDLLからコールできるようになりました。

    コードは以下になります。

    [StructLayout(ayoutKind.Sequential)]

    public class AX_AP

    {

    ..................

    }//デバイス構造体宣言

     

    public class CLSXXX

    ・・・・・・・・・

    public delegate void INTSUB(); //delegate宣言

     

    [DllImport("asxxxx.dll")]

    public static extern void AxIntStart([MarshalAs(UnmanagedType.LPStruct)] AX_AP p,  INTSUB func);

    ・・・・・・・・・

    public static AX_AP X = new AX_AP();

     

    ・・・・・・・・・・・・・・・・・・ アプリボタンイベントで 割り込み時のコール関数設定

    private void button1_Click(object sender, EventArgs e)

    CLSXXX.INTSUB SSB = new CLSXXX.INTSUB(INTXMoveEnd);

    CLSXXX.AxIntStart(CLSXXX.X, SSB);

    //割り込み処理部分

    void INTXMoveEnd(){ 

    MessageBox.Show("X軸移動完了割り込み発生しました。", "X-MoveEnd-Int",MessageBoxButtons.OK,MessageBoxIcon.Information );

    ・・・・・・・・・・・・・・・・・

    }

     

    VC++のWindowsFormApplicationはまだだめです。 引き続き調べていきます。

    皆さん有難うございました。


    cyo
    2011年8月1日 6:23
  • 皆さん、問題を解決しました。 VCでも、OKでした。  こちらのサンプルコードを参照していました。 http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx

    教えてくれた資料を見ていました、自分の知識不足をよく感じました。 沢山勉強できて、うれしいです。 皆さんが助けてくれたことは心から感謝しています。

    VC++ WindowsFormApplicationの初心者のために、関係あるコードを残しておきます。

    。。。。。。。。。。

    public delegate void INTSUB(void);

     

    [DllImport("asxxxx.dll")]

     extern void AxIntStart(AXCB_AP *p, INTSUB^ func);

    。。。。。。。。。。。

    namespace HSVC2020 {

    using ..........................

     

     

    public ref class Form1 : public System::Windows::Forms::Form

     {

     public:

             INTSUB^ myDelegate;

     

     Form1(void)

     {

     InitializeComponent();

     //

     //TODO: ここにコンストラクター コードを追加します

    myDelegate = gcnew INTSUB(this, &HSVC2020::Form1::INTXMoveEnd);

    }

    ........................................

    .......................................

     

    private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {

    AxIntStart(&X, myDelegate);

    }

     

    public: void INTXMoveEnd(void){

    MessageBox::Show("X軸移動完了割り込み発生しました。", "X-MoveEnd-Int",MessageBoxButtons::OK, MessageBoxIcon::Stop);

    AxIntStop(&X); // 割り込み解除

     }

         }

    }

     

     

     


    cyo
    • 回答としてマーク AK46.5 2011年8月1日 7:04
    2011年8月1日 7:02