C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[8]: VB2013からVC++6.0に構造体配列参照渡し【別件】


(過去ログ 127 を表示中)

[トピック内 9 記事 (1 - 9 表示)]  << 0 >>

■75637 / inTopicNo.1)  VB2013からVC++6.0に構造体配列参照渡し【別件】
  
□投稿者/ とら (20回)-(2015/04/16(Thu) 19:56:33)

分類:[.NET 全般] 

再度お世話になります。

@VB.netからVC++6.0のへの構造体配列参照渡し
http://bbs.wankuma.com/index.cgi?mode=al2&namber=75306

AVB.net⇒VC++6.0のVoid型に構造体配列参照渡
http://bbs.wankuma.com/index.cgi?mode=al2&namber=75559

にて質問した内容に近いのですが、またはまってしまったのでお知恵を拝借出来れば助かります。

なお、AのVoid型に〜の時に同様の対応を行おうと致しましたが、あの時は結局VC側にてvoid型を
受け取る構造体の型に変更し解決致しましたので、今回求めている解決法は未解決のままです。


現状と致しましては、@の時のようにSafeArrayに対して構造体配列を受け渡ししたいのですが、
前回と異なり数値だけではなく文字列変数が混ざっています。

VC側はSafeArrayで受け取った後、同じような構造体にキャストして使用しているようですが、
その際の文字列変数はBSTRで宣言してあり、数値変数はintでした。

マーシャリングも色々と追加してみたのですが、どうにもうまくいかないので、お手数ですが
お教え頂けないでしょうか。

例によってVCへの変更、及びラッパー用DLL等の作成は最終手段とし、極力VB2013のみで
完結したく考えております。


以上、宜しくお願い致します。


## VC++6.0 ###########

Test1_API int __stdcall Test1(LPSAFEARRAY *Data1, LPSAFEARRAY *Data2)


## VB6.0 #############

-- 宣言部 ----

Declare Function Test1 Lib "Test.DLL" (ByRef oType1() As Any, ByRef oType1() As Any) As Long

Public Type Type1
 Str1 As String
 Str2 As String
 Lng1 As Long
End Type

Public Type Type2
 Str3 As String
 Lng2 As Long
 Lng3 As Long
End Type

Public oType1(0 To 9) As Type1
Public oType2(0 To 9) As Type2


-- 処理 ----

Dim iRet As Long
iRet = Test1(oType1(), oType2())


## VB2013 #############

-- 宣言部 ----

Declare Function Test1 Lib "Test.DLL" (<MarshalAs(UnmanagedType.SafeArray, SafeArraySubType:=VarEnum.VT_RECORD)>
                                       ByVal oStructure() As Structure1
                                       <MarshalAs(UnmanagedType.SafeArray, SafeArraySubType:=VarEnum.VT_RECORD)>
                                       ByVal oStructure() As Structure2) As Integer


<ComVisible(True), Guid("91e3f2e5-341a-49bc-9f48-8637526c2833")>
Public Structure Structure1
 <MarshalAs(UnmanagedType.BStr)> Dim Str1 As String
 <MarshalAs(UnmanagedType.BStr)> Dim Str2 As String
 Dim Int1 As Integer
End Structure

<ComVisible(True), Guid("e6b3817c-4342-4335-985f-5b8a0df41881")>
Public Structure Structure2
 <MarshalAs(UnmanagedType.BStr)> Dim Str3 As String
 Dim Int2 As Integer
 Dim Int3 As Integer
End Structure

Public oStructure1(9) As Structure1
Public oStructure2(9) As Structure2


-- 処理 ----

Dim iRet As Integer = -1
iRet = Test1(oStructure1, oStructure2)


引用返信 編集キー/
■75641 / inTopicNo.2)  Re[1]: VB2013からVC++6.0に構造体配列参照渡し【別件】
□投稿者/ 魔界の仮面弁士 (294回)-(2015/04/17(Fri) 12:04:00)
2015/04/17(Fri) 12:04:51 編集(投稿者)

No75637 (とら さん) に返信
> VC側はSafeArrayで受け取った後、同じような構造体にキャストして使用しているようですが、

この部分のコードと、C++側の構造体定義は公開できますか?
こちらでも検証してみたいので…。
引用返信 編集キー/
■75643 / inTopicNo.3)  Re[2]: VB2013からVC++6.0に構造体配列参照渡し【別件】
□投稿者/ 通りすがり (12回)-(2015/04/17(Fri) 13:31:58)
2015/04/17(Fri) 22:20:37 編集(投稿者)
2015/04/17(Fri) 22:20:09 編集(投稿者)

<pre><pre>要はBSTRなんですが、結局C言語からはポインタなんですよね。ポイン
タってメモリアドレスそのものなワケで、68000なんかでは奇数アドレ
スから1バイト以外のデータを読み出すと「アドレエラー」っていう
のが発生するのが懐かしいものだったり。
CやC++には基本的にCPUのアーキテクチャを色濃く残す言語でintは
MS-DOS時代には16ビットしかないのが「普通」だったんですよね。

BSTRってのはPascal文字列みたいな先頭に長さがあって16ビットの
配列ってことだけどBSTR自体は確か先頭の長さではなく文字が格納さ
れている配列の先頭を指していたはず。

typedef struct
{
  short length;
  unsigned short str[1]; /* GCCでは[0]と記述できる。スマートといえ
                        ばスマート */
} BSTR;

今風?なVB ではCからみたら addresof (文字列変数)≒であって.netの
怪しげなマーシャル(変換)ってのがどこをCに渡すのかしらん??

こんな感じかな。ところがCに慣れていないとBSTRは、あたかも文字列
そのものに見えます。

BSTR st0,st1;   
st0 = st1;   /* ほぼ無意味な代入なんだけどコピーに見える */

「Void型」って何を表すのか判らない「型」がでてきたりなかなか面白い
のですがCでは「とにかくメモリアドレスだ」っていうvoid *ってのが
使えるためもはや何でもアリなんですが(特に68000のようなメモリマッ
プドなI/OアーキテクチャではCPU周辺チップすらいじれてしまう)たぶ
ん「Void型」はvoid *な引数をもつ関数なんでしょ?
構造体ってのもカッコイイものではなくCやC++では構造体の各メンバー
構造体を格納する先頭メモリアドレスからのオフセット数にしかならな
いのです。しかもそのオフセット数はコンパイルオプション等々で
調整すらできる。
キャストっていうのはC++でキャストをオーバーライドしない限りは特に
ポインタについては「コンパイルエラーにしない」という以外の作用は
ないのです。
ということで、CあるいはC++で記述されたDLLを扱う場合はVB6.0が生成
していたBSTRをきちんと渡せば期待通りに動作するはずです。
DLL側がバグっててSysFreeStringを呼んでいる場合は絶対に改善できない
メモリリークに悩むことになるでしょう。
</pre></pre>

引用返信 編集キー/
■75655 / inTopicNo.4)   VB2013からVC++6.0に構造体配列参照渡し【別件】
□投稿者/ とら (21回)-(2015/04/20(Mon) 18:34:17)
2015/04/21(Tue) 10:15:52 編集(投稿者)
■75641 (魔界の仮面弁士 さん) に返信

返信が遅れて申し訳御座いません。


> この部分のコードと、C++側の構造体定義は公開できますか?
> こちらでも検証してみたいので…。 

下記のようになっていると思われます。

-- 宣言部 ----

typedef struct {
	BSTR	wstr1;
	BSTR	wstr2;
	int	wint1;
} testStruct1

-- 処理 ----

Test1_API int __stdcall Test1(LPSAFEARRAY *Data1, LPSAFEARRAY *Data2)

	testStruct1 oTestStruct1;
	int nCnt;
	long idx,lb,ub;

	LPSAFEARRAY psa = *Data1;

	SafeArrayLock(psa);
	SafeArrayGetLBound(psa, 1, &lb);
	SafeArrayGetUBound(psa, 1, &ub);
	for( idx = lb; idx <= ub; idx++ ){
		SafeArrayGetElement(psa, &idx, &testStruct1);

		−何か処理−

		SafeArrayPutElement(psa, &idx, &testStruct1);
	}
	SafeArrayUnlock(psa);
}



■No75643 (通りすがり さん) に返信

> ということで、CあるいはC++で記述されたDLLを扱う場合はVB6.0が生成
> していたBSTRをきちんと渡せば期待通りに動作するはずです。
> DLL側がバグっててSysFreeStringを呼んでいる場合は絶対に改善できない
> メモリリークに悩むことになるでしょう。

もう少し試行錯誤してみます。






現状ではCom公開を行わず下記のようにやってみたところ、値は渡せていますが変更の反映がされていない模様
(<[In], [Out]> も試しましたが変わらずでした)

Declare Function Test1 Lib "Test.DLL" (<[In], Out> ByVal oStructure() As Structure1
                                       <[In], Out> ByVal oStructure() As Structure2) As Integer


<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)>
Public Structure Structure1
 <MarshalAs(UnmanagedType.BStr)> Dim Str1 As String
 <MarshalAs(UnmanagedType.BStr)> Dim Str2 As String
 Dim Int1 As Integer
End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)>
Public Structure Structure2
 <MarshalAs(UnmanagedType.BStr)> Dim Str3 As String
 Dim Int2 As Integer
 Dim Int3 As Integer
End Structure

Public oStructure1(9) As Structure1
Public oStructure2(9) As Structure2


-- 処理 ----

Dim iRet As Integer = -1
iRet = Test1(oStructure1, oStructure2)


引用返信 編集キー/
■75674 / inTopicNo.5)  Re[4]: VB2013からVC++6.0に構造体配列参照渡し【別件】
□投稿者/ 魔界の仮面弁士 (301回)-(2015/04/21(Tue) 20:16:08)
No75655 (とら さん) に返信
> 下記のようになっていると思われます。
> testStruct1 oTestStruct1;
> SafeArrayGetElement(psa, &idx, &testStruct1);
testStruct1 というのは、(変数名ではなく)構造体の型名ですよね。
C++ は専門外なので良く分かっていないのですが、このパターンで
「&testStruct1」を指定するとどうなるんでしょうか?


> −何か処理−
私自身、C++ 側の BSTR の扱いに不慣れなので、ここのサンプルも欲しかった…。



> ## VB6.0 #############
> Declare Function Test1 Lib "Test.DLL" (ByRef oType1() As Any, ByRef oType1() As Any) As Long

VB6 の時は、「ByRef ユーザー定義型()」で、
2013 では、「ByVal 構造体()」に変更したのは何故でしょう?
DLL 側が LPSAFEARRAY *Data1 なのだとしたら、呼び出し側は ByRef な気が。
というか、 No75398 の時は ByRef でしたよね。

Declare 側を ByVal oStructure() As Structure1 とするのであれば、
DLL 側は LPSAFEARRAY *Data1 ではなく、SAFEARRAY *Data1 じゃないのかな…。


> <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)>
うん? VB6 の String は Unicode ベースだったはず。

なので、あえて Charset を明示するとしたら Unicode 指定、そして
API 宣言も Declare Unicode Function となるのかな…。

まぁ、BSTR 指定のメンバーは、Charset の影響を受けないでしょうから、
Charset は無指定で良い気がする(というか何でも良さそう)。



> もう少し試行錯誤してみます。
C++ は専門外なので、 No75643 で指摘されてるメモリリーク云々の
話については他の方にお任せしますが、とりあえず、とらさん提示の
一連のコードを ByRef 指定で試してみましたが、DLL 側から
 hoge.wstr1 = SysAllocString(L"何某");
を使って、VB.NET および VB6 側に、配列の各要素に文字列を返却できています。
(もちろん ComVisible は True です)
引用返信 編集キー/
■75675 / inTopicNo.6)  Re[5]: VB2013からVC++6.0に構造体配列参照渡し【別件】
□投稿者/ とら (22回)-(2015/04/21(Tue) 21:48:33)
No75674 (魔界の仮面弁士 さん) に返信
> 「&testStruct1」を指定するとどうなるんでしょうか?
すみません不要物でした

>> −何か処理−
> 私自身、C++ 側の BSTR の扱いに不慣れなので、ここのサンプルも欲しかった…。
切り分けが良く解らず半端になってしまい申し訳御座いません。

> というか、 No75398 の時は ByRef でしたよね。
> うん? VB6 の String は Unicode ベースだったはず。
あれこれ試しているうちにごっちゃになってきています、面目ない


> を使って、VB.NET および VB6 側に、配列の各要素に文字列を返却できています。
> (もちろん ComVisible は True です)
マーシャリングはLPSAFEARRAY用のやつでしょうか
現在複合した問題が出てきていて何が原因か特定が難しくなっている為、
お手数ですがお教え頂けると助かります。

引用返信 編集キー/
■75676 / inTopicNo.7)  Re[6]: VB2013からVC++6.0に構造体配列参照渡し【別件】
□投稿者/ 魔界の仮面弁士 (302回)-(2015/04/22(Wed) 00:52:59)
No75675 (とら さん) に返信
>>を使って、VB.NET および VB6 側に、配列の各要素に文字列を返却できています。
>>(もちろん ComVisible は True です)
> マーシャリングはLPSAFEARRAY用のやつでしょうか

いいえ。LPSAFEARRAY ではなく、 No75637 で提示された元の LPSAFEARRAY * の実装の場合です。

この場合、Declare 側は、ByRef x() As 構造体とし、MarshalAsAttribute で SafeArray, VT_RECORD を付与しています。

VB.NET 側の構造体定義は、文字列フィールドに MarshalAsAttribute で BStr を付与し、
構造体とアセンブリは、ComVisibleAttribute を True にしておきます。

構造体に対する GuidAttribute は無ければ自動生成されるものと思いますが、
私は自分で用意した物を固定的に割り当てるようにしています。

StructLayoutAttribute については、Sequential を明示しました。

CharSet フィールドについては、無指定であれ Ansi であれ Unicode であれ、DLL 側から
SysAllocString にて割り当てた Unicode 文字列(ニイハオ、とか、立方メートル記号、とか)を
VB6 でも VB.NET でも受け取れていました。



一方、DLL が LPSAFEARRAY もしくは SAFEARRAY * の実装になっている場合は、
VB.NET 側は、 Declare を ByVal x() As 構造体に変更する以外、基本的に同じコードで OK でした。
(この実装だと、VB6 からは呼びにくそうですけれどね…)
引用返信 編集キー/
■75677 / inTopicNo.8)  Re[7]: VB2013からVC++6.0に構造体配列参照渡し【別件】
□投稿者/ 通りすがり (13回)-(2015/04/22(Wed) 10:01:55)
>>一連のコードを ByRef 指定で試してみましたが、DLL 側から
>> hoge.wstr1 = SysAllocString(L"何某");
>>を使って、VB.NET および VB6 側に、配列の各要素に文字列を返却できています。

「できる」ことと「正しい実装」とは別物ですよね。BSTRはWinsdows APIのSysAllocString
で割り当てたメモリブロックへのポインタなのでDLL側でhoge.wstr1で別なメモリブロックへ
のポインタで上書きすると、とても厄介なメモリリークを引き起こします。
これはVB側で文字列をセットしてもいなくても同じです。hoge.wstr1にセットされた
BSTRを誰が解放するんでしょうか?

引用返信 編集キー/
■75744 / inTopicNo.9)  Re[8]: VB2013からVC++6.0に構造体配列参照渡し【別件】
□投稿者/ とら (23回)-(2015/04/28(Tue) 11:19:35)
No75676 (魔界の仮面弁士 さん) に返信

ご連絡が遅れて申し訳御座いません。

こちらの環境にて色々と試してみたのですが、VBから値が渡らなかったり
マーシャリングの型が違うといったエラーが出るばかりでうまくいきませんでした。

不本意ですが、今回もVC側に大幅に手を入れてLPSAFEARRAYではなくVCで宣言した
構造体に値を渡す方向で実装致しました。

魔界の仮面弁士さんには大変お世話になりました、感謝致します。


以上、有難う御座いました。

解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -