1

In my application there is a need to pass an absolutely arbitrary object/struct into a C++ function using P/Invoke. According to the Internet, any objects may be passed as C++ structs (they get pinned to a specific memory location for the duration of the call). As I am only interested in data and do not plan to call any method, this matches my needs well enough. However, experiments show that these objects must have [StructLayout(LayoutKind.Sequential)] attribute, otherwise the code will not compile. Unfortunately the classes are created by other developers and not guaranteed to have this attribute.

To solve this problem, one solution would be to pass an object to a special C# wrapper function that will:

  1. Retrieve object's properties using Type.GetProperties
  2. Dynamically create a struct with the necessary properties and copy values from the object.
  3. Add [StructLayout(LayoutKind.Sequential)] attribute.
  4. Pass this struct into C++ along with the metadata retrieved at step 1 (so that C++ knows what's the object structure and how to read it)

As I am new to C#, I am not certain how to create structs with arbitrary properties and attributes dynamically. What I've found so far is MakeGenericType and ExpandoObject, but what's required is to create a dynamic struct that is not previously defined anywhere - not even as a generic object. Any ideas?

Community
  • 1
  • 1
Sergiy Belozorov
  • 5,856
  • 7
  • 40
  • 73

1 Answers1

1

Could something like this work for you?

    [StructLayout(LayoutKind.Sequential)]
    struct MyStruct
    {
        public int Size;
        public IntPtr PtrToArrayOfMetadata;
        public IntPtr PtrToArrayOfObjects;
    }

    public enum Type
    {
        Int, 
        Float,
        Decimal
    }

And then to send a "dynamic" object

        Type[] metadata = new Type[2];
        metadata[0] = Type.Int;
        metadata[1] = Type.Float;

        object[] obj = new object[2];
        obj[0] = 2;
        obj[1]= 1;

        GCHandle objHandle = GCHandle.Alloc(obj, GCHandleType.WeakTrackResurrection);
        IntPtr ptrObj = GCHandle.ToIntPtr(objHandle);

        objHandle = GCHandle.Alloc(metadata, GCHandleType.WeakTrackResurrection);
        IntPtr ptrMeta = GCHandle.ToIntPtr(objHandle);

        MyStruct tmp = new MyStruct();
        tmp.Size = 2;
        tmp.PtrToArrayOfMetadata = ptrMeta;
        tmp.PtrToArrayOfObjects = ptrObj;
Evelie
  • 2,919
  • 2
  • 14
  • 21
  • Will I still be able to pass such a struct to C++ using P/Invoke then? – Sergiy Belozorov Feb 26 '13 at 10:52
  • Unfortunately this is not really my area of expertise. But I would guess you could. However you have to make sure that the layout of your struct is what you expect it to be in c++. Isnt it possible to create some kind of wrapper for your data? – Evelie Feb 26 '13 at 10:54
  • The problem is that I do not know in advance what data it is. It can be any object with any properties. My task is to serialize that information into something that I can pass to C++ along with metadata describing it (so that it's not just a blob of data on C side). – Sergiy Belozorov Feb 26 '13 at 10:56
  • You have no idea at all what kinds of data it can contain? – Evelie Feb 26 '13 at 10:57
  • Well I know it may contain ints, floats, doubles, longs, strings, other objects or structs etc. It also may contain method definitions, but I am not interested in these. Custom getters/setters must be taken into account and should be executed to retrieve data before passing it into native code. – Sergiy Belozorov Feb 26 '13 at 10:58
  • I changed my solution. Could something along those lines work? – Evelie Feb 26 '13 at 11:16
  • This looks good and may indeed solve my problem. I will o'course need to support nested objects as well, but that is something I can do myself already. Thanks a lot. – Sergiy Belozorov Feb 26 '13 at 11:37
  • Hmm I think that you might even be able to not use the ptr in the struct and just go with object[]. I think its already stored as a pointer. The code will look a lot better that way :) – Evelie Feb 26 '13 at 11:57