none
How to pass a struct between C++ and C#? RRS feed

  • Question

  • I am using Visual Studio 2013 to write a WPF/C# application that provides a GUI for a C++ project/library.  I need help on how to get the data generated by the C++ code back into the C# code for display and how to get the modified data by the C# code back into the C++ code.  Hers is a very simple example of some native C++ code that deals with a struct.

    struct CppDataInfo
    {
        bool FixedPoint;
        int DigitCount;
        int PointOffset;
    };
    
    class CppMathFuncs
    {
    public:
        void SetDataInfo(CppDataInfo info);
        CppDataInfo GetDataInfo();
    };
    

    Here is some code that uses C++/CLI to wrap the native C++ code, which attempts to translate between two struct definitions.

    Wrapper.h:
    
    namespace CppClrWrapper
    {
        public struct CppDataInfoWrapper
        {
            bool FixedPoint;
            int DigitCount;
            int PointOffset;
        };
    
        public ref class Wrapper
        {
        public:
            Wrapper();
            void SetDataInfoWrapper( CppDataInfo );
            CppDataInfoWrapper GetDataInfoWrapper();
    
        private:
            CppMathFuncs *cppMathFuncsPtr;
        };
    }
    
    Wrapper.cpp:
    
    using namespace CppClrWrapper;
    
    Wrapper::Wrapper()
    {
        cppMathFuncsPtr = new CppMathFuncs();
    }
    
    void Wrapper::SetDataInfoWrapper(CppDataInfo dataInfo)
    {
        cppMathFuncsPtr->SetDataInfo(dataInfo);
    }
    
    CppDataInfoWrapper Wrapper::GetDataInfoWrapper()
    {
        CppDataInfo info = cppMathFuncsPtr->GetDataInfo();
        CppDataInfoWrapper dataInfoWrapper;
        dataInfoWrapper.FixedPoint = info.FixedPoint;
        dataInfoWrapper.DigitCount = info.DigitCount;
        dataInfoWrapper.PointOffset = info.PointOffset;
        return dataInfoWrapper;
    }
    
    

    I get a compiler error if I try to use the GetDataInfoWrapper() method in my C# code: "'CppClrWrapper.Wrapper.GetDataInfoWrapper()' is not supported by the language".  So how can I complete the following two C# functions?

    C# code:

    namespace TestCppClrWrapperApp { public struct DataInfo { bool FixedPoint; int DigitCount; int PointOffset; } public partial class MainWindow : Window { private void GetCppStructValue() { // Get the CppDataInfoWrapper values to set a DataInfo struct. } private void SetCppStructValue() { // Set the CppDataInfoWrapper using a DataInfo struct. } }

    How can I access the C++/CLI CppDataInfoWrapper structure from the C# code?  Is there a way to convert or cast between the wrapper struct and the C# struct?  Or is there a better way to pass structure data between C# and native C++?

    Thanks, Dave


    Dave R&D SW Engineer Agilent Technologies

    Thursday, July 23, 2015 11:34 PM

Answers

  • If you cannot touch the C++ code then C++/CLI isn't going to help either since you couldn't change the compilation options. More likely you're creating a new project (C++/CLI) to wrap the existing code. In that case you can just as easily create a new native C++ library that simply exposes global C functions that actually call into the unmodifiable code.

    Ultimately either native global functions or C++/CLI will work.  You just need to decide which one gets you where you want to be quicker.  C++/CLI was designed to allow you to continue to use your existing C++ code while refactoring shared code into C++/CLI so it could be used on both sides at the same time. If you cannot touch the native code base then you aren't gaining a lot by using C++/CLI since the C++ code won't be using it at all.

    When I have looked into porting C++ code into managed in the past I've looked at the following aspects

    • Amount of code - Native interop is fine for small sets of functions but becomes tedious for larger blocks
    • Expertise - If the team is not comfortable with the C++/CLI rules then the code becomes harder to write and understand
    • Types - For simple structs and disconnected data then native interop is fine, for interrelated data or complex structures then C++/CLI will be easier
    • OOP - If you need access to C++ classes and functionality then C++/CLI is the only real way to go but for a small subset a couple of global functions can emulate the access
    • STL - If the C++ code is heavily STL based then it becomes harder to create C++/CLI types that will work correctly because of limitations on members of CLI types (this was a couple of years ago so things might have changed now)

    If you want to use C++/CLI for the interop then you should probably post this question in the C++ forums as that is where the C++/CLI support is.  This forum is for C# so we aren't going to be as useful to you as far as the C++/CLI syntax goes. The C++ folks will be able to clarify how the data will have to be defined, how the nested needs to work, etc so that both sides of the fence are happy.

    Monday, July 27, 2015 4:47 PM
    Moderator

All replies

  • using a simple web service wouldn't help? is your c# asking the object from the c++ program or the c++ program send the objects and some events need to be fired in the c# program? i hope that you understood what i meant.

    Hope it helps. Spiri

    Friday, July 24, 2015 5:07 AM
  • C++ struct and .NET struct are fundamentally different things.

    In C++ struct is a class where every the accessor defaults to public (rather then private as in a class). They carry functions, support inheritance and most other stuff of classes:
    http://stackoverflow.com/questions/54585/when-should-you-use-a-class-vs-a-struct-in-c

    In .NET struct is a value type. Inlcuding missing most of the class related overhead and inmutability rules. It is closer to the struct as used in C.

    The rest is pretty much just using P/Invoke to call the native C++ code:

    http://www.codeproject.com/Articles/403285/P-Invoke-Tutorial-Basics-Part

    Friday, July 24, 2015 11:17 AM
  • C++/CLI is useful for migrating an existing C++ system over to managed code in a piecemeal fashion.  If you simply want to get data in native code over to a managed app and vice versa then it might be overkill.  The simplest approach is to simply use interop to send the structure back and forth.  For this to work you would need an exported global function on the C++ side to send/receive the structure but otherwise you simply create the struct in managed code and attribute it.

    //C# version
    public struct DataInfo
    {
            bool FixedPoint;
            int DigitCount;
            int PointOffset;
    }
    
    //C++ equivalent
    struct DataInfo
    {
       bool FixedPoint;
       int DigitCount;
       int PointOffset;
    }
    

    The caveat here is packing.  Depending upon the settings the C++ struct may have padding between the bool field and the int fields to align it correctly.  If that is the case you'll need to do the same thing on the C# side.

    To pass the data back and forth you simply need to expose the native functions.

    //C++
    extern "C" __declspec(dllexport) DataInfo GetDataInfo ()
    {
       DataInfo s = { 0 };
       ...
       return s;
    }
    
    extern "C" __declspec(dllexport) void SetDataInfo ( DataInfo const& data )
    {
       //Do something with it
    }
    
    //C#
    [DllImport("mydll.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
    private static extern DataInfo GetDataInfo ();
    
    [DllImport("mydll.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
    private static extern void SetDataInfo ( DataInfo data );
    
    //Usage
    var data = GetDataInfo();
    ...
    SetDataInfo(data);
    

    Michael Taylor
    http://blogs.msmvps.com/p3net

    Friday, July 24, 2015 3:47 PM
    Moderator
  • So after looking at the posting at the link you provided and then some others in the forum, I made a change to my code that seemed to work until I looked a little closer.  The 'bool' member of the struct is not being returned with the correct value.  Here is the change I made:

    Wrapper.cpp: void Wrapper::SetDataInfoWrapper(NativeDataInfo dataInfo) { cppMathFuncsPtr->SetDataInfo(dataInfo); } CppDataInfoWrapper Wrapper::GetDataInfoWrapper() { CppDataInfo info = cppMathFuncsPtr->GetDataInfo(); return (CppDataInfoWrapper)Marshal::PtrToStructure(IntPtr(&info), CppDataInfoWrapper::typeid); }

    Changing the GetDataInfoWrapper() method to use the PtrToStructure() method allowed my C# code (shown below) to compile and run.  Here's the C# code:

    private void GetCppStructValue()
    {
        CppDataInfoWrapper infoWrapper = m_Wrapper.GetDataInfoWrapper();
        bool fixedPoint = infoWrapper.FixedPoint;
        int digitCount = infoWrapper.DigitCount;
        int pointOffset = infoWrapper.PointOffset;
    }
    

    After changing the native CppDataInfo.FixedPoint member value from 'true' to 'false' and verifying the value by stepping through the code with the debugger, I tried retrieving the new value using the C# method GetCppStructValue().  Again stepping through the code, I note that the correct 'bool' value is being returned until I get back into the C# method, where the variable 'infoWrapper.FixedPoint' has the value of 'true' instead of 'false'.  The two 'int' member changed values are returned as expected, but not the 'bool' member.

    Am I missing some directive for getting the members to marshal back into the C# domain correctly?

    Thanks, Dave


    Dave R&D SW Engineer Agilent Technologies

    Friday, July 24, 2015 5:38 PM
  • Michael,

    The big problem with your proposed solution is that it assumes I can modify the native C++ project, like adding "extern "C" __declspec(dllexport)" to the code.  The reason I am trying to use C++/CLI is to use the native C++ project without touching it, simply wrapping it for use by a WPF/C# application.  Shouldn't this be possible?

    Thanks, Dave


    Dave R&D SW Engineer Agilent Technologies

    Monday, July 27, 2015 4:27 PM
  • If you cannot touch the C++ code then C++/CLI isn't going to help either since you couldn't change the compilation options. More likely you're creating a new project (C++/CLI) to wrap the existing code. In that case you can just as easily create a new native C++ library that simply exposes global C functions that actually call into the unmodifiable code.

    Ultimately either native global functions or C++/CLI will work.  You just need to decide which one gets you where you want to be quicker.  C++/CLI was designed to allow you to continue to use your existing C++ code while refactoring shared code into C++/CLI so it could be used on both sides at the same time. If you cannot touch the native code base then you aren't gaining a lot by using C++/CLI since the C++ code won't be using it at all.

    When I have looked into porting C++ code into managed in the past I've looked at the following aspects

    • Amount of code - Native interop is fine for small sets of functions but becomes tedious for larger blocks
    • Expertise - If the team is not comfortable with the C++/CLI rules then the code becomes harder to write and understand
    • Types - For simple structs and disconnected data then native interop is fine, for interrelated data or complex structures then C++/CLI will be easier
    • OOP - If you need access to C++ classes and functionality then C++/CLI is the only real way to go but for a small subset a couple of global functions can emulate the access
    • STL - If the C++ code is heavily STL based then it becomes harder to create C++/CLI types that will work correctly because of limitations on members of CLI types (this was a couple of years ago so things might have changed now)

    If you want to use C++/CLI for the interop then you should probably post this question in the C++ forums as that is where the C++/CLI support is.  This forum is for C# so we aren't going to be as useful to you as far as the C++/CLI syntax goes. The C++ folks will be able to clarify how the data will have to be defined, how the nested needs to work, etc so that both sides of the fence are happy.

    Monday, July 27, 2015 4:47 PM
    Moderator
  • Michael,

    Thanks for your reply.  I will consider posting to the C++ forums in the future for native C++ / C++/CLI interoperability issues.  The reason I posted in this forum was the C# compiler error I got in my prototype solution where I was trying to pass a structure between C# and native C++ using a C++/CLI wrapper.

    The current task I am working on involves creating a WPF/C# solution that provides a GUI for testing an existing native C++ project, which I can include in my test solution.  But the C++ project is targeted long-term to a native C++ environment, so I don't want to impose any changes on it to facilitate the near-term strategy of testing using C#.  After reading several blogs and other on-line postings, it seemed that using C++/CLI as a wrapper would be a good approach.

    Do you know of a good reference for learning how to use C++/CLI as a wrapper to facilitate this approach?

    Thanks, Dave


    Dave R&D SW Engineer Agilent Technologies

    Monday, July 27, 2015 8:33 PM
  • The reference book I learned on was C++/CLI In Action. But that was several years ago so, unless there is an update, then it is out of date with current versions of C++/CLI.
    Monday, July 27, 2015 11:54 PM
    Moderator

  • The current task I am working on involves creating a WPF/C# solution that provides a GUI for testing an existing native C++ project, which I can include in my test solution.  



    If you intend to build a C++/CLI DLL containg the C++/CLI wrapper class wrapping the C++ (native) class, then, the technique is quite simple. The hard work is writing the C++/CLI wrapper class. Everything is on the MSDN pages (i could provide code, but it's more C++ than C#). You build a C++/CLI DLL as you wish, and then build a C# project containing a reference to the just mentioned DLL (yes, it's as simple as that).

    Note that the thread title differs from the requirement above; also, no one knows what a struct is, because it can be simple bytes allocated on the stack, and also a beast that extends to the heap as well.


    Tuesday, July 28, 2015 6:51 AM