By Adam Haile


2008-10-09 17:27:02 8 Comments

I have the following struct in C++:

#define MAXCHARS 15

typedef struct 
{
    char data[MAXCHARS];
    int prob[MAXCHARS];
} LPRData;

And a function that I'm p/invoking into to get an array of 3 of these structures:

void GetData(LPRData *data);

In C++ I would just do something like this:

LPRData *Results;
Results = (LPRData *)malloc(MAXRESULTS*sizeof(LPRData));
GetData( Results );

And it would work just fine, but in C# I can't seem to get it to work. I've created a C# struct like this:

public struct LPRData
{

    /// char[15]
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 15)]
    public string data;

    /// int[15]
    [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 15)]
    public int[] prob;
}

And if I initialize an array of 3 of those (and all their sub-arrays) and pass it into this:

GetData(LPRData[] data);

It returns with success, but the data in the LPRData array has not changed.

I've even tried to create a raw byte array the size of 3 LPRData's and pass that into a function prototype like this:

GetData(byte[] data);

But in that case I will get the "data" string from the very first LPRData structure, but nothing after it, including the "prob" array from the same LPRData.

Any ideas of how to properly handle this?

5 comments

@GregUzelac 2008-10-16 02:37:45

The PInvoke Interop Assistant may help. http://clrinterop.codeplex.com/releases/view/14120

@JaredPar 2008-10-09 17:50:38

One trick when dealing with pointers is to just use an IntPtr. You can then use Marshal.PtrToStructure on the pointer and increment based on the size of the structure to get your results.

static extern void GetData([Out] out IntPtr ptr);

LPRData[] GetData()
{
    IntPtr value;
    LPRData[] array = new LPRData[3];
    GetData(out value);
    for (int i = 0; i < array.Length; i++)
    {
        array[i] = Marshal.PtrToStructure(value, typeof(LPRData));
        value += Marshal.SizeOf(typeof(LPRData));
    }
    return array;
}

@maxwellb 2010-07-08 19:20:03

should line 11 either: change += to = and ToInt32 to ToInt64 if running 64-bit; or, remove the value.ToInt32()?

@JaredPar 2010-07-08 19:59:45

@maxwellb, yes. The code as written is not 64 bit safe.

@maxwellb 2010-07-08 20:21:34

what I'm really getting at, is that you are increment-assigning by the increment of pointer.toInt + sizeof(struct). Wouldn't the increment be only sizeof(struct)?

@user2913305 2014-04-02 10:52:06

Excellent answer

@Epirocks 2016-11-17 00:31:21

But in the example GetData is just sending an intptr, where is the memory being created and where is it being released

@Zenexer 2012-02-08 07:20:29

A similar topic was discussed on this question, and the one of the conclusions was that the CharSet named parameter must be set to CharSet.Ansi. Otherwise, we would be making a wchar_t array instead of a char array. Thus, the correct code would be as follows:

[Serializable]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct LPRData
{
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 15)]
    public string data;

    [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 15)]
    public int[] prob;
}

@Constantin 2008-10-18 21:42:40

Did you mark GetData parameter with OutAttribute?

Combining the InAttribute and OutAttribute is particularly useful when applied to arrays and formatted, non-blittable types. Callers see the changes a callee makes to these types only when you apply both attributes.

@denny 2008-10-09 17:54:03

I would try adding some attributes to your struct decloration

[StructLayout(LayoutKind.Sequential, Size=TotalBytesInStruct),Serializable]
public struct LPRData
{
/// char[15]
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 15)]
public string data;

/// int[15]
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 15)]
public int[] prob;
}

*Note TotalBytesInStruct is not intended to represent a variable

JaredPar is also correct that using the IntPtr class could be helpful, but it has been quite awhile since I have used PInvoke so I'm rusty.

@swinefeaster 2011-04-05 17:21:40

I've used this approach, but I get exceptions in Mono that the variables are set to null references. For example, "prob" is null so it doesn't want to work. Am I supposed to be newing these at some point, or is that supposed to be handled by the framework somehow? Thanks

Related Questions

Sponsored Content

65 Answered Questions

[SOLVED] What is the difference between String and string in C#?

22 Answered Questions

[SOLVED] What is the "-->" operator in C++?

25 Answered Questions

[SOLVED] When should you use a class vs a struct in C++?

  • 2008-09-10 16:29:54
  • Alan Hinchcliffe
  • 380980 View
  • 891 Score
  • 25 Answer
  • Tags:   c++ oop class struct ooad

26 Answered Questions

37 Answered Questions

9 Answered Questions

[SOLVED] What are the correct version numbers for C#?

1 Answered Questions

[SOLVED] The Definitive C++ Book Guide and List

  • 2008-12-23 05:23:56
  • grepsedawk
  • 2239713 View
  • 4246 Score
  • 1 Answer
  • Tags:   c++ c++-faq

28 Answered Questions

[SOLVED] When to use struct?

  • 2009-02-06 17:37:55
  • Alex Baranosky
  • 258513 View
  • 1344 Score
  • 28 Answer
  • Tags:   c# struct

8 Answered Questions

[SOLVED] Difference between 'struct' and 'typedef struct' in C++?

  • 2009-03-04 20:41:12
  • criddell
  • 493789 View
  • 796 Score
  • 8 Answer
  • Tags:   c++ struct typedef

Sponsored Content