none
using C DLL in C# code : error - Cannot marshal 'parameter #2': Internal limitation: structure is too complex or too large RRS feed

  • Question

  • I have to use a dll written in C in a C# VS project.

    I have some difficulties to translate and use the structures declared in C.

    structure of the trend list :
    ===============================

    C structure
    -------------

     typedef struct
     {
        WV_PARAMETER_ID    WvParameterID [WV_MAX_TREND_PARAMETERS];                  ;
        TCHAR              Label [WV_MAX_TREND_PARAMETERS] [WV_LABEL_SIZE] ;
        int                NumberOfTrends;
     } WV_TREND_LIST ;

    WV_MAX_TREND_PARAMETERS = 60
    WV_LABEL_SIZE = 20

     

    The problem I have is that there is only one returned value for the "NumberOfTrends" but 60 for the others. Then I found that I need to create the structures as follow.

    This is working.


    C# Structure
    -------------

            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
            public struct WV_TREND_LIST
            {
                public WV_TREND_LIST_Items WV_TrendItem;
                public int NumberOfTrends;
            }

            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
            public struct WV_TREND_LIST_Items
            {
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 60, ArraySubType = System.Runtime.InteropServices.UnmanagedType.I4)]
                public int[] WV_Trend_ParameterID;
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 60, ArraySubType = System.Runtime.InteropServices.UnmanagedType.ByValTStr)]
                public WV_ParameterLabel[] labelparameter;
            }

           [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
            public struct WV_ParameterLabel
            {
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
                public string ParamLabel;
            }

     

    I have a to declare another set of structures that are about the same. The difference is that there is a lot of more data to be returned - too much I think. I got the following error when I call the procedure.

    {"Cannot marshal 'parameter #2': Internal limitation: structure is too complex or too large."}

    Structure for the trend values
    ===============================

    C structure
    -------------

     typedef struct
     {
        WV_PARAMETER_ID          WvParameterID                  ; // parameter id
        TCHAR                    Label          [WV_LABEL_SIZE] ; // label in english
        WV_NET_UNITS_OF_MEASURE  Units                          ;
        TCHAR                    Values         [WV_MAX_TREND_SAMPLES][WV_VALUE_SIZE];
     } WV_TREND_PARAMETER_DATA;

    WV_MAX_TREND_SAMPLES = 1501
    WV_LABEL_SIZE = 20
    WV_VALUE_SIZE = 20


    C# Structure
    -------------

           [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
           public struct WV_TREND_DATA
           {
               [MarshalAs(UnmanagedType.ByValArray, SizeConst = 60, ArraySubType = System.Runtime.InteropServices.UnmanagedType.Struct)]
               public WV_TREND_PARAMETER_DATA[] WV_TrendDataItem;
               //public Tren_DATA_Item_Class[] WV_TrendDataItem;
           }
           [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
            public struct WV_TREND_PARAMETER_DATA
            {
                [MarshalAs(UnmanagedType.I4)]
                public int Trend_Parameter_ID;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
                public string Trend_Label;
                [MarshalAs(UnmanagedType.I4)]
                public int Trend_Units;
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1501, ArraySubType = System.Runtime.InteropServices.UnmanagedType.ByValTStr)]
                public WV_TrendValue[] Trend_Values;
            }

            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
            public struct WV_TrendValue
            {
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
                public string TrendValue;
            }


    DLL Declarations

    [DllImport("WVAPI.dll", EntryPoint = "WvGetTrendData", SetLastError = true,

    CharSet = CharSet.Ansi, ExactSpelling = true,

    CallingConvention = CallingConvention.StdCall)]

    [DllImport("WVAPI.dll", EntryPoint = "WvGetTrendData", SetLastError = true,

    CharSet = CharSet.Ansi, ExactSpelling = true,

    CallingConvention = CallingConvention.StdCall)]

    public static extern int WvGetTrendData(int connectionID, ref structures.WV_TREND_DATA pTrendDatas, int numHours, structures.WV_TREND_LIST pTrends);

    [DllImport("WVAPI.dll", EntryPoint = "WVGetAvailableTrends", SetLastError = true,

    CharSet = CharSet.Ansi, ExactSpelling = true,

    CallingConvention = CallingConvention.StdCall)]

    public static extern int WVGetAvailableTrends(int pConnectID, ref structures.WV_TREND_LIST ptrendlist);

    Variables declarations

    structures.WV_TREND_LIST TrendList = new structures.WV_TREND_LIST();

    structures.WV_TREND_DATA DataOfTheTrends = new structures.WV_TREND_DATA();

    procedures

    int returnTrendList = NATIVE_WV.WVGetAvailableTrends(connectionID, ref TrendList);

    int returnTrendData = NATIVE_WV.WvGetTrendData(connectionID, ref DataOfTheTrends, totalhours, TrendList);

    I have found on the internet that the solution of the error can be to use class instead of structures.

    I must say that I have some difficulties to figure out how to fill a class with dynamic arrays. I can understand the mechanism with the structures  : read sequentially the memory marshalling the values etc ... but for the class ?

    Is there somebody that can help ?

    best regards

     

    • Moved by lucy-liu Friday, May 20, 2011 8:33 AM it is not related c# (From:Visual C# Language)
    Tuesday, May 17, 2011 10:29 PM

All replies

  • Hello alaindoe, you can use StructLayoutAttribute with classes too. It's not exclusive for structs.

    Hope this helps,

    Miguel.


    Tuesday, May 17, 2011 11:38 PM
  • Like this ?

     

    class

    Trend_DATA_Class

    {

    [

    StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]

    public Tren_DATA_Item_Class[] _Trend_Data;

     

    //Property declared

     

    public Tren_DATA_Item_Class

    [] Trend_Data

    {

     

    get

    {

     

    return

    _Trend_Data;

    }

     

    set

    {

    _Trend_Data =

    value

    ;

    }

    }

    }

    But where can I declare the size of the aray ?

    like this ?

     

    class Trend_DATA_Class

    {

    [

     

    StructLayout(LayoutKind.Sequential, CharSet = CharSet

    .Ansi, Pack = 1)]

     

     

    public Tren_DATA_Item_Class[] _Trend_Data

    = new Tren_DATA_Item_Class[60];

     

     

    //Property declared

     

     

    public Tren_DATA_Item_Class

    [] Trend_Data

    {

     

     

    get

    {

     

     

    return

    _Trend_Data;

    }

     

     

    set

    {

    _Trend_Data =

     

    value

    ;

    }

    }

    }

    Thanks for help.

    

    • Marked as answer by Paul Zhou Tuesday, May 24, 2011 6:43 AM
    • Unmarked as answer by Paul Zhou Tuesday, May 24, 2011 6:43 AM
    Wednesday, May 18, 2011 6:47 PM
  • of course like this

    [

    StructLayout(LayoutKind.Sequential, CharSet = CharSet

    .Ansi, Pack = 1)]

     

    class

    Trend_DATA_Class

    .....
    Wednesday, May 18, 2011 9:39 PM
  • Hi alaindoe,

    Have you solved this issue?

    If you have solved it, please mark the useful reply as answer.

    If not, please let us know, we will try our best to help solve this issue.

     

    Best regards,

    Lucy


    Lucy Liu [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Thursday, May 19, 2011 9:25 AM
  • Lucy,

    I did not find a working solution until now.

    I still need advice.

    Best regards

    Alaindoe

     

    Friday, May 20, 2011 1:18 AM
  • Hi alaindoe,

    I am moving this thread from ¡°Visual C# Language " forum to the “Common Language Runtime” forum.

    since the issue is related to CLR. There are more experts in the “Common Language Runtime” forum.

    Thank you for your understanding!

     

    Best regards,

    Lucy


    Lucy Liu [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Friday, May 20, 2011 8:32 AM
  •  

    Hi Alaindoe,

     

    There are some solutions provided by the link and the link and the thread.

    The questions are similar to yours. I hope solutions in those links can help you out.

    Any more concerns, please feel free to let me know.

     

    Sincerely,


    Paul Zhou [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Saturday, May 21, 2011 4:01 AM
  • Hello alaindoe,

     

    1. Problem Recreation and Some Observations.

    1.1 I managed to re-create the error you encountered.

    1.2 I made some noteworthy observations :

    1.2.1 The WV_TREND_DATA struct (with its associated WV_TREND_PARAMETER_DATA and WV_TrendValue structs), though complicated, can be handled well by the internal interop marshaler.

    1.2.2 The bottleneck seems to be the size of the WV_TREND_PARAMETER_DATA.Trend_Values field which is an array of WV_TrendValue structs.

    1.2.3 I noticed that if the size of this array is reduced from 1501 to 50, the struct goes through successfully to the unmanaged function. Anything beyond around 50 will cause the "Internal limitation: structure is too complex or too large..." error message.

     

    2. How the Struct Data is Marshaled to the Unmanaged API.

    2.1 The WV_TREND_DATA struct internally holds an array of WV_TREND_PARAMETER_DATA structs which itself internally holds an array of WV_TrendValue structs. The WV_TrendValue struct itself contains a string member (TrendValue).

    2.2 These all indicate that the WV_TREND_DATA struct is not blittable. This means that at runtime, the interop marshaler must internally allocate a memory buffer that will contain a complete copy of the contents of the WV_TREND_DATA struct DataOfTheTrends.

    2.3 Instructions for performing the copying operation are given by the various MarshalAsAttributes declared for the structs.

    2.4 It is this memory buffer that gets passed to the unmanaged API.

    2.5 Now, if data is to be passed 2 ways to and from the unmanaged API, that is, if it is possible that the unmanaged API make permanent changes to field values, then when the API returns, the interop marshaler must copy changed values back to the original DataOfTheTrends struct.

    2.6 By far the interop marshaler is able to correctly perform the copying process until the array size of the WV_TREND_PARAMETER_DATA.Trend_Values field is set above some upper limit.

    2.7 I personally am not clear why there is such a limit on the array size of the WV_TREND_PARAMETER_DATA.Trend_Values field. I think the number 50 is specific to my machine. It may be a higher or lower number on your machine.

     

    3. Possible Solution : Custom Marshaling.

    3.1 I then attempted to create and use a simple Custom Marshaler to perform the marshaling of the WV_TREND_DATA struct to and from the WvGetTrendData() API.

    3.2 The Custom Marshaler was successfully able to marshal the struct to and from the unmanaged API even with the size of the WV_TREND_PARAMETER_DATA.Trend_Values array set to 1501.

    3.3 The full source codes of the Custom Marshaler is listed below :

        class StructMarshaler : ICustomMarshaler
        {
            public object MarshalNativeToManaged(IntPtr pNativeData)
            {
                if (pNativeData == IntPtr.Zero)
                {
                    return null;
                }

                return (structures.WV_TREND_DATA)Marshal.PtrToStructure(pNativeData, typeof(structures.WV_TREND_DATA));
            }

            public IntPtr MarshalManagedToNative(object ManagedObj)
            {
                if (ManagedObj is structures.WV_TREND_DATA)
                {
                    structures.WV_TREND_DATA DataOfTheTrends = (structures.WV_TREND_DATA)ManagedObj;

                    int iTotalRequiredSize = Marshal.SizeOf(DataOfTheTrends);
                    IntPtr pDataOfTheTrends = Marshal.AllocHGlobal(iTotalRequiredSize);

                    if (pDataOfTheTrends != null)
                    {
                        Marshal.StructureToPtr(DataOfTheTrends, pDataOfTheTrends, false);
                        return pDataOfTheTrends;
                    }
                }
                return IntPtr.Zero;
            }

            public void CleanUpNativeData(IntPtr pNativeData)
            {
                Marshal.FreeHGlobal(pNativeData);
            }

            public void CleanUpManagedData(object ManagedObj)
            {
            }

            public int GetNativeDataSize()
            {
                return -1;
            }

            public static ICustomMarshaler GetInstance(string cookie)
            {
                // Always return the same instance
                if (marshaler == null)
                {
                    marshaler = new StructMarshaler();
                }

                return marshaler;
            }

            static StructMarshaler marshaler;
        }

    3.4 Note that to use the Custom Marshaler, we need to make several changes :

    3.4.1 Changes to the declaration of the WvGetTrendData() API :

    [DllImport(@"WVAPI.dll", EntryPoint = "WvGetTrendData", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]

    public static extern int WvGetTrendData(int connectionID, [In][Out] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StructMarshaler))] ref object pTrendDatas, int numHours, structures.WV_TREND_LIST pTrends);

    The use of the StructMarshaler must be declared via a MarshalAsAttribute() as shown above. Also, instead of being declared as of type "structures.WV_TREND_DATA" passed by reference, it must be declared as of type "object" passed by reference.

    Some other noteworthy points : the second parameter has been marked as [In][Out]. This indicates that the parameter is to be marshaled 2 ways : into the API and out of the API. This ensures that the custom marshaler's MarshalManagedToNative() method will be called on entry to WvGetTrendData() and that MarshalNativeToManaged() will be called when WvGetTrendData() returns.

    3.4.2 Changes to the way the WvGetTrendData() is called :

      object objDataOfTheTrends = (object)DataOfTheTrends;
     
      int returnTrendData = WvGetTrendData(connectionID, ref objDataOfTheTrends, totalhours, TrendList2);

      DataOfTheTrends = (structures.WV_TREND_DATA)objDataOfTheTrends;

     

    We must declare a variable of type object (e.g. "objDataOfTheTrends"), make it reference the "DataOfTheTrends" struct and then pass this object to the WvGetTrendData() API. When the API returns, we must make "DataOfTheTrends" reference "objDataOfTheTrends" in order that changes to "objDataOfTheTrends" be copied to "DataOfTheTrends".

     

    3.5 I mentioned in point 3.1 that the Custom Marshaler is a simple one. This is because at heart, it uses only the following Marshal class methods Marshal.PtrToStructure(), Marshal.AllocHGlobal(), Marshal.StructureToPtr(), Marshal.FreeHGlobal().

    3.5.1 These Marshal class methods are likely the very same ones used by the interop marshaler.

    3.5.2 I did not have to device any sophisticated algorithms to copy the data from the original WV_TREND_DATA struct to the unmanaged memory block (allocated by Marshal.AllocHGlobal(). I simply used Marshal.StructureToPtr().

    3.5.3 When data is returned from the WvGetTrendData() API, I simply used Marshal.PtrToStructure().

     

    4. Requirements For 2nd Parameter of WvGetTrendData() API.

    4.1 Note that the following is how I have declared the WvGetTrendData() API in C++ :

    int __stdcall WvGetTrendData(int connectionID, WV_TREND_DATA** pTrendDatas, int numHours, WV_TREND_LIST pTrends);

    Notice that I have made the 2nd parameter "pTrendDatas" a double pointer to WV_TREND_DATA. This is required because the equivalent "pTrendDatas" parameter for the WvGetTrendData() declaration in C# is a ref parameter :

    public static extern int WvGetTrendData(int connectionID, [In][Out] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StructMarshaler))] ref object pTrendDatas, int numHours, structures.WV_TREND_LIST pTrends);

     

    5. In Summary.

    5.1 Once again, it is not clear why a limit on the array size of the WV_TREND_PARAMETER_DATA.Trend_Values field is imposed.

    5.2 It may be that the interop marshaler imposes an overal limit on the size of data to be marshaled (the number obtained from a call to Marshal.SizeOf()) or that there are some extremities (be it related to data size or complexity) that the interop marshaler would not commit to.

    5.3 I emphasize that the StructMarshaler custom marshaler is a very simple one which probably operates in much the same way as the standard interop marshaler. The key to its success lies in the various MarshalAsAttributes applied to the WV_TREND_DATA struct and its fields.

    5.4 Please give the StructMarshaler custom marshaler a try and see if it will help resolve your problem.

    5.5 Best of luck, alaindoe.

     

    - Bio.

     

    • Proposed as answer by Paul Zhou Tuesday, May 24, 2011 6:45 AM
    Sunday, May 22, 2011 8:54 AM
  • Your example is too complex to read and understand. I am missing C++ and C# equivalents (each method/function/structure in both languages, please) and which line generates the error. Be more precise.

    My solution would be to use unsafe/fixed context and unmanaged memory - do it yourself:

    unsafe struct WV_TREND_LIST {
    	public fixed int WvParameterID[60];
    	public fixed char_or_byte Label[60*20];
    	public int NumberOfTrends;
    }
    
    

    TCHAR is CHAR or WCHAR?
    WV_PARAMETER_ID is int?

    Monday, May 23, 2011 12:00 PM
  • Hello,

    Thank you for your help.

    I will try your solution (after reading more than once just to understand everything).

    I will come back to you soon with the solution or more questions.

    Best regards

    Alaindoe

    Wednesday, May 25, 2011 8:52 PM
  • I will try the solution of Bio first.

    anyway thank you for your answer

     

    Alaindoe

    Wednesday, May 25, 2011 8:54 PM
  • After some more testing and corrections I have to come back in the post while it doesn't work.

    1. I made some corrections in the structures

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]

    public struct WV_TREND_LIST_Items_DATA

    {

    [MarshalAs(UnmanagedType.I4)]

    public int WV_Trend_ParameterID_DATA;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]

    public String ParamLabelString_DATA;

    }

    

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]

    public struct WV_TREND_PARAMETER_DATA

    {

    [MarshalAs(UnmanagedType.I4)]

    public int Trend_Parameter_ID;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]

    public string Trend_Label;

    [MarshalAs(UnmanagedType.I4)]

    public int Trend_Units;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1501, ArraySubType = System.Runtime.InteropServices.UnmanagedType.Struct)]

    public WV_TrendValue_struct[] TrendValue_struct;

    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]

    public struct WV_TrendValue_struct

    {

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]

    public string TrendValue;

    }

    The original C structure is :

    

    typedef struct

    {

    WV_TREND_PARAMETER_DATA Trend [WV_MAX_TREND_PARAMETERS]; 60

    int NumberOfTrends;

    int NumSamples;

    time_t SampleTimes[WV_MAX_TREND_SAMPLES]; 1501

    time_t TimeChanges[WV_MAX_TIME_CHANGES]; 24

    int NumTimeChanges;

    } WV_TREND_DATA;

    typedef struct

    {

    WV_PARAMETER_ID WvParameterID ; // parameter id

    TCHAR Label [WV_LABEL_SIZE] ; // label in english

    WV_NET_UNITS_OF_MEASURE Units ;

    TCHAR Values [WV_MAX_TREND_SAMPLES][WV_VALUE_SIZE];

    } WV_TREND_PARAMETER_DATA;


    ==> still the same error

    2. I tryed the solution of the marshaller
    3. I tryed to allocate memory outside of the stack

    for these two trying I receive the same error message when calling the dll
    "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

    I wonder if the structure is well defined !
    The size of the structure is 1815092 bytes

    Any help will be appreciated

    Thanks

    Friday, June 17, 2011 1:03 AM
  • Hello alaindoe,

     

    1. Not sure if this is the cause of the problem. Are you still using the WvGetTrendData() and WVGetAvailableTrends() APIs ?

     

    2. If so, note that for a C# parameter which is passed by ref, the C++ counterpart parameter must be declared as double pointers.

     

    3. For an example, please read point 4 of my post dated Sunday, May 22, 2011 8:54 AM. Here is a re-print :

     

    4. Requirements For 2nd Parameter of WvGetTrendData() API.
     
    4.1 Note that the following is how I have declared the WvGetTrendData() API in C++ :
     
    int __stdcall WvGetTrendData(int connectionID, WV_TREND_DATA** pTrendDatas, int numHours, WV_TREND_LIST pTrends);
     
    Notice that I have made the 2nd parameter "pTrendDatas" a double pointer to WV_TREND_DATA. This is required because the equivalent "pTrendDatas" parameter for the WvGetTrendData() declaration in C# is a ref parameter :
     
    public static extern int WvGetTrendData(int connectionID, [In][Out] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StructMarshaler))] ref object pTrendDatas, int numHours, structures.WV_TREND_LIST pTrends);

     

    5. >> The size of the structure is 1815092 bytes...

    This is not surprising. Observe the WV_TREND_DATA struct that you have defined in your OP :

            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
            public struct WV_TREND_DATA
            {
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 60, ArraySubType = System.Runtime.InteropServices.UnmanagedType.Struct)]
                public WV_TREND_PARAMETER_DATA[] WV_TrendDataItem;
            }

            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
            public struct WV_TREND_PARAMETER_DATA
            {
                [MarshalAs(UnmanagedType.I4)]
                public int Trend_Parameter_ID;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
                public string Trend_Label;
                [MarshalAs(UnmanagedType.I4)]
                public int Trend_Units;
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1501, ArraySubType = System.Runtime.InteropServices.UnmanagedType.ByValTStr)]
                public WV_TrendValue[] Trend_Values;
            }

            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
            public struct WV_TrendValue
            {
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
                public string TrendValue;
            }

     

    Note that for marshaling purposes, a struct like WV_TREND_DATA must be serialized into a flat block of memory which contains all the flat serialized data of all the array items of the "WV_TrendDataItem" array. Each WV_TrendDataItem struct item contains fields and even contains a WV_TrendValue array. These must all be serialized into flat data. All these can end up very large indeed.

     

    Hope this helps,

    - Bio.

     

    Friday, June 17, 2011 11:52 AM
  • Thanks for the reply,

    how I have declared the WvGetTrendData() API in C++ :

    Do you mean changing the source code of the API in C program ? I don't have the source code. The fact is that I can't change the C declaration of the function. I receive these API from the factory and have to use it like it Is.

    I still try to use the same API's.

    GetAvailableTrends is working fine. I then put the results in a class. I can do it because I have seen that I have to pass the 4th parameter as value and not as ref and then I can use a class for the last parameter. The 4th parameter can also be null (null = all the trends). I am trying with the last parameters = null, this way I am sure that the input is ok. I will test the 4th parameter later on.

    Do you think the structures are OK regarding their definition in C ?

    Best regards

     

    Friday, June 17, 2011 9:16 PM
  • Hello alaindoe,

     

    1. Concerning the Signature of WvGetTrendData().

    >> I don't have the source code. The fact is that I can't change the C declaration of the function. I receive these API from the factory and have to use it like it Is...

    1.1 What is the C/C++ signature of the WvGetTrendData() API ? It would be good if you can list it out for us.

     

    2. Regarding the Structure Definitions in C/C++.

    >> Do you think the structures are OK regarding their definition in C ?

    2.1 I think they are OK. I did manage to re-create the problem that you reported in the OP.

    2.2 I will list below the C/C++ DLL source codes that I used to re-create the error that occirred when the WvGetTrendData() API is called.

    2.3 I suggest that you study it, see the solution in action and then discuss with the engineers at the factory.

     

    3. C/C++ DLL Source Codes.

    typedef int WV_PARAMETER_ID;
    typedef int WV_NET_UNITS_OF_MEASURE;
    const int WV_MAX_TREND_PARAMETERS = 60;
    const int WV_LABEL_SIZE = 20;
    const int WV_MAX_TREND_SAMPLES = 1501;
    const int WV_VALUE_SIZE = 20;


    #pragma pack (1)
    typedef struct
    {
      WV_PARAMETER_ID    WvParameterID [WV_MAX_TREND_PARAMETERS];
      TCHAR              Label[WV_MAX_TREND_PARAMETERS][WV_LABEL_SIZE];
      int                NumberOfTrends;
    } WV_TREND_LIST;


    #pragma pack (1)
    typedef struct
    {
      WV_PARAMETER_ID          WvParameterID;
      TCHAR                    Label[WV_LABEL_SIZE];
      WV_NET_UNITS_OF_MEASURE  Units;
      TCHAR                    Values[WV_MAX_TREND_SAMPLES][WV_VALUE_SIZE];
    } WV_TREND_PARAMETER_DATA;


    #pragma pack (1)
    typedef struct
    {
      WV_TREND_PARAMETER_DATA WV_TrendDataItem[60];
      //public Tren_DATA_Item_Class[] WV_TrendDataItem;
    } WV_TREND_DATA;


    int __stdcall WvGetTrendData(int connectionID, WV_TREND_DATA** pTrendDatas, int numHours, WV_TREND_LIST pTrends)
    {
     // This API will reverse the string data. This is to show change effects.
     for (int i = 0; i < 60; i++)
     {
      strcpy(((*pTrendDatas) -> WV_TrendDataItem)[i].Label, "987654321JIHGFEDCBA");
      
      for (int j = 0; j < WV_MAX_TREND_SAMPLES; j++)
      {
        strcpy(((*pTrendDatas) -> WV_TrendDataItem)[i].Values[j], "987654321JIHGFEDCBA");
      }
     }
     
     return 0;
    }

     

    4. C# Source Codes.

        class structures
        {
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
            public struct WV_TREND_LIST_BIO
            {
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 60)]
                public int[] WV_Trend_ParameterID;
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 60)]
                public WV_ParameterLabel[] labelparameter;
                public int NumberOfTrends;
            }

            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
            public struct WV_TREND_LIST
            {
                public WV_TREND_LIST_Items WV_TrendItem;
                public int NumberOfTrends;
            }

            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
            public struct WV_TREND_LIST_Items
            {
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 60, ArraySubType = System.Runtime.InteropServices.UnmanagedType.I4)]
                public int[] WV_Trend_ParameterID;
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 60, ArraySubType = System.Runtime.InteropServices.UnmanagedType.ByValTStr)]
                public WV_ParameterLabel[] labelparameter;
            }

            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
            public struct WV_ParameterLabel
            {
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
                public string ParamLabel;
            }

            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
            public struct WV_TREND_DATA
            {
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 60, ArraySubType = System.Runtime.InteropServices.UnmanagedType.Struct)]
                public WV_TREND_PARAMETER_DATA[] WV_TrendDataItem;
                //public Tren_DATA_Item_Class[] WV_TrendDataItem;
            }
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
            public struct WV_TREND_PARAMETER_DATA
            {
                [MarshalAs(UnmanagedType.I4)]
                public int Trend_Parameter_ID;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
                public string Trend_Label;
                [MarshalAs(UnmanagedType.I4)]
                public int Trend_Units;
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1501, ArraySubType = System.Runtime.InteropServices.UnmanagedType.ByValTStr)]
                public WV_TrendValue[] Trend_Values;
            }

            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
            public struct WV_TrendValue
            {
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
                public string TrendValue;
            }
        }

        class StructMarshaler : ICustomMarshaler
        {
            public object MarshalNativeToManaged(IntPtr pNativeData)
            {
                if (pNativeData == IntPtr.Zero)
                {
                    return null;
                }

                return (structures.WV_TREND_DATA)Marshal.PtrToStructure(pNativeData, typeof(structures.WV_TREND_DATA));
            }

            public IntPtr MarshalManagedToNative(object ManagedObj)
            {
                if (ManagedObj is structures.WV_TREND_DATA)
                {
                    structures.WV_TREND_DATA DataOfTheTrends = (structures.WV_TREND_DATA)ManagedObj;

                    int iTotalRequiredSize = Marshal.SizeOf(DataOfTheTrends);
                    IntPtr pDataOfTheTrends = Marshal.AllocHGlobal(iTotalRequiredSize);

                    if (pDataOfTheTrends != null)
                    {
                        Marshal.StructureToPtr(DataOfTheTrends, pDataOfTheTrends, false);
                        return pDataOfTheTrends;
                    }
                }
                return IntPtr.Zero;
            }

            public void CleanUpNativeData(IntPtr pNativeData)
            {
                // There are 2 situations in which this method
                // will be called :
                // In response to MarshalManagedToNative() and
                // in response to MarshalNativeToManaged().
                //
                // For the first situation, data is being passed
                // from managed code to unmanaged code via an
                // "in" parameter.
                //
                // If unmanaged memory has been allocated for
                // the "in" parameter, then, the managed code
                // is the owner of this memory and after the call
                // to the unmanaged function, CleanUpNativeData()
                // will be called to clean up this unmanaged
                // memory.
                //
                //
                // For the second situation, data is passed from
                // unmanaged memory to managed memory via a "ref"
                // parameter or an "out" parameter. The managed
                // code therefore owns this unmanaged memory.
                //
                // CleanUpNativeData() is called to clean up the IntPtr
                // which was passed into the MarshalNativeToManaged()
                // method.
                //
                // For example, if native data had been allocated
                // in unmanaged code using GlobalAlloc()
                // and then passed to managed code via a "ref"
                // or an "out" parameter, then the managed code
                // owns the memory.
                //
                // If this memory has been used and will need to
                // be freed, CleanUpNativeData() is where this can
                // be done.
                //
                // We use Marshal.FreeHGlobal() to free the incoming IntPtr.
                Marshal.FreeHGlobal(pNativeData);
            }

            public void CleanUpManagedData(object ManagedObj)
            {
                // CleanUpManagedData() is called when data is
                // passed from managed code to unmanaged code
                // via a "ref" parameter.
                //
                // In this case, when the unmanaged function
                // has been called, an entirely new object may
                // be returned and so the original object which
                // was passed to the function may be completely
                // replaced by another and so it may need cleaning
                // up.
                //
                // If so CleanUpManagedData() is where this can
                // be done.
                //
                // However, because all .NET objects are subject
                // to garbage collection, typically nothing needs
                // to be done here.
                long l = 0;
                l++;
            }

            public int GetNativeDataSize()
            {
                //UnmanagedFramePosStruct unmanagedFramePosStruct_temp = new UnmanagedFramePosStruct();
                //return Marshal.SizeOf(unmanagedFramePosStruct_temp);
                return -1;
            }

            public static ICustomMarshaler GetInstance(string cookie)
            {
                // Always return the same instance
                if (marshaler == null)
                {
                    marshaler = new StructMarshaler();
                }

                return marshaler;
            }

            static StructMarshaler marshaler;
        }

        class Program
        {
            [DllImport("alaindoe_dll.DLL", EntryPoint = "WvGetTrendData", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
            public static extern int WvGetTrendData(int connectionID, [In][Out] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StructMarshaler))] ref object pTrendDatas, int numHours, structures.WV_TREND_LIST pTrends);

            static void Main(string[] args)
            {
                int connectionID = 100;

                structures.WV_TREND_LIST TrendList2 = new structures.WV_TREND_LIST();
                structures.WV_TREND_DATA DataOfTheTrends = new structures.WV_TREND_DATA();
                int totalhours = 0;

                TrendList2.WV_TrendItem.WV_Trend_ParameterID = new int[60];
                TrendList2.WV_TrendItem.labelparameter = new structures.WV_ParameterLabel[60];
                for (int i = 0; i < 60; i++)
                {
                    TrendList2.WV_TrendItem.WV_Trend_ParameterID[i] = i;
                }
                for (int i = 0; i < 60; i++)
                {
                    TrendList2.WV_TrendItem.labelparameter[i].ParamLabel = "1234567890123456789";
                }
                TrendList2.NumberOfTrends = 60;

     

                DataOfTheTrends.WV_TrendDataItem = new structures.WV_TREND_PARAMETER_DATA[60];
                for (int i = 0; i < 60; i++)
                {
                    DataOfTheTrends.WV_TrendDataItem[i].Trend_Parameter_ID = i;
                    DataOfTheTrends.WV_TrendDataItem[i].Trend_Label = "ABCDEFGHIJ123456789";
                    DataOfTheTrends.WV_TrendDataItem[i].Trend_Units = i + 10;
                    DataOfTheTrends.WV_TrendDataItem[i].Trend_Values = new structures.WV_TrendValue[1501];
                    for (int j = 0; j < 1501; j++)
                    {
                        DataOfTheTrends.WV_TrendDataItem[i].Trend_Values[j].TrendValue = "ABCDEFGHIJ123456789";
                    }
                }

                object objDataOfTheTrends = (object)DataOfTheTrends;

                int returnTrendData = WvGetTrendData(connectionID, ref objDataOfTheTrends, totalhours, TrendList2);

                DataOfTheTrends = (structures.WV_TREND_DATA)objDataOfTheTrends;
            }
        }

     

    Best of luck, alaindoe,

    - Bio.

     

    • Proposed as answer by Paul Zhou Monday, June 20, 2011 3:21 AM
    Saturday, June 18, 2011 1:16 AM