スレッド内からVBの関数をコールバックするには

解決


とおりすがりの犬  2006-03-02 07:19:54  No: 60851

みなさんこんにちは。

いま、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の部分をはずすとスレッドは動作します)


RAPT  2006-03-02 07:44:58  No: 60852

関数ポインタが原因だとは、何をもって判断したのでしょうか。


Blue  2006-03-02 07:56:04  No: 60853

VC側でデバッグは可能でしょうか?
スレッドでないときは
VBで作成したEXEをVC側でもデバッグセッションの実行可能ファイルに指定すればできるましたけど。

ところで、
コールバック関数の引数はBSTRでいけるんですね。
コールバックでない時は、勝手にString型(VB)からLPCSTR型に変換されちゃうんですけどね。


とおりすがりの犬  2006-03-02 19:54:31  No: 60854

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でいけるんですね。
どこかの掲示板で掲載されていた情報でした。
少し改造すると文字列を相互で書き換えもできるとかなんとか。。

#こちらで継続して調べてみます。
  そのうち自己解決できるかもしれませんが、他にも
  有識者の意見をお聞かせください。


とおりすがりの犬  2006-03-03 05:21:07  No: 60855

少し進展しました。

EXEをVCでデバッグ(いままで知らなかったのですが、VC画面で、
VBのソースを開き、ブレークポイントが貼れるんですね。
Blue殿情報いただきありがとうございます。)
したところ、スレッド内からVBへ実行が移っているところまで
確認取れました。
アプリケーションエラーになる理由は、いまだに謎ですが
現状、VB側のDbgHandler関数内の
fp=FreeFile(0)
の中(細かく言うと、MSVBVM60.dll)で落ちます。
(すいません、質問で出したソースは簡単にしてありまして、
実際はもっとロジックが入っているのです)

#とりあえず、悩んでいた事項は解決しました。
  VB側で死んでしまうという新たな問題も見つかりましたが。
  継続調査ですね。


とおりすがりの犬  2006-03-04 02:50:15  No: 60856

スレッド内から、じかにVBの関数を関数ポインタを使って
呼び出す(コールバックする)は、やめました。

いろいろ探っているうちに、どうやら、
スタック領域がずれてくるように見えたからです。
(すいません、VCのレジスタウインドウで見ているのですが
  憶測で言っているので気にしないでください)

そこで、以下の方法に切り替え、無事動作するようになりました。

・DLL(スレッドを立ち上げる前段階で)、VBのウインドウハンドルを
  渡しておく。
・VB側は、メッセージフックAPIを用いて、自分が作ったオリジナルの
  メッセージWM_APPとwParamの組み合わせと受信できるように
  待ち受けさせる。
・DLLはコールバックを行わない代わりに、SendMessageを経由して
  VBに通知する
・通知を受けたVBはDLL側で保持している文字列などの情報を
  読み出す。

#_beginthread を使うと、DLLからみたVBはスレッドセーフでは
  ないようなイメージなんでしょうかね。あと、VBというのは
  VB自身が多重で呼ばれるようなシーンは対応できていないのか?
  と勝手に解釈してます。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加