none
COM相互運用機能における構造体配列の受け渡し RRS feed

  • 質問

  •  

    VB2005で作成したクラスライブラリを、COM相互運用機能によってアンマネージコードから使用しようとしています。

     

    VB2005のクラスライブラリが提供する関数のI/Fに、構造体配列をメンバに持つ構造体が存在する場合、

    アンマネージコード側ではこの構造体をどのように扱えばよいのでしょうか?

    SAFEARRAYの使用方法がよくわからないため、御指導いただければ幸いです。

    宜しくお願い致します。

     

    2008年3月13日 14:23

回答

  •  Anonymous573849 さんからの引用

    VB2005のクラスライブラリが提供する関数のI/Fに、構造体配列をメンバに持つ構造体が存在する場合、

    アンマネージコード側ではこの構造体をどのように扱えばよいのでしょうか?

    通常、SAFEARRAYのVT_RECORDで情報が格納されると思います。

    VT_RECORDについては次のサイトとか参照してみて下さい。

    http://msdn2.microsoft.com/en-us/library/ms221212(VS.85).aspx

     

    Code Snippet

    public 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 Snippet

    public 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で取り込んだものです。

    2008年3月13日 16:02
    モデレータ
  •  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を実装する必要がない)のではないかと推測しますが、確認していません)

     

    今のコードではサイズが不明な配列に対して、色々と詰め込んでいるので、確保されたサイズ以上の領域に書き込んでいることになります。

    2008年3月18日 14:22
    モデレータ

すべての返信

  •  Anonymous573849 さんからの引用

    VB2005のクラスライブラリが提供する関数のI/Fに、構造体配列をメンバに持つ構造体が存在する場合、

    アンマネージコード側ではこの構造体をどのように扱えばよいのでしょうか?

    通常、SAFEARRAYのVT_RECORDで情報が格納されると思います。

    VT_RECORDについては次のサイトとか参照してみて下さい。

    http://msdn2.microsoft.com/en-us/library/ms221212(VS.85).aspx

     

    Code Snippet

    public 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 Snippet

    public 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で取り込んだものです。

    2008年3月13日 16:02
    モデレータ
  • お世話になります。

     

    SAFEARRAYのVT_RECODEを使用してみましたが、

    ヒープが壊れているかもしれないというエラーが出て止まってしまいます。

     

    基本、ポインタを定義して、SAFEARRAY配列の作成、SafeArrayAccessDataを実行後、

    ポインタ経由でデータをセットすればOKだったと思うのですが、

    何か見落としているのでしょうか?

     

    不勉強で申し訳ありません。

    ご教授いただきますよう、宜しくお願い致します。

    2008年3月14日 1:37
  •  Anonymous573849 さんからの引用

    何か見落としているのでしょうか?

    失敗したコードでも良いので、切り出して貼り付けてみませんか?

    (利用側のコード、COM側のコード、構造体の宣言、インターフェースの定義など)

     

    これまでの流れで、どちらがそのメモリを確保し、どのようにして渡すかが見えていません。

    その中でアドバイスをしても、迷走するだけだと感じましたので、一旦きっちりとした情報を頂けないかと考えます。

    2008年3月14日 13:49
    モデレータ
  • お世話になります。

    レスが遅れてしまい、大変申し訳ありません。

    以下のようなコーディングをしております。

    宜しくお願い致します。

     

    'COM側のコード(VB2005)---------------------------------------------------------------------------------

    <ComClass(ComClass1.ClassId, ComClass1.InterfaceId, ComClass1.EventsId)> _
    Public Class ComClass1

    #Region "COM GUID"
        ' (省略)
    #End Region

        Public Enum enmItemType
            TYPE_A = 1
            TYPE_B = 2
            TYPE_C = 3
            TYPE_D = 4
        End Enum

        Public Enum enmItemClass
            CLASS_A = 1
            CLASS_B = 2
            CLASS_C = 3
            CLASS_D = 4
        End Enum

        Public Enum enmItemKind
            KIND_A = 1
            KIND_B = 2
            KIND_C = 3
            KIND_D = 4
        End Enum

        Public Enum enmItemRank
            RANK_A = 1
            RANK_B = 2
            RANK_C = 3
            RANK_D = 4
        End Enum

        Public Enum enmRet
            RET_OK = 1
            RET_NG = 0
            RET_ERR = -1
        End Enum

        Public 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 Structure

        Public 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 Structure

        Private guMainItem As udtMainItem

     

        Public Sub New()
            MyBase.New()
        End Sub

     

        Public Function InitMainItem(ByRef uMainItem As udtMainItem) As enmRet

            Dim eRet As enmRet
            Dim intIdx As Integer

            Try

                'グローバルエリアに入力情報を保持
                ReDim guMainItem.uSubItem(CInt(uMainItem.lngSubItemNum))
                guMainItem = uMainItem

                If 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)
                    Next

                    uMainItem.strComment = "SUCCESS!!"
                    eRet = enmRet.RET_OK

                End 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 Select

            Catch ex As Exception

                MsgBox("ERROR")
                Return enmRet.RET_ERR
            End Try

        End 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);

    //---------------------------------------------------------------------------------------
    2008年3月18日 11:28
  •  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を実装する必要がない)のではないかと推測しますが、確認していません)

     

    今のコードではサイズが不明な配列に対して、色々と詰め込んでいるので、確保されたサイズ以上の領域に書き込んでいることになります。

    2008年3月18日 14:22
    モデレータ
  • こんにちは。中川俊輔 です。

     

    Azuleanさん、回答ありがとうございます。

     

    Anonymousさん、フォーラムのご利用ありがとうございます。

    その後いかがでしょうか?

    有用な情報と思われたため、Azuleanさんの回答へ回答済みチェックをつけさせていただきました。

    追加の質問等ありましたら、是非投稿してください!

     

    回答済みチェックが付くことにより、有用な情報を探している方が情報を見つけやすくなります。
    有用な情報と思われる回答があった場合は、なるべく回答済みボタンを押してチェックを付けてください。

    Anonymousさんはチェックを解除することもできますので、ご確認ください。

     

    それでは!

     

    2008年3月24日 10:14