Passing class/struct with methods to C++ DLL

  • Thread starter Charles Calvert
  • Start date
C

Charles Calvert

All,

Am I correct in thinking that passing a class/struct with methods to a
C++ DLL that expects a C++ struct might be problematic? The addition
of the vtable will change the size of the structure, but will it be
placed after the data, which should allow the C++ code to write to the
correct portion of memory?

E.g.:

C++ struct:

struct FooStruct
{
int i;
char a;
};


C# code:

[DllImport("foo.dll")]
private static extern uint ExportedFunction(ref FooStruct fs);

public struct FooStruct
{
int i;
char a;

public FooStruct()
{
i = 0;
a = 'a';
}
}

public uint Function(ref FooStruct fs)
{
return ExportedFunction(ref fs);
}
 
N

Nicholas Paldino [.NET/C# MVP]

Charles,

Yes, your thinking is correct. Quite simply, don't do it. Marshal the
structure and then copy over to local instances before and after you are
done calling the API (depending on your need).

Also, your C# code won't compile, as you can't override the default
constructor on a structure.
 
J

Jeroen Mostert

Charles said:
Am I correct in thinking that passing a class/struct with methods to a
C++ DLL that expects a C++ struct might be problematic?

Eh... no. I suspect either of these "C++" instances should be "C#".
The addition of the vtable will change the size of the structure, but
will it be placed after the data, which should allow the C++ code to
write to the correct portion of memory?
For Visual C++ at least (and most other C++ compilers, in fact), if you have
no virtual methods, there will be no vtable and hence no layout change.

However, this is happenstance. The C++ standard guarantees no such thing:
layout compatibility with C (and hence C#) is only guaranteed if your struct
is Plain Old Data (POD), and doing anything with it that C can't breaks this
contract.
C++ struct:

struct FooStruct
{
int i;
char a;
};
Well, there's no methods here, so this is fine...
C# code:

[DllImport("foo.dll")]
private static extern uint ExportedFunction(ref FooStruct fs);

public struct FooStruct
{
int i;
char a;

public FooStruct()
{
i = 0;
a = 'a';
}
}
Hang on, are you actually talking about passing a C# struct with methods to
C++? That's a different story.

First, as Nicholas pointed out, this doesn't compile. C# demands that
structs behave like dumb bit buckets as far as initialization and disposal
go, so no overriding the default constructor and no finalizers. You can
define *additional* constructors, but the default one has to initialize
everything to default values, and the compiler will enforce this by not
allowing you to even suggest anything else.

Second, C# will marshal the struct in its linearized form, regardless of
what methods you define on it. In fact, you can make it a class, inherit
from another class, implement an interface, give it properties, put it in a
dress and call it Shirley, and it will still work as long as you get the
fields right and attach a proper [StructLayout] to all classes involved.
Obviously, inheritance and interfaces usually make things more complicated
than necessary, but the ability to implement properties to abstract away
from the more hairy interops does come in handy.
 
C

Charles Calvert

Yes, your thinking is correct. Quite simply, don't do it. Marshal the
structure and then copy over to local instances before and after you are
done calling the API (depending on your need).

I'm glad that I was thinking along the correct lines.

[snip example code]
Also, your C# code won't compile, as you can't override the default
constructor on a structure.

Whoops. I should have specified that it was untested. :) I don't use
structs much, so I didn't remember that, though I'm sure that I've
tripped over it before.

Thanks,
 
C

Charles Calvert

Eh... no. I suspect either of these "C++" instances should be "C#".

Nope, I meant what I said. Sorry I wasn't more clear.
For Visual C++ at least (and most other C++ compilers, in fact), if you have
no virtual methods, there will be no vtable and hence no layout change.

However, this is happenstance. The C++ standard guarantees no such thing:

I was aware of that, which was one of the reasons that I was asking.
layout compatibility with C (and hence C#) is only guaranteed if your struct
is Plain Old Data (POD), and doing anything with it that C can't breaks this
contract.

[snip my example code]
Hang on, are you actually talking about passing a C# struct with methods to
C++? That's a different story.

I was interested in both structs and classes, either of which may have
methods in C#.
First, as Nicholas pointed out, this doesn't compile. C# demands that
structs behave like dumb bit buckets as far as initialization and disposal
go, so no overriding the default constructor and no finalizers.

I'd forgotten about that since I rarely use structs. I'm sure the
compiler would have yelled at me if I'd bothered to compile it. :)

[snip]
Second, C# will marshal the struct in its linearized form, regardless of
what methods you define on it. In fact, you can make it a class, inherit
from another class, implement an interface, give it properties, put it in a
dress and call it Shirley, and it will still work as long as you get the
fields right and attach a proper [StructLayout] to all classes involved.
Obviously, inheritance and interfaces usually make things more complicated
than necessary, but the ability to implement properties to abstract away
from the more hairy interops does come in handy.

Interesting. Obviously if you get the bytes in the right order and
both sides agree on the layout, it should be fine.

I think that I was somewhat unclear, so let me restate the question
and tell me if this is different from what you understood it to be
initially:

If the C/C++ function expects a struct (a "dumb bit bucket"), say,

struct FooStruct
{
int i;
char a;
};

can I pass it the following C# struct

struct FooStruct // C# version
{
public int i;
public char a;
public FooStruct(ArrayList vals)
{
// code omitted
}
}

and expect the Marshaling to work and the C/C++ function to not muck
things up?

I would expect the latter to be okay, at least in the case of an
instance of the struct on the heap, because the C/C++ function will
only read/write the first 5 bytes (the int and char, ignoring
alignment).

I'd be worried about passing an instance on the stack, as the C#
struct is larger than the C++ version, the size of the stack would be
affected, which could be a problem if there are multiple arguments,
depending on the order.

The size of the stack might also be a problem, depending on the
calling convention of the C/C++ function (e.g. __stdcall vs. __cdecl).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top