- PR -

VB.NETの構造体配列の、VC++DLLへの受け渡し方法

1
投稿者投稿内容
ZX
会議室デビュー日: 2008/03/12
投稿数: 3
投稿日時: 2008-03-12 20:52
はじめまして。

現在、VB6&VC++6で開発していたアプリケーションを、Visual Studio2005(以降"VS2005")への移植作業を行っていますが、VB.NET→VC++へのマーシャライズがうまく行えず、困っています。
解決方法をご存知の方がいれば、ご教授願います。

<前提>
実行ファイル:VB6→VB.NET2005(8.0)
DLLファイル:VC++6→VC++2005(8.0)
※「/clr」オプションは未指定。
 (intel MKLライブラリがリンクエラーになってしまうため)

<問題点>
VB.NET上で定義した構造体(ユーザ定義型)の配列が、VC++で作成したアンマネージドDLLとの受け渡しでエラーになる。

<コード>
**VB.NET**
Imports System.Runtime.InteropServices

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)> _
Public Structure AAA
Dim aaa As Integer
Dim bbb As Integer
Dim ccc As Double
Dim ddd As Double
End Structure
Public a_dat() As AAA

Private Declare Function AAA_FUNC Lib "TARGET.dll" Alias "aaa_func" (ByRef RecCnt As Integer, ByRef a_dat() As AAA) As Integer


Public Function func1() As Integer

Public RecCnt As Integer
Public ret As Integer

'RecCntにレコード件数を取得後、確保
ReDim a_dat(RecCnt - 1)

'a_datにレコードセット


'アンマネージドDLLを呼び出し
ret = AAA_FUNC(IssReCount, a_dat)
If ret <> 0 Then
func1 = ret
Exit Function
End If
End Function


**VC++**
__declspec(dllexport) int __stdcall aaa_func(long *,LPSAFEARRAY *);

typedef struct{
long aaa;
long bbb;
double ccc;
double ddd;
} AAA_C;
vector<AAA_C> a_data;

__declspec(dllexport) int __stdcall aaa_func(long *m_row,LPSAFEARRAY *va_data)
{
long idx;
long lb, ub;
long i;

vector<AAA_C>& vdata=a_data;
LPSAFEARRAY psa;
a_data.reserve(*m_row);
psa = *va_data;
if(psa->cDims != 1) { // psa->cDims : 2 となってしまう?
return <エラーコード>;
}
SafeArrayLock(psa);
SafeArrayGetLBound(psa, 1, &lb);
SafeArrayGetUBound(psa, 1, &ub);
if(lb != 0 || ub != (*m_row)-1){
SafeArrayUnlock(psa);
return <エラーコード>;
}
for( idx = lb, i=0; idx <= ub; idx++, i++ ){
SafeArrayGetElement(psa, &idx, &vdata[i]);
}
SafeArrayUnlock(psa);

return 0;
}


以上、よろしくお願いします。
Azulean
大ベテラン
会議室デビュー日: 2008/01/04
投稿数: 123
お住まい・勤務地: 大阪府
投稿日時: 2008-03-12 23:15
引用:

__declspec(dllexport) int __stdcall aaa_func(long *,LPSAFEARRAY *);


このページとかどうでしょうか。
TestArrayOfStructsあたりがご所望のものかと存じます。
http://msdn2.microsoft.com/ja-jp/library/hk9wyw21.aspx

P/Invokeで特に指定しなかった場合、配列はSafeArrayにはなりません。
(MarshalAsで指定すればなるかもしれませんが、未確認)
ZX
会議室デビュー日: 2008/03/12
投稿数: 3
投稿日時: 2008-03-13 16:25
Azuleanさん、貴重な情報ありがとうございました。

引用:
このページとかどうでしょうか。
TestArrayOfStructsあたりがご所望のものかと存じます。
http://msdn2.microsoft.com/ja-jp/library/hk9wyw21.aspx


本日、上記を参考にして試してみました。
一見、うまく行きそうにも思えたのですが、期待した結果にはなりませんでした・・・。
SafeArrayGetLBound()/SafeArrayGetUBound()が正しく実行できませんでした。
(下記の例ならlb : 0、ub : 2のはず・・・。
引用:
<コード>
**VB.NET**
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)> _
Public Structure AAA
Dim aaa As Integer
Dim bbb As Integer
Dim ccc As Double
Dim ddd As Double
Public Sub New( aaa As Integer, bbb As Integer, ccc As Double,ddd As Double )
Me.aaa = aaa
Me.bbb = bbb
Me.ccc = ccc
Me.ddd = ddd
End Sub

End Structure

Private Declare Function AAA_FUNC Lib "TARGET.dll" Alias "aaa_func" (ByRef RecCnt As Integer, _
<InAttribute(), OutAttribute()> ByRef a_dat() As AAA) As Integer

'a_datにレコードセット
Dim a_dat As AAA() = {
New AAA(1, 1, 1.1, 1.2), New AAA(2, 2, 2.1, 2.2), New AAA(3, 3, 3.1, 3.2) _
}

'アンマネージドDLLを呼び出し
ret = AAA_FUNC(IssReCount, a_dat)
If ret <> 0 Then
func1 = ret
Exit Function
End If
End Function

**VC++**
__declspec(dllexport) int __stdcall aaa_func(long *m_row,LPSAFEARRAY *va_data)
{
long idx;
long lb, ub;
long i;

vector<AAA_C>& vdata=a_data;
LPSAFEARRAY psa;
a_data.reserve(*m_row);
psa = *va_data;
if(psa->cDims != 1) { // psa->cDims : 1 となった!
return <エラーコード>;
}
SafeArrayLock(psa);
SafeArrayGetLBound(psa, 1, &lb); // lb : 1になった?
SafeArrayGetUBound(psa, 1, &ub); // ub : 1になった?
if(lb != 0 || ub != (*m_row)-1){
SafeArrayUnlock(psa);
return <エラーコード>;
}
for( idx = lb, i=0; idx <= ub; idx++, i++ ){
SafeArrayGetElement(psa, &idx, &vdata[i]);
}
SafeArrayUnlock(psa);
return 0;
}


正直、MKLライブラリ部分を残して、C#に切り替えたほうが・・・とも考えたのですが、
C#は未経験にも関わらず、その場合は20Kステップのプログラムを2週間で対応しろとか言われてしまって・・・

出来れば、VC++のまま行きたいと考えているので、思いつきでも構わないので情報を頂けると助かります!

よろしくお願いします。
Azulean
大ベテラン
会議室デビュー日: 2008/01/04
投稿数: 123
お住まい・勤務地: 大阪府
投稿日時: 2008-03-14 00:18
昨日、私が示したページのC++側のコード(宣言)をお読みになられましたか?
あのサンプルはSAFEARRAYではなく、一般的なC言語配列になりますよ。

コード:
int TestArrayOfStructs(MYPOINT* pPointArray, int size);

ZX
会議室デビュー日: 2008/03/12
投稿数: 3
投稿日時: 2008-03-14 16:52
Azuleanさん、度々ありがとうございます。

申し訳ありませんでした。
構造体を配列を、出来ればVC++側には手を入れないで受け渡ししたいと考えていたので、勝手に先入観を持ってしまいました。
確かに、サンプルのVC++の宣言部はSAFEARRAYを使用していませんね。
元々のコードを、下記のように変更して実行したところ、VB.NET側で定義/セットした構造体の配列を、VC++側で受け取れるようになれました。

コード:
**VB.NET**
Imports System.Runtime.InteropServices

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)> _
Public Structure AAA
	Dim aaa As Integer
	Dim bbb As Integer
	Dim ccc As Double
	Dim ddd As Double
End Structure
Public a_dat() As AAA

Private Declare Function AAA_FUNC Lib "TARGET.dll" Alias "aaa_func" ( _
	ByRef RecCnt As Integer, _
	<InAttribute(), OutAttribute()> ByVal a_dat() As AAA _
) As Integer

Public Function func1() As Integer

	Public RecCnt As Integer
	Public ret As Integer

	'RecCntにレコード件数を取得後、確保
	ReDim a_dat(RecCnt - 1)

	'a_datにレコードセット

	'アンマネージドDLLを呼び出し
	ret = AAA_FUNC(IssReCount, a_dat)
	If ret <> 0 Then
		func1 = ret
		Exit Function
	End If
End Function


**VC++**
typedef struct{
	long aaa;
	long bbb;
	double ccc;
	double ddd;
} AAA_C;
vector<AAA_C> a_data;
__declspec(dllexport) int __stdcall aaa_func(long *,AAA_C *);

__declspec(dllexport) int __stdcall aaa_func(long *m_row,AAA_C *va_data)
{
	long i;

	vector<AAA_C>& vdata=a_data;
	for( i=0; i < *m_row; i++ ){
		vdata.push_back(va_data[i]);
	}
	return 0;
}


元々の希望とは違い、多少VC++側にも手を入れる必要がありますが、何とか目的を果たせそうです。
ありがとうございました!
1

スキルアップ/キャリアアップ(JOB@IT)