问题描述
调取这个C++dll中的某个函数,这个函数返回回来的是个结构体指针,而我需要获取的值在这个结构体里面的联合体(Value)中,如何做处理呢,C++原型:typedefstruct_TAG_VALUE_OUT_{unsignedintID;//TagIDunsignedcharType;//类型unsignedcharQuality;//质量TIMESTAMPTimestamp;//时间戳PointValueValue;//值联合体}PointData;typedefunion_TAG_VALUE_GROUP_OUT_{unsignedchardigitalVal;//Digital(8bits)shortint16Val;//short(16bits)intint32Val;//int(32bits)floatfloatVal;//float(4Bytes)doubledoubleVal;//double(8Bytes)char*stringVal;//stringpoint(4Bytes)->778BytesBuffer}PointValuetypedefstruct_RYTHON_TIMESTAMP_//毫秒级时间戳类型定义{RYTHON_TIME_SEC_LEVELtimeStamp;//秒shortms;//毫秒}TIMESTAMP;调用函数为:intRYTHONAPIDBSN_GetValue(INRYTHON_SRV_HANDLEnHandle,INTAG_IDnTagID,OUTPointData*pPointData);前两个值我是保证正确的,但是如何接受这个返回的结构体指针,然后从里面读取数据呢?返回回来的指针值跟TagID号是一样的,显然Value不是这个值,估计要从指针指向的内存块中读取这个结构体里面的联合体,请问如何处理???
解决方案
解决方案二:
这个问题不难,注意一点:在C#中联合这种类型是不能直接封送到非托管内存的,因为内存边界对不齐。换句话说,每一个可能的联合类型都需要在C#中声明相应的类,并根据需要补充数据进行对齐。
解决方案三:
这个是标准的capi,把需要的结构体一一对应好,就应该没有问题[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi,Pack=1)]publicstructTIMESTAMP{///<summary>///秒级时间戳///</summary>publicintSecond;//这个应该是Int类型,具体应该看.h文件中的定义///<summary>///毫秒///</summary>publicshortMilliSecond;}[System.Runtime.InteropServices.StructLayout(LayoutKind.Explicit)]publicstructValue{[FieldOffset(0)]publicbyteByte;[FieldOffset(0)]publicshortShort;[FieldOffset(0)]publicintInteger;[FieldOffset(0)]publicfloatFloat;[FieldOffset(0)]publicdoubleDouble;///<summary>///字符串///</summary>[FieldOffset(0)]publicIntPtrString;}[StructLayout(LayoutKind.Explicit,CharSet=CharSet.Ansi,Pack=1)]publicstructPointData{[FieldOffset(0)]publicuintID;[FieldOffset(4)]publicbyteType;[FieldOffset(5)]publicbyteStatus;[FieldOffset(6)]publicTIMESTAMPTime;[FieldOffset(12)]publicValueValue;}
解决方案四:
C#里有指针,你C++怎么声明的,C#就怎么调用,C#里pPointData.Value就是联合体的值,具体要啥值你自己强转就行了
解决方案五:
函数定义使用DllImport[DllImport("yourdllname",EntryPoint="DBSN_GetValue",CharSet=CharSet.Ansi,CallingConvention=CallingConvention.StdCall)]publicstaticexternintGetValue(intnHandle,uinttagID,outPointDatapDate);
这个PointData需要在调用前申请,使用后释放
解决方案六:
这个函数无法进行P/INVOKE,因为返回值包含了非托管的指针,必定会造成内存泄漏。这个函数即使在C内部也是危险的,因为结构体内部包含了指针,这意味着你释放这个结构体内存时,必须要先释放内部指针的内存,否则就会造成内存泄漏,因此一般不会定义这样的结构体,除非是自己内部的调用,自己清楚释放过程。
解决方案七:
这东西我只能说呵呵了两种语言要相互调用,起码数据类型要统一,要使用2种语言都支持的某种数据类型你C#里定义个返回DataTable的函数,C++里能认识才怪了,除非你自己在C++里实现这个DataTable类型你这个问题也一样,C里定义的结构体里带指针,C#要想用,就得自己先实现完全相同的类型
解决方案八:
定义相同类型也没用,方法返回的不是结构体而是结构体的指针!尼玛又没给出内存地址,C#怎么知道你给指在什么地方?
解决方案:
引用6楼Z65443344的回复:
这东西我只能说呵呵了两种语言要相互调用,起码数据类型要统一,要使用2种语言都支持的某种数据类型你C#里定义个返回DataTable的函数,C++里能认识才怪了,除非你自己在C++里实现这个DataTable类型你这个问题也一样,C里定义的结构体里带指针,C#要想用,就得自己先实现完全相同的类型
标准C的话,C#使用毫无压力啊。各种语言交互都使用纯C的接口或者COM。
解决方案:
引用5楼qldsrx的回复:
这个函数无法进行P/INVOKE,因为返回值包含了非托管的指针,必定会造成内存泄漏。这个函数即使在C内部也是危险的,因为结构体内部包含了指针,这意味着你释放这个结构体内存时,必须要先释放内部指针的内存,否则就会造成内存泄漏,因此一般不会定义这样的结构体,除非是自己内部的调用,自己清楚释放过程。
这个API设计是没问题的,不牵涉到内存分配释放。Win32API中传递结构体指针的API太多了。
解决方案:
很简单的问题,MSDN中有现成的例子。
解决方案:
返回指针,就用IntPtr接受,然后Marshal.PtrToStructure把指针指向数据拷贝到托管结构体里不久好了
解决方案:
引用10楼akirya的回复:
很简单的问题,MSDN中有现成的例子。
你的示例我看了,定义的类型中不含指针,那样肯定不会有问题,而楼主的则不同,他那个是要两次分配内存,我虽然没写过C++,但我写过C。我不清楚C#的非托管内存和C++里面的内存地址是否重叠,如果重叠,则可以使用Marshal类来释放这个结构体内指针的内存,也可以去读取它,如果内存地址是独立的,那C#在调用C++函数后,就得不到那个指针所指向内存的内容,且会造成内存泄漏。毕竟这个是P/INVOKE,和C++里面直接include不同。
解决方案:
引用12楼qldsrx的回复:
Quote: 引用10楼akirya的回复:
很简单的问题,MSDN中有现成的例子。你的示例我看了,定义的类型中不含指针,那样肯定不会有问题,而楼主的则不同,他那个是要两次分配内存,我虽然没写过C++,但我写过C。我不清楚C#的非托管内存和C++里面的内存地址是否重叠,如果重叠,则可以使用Marshal类来释放这个结构体内指针的内存,也可以去读取它,如果内存地址是独立的,那C#在调用C++函数后,就得不到那个指针所指向内存的内容,且会造成内存泄漏。毕竟这个是P/INVOKE,和C++里面直接include不同。
char*么?lz的问题是取结构体中的union。
解决方案:
引用9楼akirya的回复:
Quote: 引用5楼qldsrx的回复:
这个函数无法进行P/INVOKE,因为返回值包含了非托管的指针,必定会造成内存泄漏。这个函数即使在C内部也是危险的,因为结构体内部包含了指针,这意味着你释放这个结构体内存时,必须要先释放内部指针的内存,否则就会造成内存泄漏,因此一般不会定义这样的结构体,除非是自己内部的调用,自己清楚释放过程。这个API设计是没问题的,不牵涉到内存分配释放。Win32API中传递结构体指针的API太多了。
+1同意,自己申请,自己释放,没有问题的
解决方案:
引用4楼xian_wwq的回复:
函数定义使用DllImport[DllImport("yourdllname",EntryPoint="DBSN_GetValue",CharSet=CharSet.Ansi,CallingConvention=CallingConvention.StdCall)]publicstaticexternintGetValue(intnHandle,uinttagID,outPointDatapDate);这个PointData需要在调用前申请,使用后释放
兄弟,你这里调用GetValue函数时,定义的参数是outPointData,实际上,他返回来的应该是个结构体指针啊,取得位置应该不对。
解决方案:
引用11楼wjq的回复:
返回指针,就用IntPtr接受,然后Marshal.PtrToStructure把指针指向数据拷贝到托管结构体里不久好了
按照这个API的函数介绍,返回来的应该是个结构体指针,我用outIntPtr回来能得到一个值,但是如何通过指针来访问到该指针指向的内存内容呢?Marshal.PtrToStructure怎么用
解决方案:
标题很劲爆,进来看看,纯支持
解决方案:
引用15楼aqjwqrllw的回复:
Quote: 引用4楼xian_wwq的回复:
函数定义使用DllImport[DllImport("yourdllname",EntryPoint="DBSN_GetValue",CharSet=CharSet.Ansi,CallingConvention=CallingConvention.StdCall)]publicstaticexternintGetValue(intnHandle,uinttagID,outPointDatapDate);这个PointData需要在调用前申请,使用后释放
兄弟,你这里调用GetValue函数时,定义的参数是outPointData,实际上,他返回来的应该是个结构体指针啊,取得位置应该不对。
指针就是个地址呀,c#中的out和ref类型传的一样也是地址你按照dll的约定定义对应的结构体,传给dll因为结构体已经处理了字节对齐问题dll把数据返回给调用者,会有什么问题呢?
解决方案:
引用18楼xian_wwq的回复:
Quote: 引用15楼aqjwqrllw的回复:
Quote: 引用4楼xian_wwq的回复:
函数定义使用DllImport[DllImport("yourdllname",EntryPoint="DBSN_GetValue",CharSet=CharSet.Ansi,CallingConvention=CallingConvention.StdCall)]publicstaticexternintGetValue(intnHandle,uinttagID,outPointDatapDate);这个PointData需要在调用前申请,使用后释放
兄弟,你这里调用GetValue函数时,定义的参数是outPointData,实际上,他返回来的应该是个结构体指针啊,取得位置应该不对。
指针就是个地址呀,c#中的out和ref类型传的一样也是地址你按照dll的约定定义对应的结构体,传给dll因为结构体已经处理了字节对齐问题dll把数据返回给调用者,会有什么问题呢?
intsize=Marshal.SizeOf(typeof(PointData));IntPtrPointDataPtr=Marshal.AllocHGlobal(size);intcc=DBSN_GetValue(pnServerHandle,pTagID,outPointDataPtr);PointDatapPointData=(PointData)Marshal.PtrToStructure(PointDataPtr,typeof(PointData));Console.WriteLine(pPointData.Value.Float+"=="+PointDataPtr+"=="+pTagID);Marshal.FreeHGlobal(PointDataPtr);Console.WriteLine("结束");Console.Read();我是这样写的,out回来一个指针,然后通过指针找结构体,但是问题依旧
解决方案:
引用19楼aqjwqrllw的回复:
Quote: 引用18楼xian_wwq的回复:
Quote: 引用15楼aqjwqrllw的回复:
Quote: 引用4楼xian_wwq的回复:
函数定义使用DllImport[DllImport("yourdllname",EntryPoint="DBSN_GetValue",CharSet=CharSet.Ansi,CallingConvention=CallingConvention.StdCall)]publicstaticexternintGetValue(intnHandle,uinttagID,outPointDatapDate);这个PointData需要在调用前申请,使用后释放
兄弟,你这里调用GetValue函数时,定义的参数是outPointData,实际上,他返回来的应该是个结构体指针啊,取得位置应该不对。
指针就是个地址呀,c#中的out和ref类型传的一样也是地址你按照dll的约定定义对应的结构体,传给dll因为结构体已经处理了字节对齐问题dll把数据返回给调用者,会有什么问题呢?
intsize=Marshal.SizeOf(typeof(PointData));IntPtrPointDataPtr=Marshal.AllocHGlobal(size);intcc=DBSN_GetValue(pnServerHandle,pTagID,outPointDataPtr);PointDatapPointData=(PointData)Marshal.PtrToStructure(PointDataPtr,typeof(PointData));Console.WriteLine(pPointData.Value.Float+"=="+PointDataPtr+"=="+pTagID);Marshal.FreeHGlobal(PointDataPtr);Console.WriteLine("结束");Console.Read();我是这样写的,out回来一个指针,然后通过指针找结构体,但是问题依旧
思路不对,按照我上面定义的结构体,用DllImport,除了string类型不需要Marshal的转换
解决方案:
引用12楼qldsrx的回复:
Quote: 引用10楼akirya的回复:
很简单的问题,MSDN中有现成的例子。你的示例我看了,定义的类型中不含指针,那样肯定不会有问题,而楼主的则不同,他那个是要两次分配内存,我虽然没写过C++,但我写过C。我不清楚C#的非托管内存和C++里面的内存地址是否重叠,如果重叠,则可以使用Marshal类来释放这个结构体内指针的内存,也可以去读取它,如果内存地址是独立的,那C#在调用C++函数后,就得不到那个指针所指向内存的内容,且会造成内存泄漏。毕竟这个是P/INVOKE,和C++里面直接include不同。
引用1楼LargeSkyMensk的回复:
这个问题不难,注意一点:在C#中联合这种类型是不能直接封送到非托管内存的,因为内存边界对不齐。换句话说,每一个可能的联合类型都需要在C#中声明相应的类,并根据需要补充数据进行对齐。
couldyouguyspleaseexplainwhatthehellthishasanythingtodowithaddressalignment?Or"addressoverlap"?Whatthehackisthat?couldyoupleaseelaborateabit?