tagCANDY CGI VBレスキュー(花ちゃん)の Visual Basic 6.0用 掲示板 [ツリー表示へ]   [Home]
一括表示(VB6.0)
タイトル Dll 内の関数を別の Dll を使って呼び出したい。
記事No15923
投稿日: 2014/07/07(Mon) 17:11
投稿者みゆき族
初めまして。みゆき族と申します。
プログラミング歴、VB 歴ともに恥ずかしながら3か月足らずです。

こちらの掲示板はいつも良く参考にさせて頂いております。
特に魔界の仮面弁士様の回答は自分の問題と関係するところが多く、非常に為になっております。有難うございます。

さて、早速ですが、質問させて頂きます。


ある Dll( Dll_A とします) 内にある関数( foo とします)を別の Dll( Dll_B とします) から呼び出したいのですが、どうすれば宜しいでしょうか?



Dll_A 内にある関数は declare 関数を使って、以下のようにして呼び出せました。


'標準モジュール内
  Declare Function foo Lib "Dll_A.dll" _
     (ByRef val1 As Any, ByVal val2 As Long, _
      ByVal val3 As Long, ByVal val4 As Long, _
      ByRef val5 As Any) As Long

'フォーム内
    return = foo(val1, val2, val3, val5, val6)


ここで、別の Dll を介して Dll_A を呼び出したいという要望を受け、VB6 の ActiveX Dll から Dll_B を作成しました。
DLL_B の中身は以下の通りです。(海外の情報を参考にしました)


Option Explicit

Private Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long
Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long, ByVal mParam As Long) As Long

Private Function bar(val1 As Long, val2 As Long, val3 As Long, val4 As Long, val5 As Long)
On Error Resume Next
'We're going to call an API-function, without declaring it!
Dim lb As Long, pa As Long
'map 'user32' into the address space of the calling process.
lb = LoadLibrary("Dll_A")
'retrieve the address of 'SetWindowTextA'
pa = GetProcAddress(lb, "foo")
'Call the SetWindowTextA-function
CallWindowProc pa, ByVal val1, ByVal val2, ByVal val3, ByVal val4, ByVal val5
'unmap the library's address
FreeLibrary lb
End Function


(参考にしたコードここから)
Create a New project And add this code To Form1
Private Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long
Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Any, ByVal wParam As Any, ByVal lParam As Any) As Long

Private Sub Form_Load()
On Error Resume Next
'We're going to call an API-function, without declaring it!
Dim lb As Long, pa As Long
'map 'user32' into the address space of the calling process.
lb = LoadLibrary("user32")
'retrieve the address of 'SetWindowTextA'
pa = GetProcAddress(lb, "SetWindowTextA")
'Call the SetWindowTextA-function
CallWindowProc pa, Me.hWnd, "Hello !", ByVal 0&, ByVal 0&
'unmap the library's address
FreeLibrary lb
End Sub
(参考にしたコードここまで)


これを実行して作成した Dll_B を冒頭と同じように declare 関数を使って、以下のようにして呼び出しました。


'標準モジュール内
  Declare Function bar Lib "Dll_B.dll" _
     (ByRef val1 As Any, ByVal val2 As Long, _
      ByVal val3 As Long, ByVal val4 As Long, _
      ByRef val5 As Any) As Long

'フォーム内
    return = bar(val1, val2, val3, val4, val5)


しかし、いざコードを走らせると、
    「実行時エラー'453': エントリ bar が DLL ファイル Dll_B 内に見つかりません。」
と出てしまいます。

コマンドプロンプトから DLL_B のレジストリ登録を済ませてもエラーは相変わらず表示されます。

(環境: Windows 7 Professional / Microsoft Visual Basic 6.0)


解決策はありますでしょうか?
お忙しいところ恐縮ですが、諸先輩方の知恵をお借り出来れば幸いです。

宜しくお願い致します。

[ツリー表示へ]
タイトルRe: Dll 内の関数を別の Dll を使って呼び出したい。
記事No15924
投稿日: 2014/07/08(Tue) 11:07
投稿者魔界の仮面弁士
> ここで、別の Dll を介して Dll_A を呼び出したいという要望を受け
その要望が、どのような理由から発生したのか、事情をお聞かせ頂けないでしょうか。

「DLL」にも種類があります。目的としている理由如何では、今回みゆき族さんが
選択された DLL 利用法とは、別の道(注1)を模索するという選択肢もありえるかも知れません。


> ある Dll( Dll_A とします) 内にある関数( foo とします)を別の Dll( Dll_B とします) から呼び出したいのですが、どうすれば宜しいでしょうか?
> VB6 の ActiveX Dll から Dll_B を作成しました。
AcitveX DLL で作成された DLL は、関数公開型のものではないため、
基本的には(注2)、Declare ステートメントで利用することはできません。

DLL_A 側で「DLL_B を『参照設定』する」ことで利用できるようになります。


(1) AcitveX DLL である以上、Dll_B を作成する際に「クラスモジュール」を
 用意していたと思います。そこに Public Function を追加し、そこに
 DLL_A を呼び出すコードを記述して下さい。

(2) 作成した DLL_B を、EXE 側から「参照設定」してください。
 EXE から DLL_B のコードを利用するために、
  Dim o As Class1
  Set o = New Class1
  ret = o.Foo(val1, val2, val3, val5, val6)
  Set o = Nothing
 のようにします。




(注1) たとえば、.local あるいは manifest によって、AcitveX DLL を side-by-side で
 呼び出すといった方法があります。
http://msdn.microsoft.com/ja-jp/library/ms811694.aspx
http://msdn.microsoft.com/ja-jp/library/ms811700.aspx
http://msdn.microsoft.com/ja-jp/library/cc482775.aspx


(注2) 関数を無理矢理公開する方法もあるにはあるのですが、undocumented な手法でありお奨めしません。
http://www1.koalanet.ne.jp/akiya/vbindeep/index.html#option2

[ツリー表示へ]
タイトルRe^2: Dll 内の関数を別の Dll を使って呼び出したい。
記事No15929
投稿日: 2014/07/08(Tue) 15:13
投稿者みゆき族
魔界の仮面弁士 様

お世話になります。みゆき族です。

早速にお返事いただきまして誠に有難うございます。
いつも貴殿の書き込みは参考にさせて頂いております。

>その要望が、どのような理由から発生したのか、
>事情をお聞かせ頂けないでしょうか。
私も先方が何がしたいのかよく分からなかったので(そんな二度手間をする必要あるのか疑問に思った)、質問したところ、DLL_Bを表に出したくないとの回答でした。カレントディレクトリやプロジェクトで使う DLL は DLL_A としてではなく、DLL_B として使いたとのことです。


>(1) AcitveX DLL である以上、Dll_B を作成する際に「クラスモジュール」を
> 用意していたと思います。そこに Public Function を追加し、そこに>
> DLL_A を呼び出すコードを記述して下さい。
クラスモジュールを以下のように書き換えたのですが、これで宜しいでしょうか?

Option Explicit

Private Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long
Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
Private Declare Function CallWindowProc Lib "DLL_A" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long, ByVal mParam As Long) As Long

Private Function Bar(var1 As Long, var2 As Long, var3 As Long, var4 As Long, var5 As Long)
On Error Resume Next
'We're going to call an API-function, without declaring it!
Dim lb As Long, pa As Long
'map 'user32' into the address space of the calling process.
lb = LoadLibrary("DLL_A")
'retrieve the address of 'SetWindowTextA'
pa = GetProcAddress(lb, "Foo")
'Call the SetWindowTextA-function
CallWindowProc pa, ByVal var1, ByVal var2, ByVal var3, ByVal var4, ByVal var5
'unmap the library's address
FreeLibrary lb
End Function


>(2) 作成した DLL_B を、EXE 側から「参照設定」してください。
> EXE から DLL_B のコードを利用するために、
>  Dim o As Class1
>  Set o = New Class1
>  ret = o.Foo(val1, val2, val3, val5, val6)
>  Set o = Nothing
> のようにします。
こちらを試してみましたが、
「コンパイルエラー:メソッドまたはデータメンバが見つかりません。」
となり、やはり上手くいきません。

Dim o As Object
set o = CreateObject("Project1.Class1")
call o.bar()
set o = Nothing

こちらも試してみましたが、これも駄目でした。


お時間があるときで構いませんので、アドバイス頂けますと幸いです。

宜しくお願い致します。

[ツリー表示へ]
タイトルRe^3: Dll 内の関数を別の Dll を使って呼び出したい。
記事No15930
投稿日: 2014/07/08(Tue) 15:34
投稿者魔界の仮面弁士
> DLL_Bを表に出したくないとの回答でした。
???
であれば、そもそも DLL_B など作らず、最初の DLL_A をそのまま使えば良いような…。

かといって、DLL_A を表に出したくない、という意味だとしても、
DLL_A を必要とすることには変わりないので、まだ意図が分からないです。


> カレントディレクトリやプロジェクトで使う DLL は
> DLL_A としてではなく、DLL_B として使いたとのことです。

その発言だけ聞くと、DLL_A をリネームして、DLL_B という名前で配置しておけば
解決するような気もしますが、そういう話ではないのですね?



今回選択した「DLL_B という ActiveX DLL で中継する」という手法は、
DLL_A を直接 Declare する場合に比べて、状況が悪化する可能性も考えられますが、
本当にその手法で問題が無いか、発注者に確認されていますでしょうか。


・参照設定することで、VB 以外(スクリプト等)からも呼びやすくなる。
 →「表に出したくない」というが、むしろ、公開度が上がる結果となる。

・ActiveX DLL 化することで、システム登録が必須となる。また、バイナリ互換性の維持も重要となる。
 →「別の DLL として使いたい」という理由の如何によっては、管理の煩雑度を増す結果となる。


> > 「クラスモジュール」を用意していたと思います。
> > そこに Public Function を追加し、
> クラスモジュールを以下のように書き換えたのですが、これで宜しいでしょうか?

で、肝心の『Public Function』はどこでしょうか?


> 「コンパイルエラー:メソッドまたはデータメンバが見つかりません。」
> となり、やはり上手くいきません。
.Foo というのは仮の名前です。実際には、参照設定した自作 DLL の
Class1 に作成した「Public Function」の名前と一致させて下さい。


> set o = CreateObject("Project1.Class1")
> call o.bar()
> こちらも試してみましたが、これも駄目でした。
CreateObject で失敗するのであれば、プログラムIDが間違っているか、
もしくは DLL の登録に失敗しているかのいずれかです。

bar メソッドの呼び出しで失敗するのであれば、メソッド名や引数が間違っているか、
Public Sub bar() または Public Function bar() を実装し忘れているかのいずれかです。

[ツリー表示へ]