0

I've been marshalling objects in C# (read from a binary file stream) that are written from unmanaged C++ structs using C#'s COM interop functionalities. My goal is to make these objects available to data binding by implementing the INotifyPropertyChanged interface.

The problem is, that implementing INotifyPropertyChanged inevitably comes with exposing a property changed event as public class member. This is recognized and treated as 4-byte structure member by the marshalling services, thus changing the binary layout during marshalling operations.

This is a simplistic demonstration of the issue. Consider this C++ struct:

struct Struct
{
    int SomeMember; // example member
}

and this C# class adaption of the struct:

[StructLayout(LayoutKind.Sequential)]   // required for class marshalling
public class CppStruct
{
    private int someMember; // encapsuled properties are recognized during marshalling
    public int SomeMember { get => someMember; set => someMember = value; } // example member
}

and this same class with the implemented INotifyPropertyChanged interface:

[StructLayout(LayoutKind.Sequential)]   // required for class marshalling
public class CppStructNotifyPropertyChanged : INotifyPropertyChanged
{
    private int someMember; // encapsuled properties are recognized during marshalling
    public int SomeMember
    {
        get => someMember;
        set
        {
            someMember = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SomeMember)));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged; // required by INotifyPropertyChanged implementation
}

Then System.Runtime.InteropServices.Marshal.SizeOf< CppStruct >() gives 4, which is the correct size of the binary layout of this structure, but System.Runtime.InteropServices.Marshal.SizeOf<CppStructNotifyPropertyChanged>() gives 8. This shows that the PropertyChanged member is automatically treated as 4-byte type.

I've tried finding a way to hide the event member to marshaling but this just doesn't seem to be possible. Any sort of inheritance related solution to this seem inapplicable as for INotifyPropertyChanged events to cover all members, they need to be invoked on all (including base class') members at assignment time. I've also thought of writing a custom serializer for the class, that would ignore the event member or introduce custom attributes that allow hiding of members, but that's a worst-case option for me. I would like to keep using the default marshalling functionalities if at all possible.

Is there any way to achieve this?

It might be worth to mention, I have no access nor control over the C++ side writing and reading these structures and I don't see a reason for that either. Having the event object in any form on the file is of no use. Also I'm using .NET Framework 4.7.2 with Visual Studio 2017.

Vinz
  • 3,030
  • 4
  • 31
  • 52
  • 1
    There is no obvious need to force one class do both jobs. Keep the struct declaration for marshaling, you might like a constructor that takes the struct as a parameter. – Hans Passant Mar 06 '20 at 20:15
  • @HansPassant I'm not a big fan of having multiple classes / structs of the same object. Unnecessarily long code, ambiguity, redundancy and all problems that come with it are the main reasons. – Vinz Mar 06 '20 at 21:48
  • You assume you have a choice. You don't. – Hans Passant Mar 06 '20 at 22:02

0 Answers0