none
marshal custom attribute type RRS feed

  • Question

  • i have a C++ function:

    void Reshape(const BlobShape& shape)

    where  BlobShape is a C++ class

    i  convert the BlobShape class into c#; so the c# BlobShape is :

    public partial class BlobShape
    	{
    		public List<long> Dim { get; set; }
    
    	}

    I have exported the Reshape method and call it from c# like this:

    BlobShape shape = new BlobShape();
    
    void Reshape(ref shape);

    i got this error:

    MarshalDirectiveException: Type BlobShape which is passed to unmanaged code must have a StructLayout attribute.

    Tuesday, June 5, 2018 5:28 PM

All replies

  • Note a convenient, specially-designed way of using C++ in C#: creating a CLR Class Library (available in New Project wizard). If you already have a DLL made in C++, then try adding the “Common Language Runtime Support (/clr)” option.

    These assemblies can be referenced by C# using Add Reference dialog.

    In such assemblies you can declare classes and functions that can be accessed from C#. For example, in C++:

       namespace ClassLibrary1

       {

          public ref class BlobShape

          {

             public:

                property System::Collections::Generic::List<Int64> ^ Dim;

          };

       }

    You do not need to repeat the definition in C#, because it is exported from C++. In C# you can write:

       var b = new ClassLibrary1.BlobShape();

       b.Dim = new List<long>();

     

    You can also add member functions.

    If required (to reuse some existing code), in C++ you can copy the data to other kind. For example, List can be transferred to std::vector using a loop.


    • Edited by Viorel_MVP Tuesday, June 5, 2018 8:59 PM
    Tuesday, June 5, 2018 8:54 PM
  • i have shared library not a dll, so i think i can't use  CLR
    Wednesday, June 6, 2018 1:34 PM
  • That isn't going to work. You cannot mingle C++ and C# classes like that. They are 2 different layouts. You also cannot marshal a .NET type like List<T> to unmanaged code. It won't work. Structs can be used but only for passing the data back and forth.

    Please provide the BlobShape definition in C++. If it is just data then convert to a struct. If it is a true C++ class with methods then you cannot marshal that. You'll have to accept a parameter that is just data and then convert it to your C++ class type. If the native code is responsible for the lifetime of the object then expose C functions  to create and destroy it. Then use IntPtr to pass the object back and forth.

    Just a clarification, yes you can technically use a C# class if you attribute it with StructLayout but the preferred approach is to define it as a struct instead which doesn't need the attribute.


    Michael Taylor http://www.michaeltaylorp3.net


    Wednesday, June 6, 2018 5:02 PM
    Moderator
  • Hello

    Thank you for your reply, here is the C++ BlobShape class:

    class BlobShape : public ::google::protobuf::MessageLite {
     public:
      BlobShape();
      virtual ~BlobShape();
    
      BlobShape(const BlobShape& from);
    
      inline BlobShape& operator=(const BlobShape& from) {
        CopyFrom(from);
        return *this;
      }
    
      inline const ::std::string& unknown_fields() const {
        return _unknown_fields_;
      }
    
      inline ::std::string* mutable_unknown_fields() {
        return &_unknown_fields_;
      }
    
      static const BlobShape& default_instance();
    
      #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
      // Returns the internal default instance pointer. This function can
      // return NULL thus should not be used by the user. This is intended
      // for Protobuf internal code. Please use default_instance() declared
      // above instead.
      static inline const BlobShape* internal_default_instance() {
        return default_instance_;
      }
      #endif
    
      void Swap(BlobShape* other);
    
      // implements Message ----------------------------------------------
    
      BlobShape* New() const;
      void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
      void CopyFrom(const BlobShape& from);
      void MergeFrom(const BlobShape& from);
      void Clear();
      bool IsInitialized() const;
    
      int ByteSize() const;
      bool MergePartialFromCodedStream(
          ::google::protobuf::io::CodedInputStream* input);
      void SerializeWithCachedSizes(
          ::google::protobuf::io::CodedOutputStream* output) const;
      void DiscardUnknownFields();
      int GetCachedSize() const { return _cached_size_; }
      private:
      void SharedCtor();
      void SharedDtor();
      void SetCachedSize(int size) const;
      public:
      ::std::string GetTypeName() const;
    
      // nested types ----------------------------------------------------
    
      // accessors -------------------------------------------------------
    
      // repeated int64 dim = 1 [packed = true];
      inline int dim_size() const;
      inline void clear_dim();
      static const int kDimFieldNumber = 1;
      inline ::google::protobuf::int64 dim(int index) const;
      inline void set_dim(int index, ::google::protobuf::int64 value);
      inline void add_dim(::google::protobuf::int64 value);
      inline const ::google::protobuf::RepeatedField< ::google::protobuf::int64 >&
          dim() const;
      inline ::google::protobuf::RepeatedField< ::google::protobuf::int64 >*
          mutable_dim();
    
      // @@protoc_insertion_point(class_scope:caffe.BlobShape)
     private:
    
      ::std::string _unknown_fields_;
    
      ::google::protobuf::uint32 _has_bits_[1];
      mutable int _cached_size_;
      ::google::protobuf::RepeatedField< ::google::protobuf::int64 > dim_;
      mutable int _dim_cached_byte_size_;
      #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
      friend void  protobuf_AddDesc_caffe_2eproto_impl();
      #else
      friend void  protobuf_AddDesc_caffe_2eproto();
      #endif
      friend void protobuf_AssignDesc_caffe_2eproto();
      friend void protobuf_ShutdownFile_caffe_2eproto();
    
      void InitAsDefaultInstance();
      static BlobShape* default_instance_;
    };

    The BlobShape class was generated from .proto file using google protocol buffer, so using the protobuf generator i got it in c#

    Thursday, June 7, 2018 11:10 AM
  • Yeah, that isn't going to be marshalable most likely. The only thing you should be marshaling between managed and unmanaged code is the data. Nothing else (e.g. vtables) will work. It doesn't matter whether the layouts are the same or not the compilers generate different memory structures.

    You should create a struct that represents the data that is needed on the managed side. Then expose C-based functions to use that data to map to your BlobShape. If you really need the entire object then instead expose the object as an IntPtr in C#. Move the object creation/destruction into the C++ code and just treat it as an opaque structure in managed code. You can still expose properties and whatnot but they would end up marshaling back to unmanaged code to get the data. This is going to be a little slow though.

    Another alternative is to forego C++ altogether. If you don't need the unmanaged code then just implement it all in C#. The protocol is supported in C# as documented here. I've never used it myself but it seems like they've already wrapped everything for you.


    Michael Taylor http://www.michaeltaylorp3.net

    Thursday, June 7, 2018 2:09 PM
    Moderator