• ベストアンサー

VBAからDLLをCALLしたいのですが

仕事上今まで蓄積されているFortranプログラムを効率よく使いたくDLL,VB,VBAにチャレンジしていますが, Intel(R) Fortran Compiler9.0で作成したDLLをEXCELのVBAからCALLしたところ 実行時エラー'49' DLLが正しく呼び出せません のエラーが表示されました。 このDLLはVisual Basic .NETで作成したVBからは正しくCALLできています。 色々原因を調べているのですがVBAからDLLの呼び出しとDLL内の計算は正しく行われておりDLLからVBAに戻るときにエラーになっているようです。 テストを行っているFortranとVBAのソースは以下です。VBAでDLLをCALL出来るように解決いたしたくご教授願お願いいたします。 (1)---- Fortran ソース --------------------------- subroutine DLL1(Q,QQ,QQQ) ! Expose subroutine DLL1 to users of this DLL ! !DEC$ ATTRIBUTES DLLEXPORT::DLL1 !DEC$ ATTRIBUTES ALIAS:'DLL1'::DLL1 ! Variables ! Body of DLL1 real*4 Q,QQ,QQQ QQ = Q*2 QQQ = Q**3 end subroutine DLL1 (2)----------- VBA ソース -------------------- Private Q As Single Private QQ As Single Private QQQ As Single Declare Sub DLL1 Lib "DLL1.dll" (ByRef Q As Single, ByRef QQ As Single, ByRef QQQ As Single) Sub Macro1() ChDrive ActiveWorkbook.Path ChDir ActiveWorkbook.Path Q = 2# Call DLL1(Q, QQ, QQQ) Cells(5, 2) = Format(Q, "#####.#0") Cells(6, 2) = Format(QQ, "#####.#0") Cells(7, 2) = Format(QQQ, "#####.#0") End Sub

質問者が選んだベストアンサー

  • ベストアンサー
回答No.7

#3, #4 です。 あまり意図が伝わらなかったようなので、再度。 dllは、作成する時点で、呼び出し規約が確定し VB.NETは、cdecl 呼び出し規約用に作成されたdllを扱えますが VBAは、stdcall 呼び出し規約用に作成されたdllしか扱えません。 すなわち、VB.NETで動いたからVBAで動くとは必ずしもいえない ということです。 そこで、私自身は、Fortranは扱ったことがありませんが、 #4のサイトに従えば、 !DEC$ ATTRIBUTES DLLEXPORT::DLL1 !DEC$ ATTRIBUTES ALIAS:'DLL1'::DLL1 を !DEC$ ATTRIBUTES DLLEXPORT,STDCALL::DLL1 !DEC$ ATTRIBUTES ALIAS:'DLL1'::DLL1 にして、dllをリコンパイルすれば、stdcall呼び出し規約用dllとして コンパイルできるので、使えるだろうということです。

dkdk17
質問者

補足

回答ありがとうございました。 教えていただいたHPアクセスして内容確認したのですが理解不足でした。 新たなご指摘どおりの修正でDLLをCALLすることができました。 ものすごく前進しました。ただ、QQとQQQに結果が返ってきていません。DLLの処理ではQQは4.0、QQQは8.0が戻されるはずですがVBAで呼出し語確認したところ初期設定された値のままです。(両方とも0.0に初期設定しました) 値渡しと参照渡しでまだとり違いがあるのでしょうか。 更なるご指導よろしくお願いいたします。

すると、全ての回答が全文表示されます。

その他の回答 (8)

回答No.9

#3,4,7 です。 QQ,QQQだけでなく、たぶん、Qもdll内で もし変更するようにしたら、Qの変更も VBA側に反映されていないでしょう。 Fortranは、みたこともなく、全然わかりませんが、 googleで検索して http://www.ae.utexas.edu/lrc/fortran/intel/f_ug1/pgwpasar.htm を見る限り、 ByRefで呼ぶ場合は、Fortran側でその旨、指示する必要があり たぶん !DEC$ ATTRIBUTES REFERENCE :: Q !DEC$ ATTRIBUTES REFERENCE :: QQ !DEC$ ATTRIBUTES REFERENCE :: QQQ が必要でしょう。

dkdk17
質問者

お礼

ご指摘ありがとうございます。 kurinkurinさんの前回指摘の後、dsuekichiさんのヒントで皆さんから頂いた情報を立ち戻りDLLが正しく動作いたしました♪ Fortran側で参照渡しのPointerの宣言を行うことで目的を達することができました。(Real, Pointer :: Q,QQ,QQQを挿入しました) これからは配列の取り扱いにチャレンジいたします。 すっきりしました。ありがとうございました。

すると、全ての回答が全文表示されます。
  • dsuekichi
  • ベストアンサー率64% (171/265)
回答No.8

ANo 2です。 > 値渡しと参照渡しでまだとり違いがあるのでしょうか。 ANo.4さんの提示されたページにちゃんと書いてありますよね? -------------------------------------------------------------- II.サブルーチンのエクスポート 呼出し規約としてSTDCALLを指定した場合, 実数型および整数型の引数は基本的に値渡し (仮引数は実引数のコピーであり,関数内部で仮引数を変更しても実引数には影響がない.) となる. -------------------------------------------------------------- で、対応方法も書いてますよね・・・

dkdk17
質問者

お礼

ありがとうございました。 ご指摘のとおりです。再度見直してDLL動作いたしました。 すっきりしました♪

すると、全ての回答が全文表示されます。
  • junbei
  • ベストアンサー率55% (5/9)
回答No.6

#5です。 > regsvr32を実行したところ『DllregiSterserverエントリポイントが> 見つかりませんでした』の > メッセージでDLLが登録されなかったようです。 > (ここでつまづいたので先は未実施です)。 > DLLの作成に問題ありでしょうか? その可能性が高いのかなぁ・・・ でもVB.NETではCall出来ているというし、、、(←間違いないですよね??) > 尚、教えていただいた上記VBA構文を実行したところ > コンパイルエラーとなりましたので元に戻しました。 DLLのレジスト(regsvr32)および参照設定が行えておりませんので、これはコンパイルエラーに なります。(当初私が想定した理由とは別の理由ですが・・・) ※as New ~ ではなく、CreateObjectする方法もありますが、いずれにせよDLLレジストが必要です。 ちなみに、一番初めにご提示いただいた状態のVBAソースで   Q = 2# のところを   Q = 2!   QQ = 0!   QQQ = 0! としても、「実行時エラー'49' DLLが正しく呼び出せません」のままでしょうか?

dkdk17
質問者

補足

回答ありがとうございます。 >ちなみに、一番初めにご提示いただいた状態のVBAソースで >  Q = 2# >のところを >  Q = 2! >  QQ = 0! >  QQQ = 0! >としても、「実行時エラー'49' DLLが正しく呼び出せません」のままでしょうか? だめでした........ ちなみに2!がSingle表示で2#がDoubleなんですね。 基本的なことが理解できていませんでした。 Q=2.0と入力すると自動でQ=2#と変換されるのでそんなものかと思っておりましたが式の右辺がDoubleでもQがSingleであれば=Qの式でSingleに変換されて代入されると暗黙に信じておりました。(Fortran知識)

すると、全ての回答が全文表示されます。
  • junbei
  • ベストアンサー率55% (5/9)
回答No.5

#1です。 「ロギングしている?」とは「Fortran側関数内で通過確認のログ(時分秒など)を出力しているのかな?」という意味です。 > VBAでエラーになったときデバッグモードでステートメントCALL DLL1(Q,QQ,QQQ) の > 変数Q、QQ、QQQの上にカーソルを重ねるとそのDLLで計算された数値が表示されました とは、「Call DLL1(Q, QQ, QQQ)」の行が黄色い時のことですか? デバッガで行が黄色いのは「これからその行を実行しますよ」という意味なので、まだそのステートメントは(完全には)実行されていません。 ごみが残っている場合なども考えられますので、マウスポインタを重ねた状態で変数に値が入っているから、DLLの実行が完了しているとは言い切れないと思います。 まずは以下を試してみてください。 ・(#2さんも言っていますが)Fortran側関数の引数は間違いなくSingle型(なんというのか分かりませんが、Fortranで言うところの単精度浮動少数型)ですか? ・[スタート]-[ファイル名を指定して実行]から「regserv32 C:\aaa\bbb\hogehoge.dll」を実行してください。(Fortranで作成した該当DLLを配置しているフルパスに読み替えてください。) ・VBAの参照設定から、hogehogeをチェックしてOKを押してください。(既にチェックが付いていたり、参照不可になっていたら、一度はずしてOKを押してから、再度参照設定を開いてやり直してください。) ・VBAの書き方を以下のように変更してみてください。(いろいろ怪しい部分があります。。。) -------------------------- Private Q As Single Private QQ As Single Private QQQ As Single Sub Macro1() Dim oFortran As New hogehoge.hogehoge 'もしかしたら「As New DLL1」や「As New hogehoge」、「As New hogehoge.DLL1」かも(もしコンパイルが通らない場合は元の記述に戻して結構です。Declareも) '変数は初期化してから使いましょう(初期化しなくても動作保障されるのは使えるのはVBぐらいです。他言語を呼ぶ以上これはお作法です。) Q = 2! '#はDouble型の型宣言文字です。←これがいちばん怪しい気がする。。。 QQ = 0! QQQ = 0! Call oFortran.DLL1(Q, QQ, QQQ) Cells(5, 2) = Format(Q, "#####.#0") Cells(6, 2) = Format(QQ, "#####.#0") Cells(7, 2) = Format(QQQ, "#####.#0") End Sub --------------------------

dkdk17
質問者

補足

回答ありがとうございます。 >デバッガで行が黄色いのは「これからその行を実行しますよ」という意味なので、まだそのステートメントは(完全には)実行されていません。 >ごみが残っている場合なども考えられますので、マウスポインタを重ねた状態で変数に値が入っているから、DLLの実行が完了しているとは言い切れないと思います。 ご指摘どおりですね、QQ=0とQQQ=0を挿入します。 >・(#2さんも言っていますが)Fortran側関数の引数は間違いなくSingle型(なんというのか分かりませんが、Fortranで言うところの単精度浮動少数型)ですか? Fortran内でReal*4と型宣言していますがこれは4バイトの変数定義ですのでQ,QQ,QQQはそれぞれSingle(4Byteと理解しています)です。 >・[スタート]-[ファイル名を指定して実行]から「regserv32 C:\aaa\bbb\hogehoge.dll」を実行してください。(Fortranで作成した該当DLLを配置しているフルパスに読み替えてください。) >・VBAの参照設定から、hogehogeをチェックしてOKを押してください。(既にチェックが付いていたり、参照不可になっていたら、一度はずしてOKを押してから、再度参照設定を開いてやり直してください。) regsvr32を実行したところ『DllregiSterserverエントリポイントが見つかりませんでした』のメッセージでDLLが登録されなかったようです。(ここでつまづいたので先は未実施です)。 DLLの作成に問題ありでしょうか? ご指摘よろしくお願いいたします。 >Dim oFortran As New hogehoge.hogehoge 'もしかしたら「As New DLL1」や「As New hogehoge」、「As New hogehoge.DLL1」かも(もしコンパイルが通らない場合は元の記述に戻して結構です。Declareも) 尚、教えていただいた上記VBA構文を実行したところコンパイルエラーとなりましたので元に戻しました。

すると、全ての回答が全文表示されます。
回答No.4

#3 です。 すいません。 #3は、間違った指摘ですね。 忘れてください。 やはり、呼び出し規約に問題があると思われ http://rakasaka.fc2web.com/propath/prodll_export.html が参考になると思います。

dkdk17
質問者

補足

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

すると、全ての回答が全文表示されます。
回答No.3

おそらくDll1.dllのパス指定が足りません。 フルパス指定するか、Dll1.dllをExcel.exeと同じフォルダに 置かないといけません。 xlsファイルと同じところにおいても 認識されません。 あくまで実行ファイルはExcel.exeです。

すると、全ての回答が全文表示されます。
  • dsuekichi
  • ベストアンサー率64% (171/265)
回答No.2

> 実行時エラー'49' DLLが正しく呼び出せません  は、VBAのヘルプにあるように、主に、 ・引数の数が間違っている場合 ・引数の型が間違っている場合 ・DLLが、StdCall 呼び出し規約に従って作成されていない場合 に発生します。 FortranのDLL作成方法は詳しくありませんが、この辺り大丈夫でしょうか? また、 > Visual Basic .NETで作成したVBからは正しくCALLできています。 とのことですが、VB.NETでは、どういう宣言を行って、どうやってCallしているのでしょう? #VB.NETのコードも提示してください。

dkdk17
質問者

補足

回答ありがとうございます。 VB.NETのソースは以下のとおりです。 (3)----------- VB.NET ソース -------------------- Declare Sub DLL1 Lib "DLL1.dll" (ByRef Q As Single, ByRef QQ As Single, ByRef QQQ As Single) Private Q As Single Private QQ As Single Private QQQ As Single Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Q = 2.0 Call DLL1(Q, QQ, QQQ) TextBox1.Text = Format(QQ, "####.#0") TextBox2.Text = Format(QQQ, "####.#0") End Sub >・引数の数が間違っている場合 >・引数の型が間違っている場合 >・DLLが、StdCall 呼び出し規約に従って作成されていない場合に発生します。 >FortranのDLL作成方法は詳しくありませんが、この辺り大丈夫でしょ うか? ご指摘どおり変数の型と個数の確認も行っておりますが間違いがないとは言い切れません。DLLの作成方法も同じです。よりどころはVBで正しく利用できたということです。 StdCallの規約はそのようなものがあることはネットで確認しておりますが内容はまだ理解できておりません(未熟です)。 そのような状況ですみませんがアドバイス頂ければ幸いです。

すると、全ての回答が全文表示されます。
  • junbei
  • ベストアンサー率55% (5/9)
回答No.1

> VBAからDLLの呼び出しとDLL内の計算は正しく行われており 間違いなく事実ですか?(根拠は?DLL内でロギングしているとか??) 単純に参照設定されていないとか、regserv32されていないとかではないですか・・・? (個人的には呼び出しがうまくいっていないんじゃないのかなぁと思うんですが・・・) #自分はシステム屋で業務でVBAは使うので得意ですが、Fortranは全くの素人です。

dkdk17
質問者

補足

>> VBAからDLLの呼び出しとDLL内の計算は正しく行われており >間違いなく事実ですか?(根拠は?DLL内でロギングしているとか??) 回答ありがとうございます。 FORTRANのDLL内部で数値の確認を行ったところ値は正しく引き渡されておりその引数を使っての数値計算結果も正しく行われておりました。 その確認とは別にVBAでエラーになったときデバッグモードでステートメントCALL DLL1(Q,QQ,QQQ) の変数Q、QQ、QQQの上にカーソルを重ねるとそのDLLで計算された数値が表示されましたのでDLLでの計算は正しく行われたものと判断しまし、制御がDLLからVBAに戻るときに何かのエラーになっているのではないかと睨んだしだいです。 (別途regserv32とロギングの知識がないので確認してみます)

すると、全ての回答が全文表示されます。

関連するQ&A