none
P/Invoke stuctures from custom Api written in C RRS feed

  • Question

  • Hi everybody

    I'm working on a project to conect custom API written in C to C#. I'm using P/invoke.

    Ok, I have a custom API with structures:

    typedef struct {
      char label[MAXLABEL];
      char display_name[MAXDISP];
      char parent[VNAMELEN];
      int flags;
      int transform;
      int lag;
      char compact_method;
      char stack_level; 
      char line_width;
    } VARINFO;
    
    typedef struct {
      int v;       /* number of variables */
      int n;       /* number of observations */
      int pd;       /* periodicity or frequency of data */
      int structure;   /* time series, cross section or whatever */
      double sd0;     /* float representation of stobs */
      int t1, t2;     /* start and end of current sample */
      char stobs[OBSLEN]; /* string representation of starting obs (date) */
      char endobs[OBSLEN]; /* string representation of ending obs */
      char **varname;   /* array of names of variables */
      VARINFO **varinfo; /* array of specific info on vars */
      char markers;    /* NO_MARKERS (0), REGULAR MARKERS or DAILY_DATE_STRINGS */
      char delim;     /* default delimiter for "CSV" files */
      char decpoint;   /* character used to represent decimal point */
      char **S;      /* to hold observation markers */
      char *descrip;   /* to hold info on data sources etc. */
      char *submask;   /* subsampling mask */
      char *restriction; /* record of sub-sampling restriction */
    } DATAINFO;
    

    and so far I have managed to make working sample with this structures:

     
        [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public struct Varinfo
        {
      
          [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 128)]
          public string Label;
    
          [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]
          public string DisplayName;
    
          [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]
          public string Parent;
    
          public int Flags;
    
          public int Transform;
    
          public int Lag;
    
          public byte CompactMethod;
    
          public byte StackLevel;
    
          public byte LineWidth;
        }
    
    
        [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public struct Datainfo
        {
    
          public int V;
    
          public int N;
    
          public int Pd;
    
          public int Structure;
    
          public double Sd0;
    
          public int T1;
    
          public int T2;
    
          [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]
          public string Stobs;
    
          [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]
          public string Endobs;
    
          /// <summary>
          /// array of names of variables
          /// </summary>
          public IntPtr Varname;
          
          /// <summary>
          /// array of specific info on vars
          /// </summary>
          public IntPtr VarInfo;
    
          public byte Markers;
    
          public byte Delim;
    
          public byte Decpoint;
    
          /// <summary>
          /// to hold observation markers
          /// </summary>
          public IntPtr S;
    
          /// <summary>
          /// to hold info on data sources etc.
          /// Cann't use type string. Why?
          /// </summary>
          public IntPtr Descrip;
    
          [MarshalAsAttribute(UnmanagedType.LPStr)]
          public string Submask;
    
          [MarshalAsAttribute(UnmanagedType.LPStr)]
          public string Restriction;
        }
    

    Ok, I would like to make stuctures in C# more specific. I would like to get rid of those IntPtr. But anything I try seem not to work. Any suggestions?

    Tuesday, August 9, 2011 8:30 PM

Answers

  • Hello Saymon Rupar,

     

    1. Possible Changes to the Datainfo Structure.

    The following describes the possible changes to the data types of fields of the Datainfo structure :

    1.1 The C DATAINFO.varname (type char**) can be changed to a SAFEARRAY of BSTRs, e.g. :

    SAFEARRAY* pSafeArrayOfVarName;

    The C# counterpart Datainfo.Varname (type IntPtr) can be changed into an array of strings declared to be marshaled as a SAFEARRAY of BSTRs :

    [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_BSTR)]
    public string[] Varname;

     

    1.2 The C DATAINFO.S (type char **) can be changed be similarly changed to SAFEARRAY of BSTRs, e.g. :

    SAFEARRAY* pSafeArrayOfObservationMarkers;

    The C# counterpart Datainfo.S (type IntPtr) is then changed into an array of strings declared to be marshaled as a SAFEARRAY of BSTRs :

    [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)]
    public string[] S;

     

    1.3 The C DATAINFO.descrip (type char*) can be declared as a string which is marshaled as a pointer to a NULL-terminated ANSI character array :

    [MarshalAsAttribute(UnmanagedType.LPStr)]
    public string Descrip;

    There is no problem as far as this is concerned.

     

    1.4 Finally, the C DATAINFO.varinfo (type VARINFO **)  can be changed to a SAFEARRAY of VARINFO structures :

    SAFEARRAY* pSafeArrayOfVarInfo;

    The C# counterpart Datainfo.VarInfo (type IntPtr) can be changed into an array of Varinfo structures declared to be marshaled as a SAFEARRAY of Varinfo structures :

    [MarshalAs(UnmanagedType.SafeArray, SafeArrayUserDefinedSubType = typeof(Varinfo))]
    public Varinfo[] VarInfo;

    However, note that the C# Varinfo structure must be marked with the ComVisibleAttribute and the GuidAttribute :

    [ComVisible(true)]
    [Guid("B613F5F7-C45B-4731-81C9-5E8BE96342F6")]
    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
    public struct Varinfo
    {
            [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 128)]
            public string Label;
            [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]
            public string DisplayName;
            [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]
            public string Parent;
            public int Flags;
            public int Transform;
            public int Lag;
            public byte CompactMethod;
            public byte StackLevel;
            public byte LineWidth;
    }

    These attributes will make the structure marshalable inside a SAFEARRAY as a VT_RECORD type.

     

    1.5 In all 4 examples above, the use of SAFEARRAYs ensures that the number of elements of each array be variable. The next section provides sample codes.

     

    2. Sample Codes.

    2.1 The following is a sample C# code that uses the modified Datainfo and the Varinfo structures. I have renamed them to Datainfo2 and the Varinfo2 respectively. The code creates a Datainfo2 structure and passes the structure to a test API named TestCall02() exported from a DLL named SaymonRuparDLL.dll (written in C++) :

        [ComVisible(true)]
        [Guid("B613F5F7-C45B-4731-81C9-5E8BE96342F6")]
        [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        public struct Varinfo2
        {
            [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 128)]
            public string Label;
            [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]
            public string DisplayName;
            [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]
            public string Parent;
            public int Flags;
            public int Transform;
            public int Lag;
            public byte CompactMethod;
            public byte StackLevel;
            public byte LineWidth;
        }

        [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack=1)]
        public struct Datainfo2
        {
            public int V;
            public int N;
            public int Pd;
            public int Structure;
            public double Sd0;
            public int T1;
            public int T2;
            [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]
            public string Stobs;
            [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]
            public string Endobs;
            [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_BSTR)]
            public string[] Varname;
            [MarshalAs(UnmanagedType.SafeArray, SafeArrayUserDefinedSubType = typeof(Varinfo2))]
            public Varinfo2[] VarInfo;
            public byte Markers;
            public byte Delim;
            public byte Decpoint;
            [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)]
            public string[] S;
            [MarshalAsAttribute(UnmanagedType.LPStr)]
            public string Descrip;
            [MarshalAsAttribute(UnmanagedType.LPStr)]
            public string Submask;
            [MarshalAsAttribute(UnmanagedType.LPStr)]
            public string Restriction;
        }

        class Program
        {
            [DllImport("SaymonRuparDLL.dll", CallingConvention = CallingConvention.StdCall)]
            private static extern void TestCall02([In] Datainfo2 datainfo2);

            static void TestDatainfo2()
            {
                int i = 0;
                Datainfo2 datainfo2 = new Datainfo2();

                datainfo2.V = 0;
                datainfo2.N = 0;
                datainfo2.Pd = 0;
                datainfo2.Structure = 0;
                datainfo2.Sd0 = 0;
                datainfo2.T1 = 0;
                datainfo2.T2 = 0;
                datainfo2.Stobs = "Stobs";
                datainfo2.Endobs = "Endobs";

                datainfo2.Varname = new string[5];
                for (i = 0; i < 5; i++)
                {
                    datainfo2.Varname[i] = string.Format("Varname {0:S}", i.ToString());
                }

                datainfo2.VarInfo = new Varinfo2[10];
                for (i = 0; i < 10; i++)
                {
                    datainfo2.VarInfo[i].Label = string.Format("datainfo2.VarInfo[{0:S}].Label", i.ToString());
                    datainfo2.VarInfo[i].DisplayName = string.Format("datainfo2.VarInfo[{0:S}].DisplayName", i.ToString());
                    datainfo2.VarInfo[i].Parent = string.Format("[{0:S}].Parent", i.ToString());
                }

                datainfo2.Markers = 0;
                datainfo2.Delim = 0;
                datainfo2.Decpoint = 0;

                datainfo2.S = new string[5];
                for (i = 0; i < 5; i++)
                {
                    datainfo2.S[i] = string.Format("S {0:S}", i.ToString());
                }

                datainfo2.Descrip = "datainfo2.Descrip";
                datainfo2.Submask = "datainfo2.Submask";
                datainfo2.Restriction = "datainfo2.Restriction";

                TestCall02(datainfo2);
            }

            static void Main(string[] args)
            {
                TestDatainfo2();
            }
        }

    2.2 The code below lists the counterpart VARINFO2 and DATAINFO2 structures in C++ and the TestCall02() API :

    typedef struct
    {
      char label[MAXLABEL];
      char display_name[MAXDISP];
      char parent[VNAMELEN];
      int flags;
      int transform;
      int lag;
      char compact_method;
      char stack_level;
      char line_width;
    } VARINFO2;

    typedef struct
    {
      int v;       /* number of variables */
      int n;       /* number of observations */
      int pd;       /* periodicity or frequency of data */
      int structure;   /* time series, cross section or whatever */
      double sd0;     /* float representation of stobs */
      int t1, t2;     /* start and end of current sample */
      char stobs[OBSLEN]; /* string representation of starting obs (date) */
      char endobs[OBSLEN]; /* string representation of ending obs */
      SAFEARRAY* pSafeArrayOfVarName; /* SAFEARRAY of BSTRs */
      SAFEARRAY* pSafeArrayOfVarInfo;
      char markers;    /* NO_MARKERS (0), REGULAR MARKERS or DAILY_DATE_STRINGS */
      char delim;     /* default delimiter for "CSV" files */
      char decpoint;   /* character used to represent decimal point */
      SAFEARRAY* pSafeArrayOfObservationMarkers; /* SAFEARRAY of BSTRs */
      char *descrip;   /* to hold info on data sources etc. */
      char *submask;   /* subsampling mask */
      char *restriction; /* record of sub-sampling restriction */
    } DATAINFO2;

    Note that the code listing below displays the values of fields of the DATAINFO2 structure which has been re-declared as SAFEARRAYs. Also, not all fields of VARINFO2 structures contained in the pSafeArrayOfVarInfo field will be displayed.

    void __stdcall TestCall02(/*[in]*/ DATAINFO2 datainfo2)
    {
     SAFEARRAY* pSafeArrayOfVarName = datainfo2.pSafeArrayOfVarName;
     if (pSafeArrayOfVarName)
     {
      // Obtain bounds information of the SAFEARRAY.
      long lLbound = 0;
      long lUbound = 0;
      
      SafeArrayGetLBound(pSafeArrayOfVarName, 1, &lLbound);
      SafeArrayGetUBound(pSafeArrayOfVarName, 1, &lUbound);
      long lDimSize = lUbound - lLbound + 1;  
      
      // Obtain a copy of each BSTR contained in the SAFEARRAY.
      for (int i = 0; i < lDimSize; i++)
      {
       long rgIndices[1];
       BSTR bstrVarName = NULL;
              
       // SafeArrayGetElement() will return
       // a copy of the relevant element in
       // the structure. Hence it is important
       // that when "bstrVarName" is no longer required,
       // it be freed (via ::SysFreeString())
       rgIndices[0] = i;
       SafeArrayGetElement
       (
        pSafeArrayOfVarName,
        rgIndices,
        (void FAR*)&bstrVarName
       );
       
       if (bstrVarName)
       {
         printf ("datainfo2.pSafeArrayOfVarName[%d] : [%S].\r\n", i, bstrVarName);
         ::SysFreeString(bstrVarName);
         bstrVarName = NULL;
       }
      }
     }
     
     SAFEARRAY* pSafeArrayOfVarInfo = datainfo2.pSafeArrayOfVarInfo;
     
     if (pSafeArrayOfVarInfo)
     {
         // Call the SafeArrayGetVartype() to determine
         // the VARTYPE of the contained elements.
      VARTYPE vt; 
      
      // The value is VT_RECORD (i.e. 36).
      SafeArrayGetVartype(pSafeArrayOfVarInfo, &vt);
      
      // Get the IRecordInfo object associated with the structure.
      IRecordInfoPtr spIRecordInfo = NULL;  
      SafeArrayGetRecordInfo(pSafeArrayOfVarInfo, &spIRecordInfo);
      
      // Get the GUID associated with the IRecordInfo object.
      // This is the same GUID associated with the structure,
      // i.e. B613F5F7-C45B-4731-81C9-5E8BE96342F6.
      GUID guid;
      spIRecordInfo -> GetGuid(&guid);   
      
      // Obtain bounds information of the SAFEARRAY.
      long lLbound = 0;
      long lUbound = 0;
      
      SafeArrayGetLBound(pSafeArrayOfVarInfo, 1, &lLbound);
      SafeArrayGetUBound(pSafeArrayOfVarInfo, 1, &lUbound);
      long lDimSize = lUbound - lLbound + 1;  
      
      // Obtain a copy of each structure contained
      // in the SAFEARRAY.
      for (int i = 0; i < lDimSize; i++)
      {
       long  rgIndices[1];
       VARINFO2 value;
       
       // Note that we must set all fields
       // inside value to zero.
       // This is important because
       // SafeArrayGetElement() will first
       // clear all members of value.
       // For the "m_string" BSTR member,
       // it will call the ::SysFreeString()
       // API. Now if "m_string" contains
       // random data, it will result in
       // a crash. If it is NULL, it is OK.
       memset(&value, 0, sizeof(value));
             
       // SafeArrayGetElement() will return
       // a copy of the relevant element in
       // the structure. Hence it is important
       // that when "value" is no longer required,
       // its members be cleared. This is especially
       // so for members which point to allocated
       // memory (e.g. BSTRs).
       rgIndices[0] = i;
       SafeArrayGetElement
       (
        pSafeArrayOfVarInfo,
        rgIndices,
        (void FAR*)&value
       );
       
       printf ("VARINFO2[%d].label        : [%s].\r\n", i, value.label);
       printf ("VARINFO2[%d].display_name : [%s].\r\n", i, value.display_name);
       printf ("VARINFO2[%d].parent       : [%s].\r\n", i, value.parent);
            
       // We clear the members of "value"
       // which includes the "m_string" BSTR member.
       spIRecordInfo -> RecordClear((PVOID)&value);
      }
     }
     
     SAFEARRAY* pSafeArrayOfObservationMarkers = datainfo2.pSafeArrayOfObservationMarkers;
     if (pSafeArrayOfObservationMarkers)
     {
      // Obtain bounds information of the SAFEARRAY.
      long lLbound = 0;
      long lUbound = 0;
      
      SafeArrayGetLBound(pSafeArrayOfObservationMarkers, 1, &lLbound);
      SafeArrayGetUBound(pSafeArrayOfObservationMarkers, 1, &lUbound);
      long lDimSize = lUbound - lLbound + 1;  
      
      // Obtain a copy of each BSTR contained in the SAFEARRAY.
      for (int i = 0; i < lDimSize; i++)
      {
       long rgIndices[1];
       BSTR bstrS = NULL;
              
       // SafeArrayGetElement() will return
       // a copy of the relevant element in
       // the structure. Hence it is important
       // that when "bstrS" is no longer required,
       // it be freed (via ::SysFreeString())
       rgIndices[0] = i;
       SafeArrayGetElement
       (
        pSafeArrayOfObservationMarkers,
        rgIndices,
        (void FAR*)&bstrS
       );
       
       if (bstrS)
       {
         printf ("datainfo2.pSafeArrayOfObservationMarkers[%d] : [%S].\r\n", i, bstrS);
         ::SysFreeString(bstrS);
         bstrS = NULL;
       }
      }
     } 
    }

    - Bio.

     


    Please visit my blog : http://limbioliong.wordpress.com/
    • Marked as answer by Saymon Rupar Sunday, November 6, 2011 3:12 PM
    Wednesday, August 10, 2011 6:09 AM
  • You can leave the data on native memory and store their address in IntPtrs until you want to cache a copy of the data (e.g. Marshal.PtrToStringUni) in managed memory, this does not require a wrapper class. 

     



    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful, so they will appear differently to other users who are visiting your thread for the same problem.
    Visual C++ MVP
    • Marked as answer by Saymon Rupar Monday, August 15, 2011 8:54 PM
    Monday, August 15, 2011 5:23 PM
  • Yes, but you need to write code to move data from native to manage and from managed to native, either directly or wrap up with a customized marshaller for reuse, because those types are not supported by the default marshaller. 

    If the roundtrip cost is high (a few CPU cycles for native-managed transition plus the amount of time and memory to create the managed cache), you may want to move the logic of value-changing to native as well.



    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful, so they will appear differently to other users who are visiting your thread for the same problem.
    Visual C++ MVP
    • Marked as answer by Saymon Rupar Monday, August 15, 2011 8:54 PM
    Monday, August 15, 2011 8:26 PM

All replies

  • Because the pointers are likely pointing to addresses on the native heap, I don't think you can get ride of those IntPtrs short of writing a customized marshaller to copy the data off the native heap.

    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful, so they will appear differently to other users who are visiting your thread for the same problem.
    Visual C++ MVP
    Wednesday, August 10, 2011 1:37 AM
  • Hello Saymon Rupar,

     

    1. Possible Changes to the Datainfo Structure.

    The following describes the possible changes to the data types of fields of the Datainfo structure :

    1.1 The C DATAINFO.varname (type char**) can be changed to a SAFEARRAY of BSTRs, e.g. :

    SAFEARRAY* pSafeArrayOfVarName;

    The C# counterpart Datainfo.Varname (type IntPtr) can be changed into an array of strings declared to be marshaled as a SAFEARRAY of BSTRs :

    [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_BSTR)]
    public string[] Varname;

     

    1.2 The C DATAINFO.S (type char **) can be changed be similarly changed to SAFEARRAY of BSTRs, e.g. :

    SAFEARRAY* pSafeArrayOfObservationMarkers;

    The C# counterpart Datainfo.S (type IntPtr) is then changed into an array of strings declared to be marshaled as a SAFEARRAY of BSTRs :

    [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)]
    public string[] S;

     

    1.3 The C DATAINFO.descrip (type char*) can be declared as a string which is marshaled as a pointer to a NULL-terminated ANSI character array :

    [MarshalAsAttribute(UnmanagedType.LPStr)]
    public string Descrip;

    There is no problem as far as this is concerned.

     

    1.4 Finally, the C DATAINFO.varinfo (type VARINFO **)  can be changed to a SAFEARRAY of VARINFO structures :

    SAFEARRAY* pSafeArrayOfVarInfo;

    The C# counterpart Datainfo.VarInfo (type IntPtr) can be changed into an array of Varinfo structures declared to be marshaled as a SAFEARRAY of Varinfo structures :

    [MarshalAs(UnmanagedType.SafeArray, SafeArrayUserDefinedSubType = typeof(Varinfo))]
    public Varinfo[] VarInfo;

    However, note that the C# Varinfo structure must be marked with the ComVisibleAttribute and the GuidAttribute :

    [ComVisible(true)]
    [Guid("B613F5F7-C45B-4731-81C9-5E8BE96342F6")]
    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
    public struct Varinfo
    {
            [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 128)]
            public string Label;
            [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]
            public string DisplayName;
            [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]
            public string Parent;
            public int Flags;
            public int Transform;
            public int Lag;
            public byte CompactMethod;
            public byte StackLevel;
            public byte LineWidth;
    }

    These attributes will make the structure marshalable inside a SAFEARRAY as a VT_RECORD type.

     

    1.5 In all 4 examples above, the use of SAFEARRAYs ensures that the number of elements of each array be variable. The next section provides sample codes.

     

    2. Sample Codes.

    2.1 The following is a sample C# code that uses the modified Datainfo and the Varinfo structures. I have renamed them to Datainfo2 and the Varinfo2 respectively. The code creates a Datainfo2 structure and passes the structure to a test API named TestCall02() exported from a DLL named SaymonRuparDLL.dll (written in C++) :

        [ComVisible(true)]
        [Guid("B613F5F7-C45B-4731-81C9-5E8BE96342F6")]
        [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        public struct Varinfo2
        {
            [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 128)]
            public string Label;
            [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]
            public string DisplayName;
            [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]
            public string Parent;
            public int Flags;
            public int Transform;
            public int Lag;
            public byte CompactMethod;
            public byte StackLevel;
            public byte LineWidth;
        }

        [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack=1)]
        public struct Datainfo2
        {
            public int V;
            public int N;
            public int Pd;
            public int Structure;
            public double Sd0;
            public int T1;
            public int T2;
            [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]
            public string Stobs;
            [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]
            public string Endobs;
            [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_BSTR)]
            public string[] Varname;
            [MarshalAs(UnmanagedType.SafeArray, SafeArrayUserDefinedSubType = typeof(Varinfo2))]
            public Varinfo2[] VarInfo;
            public byte Markers;
            public byte Delim;
            public byte Decpoint;
            [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)]
            public string[] S;
            [MarshalAsAttribute(UnmanagedType.LPStr)]
            public string Descrip;
            [MarshalAsAttribute(UnmanagedType.LPStr)]
            public string Submask;
            [MarshalAsAttribute(UnmanagedType.LPStr)]
            public string Restriction;
        }

        class Program
        {
            [DllImport("SaymonRuparDLL.dll", CallingConvention = CallingConvention.StdCall)]
            private static extern void TestCall02([In] Datainfo2 datainfo2);

            static void TestDatainfo2()
            {
                int i = 0;
                Datainfo2 datainfo2 = new Datainfo2();

                datainfo2.V = 0;
                datainfo2.N = 0;
                datainfo2.Pd = 0;
                datainfo2.Structure = 0;
                datainfo2.Sd0 = 0;
                datainfo2.T1 = 0;
                datainfo2.T2 = 0;
                datainfo2.Stobs = "Stobs";
                datainfo2.Endobs = "Endobs";

                datainfo2.Varname = new string[5];
                for (i = 0; i < 5; i++)
                {
                    datainfo2.Varname[i] = string.Format("Varname {0:S}", i.ToString());
                }

                datainfo2.VarInfo = new Varinfo2[10];
                for (i = 0; i < 10; i++)
                {
                    datainfo2.VarInfo[i].Label = string.Format("datainfo2.VarInfo[{0:S}].Label", i.ToString());
                    datainfo2.VarInfo[i].DisplayName = string.Format("datainfo2.VarInfo[{0:S}].DisplayName", i.ToString());
                    datainfo2.VarInfo[i].Parent = string.Format("[{0:S}].Parent", i.ToString());
                }

                datainfo2.Markers = 0;
                datainfo2.Delim = 0;
                datainfo2.Decpoint = 0;

                datainfo2.S = new string[5];
                for (i = 0; i < 5; i++)
                {
                    datainfo2.S[i] = string.Format("S {0:S}", i.ToString());
                }

                datainfo2.Descrip = "datainfo2.Descrip";
                datainfo2.Submask = "datainfo2.Submask";
                datainfo2.Restriction = "datainfo2.Restriction";

                TestCall02(datainfo2);
            }

            static void Main(string[] args)
            {
                TestDatainfo2();
            }
        }

    2.2 The code below lists the counterpart VARINFO2 and DATAINFO2 structures in C++ and the TestCall02() API :

    typedef struct
    {
      char label[MAXLABEL];
      char display_name[MAXDISP];
      char parent[VNAMELEN];
      int flags;
      int transform;
      int lag;
      char compact_method;
      char stack_level;
      char line_width;
    } VARINFO2;

    typedef struct
    {
      int v;       /* number of variables */
      int n;       /* number of observations */
      int pd;       /* periodicity or frequency of data */
      int structure;   /* time series, cross section or whatever */
      double sd0;     /* float representation of stobs */
      int t1, t2;     /* start and end of current sample */
      char stobs[OBSLEN]; /* string representation of starting obs (date) */
      char endobs[OBSLEN]; /* string representation of ending obs */
      SAFEARRAY* pSafeArrayOfVarName; /* SAFEARRAY of BSTRs */
      SAFEARRAY* pSafeArrayOfVarInfo;
      char markers;    /* NO_MARKERS (0), REGULAR MARKERS or DAILY_DATE_STRINGS */
      char delim;     /* default delimiter for "CSV" files */
      char decpoint;   /* character used to represent decimal point */
      SAFEARRAY* pSafeArrayOfObservationMarkers; /* SAFEARRAY of BSTRs */
      char *descrip;   /* to hold info on data sources etc. */
      char *submask;   /* subsampling mask */
      char *restriction; /* record of sub-sampling restriction */
    } DATAINFO2;

    Note that the code listing below displays the values of fields of the DATAINFO2 structure which has been re-declared as SAFEARRAYs. Also, not all fields of VARINFO2 structures contained in the pSafeArrayOfVarInfo field will be displayed.

    void __stdcall TestCall02(/*[in]*/ DATAINFO2 datainfo2)
    {
     SAFEARRAY* pSafeArrayOfVarName = datainfo2.pSafeArrayOfVarName;
     if (pSafeArrayOfVarName)
     {
      // Obtain bounds information of the SAFEARRAY.
      long lLbound = 0;
      long lUbound = 0;
      
      SafeArrayGetLBound(pSafeArrayOfVarName, 1, &lLbound);
      SafeArrayGetUBound(pSafeArrayOfVarName, 1, &lUbound);
      long lDimSize = lUbound - lLbound + 1;  
      
      // Obtain a copy of each BSTR contained in the SAFEARRAY.
      for (int i = 0; i < lDimSize; i++)
      {
       long rgIndices[1];
       BSTR bstrVarName = NULL;
              
       // SafeArrayGetElement() will return
       // a copy of the relevant element in
       // the structure. Hence it is important
       // that when "bstrVarName" is no longer required,
       // it be freed (via ::SysFreeString())
       rgIndices[0] = i;
       SafeArrayGetElement
       (
        pSafeArrayOfVarName,
        rgIndices,
        (void FAR*)&bstrVarName
       );
       
       if (bstrVarName)
       {
         printf ("datainfo2.pSafeArrayOfVarName[%d] : [%S].\r\n", i, bstrVarName);
         ::SysFreeString(bstrVarName);
         bstrVarName = NULL;
       }
      }
     }
     
     SAFEARRAY* pSafeArrayOfVarInfo = datainfo2.pSafeArrayOfVarInfo;
     
     if (pSafeArrayOfVarInfo)
     {
         // Call the SafeArrayGetVartype() to determine
         // the VARTYPE of the contained elements.
      VARTYPE vt; 
      
      // The value is VT_RECORD (i.e. 36).
      SafeArrayGetVartype(pSafeArrayOfVarInfo, &vt);
      
      // Get the IRecordInfo object associated with the structure.
      IRecordInfoPtr spIRecordInfo = NULL;  
      SafeArrayGetRecordInfo(pSafeArrayOfVarInfo, &spIRecordInfo);
      
      // Get the GUID associated with the IRecordInfo object.
      // This is the same GUID associated with the structure,
      // i.e. B613F5F7-C45B-4731-81C9-5E8BE96342F6.
      GUID guid;
      spIRecordInfo -> GetGuid(&guid);   
      
      // Obtain bounds information of the SAFEARRAY.
      long lLbound = 0;
      long lUbound = 0;
      
      SafeArrayGetLBound(pSafeArrayOfVarInfo, 1, &lLbound);
      SafeArrayGetUBound(pSafeArrayOfVarInfo, 1, &lUbound);
      long lDimSize = lUbound - lLbound + 1;  
      
      // Obtain a copy of each structure contained
      // in the SAFEARRAY.
      for (int i = 0; i < lDimSize; i++)
      {
       long  rgIndices[1];
       VARINFO2 value;
       
       // Note that we must set all fields
       // inside value to zero.
       // This is important because
       // SafeArrayGetElement() will first
       // clear all members of value.
       // For the "m_string" BSTR member,
       // it will call the ::SysFreeString()
       // API. Now if "m_string" contains
       // random data, it will result in
       // a crash. If it is NULL, it is OK.
       memset(&value, 0, sizeof(value));
             
       // SafeArrayGetElement() will return
       // a copy of the relevant element in
       // the structure. Hence it is important
       // that when "value" is no longer required,
       // its members be cleared. This is especially
       // so for members which point to allocated
       // memory (e.g. BSTRs).
       rgIndices[0] = i;
       SafeArrayGetElement
       (
        pSafeArrayOfVarInfo,
        rgIndices,
        (void FAR*)&value
       );
       
       printf ("VARINFO2[%d].label        : [%s].\r\n", i, value.label);
       printf ("VARINFO2[%d].display_name : [%s].\r\n", i, value.display_name);
       printf ("VARINFO2[%d].parent       : [%s].\r\n", i, value.parent);
            
       // We clear the members of "value"
       // which includes the "m_string" BSTR member.
       spIRecordInfo -> RecordClear((PVOID)&value);
      }
     }
     
     SAFEARRAY* pSafeArrayOfObservationMarkers = datainfo2.pSafeArrayOfObservationMarkers;
     if (pSafeArrayOfObservationMarkers)
     {
      // Obtain bounds information of the SAFEARRAY.
      long lLbound = 0;
      long lUbound = 0;
      
      SafeArrayGetLBound(pSafeArrayOfObservationMarkers, 1, &lLbound);
      SafeArrayGetUBound(pSafeArrayOfObservationMarkers, 1, &lUbound);
      long lDimSize = lUbound - lLbound + 1;  
      
      // Obtain a copy of each BSTR contained in the SAFEARRAY.
      for (int i = 0; i < lDimSize; i++)
      {
       long rgIndices[1];
       BSTR bstrS = NULL;
              
       // SafeArrayGetElement() will return
       // a copy of the relevant element in
       // the structure. Hence it is important
       // that when "bstrS" is no longer required,
       // it be freed (via ::SysFreeString())
       rgIndices[0] = i;
       SafeArrayGetElement
       (
        pSafeArrayOfObservationMarkers,
        rgIndices,
        (void FAR*)&bstrS
       );
       
       if (bstrS)
       {
         printf ("datainfo2.pSafeArrayOfObservationMarkers[%d] : [%S].\r\n", i, bstrS);
         ::SysFreeString(bstrS);
         bstrS = NULL;
       }
      }
     } 
    }

    - Bio.

     


    Please visit my blog : http://limbioliong.wordpress.com/
    • Marked as answer by Saymon Rupar Sunday, November 6, 2011 3:12 PM
    Wednesday, August 10, 2011 6:09 AM
  • Hey Sheng Jiang and Lim Bio Liong.

    First I would like to thank you for your fast and detailed answers. Second I would like to apologize to for my slow replay, I was very busy lately. 

     

    Sheng Jiang: That is what I exactly what I  would needed. I was hoping for positive answer.  

    Lim Bio Liong: Thank you for your detailed solution. But I cann't change customAPI. 

    So to summarize. Only solution is to make another c++ wrapper (my customAPI is in C), or there is some other solution?  

     

     

     

    Monday, August 15, 2011 1:04 PM
  • You can leave the data on native memory and store their address in IntPtrs until you want to cache a copy of the data (e.g. Marshal.PtrToStringUni) in managed memory, this does not require a wrapper class. 

     



    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful, so they will appear differently to other users who are visiting your thread for the same problem.
    Visual C++ MVP
    • Marked as answer by Saymon Rupar Monday, August 15, 2011 8:54 PM
    Monday, August 15, 2011 5:23 PM
  • So basicly, I have to create new managed stucture, and then Marshal all those Intptrs to managed types. Is this correct?


    Like this?

    [...]
    public struct A
    {
      // array of strings
      IntPtr s;
      // array of ints
      IntPtr i;
    }
    
    
    //"managed" struct
    public struct newA
    {
      string[] s;
      int [] i;
    }
    


    Because I would like to read from native memory, change values of structure, and then write back to native memory.  

     

    Monday, August 15, 2011 8:17 PM
  • Yes, but you need to write code to move data from native to manage and from managed to native, either directly or wrap up with a customized marshaller for reuse, because those types are not supported by the default marshaller. 

    If the roundtrip cost is high (a few CPU cycles for native-managed transition plus the amount of time and memory to create the managed cache), you may want to move the logic of value-changing to native as well.



    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful, so they will appear differently to other users who are visiting your thread for the same problem.
    Visual C++ MVP
    • Marked as answer by Saymon Rupar Monday, August 15, 2011 8:54 PM
    Monday, August 15, 2011 8:26 PM
  • Thank you very much. I'll do that. 
    Monday, August 15, 2011 8:54 PM