Is it possible to specify the struct packing size in a C# implementation of a COM interface?
(I know how to do it when the struct is defined on the managed side, but my question is about when it's defined on the unmanaged side and implemented on the managed side.)
I've got a COM type library that defines a struct type and an interface method that returns an array of those structs. I've got a C# server and an unmanaged C++ server that both implement this interface, and a C++ client that consumes it. The C++ server and the C++ client both pack the struct to 4 bytes in a 32-bit build, and to 8 bytes in a 64-bit build.
But the C# server always packs to 4 bytes, regardless of platform (x86, x64, AnyCPU). Is this normal? Can it be overridden?
The struct looks like this:
typedef [v1_enum] enum { blah... } HandlerPriority;
struct HandlerInfo { BSTR MessageName; HandlerPriority Priority; }
The Visual Studio C++ and MIDL compilers use a default packing of /Zp8. In a 32-bit build both members of the struct are 4 bytes wide, and so they are not padded. In a 64-bit build the string pointer is 8 bytes and the enum 4, so the enum is padded. Naturally, this causes problems when the C# client sends unpadded data.
I can fix (work around?) the problem by specifying /Zp4 to remove the padding, and everything seems to work fine. But I wonder whether that's the best solution.
I imagine the default packing is /Zp8 for performance reasons only. As I understand it, by default on x64 the hardware traps and handles alignment exceptions and so at least we won't crash. And in this particular situation I don't care about the performance penalty because the interface function is only called at system startup. And even if I did care I might still accept it as the cost of COM/.NET interop. But I'm a little uneasy because it feels wrong (coming from a C++ background, I suppose.)
On the other hand, if it's simply not possible to change the packing on the managed side, then I'll live with it.
Can anyone give me some advice?
The packing size is specified by tlbimp.exe in the RCW that it generates. If I pass /platform:x64 on the command line then the RCW says ".pack 8", but if I say /platform:x86 or /platform:agnostic then it says ".pack 4".
I do want /platform:agnostic so that I can use the same RCW on both 32- and 64-bit platforms. Normally I find that AnyCPU is more trouble than it's worth, but this project is an SDK and I don't want to impose my views on that topic on my users if I can avoid it.
I also want 8-byte packing because 4-byte packing on x64 can be expensive. See http://msdn.microsoft.com/en-us/library/aa290049%28v=vs.71%29.aspx.
The solution I've settled on is to decompile the RCW, change the .pack directive in the generated source code, and recompile.