使用指向其他类对象的指针编组类 [英] Marshalling class with pointers to other class objects

查看:78
本文介绍了使用指向其他类对象的指针编组类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

亲爱的,

我想弄清楚一件事。我在C ++中有 A类 B类。 A包含一个变量,它是指向B对象的指针。



现在问题是,我试图在C#中检索A的对象。我在C#中有类似的A和B类定义,但是在A中,我没有将指针存储到B.而是A直接包含B的对象。我如何编组?我是否需要创建2个不同的A定义:

1使用IntPtr到B而另一个用B作为A的属性?



请告诉我可以做些什么。我完全迷失在编组中。

  //   CHouse类的C ++定义 
class CHouse
{
public
CHouse( void );
~CHouse( void );

public
char * HouseNumber;
char * HouseType;
char *条件;
double MarketValue;
房间*房间;
};

// Room class的C ++定义
class 会议室
{
public
char *名称;
int size;

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

// C ++公开函数
_EXPORTS_API void getData(CHouse * houses)
{
houses [ 0 ]。HouseNumber = 882-100;
容纳[ 0 ]。HouseType = 单身家庭;
容纳[ 0 ]。条件= ;
容纳[ 0 ]。MarketValue = 685440 ;

房间r;
r.name = Adghgh;
r.size = 1 ;
容纳[ 1 ]。room =& r;

容纳[ 1 ]。HouseNumber = 8882-100;
容纳[ 1 ]。HouseType = GSingle Family;
容纳[ 1 ]。条件= GExcellent;
容纳[ 1 ]。MarketValue = 885440 ;

房间r1;
r1.name = Basdf;
r1.size = 2 ;
容纳[ 0 ]。room =& r1;
}

// House类的C#定义
[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条件;
[MarshalAs(UnmanagedType.R8)]
public double MarketValue;
[MarshalAs(UnmanagedType.SysUInt)]
public IntPtr r;

public HouseWithPtr()
{
r = new IntPtr ();
}
}
// Room类的C#定义
[StructLayout(LayoutKind.Sequential,CharSet = CharSet.Ansi)]
class Room
{
[MarshalAs(UnmanagedType) .LPStr)]
public 字符串名称;
[MarshalAs(UnmanagedType.I4)]
public int size;
}

// 用于调用C ++函数的C#代码
getData getDataObj =(getData)Marshal.GetDelegateForFunctionPointer(dllEntryPoint,typeof(getData));

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

IntPtr 已分配= Marshal.AllocHGlobal(Marshal.SizeOf(typeof(HouseWithPtr)));
getDataObj(已分配);
Marshal.PtrToStructure(已分配,房屋[ 0 ]);

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

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



使用这种方法,House正确填充,但House内部的房间没有正确填充。这段代码有什么问题?



谢谢,

Ashish

解决方案

Yuck ...



仔细查看您的托管/本机界面,并找出如何最大限度地减少此类事情。



我有一个相当复杂的系统,有很多原生C ++,它做了很多计算密集的东西和一些C#用户界面组件(大多数是一些我不能买的花哨的第三方库)原生格式)。



在一个地方我需要传递一个相当复杂的树,如从原生C ++到C#的结构 - 我认为最简单的是在C#中创建一个并行类然后编写一个C ++ / CLR例程,它基本上将数据从native-C类复制到等效的managed-C#类。



我有一个例程从本机类复制到托管类,还有一个例程从托管类复制到本机类。我使用它来传递本机/托管接口上的所有数据。



我在原生方面完成所有繁重的计算工作,将其隐藏并传递给托管用户界面,让用户操纵他喜欢的一切,然后隐蔽然后传回去。



C ++ / CLR处理所有魔法,我从不写一个单一的元帅声明。



如果你发现你自己编写Marshal或Marshal超过一两次,那么现在是时候回顾一下事情如何分裂并找出更好的分工。



假设您编写了一个名为MyCSharpModule的C#模块,其中包含类CLRHouse和CLRRoom,那么您可以编写一个类似于这样的C ++ / CLR例程:



< pre lang =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为您处理基本类型的编组,例如char *到String和int到Int32,所以如果您的CLRHouse类有一个名为SetHouseNumber的成员函数,它接受一个String,那么您可以在C ++ / CLR中使用char *调用它,它将为您进行编组。



Microsoft实际上试图将CLR和Native代码混合起来非常容易,如果正确设计了CLR和Native代码之间的划分,它就能很好地工作。


你的方式正在整理你的C ++课程并不是最好的。根据我的经验,我不想使用C#类编组C ++类,我宁愿使用本机内存指针来保存本机C ++类的大小,然后你可以简单地操作本机指针本机C ++类而不是搞乱C#类。所有类的字段都是固定的,您可以使用Marshal静态方法轻松读取或写入它们。当您将C#类的引用传递给本机DLL时,请使用本机内存指针IntPtr。



我是
$ b $的作者b

PInvoke Interop用于C ++ DLL的SDK - 用于C ++ DLL的AC#Wrapper Geneartor



所以我在使用.NET的C ++ DLL方面有很多经验。如果你的代码仍有问题,请随时在这里或我的博客上提问。因为你已经差不多用了一年了。


可能有效的另一种选择是从C ++ / CLI(混合模式)定义一个包含指针的ref类。



作为旁注,你需要一个包含非托管类的非托管类一个托管类,可以使用gcroot。



类似于:

  ref   class  RoomManaged {
public
property System :: String ^ Name
{
系统: :字符串 ^ get(){返回 gcnew System :: String (room-> name); }
// 如果需要,定义集合。因为你没有使用相同的字符集而有点困难
}
property int 大小
{
int get(){ return room->尺寸; }
vois set( int value ){room-> size = ; }
}

私人
房间*房间; // 在某处初始化...
};





如果有很多属性,这可能不是最有效的方法,但它在很多情况下都能很好地工作。


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++ 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

解决方案

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:

#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.


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.


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:

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.


这篇关于使用指向其他类对象的指针编组类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆