Click here to Skip to main content
15,887,746 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Dear all,
I am trying to figure out a thing. I have class A and class B in C++. A contains a variable which is a pointer to an object of B.

Now the problem is, I am trying to retrieve an object of A in C#. I have a similar class definition of A and B in C#, but in A, I haven't stored the pointer to B. Instead A contains directly an object of B. How do I marshal? Do I need to create 2 different definitions of A:
1 with IntPtr to B and another with B as a property of A?

Please let me know what can be done. I am totally lost in marshalling.
C++
//C++ definition of CHouse class
class CHouse
{
	public:
		CHouse(void);
		~CHouse(void);

	public:
		char* HouseNumber;
		char* HouseType;
		char* Condition;
		double  MarketValue;
		Room* room; 
};

//C++ definition of Room class
class Room
{
public :
char* name;
	int size;

public:
	Room(void);
public:
	~Room(void);
};

//C++ exposed function
_EXPORTS_API void getData(CHouse* houses)
{
	houses[0].HouseNumber = "882-100"; 	     
	houses[0].HouseType   = "Single Family"; 
	houses[0].Condition   = "Excellent";     
	houses[0].MarketValue = 685440; 	     

	Room r;
	r.name = "Adghgh";
	r.size = 1;
	houses[1].room = &r;

	houses[1].HouseNumber = "8882-100"; 	
	houses[1].HouseType   = "GSingle Family"; 
	houses[1].Condition   = "GExcellent";     
	houses[1].MarketValue = 885440; 

	Room r1;
	r1.name = "Basdf";
	r1.size = 2;
	houses[0].room = &r1;
}

//C# definition of House class
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        class HouseWithPtr
        {
            [MarshalAs(UnmanagedType.LPStr)]
            public string HouseNumber;
            [MarshalAs(UnmanagedType.LPStr)]
            public string HouseType;
            [MarshalAs(UnmanagedType.LPStr)]
            public string Condition;
            [MarshalAs(UnmanagedType.R8)]
            public double MarketValue;
            [MarshalAs(UnmanagedType.SysUInt)]
            public IntPtr r;
            
            public HouseWithPtr()
            {
                r = new IntPtr();
            }
        }
//C# definition of Room class
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        class Room
        {
            [MarshalAs(UnmanagedType.LPStr)]
            public string name;
            [MarshalAs(UnmanagedType.I4)]
            public int size;
        }

//C# code to call the C++ function
getData getDataObj = (getData)Marshal.GetDelegateForFunctionPointer(dllEntryPoint, typeof(getData));

HouseWithPtr[] houses = new HouseWithPtr[2];
houses[0] = new HouseWithPtr();
houses[1] = new HouseWithPtr();

IntPtr allocated = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(HouseWithPtr)));
getDataObj (allocated);
Marshal.PtrToStructure(allocated, houses[0]);

IntPtr temp = new IntPtr(allocated.ToInt32() + Marshal.SizeOf(typeof(HouseWithPtr)));

Marshal.PtrToStructure(temp, houses[1]);

Using this approach, House is populated correctly, but Room inside House isn't populated correctly. What is wrong in this code?

Thanks,
Ashish
Posted
Updated 1-May-13 8:41am
v2
Comments
Sergey Alexandrovich Kryukov 8-May-12 11:38am    
If would be much simpler to see if you write your C++ function prototype and with all relevant definition used by it.
--SA
Ashish0603 8-May-12 14:22pm    
Done now. Pls let me know if this is enough.
Sergey Alexandrovich Kryukov 8-May-12 14:57pm    
Oh...
--SA

Yuck...

Look very carefully at your managed / native interface and figure out how to minimize this sort of thing.

I have a fairly complex system with a lot of native C++ that does a lot of computationaly intensive stuff and some C# user interface components (that are mostly some fancy 3rd party libraries that I couldn't buy in native format).

In one place I need to pass a fairly complex tree like structure from native C++ to C# -- what I decided was easiest was to create a parallel class in C# and then write a C++/CLR routine that basically copies the data from the native-C class to the equivalent managed-C# class.

I have one routine that copies from the native class to the managed class and one routine that copies from the managed class to the native class. I use that to pass all the data across the native / managed interface.

I do all the heavy computation stuff in the native side, covert it and pass it through to the managed UI, let the user manipulate it all he likes, then covert it and pass it back.

C++/CLR handles all the magic, I never write a single marshal statement.

If you find yourself writing Marshal or MarshalAs more than one or twice, it's time to review how things are split up and figure out a better division.

Assuming you have written a C# module called MyCSharpModule with classes CLRHouse and CLRRoom, then you can write a C++/CLR routine that looks something like this:

C++
#using <MyCSharpModule.dll>

MyCSharpModule::CLRHouse ^ ToCLRHouse( CHouse *pHouse)
{
  MyCSharpModule::CLRHouse ^newHouse = gcnew MyCSharpModule::CLRHouse();
  newHouse->SetHouseNumber( pHouse->HouseNumber );
  newHouse->SetHouseType( pHouse->HouseType );
  ...
  newHouse->AddRoom ( ToCLRRoom ( pHouse->Room ) );

  return newHouse;
}


C++/CLR handles marshalling of basic types such as char * to String and int to Int32 for you, so if your CLRHouse class has a member function called SetHouseNumber that takes a String, then you can call it with a char * in C++/CLR and it will do the marshalling for you.

Microsoft actually tried to make it very easy to mix CLR and Native code, and it works very well if the division between your CLR and Native code is correctly designed.
 
Share this answer
 
v2
Comments
Ashish0603 8-May-12 23:26pm    
Its very difficult to imagine your solution. I am not able to digest the fact that you never needed to write a single marshal even when you were passing user-defined structures/classes. Can you pls throw more light on this?
1 more thing: in C++, do I need to create 2 copies of every class(here House and Room)? 1st copy of House will be containing pointers to Room and 2nd copy of House will not be containing pointers, instead direct object of Room. I then pass this 2nd copy to the C#/managed UI. Is this similar to what you are saying?
TRK3 9-May-12 12:51pm    
You have native C++ classes: House and Room.

Define managed C# classes in a C# module: CLRHouse and CLRRoom that are functionally equivalent to the C++ classes.

Write a C++/CLR routine that translates House to/from CLRHouse and Room to/from CLRRoom.

When you write C++/CLR it knows about both native classes stored on the unmanaged heap (dereferenced via * )and managed classes on the managed heap (dereferenced via ^ ).

See my updated solution with sample C++/CLR code.
The way you are marshaling your C++ class is not the best. From my experience, I would not want to marshal a C++ class using C# class, I would rather use a native memory pointer to hold the native C++ pointer with the size of the native c++ class, then you can simply manipulate your native pointer with the native C++ class instead of mess up with the C# class. The field of all the class are fixed, you can easily read or write them using Marshal static methods. When you pass a reference of the C# class to the native DLL, use IntPtr, the native memory pointer.

I am the author of

PInvoke Interop SDK for C++ DLL - A C# Wrapper Geneartor for C++ DLL

So I have lots of experience with working with C++ DLL from .NET. Feel free to ask questions here or on my blog as well if you are still having problem with your code since It has been almost a year since you posted here.
 
Share this answer
 
Another alternative that might works would be to defined a ref class from C++/CLI (mixed mode) that contains a pointer.

As a side note, it you want a unmanaged class containing a managed class, gcroot can be used.

Something like:
C++
ref class RoomManaged {
public:
  property System::String ^Name
  {
     System::String ^get() { return gcnew System::String(room->name); }
     // Define set if desired. A bit harder since your not using same charset
  }
  property int Size
  {
     int get() { return room->size; }
     vois set(int value) { room->size = value; }
  }

private:
  Room *room;  // Initialize somewhere...
};


This might not be the most efficient way to do it particulary if there is a lot of properties but it works quite well in many situations.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900