トップ回答者
COM相互運用機能における構造体配列の受け渡し

質問
回答
-
Anonymous573849 さんからの引用 VB2005のクラスライブラリが提供する関数のI/Fに、構造体配列をメンバに持つ構造体が存在する場合、
アンマネージコード側ではこの構造体をどのように扱えばよいのでしょうか?
通常、SAFEARRAYのVT_RECORDで情報が格納されると思います。
VT_RECORDについては次のサイトとか参照してみて下さい。
http://msdn2.microsoft.com/en-us/library/ms221212(VS.85).aspx
Code Snippetpublic struct Layouted
{
public int a;
public int b;
public NoLayout[] test;
}// こうなる
struct __declspec(uuid("921f22aa-330d-3a22-b8b6-a868ae10c111"))
Layouted
{
long a;
long b;
SAFEARRAY * test;
};配列のメンバーに対して"[MarshalAs(UnmanagedType.ByValArray)]"等とつけておくと、C言語スタイルの配列になります。
しかし、可変長配列(要素数[1]として宣言されるもの)となって取り込まれるため、実際の要素数を別のメンバーとして持つ必要があります。
Code Snippetpublic struct Layouted2
{
public int a;
public int b;
[MarshalAs(UnmanagedType.ByValArray)]
public NoLayout[] test;
}// こうなる
struct __declspec(uuid("e2214c04-debc-388f-bb19-ed4833e7fcec"))
Layouted2
{
long a;
long b;
struct NoLayout test[1];
};ちなみに構造体配列の中の構造体配列は正しくマーシャリングされない可能性がありますので、配列の中に配列が含まれるパターンは避けるべきです。
( 参考:http://forums.microsoft.com/MSDN-JA/ShowPost.aspx?PostID=2867810&SiteID=7 )
※前述のC++での出力結果はtlbファイルを#importで取り込んだものです。
-
Anonymous573849 さんからの引用 psa = SafeArrayCreate(VT_RECORD, 1, rgsabound);
先日、示しましたURLはご覧になりましたか?(→ http://msdn2.microsoft.com/en-us/library/ms221212(VS.85).aspx )
SafeArrayを作る際に使う関数はSafeArrayCreateではありません。
また、SafeArrayAccessDataの後は、ちゃんとSafeArrayUnaccessDataを呼ぶべきでしょう。
VT_RECORDはサイズが可変です。
IRecordInfoというインターフェースでその型の情報を提供する必要があります。
(tlbをインポートしてある場合は、前述のサンプルとほぼ同じ手順になる(=IRecordInfoを実装する必要がない)のではないかと推測しますが、確認していません)
今のコードではサイズが不明な配列に対して、色々と詰め込んでいるので、確保されたサイズ以上の領域に書き込んでいることになります。
すべての返信
-
Anonymous573849 さんからの引用 VB2005のクラスライブラリが提供する関数のI/Fに、構造体配列をメンバに持つ構造体が存在する場合、
アンマネージコード側ではこの構造体をどのように扱えばよいのでしょうか?
通常、SAFEARRAYのVT_RECORDで情報が格納されると思います。
VT_RECORDについては次のサイトとか参照してみて下さい。
http://msdn2.microsoft.com/en-us/library/ms221212(VS.85).aspx
Code Snippetpublic struct Layouted
{
public int a;
public int b;
public NoLayout[] test;
}// こうなる
struct __declspec(uuid("921f22aa-330d-3a22-b8b6-a868ae10c111"))
Layouted
{
long a;
long b;
SAFEARRAY * test;
};配列のメンバーに対して"[MarshalAs(UnmanagedType.ByValArray)]"等とつけておくと、C言語スタイルの配列になります。
しかし、可変長配列(要素数[1]として宣言されるもの)となって取り込まれるため、実際の要素数を別のメンバーとして持つ必要があります。
Code Snippetpublic struct Layouted2
{
public int a;
public int b;
[MarshalAs(UnmanagedType.ByValArray)]
public NoLayout[] test;
}// こうなる
struct __declspec(uuid("e2214c04-debc-388f-bb19-ed4833e7fcec"))
Layouted2
{
long a;
long b;
struct NoLayout test[1];
};ちなみに構造体配列の中の構造体配列は正しくマーシャリングされない可能性がありますので、配列の中に配列が含まれるパターンは避けるべきです。
( 参考:http://forums.microsoft.com/MSDN-JA/ShowPost.aspx?PostID=2867810&SiteID=7 )
※前述のC++での出力結果はtlbファイルを#importで取り込んだものです。
-
お世話になります。
レスが遅れてしまい、大変申し訳ありません。
以下のようなコーディングをしております。
宜しくお願い致します。
'COM側のコード(VB2005)---------------------------------------------------------------------------------
<ComClass(ComClass1.ClassId, ComClass1.InterfaceId, ComClass1.EventsId)> _
Public Class ComClass1#Region "COM GUID"
' (省略)
#End RegionPublic Enum enmItemType
TYPE_A = 1
TYPE_B = 2
TYPE_C = 3
TYPE_D = 4
End EnumPublic Enum enmItemClass
CLASS_A = 1
CLASS_B = 2
CLASS_C = 3
CLASS_D = 4
End EnumPublic Enum enmItemKind
KIND_A = 1
KIND_B = 2
KIND_C = 3
KIND_D = 4
End EnumPublic Enum enmItemRank
RANK_A = 1
RANK_B = 2
RANK_C = 3
RANK_D = 4
End EnumPublic Enum enmRet
RET_OK = 1
RET_NG = 0
RET_ERR = -1
End EnumPublic Structure udtSubItem
Dim lngItemA As Long
Dim strItemB As String
Dim strItemC As String
Dim strItemD As String
Dim intItemE As Integer
Dim eItemType As enmItemType
Dim eItemClass As enmItemClass
End StructurePublic Structure udtMainItem
Dim lngSubItemNum As Long
Dim eItemKind As enmItemKind
Dim intItemX As Integer
Dim eItemRank As enmItemRank
Dim lngItemY As Long
Dim lngItemZ As Long
Dim strComment As String
Dim uSubItem() As udtSubItem
End StructurePrivate guMainItem As udtMainItem
Public Sub New()
MyBase.New()
End SubPublic Function InitMainItem(ByRef uMainItem As udtMainItem) As enmRet
Dim eRet As enmRet
Dim intIdx As IntegerTry
'グローバルエリアに入力情報を保持
ReDim guMainItem.uSubItem(CInt(uMainItem.lngSubItemNum))
guMainItem = uMainItemIf uMainItem.lngSubItemNum < 1 Then
eRet = enmRet.RET_NG
Else
eRet = enmRet.RET_NG
MsgBox(uMainItem.eItemKind)
MsgBox(uMainItem.eItemRank)
MsgBox(uMainItem.intItemX)
MsgBox(uMainItem.lngItemY)
MsgBox(uMainItem.lngItemZ)
MsgBox(uMainItem.strComment)
For intIdx = 0 To CInt(uMainItem.lngSubItemNum) - 1
MsgBox(uMainItem.uSubItem(intIdx).eItemClass)
MsgBox(uMainItem.uSubItem(intIdx).eItemType)
MsgBox(uMainItem.uSubItem(intIdx).lngItemA)
MsgBox(uMainItem.uSubItem(intIdx).strItemB)
MsgBox(uMainItem.uSubItem(intIdx).strItemC)
MsgBox(uMainItem.uSubItem(intIdx).strItemD)
MsgBox(uMainItem.uSubItem(intIdx).intItemE)
NextuMainItem.strComment = "SUCCESS!!"
eRet = enmRet.RET_OKEnd If
''-戻り値セット
Select Case eRet
Case enmRet.RET_OK
MsgBox("OK")
Return enmRet.RET_OK
Case enmRet.RET_NG
MsgBox("NG")
Return enmRet.RET_NG
Case Else
MsgBox("ERROR")
Return enmRet.RET_ERR
End SelectCatch ex As Exception
MsgBox("ERROR")
Return enmRet.RET_ERR
End TryEnd Function
End Class
'--------------------------------------------------------------------------------------------
//利用側のコード(VC++2005 アンマネージ)--------------------------------
_ComClass1Ptr COMTest;
HRESULT com_ret;
HRESULT hret;
udtMainItem uMainItem;
udtMainItem* puMainItem;
udtSubItem uSubItem[1];
enmRet eRet;com_ret = COMTest.CreateInstance( CLSID_ComClass1 );
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = 1;SAFEARRAY FAR* psa; //SAFEARRAY型ポインタ変数の宣言
//SAFEARRAYの作成(VARTYPE,1次元,SAFEARRAYBOUND型配列のポインタ)
psa = SafeArrayCreate(VT_RECORD, 1, rgsabound);
//SAFEARRAYにセット
SafeArrayAccessData( psa, (void**)&uSubItem );
uSubItem[0].eItemClass = enmItemClass_CLASS_A;
uSubItem[0].eItemType = enmItemType_TYPE_B;
uSubItem[0].lngItemA = 10;
uSubItem[0].strItemB = "20";
uSubItem[0].strItemC = "30";
uSubItem[0].strItemD = "40";
uSubItem[0].intItemE = 50;
uMainItem.lngSubItemNum = 1;
uMainItem.eItemKind = enmItemKind_KIND_C;
uMainItem.eItemRank = enmItemRank_RANK_D;
uMainItem.intItemX = 1;
uMainItem.lngItemY = 2;
uMainItem.lngItemZ = 3;
uMainItem.strComment = "comment";
uMainItem.uSubItem = psa;hret = COMTest->InitMainItem(&uMainItem,&eRet);
//--------------------------------------------------------------------------------------- -
Anonymous573849 さんからの引用 psa = SafeArrayCreate(VT_RECORD, 1, rgsabound);
先日、示しましたURLはご覧になりましたか?(→ http://msdn2.microsoft.com/en-us/library/ms221212(VS.85).aspx )
SafeArrayを作る際に使う関数はSafeArrayCreateではありません。
また、SafeArrayAccessDataの後は、ちゃんとSafeArrayUnaccessDataを呼ぶべきでしょう。
VT_RECORDはサイズが可変です。
IRecordInfoというインターフェースでその型の情報を提供する必要があります。
(tlbをインポートしてある場合は、前述のサンプルとほぼ同じ手順になる(=IRecordInfoを実装する必要がない)のではないかと推測しますが、確認していません)
今のコードではサイズが不明な配列に対して、色々と詰め込んでいるので、確保されたサイズ以上の領域に書き込んでいることになります。
-
こんにちは。中川俊輔 です。
Azuleanさん、回答ありがとうございます。
Anonymousさん、フォーラムのご利用ありがとうございます。
その後いかがでしょうか?
有用な情報と思われたため、Azuleanさんの回答へ回答済みチェックをつけさせていただきました。
追加の質問等ありましたら、是非投稿してください!
回答済みチェックが付くことにより、有用な情報を探している方が情報を見つけやすくなります。
有用な情報と思われる回答があった場合は、なるべく回答済みボタンを押してチェックを付けてください。Anonymousさんはチェックを解除することもできますので、ご確認ください。
それでは!