C++Builderで作成したDLLからVBアプリのコールバック関数を呼びます。
その際、VBアプリのコールバック関数の引数にDLLと同じ構造体の配列の
ポインタを渡しているのですが、VBアプリで値が取得できません。
どのようにすれば値が取得できるのか教えてください。
[開発環境]
VB6
[標準モジュール]
Public Declare Function AAA Lib "AAA.dll" (ByVal Callback As Any) As Long
[コールバック関数]
Sub Callback(ByRef b() As TAMESI)
Dim a As TAMESI
Dim i As Integer
For i = 0 To 1
a.kekka = b(i).kekka
a.waruatai = b(i).waruatai
Main.List1.AddItem ("KEKKA = " + CStr(a.kekka))
Main.List1.AddItem ("WARUATAI = " + CStr(a.waruatai))
Next
End Sub
[コールバック関数設定]
Private Sub OKButton_Click()
Dim ReturnValue As Long
ReturnValue = AAA(AddressOf Callback)
End Sub
[Cでの構造体宣言]
typedef struct TAMESI {
long kekka;
long waruatai;
}TAMESI;
[VBでの構造体宣言]
Public Type TAMESI
kekka As Long
waruatai As Long
End Type
C++Builderにおける、関数及びコールバックの定義は
どうなっているのでしょうか?
# というか、これらを隠して情報が得られるはずはないでしょう。
> Sub Callback(ByRef b() As TAMESI)
となっているので、DLL内で TAMESI の配列をつくっているのでしょうか?
おそらく、DLL内の記述に問題があると思われます。
# SAFEARRAYとして扱い、SafeArrayCreate関数等を使わないとまともに動かないかと。
いろいろ調べてみましたが、
SafeArrayCreate関数では、自前のstructを設定できないらしいです。
http://search.acty-net.ne.jp/mfc_search/archive/2004-1/msg00901.html
VARTYPEに設定できる型の配列は可能。(文字列、数値等)
# ここまでくると完全にCの話です罠。
> Sub Callback(ByRef b() As TAMESI)
だったら、配列渡すんじゃなくて、DLL側で配列分ループまわせばいいと思うのですが。
リンク先がずれていました。
ここ↓がスレの先頭。
http://search.acty-net.ne.jp/mfc_search/archive/2004-1/msg00900.html
そうですか、難しいのですね。
それでは、
[Cでの構造体宣言]
typedef struct TAMESI {
long kekka;
long waruatai;
}TAMESI;
typedef struct PPP {
TAMESI tamesi[5];
}PPP;
として、C側は
void (CALLBACK *lpCallback)(PPP*);
とすれば、
[VBでの構造体宣言]
type TAMESI {
kekka as long;
waruatai as long;
}TAMESI;
typedef struct PPP {
tamesi(6) as TAMESI;
}PPP;
[VBコールバック関数]
Sub Callback(ByRef b() As PPP)
Dim a As TAMESI
Dim i As Integer
For i = 0 To 6
a.kekka = b(i).kekka
a.waruatai = b(i).waruatai
Main.List1.AddItem ("KEKKA = " + CStr(a.kekka))
Main.List1.AddItem ("WARUATAI = " + CStr(a.waruatai))
Next
End Sub
のように、構造体の配列を持った構造体というやり方はVBでは
ゆるされているのでしょうか。
先にも書いたと思いますが、
C側でVBが扱える構造体の配列をつくることが出来ないらしいので、
> Sub Callback(ByRef b() As PPP)
というのはムリです。
あらかじめ、VB側で領域を確保した構造体を渡して、C側で設定するのは可能です。
Private Sub OKButton_Click()
Dim ReturnValue As Long
Dim p As PPP
ReturnValue = AAA(AddressOf(Callback),p)
End Sub
みたいに。
それと、これは基本的にVB側の問題ではないので
VBのコードばかり書かれても、解決しません。
Cで書かれたDLLの関数の定義を書いてくださいね。
というか Cの知識が乏しいようですが。
>typedef struct PPP {
> TAMESI tamesi[5];
>}PPP;
typedef の構造体名と別名が同じなのは出来ないはずですが。(PPPが同じ)
>For i = 0 To 6
tamesi[ 5 ]と配列を宣言したら 0〜4までの配列になります。(要素数が5)
> void (CALLBACK *lpCallback)(PPP*);
DLLでexprotできるように書いてくださいね。
(この宣言の仕方ではどこからも使えないはず。)
> typedef の構造体名と別名が同じなのは出来ないはずですが。(PPPが同じ)
ごめんなさい、出来ました。でも普通はこのような書き方はしないはずですが。
# 書くならば無名構造体にするのかな。
> 先にも書いたと思いますが、
> C側でVBが扱える構造体の配列をつくることが出来ないらしいので、
> > Sub Callback(ByRef b() As PPP)
> というのはムリです。
前言撤回させてください。
調べ方が悪かったらしく、SafeArrayCreate関数ではなく、
SafeArrayAllocDescriptor,SafeArrayAllocDataを使えば設定できました。
以下サンプルソース
/* C側ソース(VC6で作成) */
#include <windows.h>
extern "C"
{
#endif
typedef struct
{
long int kekka;
long int waruatai;
} TAMESI;
__declspec( dllexport ) HRESULT WINAPI AttachEventProc( void ( CALLBACK *lpCallback )( LPSAFEARRAY* ) )
{
TAMESI tElement;
SAFEARRAY* psaRelation;
long int index;
static const int size = 5;
if ( FAILED( SafeArrayAllocDescriptor( 1, &psaRelation ) ) )
{
return E_UNEXPECTED;
}
/* 要素のサイズを設定 */
psaRelation->cbElements = sizeof( TAMESI );
psaRelation->fFeatures = FADF_STATIC;
/* 配列のサイズを設定 */
psaRelation->rgsabound[ 0 ].cElements = size;
psaRelation->rgsabound[ 0 ].lLbound = 0;
if ( FAILED( SafeArrayAllocData( psaRelation ) ) )
{
SafeArrayDestroyDescriptor( psaRelation );
return E_UNEXPECTED;
}
/* 配列に設定 */
SafeArrayLock( psaRelation );
for ( index = 0; index < size; ++index )
{
tElement.waruatai = index + 1;
tElement.kekka = 100 / ( index + 1 );
SafeArrayPutElement( psaRelation, &index, &tElement );
}
SafeArrayUnlock( psaRelation );
/* コールバック関数の呼び出し */
( *lpCallback )( &psaRelation );
/* 配列の削除 */
SafeArrayDestroyData( psaRelation );
SafeArrayDestroyDescriptor( psaRelation );
return S_OK;
}
#ifdef __cplusplus
}
#endif
' VB側Declare宣言文
Public Declare Function AttachEventProc Lib "test.dll" (ByVal func As Any) As Long
ツイート | ![]() |