みなさんこんにちは。
いま、VBとVCの両方使いで、掲題の件で悩んでいます。
見苦しいですが、下記ソースを作成しましたが、
うまくいっていません。
環境:
WindowsXP
VB6.0 SP6
VC++6.0 SP4 (DLLは、/MTオプションで、マルチスレッドで
作成)
↓--- VB側 ---
'test.frm
Private Declare Sub SetDebugCallBack Lib _
"test.DLL" (ByVal CallBack As Long)
Public Sub mInitLine()
Call SetDebugCallBack(AddressOf DbgHandler)
End Sub
'test.bas
Public Sub DbgHandler(ByVal aaaa As String)
Debug.Print aaaa
DoEvents
End Sub
↑--- VB側 ---
↓--- dll側(VC) ---
'test.h
_bstr_t dbuf;
char buff[200];
void (__stdcall *debugproc)(BSTR buf);
COM_API void __stdcall SetDebugCallBack(
void (__stdcall dbgfunc)(BSTR buf) );
'test.cpp
COM_API void __stdcall SetDebugCallBack( void (__stdcall *dbgfunc)(BSTR buf) )
{
debugproc=dbgfunc;
}
'test.cpp(スレッド起動を行う関数)
COM_API void __stdcall StartLine(void)
{
pThread = _beginthread( SerialRxThread, 0, NULL);
※1{
sprintf(buff,"Thread Running..handle is %ld", pThread);
dbuf=buff;
debugproc(dbuf);
}
}
'test.cpp(スレッド用の関数の内部)
void SerialRxThread(void* dummy)
{
:
:
:
※2{
sprintf(buff,"thread(): xxx=%d", sts );
dbuf=buff;
debugproc(dbuf);
}
:
:
:
}
↑--- dll側(VC) ---
※1 VBで、F5キーで実行するときと、EXEにしてから実行する場合で
両方で動作を確認しています。
※2 VBで、F5キーで実行するときは動作しますが、EXEにすると
アプリケーションエラーが発生します。
スレッドの内部と、スレッドを起動した側とで同じ関数ポインタを
使っているのですが、なぜかVBプログラムをEXEにしたときだけは、
※2の方でアプリケーションエラーになってしまいます。
(不便をかけて申し訳ありません画面)
スレッド内では関数ポインタ(で示される先)を持つためには何か
仕組みいるのでしょうか?
(ちなみに、※2の部分をはずすとスレッドは動作します)
関数ポインタが原因だとは、何をもって判断したのでしょうか。
編集 削除VC側でデバッグは可能でしょうか?
スレッドでないときは
VBで作成したEXEをVC側でもデバッグセッションの実行可能ファイルに指定すればできるましたけど。
ところで、
コールバック関数の引数はBSTRでいけるんですね。
コールバックでない時は、勝手にString型(VB)からLPCSTR型に変換されちゃうんですけどね。
RAPT殿
質問に載せたソースは、調べている段階では整理してしまって
ますが、いろんなデバックが入っており、次のことは分かっていました。
・debugprocという関数ポインタの指しているアドレス位置は
スレッド内外共(※1と※2の部分)に、同じ場所を指しています。
VB側でもアドレスを出してみようとしたのですが、できませんでした。
Dim aaaa As Long
aaaa =addressof dbghandler
MsgBox "address is " & Hex(aaaa)
こんなカンジで表示しようとしてみたのですが、文法エラーになってしまいます。
・アプリケーションエラーが発生するタイミングは、※2の、
debugprocを実行している部分でした
メッセージ"4"を表示せずに、アプリケーションエラーになります。
※2{
MessageBox(0,"1",NULL,MB_OK);
sprintf(buff,"thread(): xxx=%d", sts );
MessageBox(0,"2",NULL,MB_OK);
dbuf=buff;
MessageBox(0,"3",NULL,MB_OK);
debugproc(dbuf);
MessageBox(0,"4",NULL,MB_OK);
}
#関数ポインタが悪いというよりも、VBのEXEの状態と
DLLのスレッドとの関係を調べなければならないようですね。
Blue殿
>VC側でデバッグは可能でしょうか?
はい。やってみようと思っています。
>コールバック関数の引数はBSTRでいけるんですね。
どこかの掲示板で掲載されていた情報でした。
少し改造すると文字列を相互で書き換えもできるとかなんとか。。
#こちらで継続して調べてみます。
そのうち自己解決できるかもしれませんが、他にも
有識者の意見をお聞かせください。
少し進展しました。
EXEをVCでデバッグ(いままで知らなかったのですが、VC画面で、
VBのソースを開き、ブレークポイントが貼れるんですね。
Blue殿情報いただきありがとうございます。)
したところ、スレッド内からVBへ実行が移っているところまで
確認取れました。
アプリケーションエラーになる理由は、いまだに謎ですが
現状、VB側のDbgHandler関数内の
fp=FreeFile(0)
の中(細かく言うと、MSVBVM60.dll)で落ちます。
(すいません、質問で出したソースは簡単にしてありまして、
実際はもっとロジックが入っているのです)
#とりあえず、悩んでいた事項は解決しました。
VB側で死んでしまうという新たな問題も見つかりましたが。
継続調査ですね。
スレッド内から、じかにVBの関数を関数ポインタを使って
呼び出す(コールバックする)は、やめました。
いろいろ探っているうちに、どうやら、
スタック領域がずれてくるように見えたからです。
(すいません、VCのレジスタウインドウで見ているのですが
憶測で言っているので気にしないでください)
そこで、以下の方法に切り替え、無事動作するようになりました。
・DLL(スレッドを立ち上げる前段階で)、VBのウインドウハンドルを
渡しておく。
・VB側は、メッセージフックAPIを用いて、自分が作ったオリジナルの
メッセージWM_APPとwParamの組み合わせと受信できるように
待ち受けさせる。
・DLLはコールバックを行わない代わりに、SendMessageを経由して
VBに通知する
・通知を受けたVBはDLL側で保持している文字列などの情報を
読み出す。
#_beginthread を使うと、DLLからみたVBはスレッドセーフでは
ないようなイメージなんでしょうかね。あと、VBというのは
VB自身が多重で呼ばれるようなシーンは対応できていないのか?
と勝手に解釈してます。