c# 操作C或C++ DLL:如何解析结构体中指向另一结构体的指针?

yinxue2010 2011-07-14 12:23:18
详细描述:

主要是由C#调用DLL库造成的问题。 现有两个C#结构体,是从C语言中翻译过来的。

第一个:

[StructLayoutAttribute(LayoutKind.Sequential)]
public struct STATUS
{
public ushort fwDevice;
public ushort fwMedia;
public ushort fwRetainBin;
public ushort fwSecurity;
public ushort usCards;
public ushort fwChipPower;
[MarshalAsAttribute(UnmanagedType.LPStr)]
public string lpszExtra;
}

第二个:

[StructLayoutAttribute(LayoutKind.Sequential)]
public struct RESULT
{
public uint RequestID;
public ushort hService;
public int hResult;
public int u;
public IntPtr lpBuffer; //指向第一个结构体的指针;
}

通过消息处理,我从WinForm的Message.LParam中获得了一个第二个结构体(RESULT)的指针并将其转化为了RESULT的一个变量,通过这个变量可以正常访问其字段,唯有: “public IntPtr lpBuffer; //指向第一个结构体(STATUS)的指针”使用失败,该如何定义和操作结构体才能正确使用该指针呢?

补充说明:我用WinForm窗体来接收C++编写的DLL发送过来的消息,DLL导出函数中将WinForm窗体句柄传给导出函数,其会将消息传送给WinForm。经测试证明:DLL传过来的消息Message.LParam指针可以转化为对应的结构体,就是该结构体内的指向另结构体的指针出错,不能转化为对于的结构体,但值有的。为什么呢?该如何解决这类问题?


产生错误的代码: Status = (STATUS)Marshal.PtrToStructure(Result.lpBuffer, typeof(STATUS)); 其中Status为STATUS类型的结构体,Result为RESULT类型的结构体变量。

错误提示: 检测到FatalExecutionEngineError 运行时遇到了错误。此错误的地址为 0x661ffc37,在线程 0xe0c 上。错误代码为 0xc0000005。此错误可能是 CLR 中的 bug,或者是用户代码的不安全部分或不可验证部分中的 bug。此 bug 的常见来源包括用户对 COM-interop 或 PInvoke 的封送处理错误,这些错误可能会损坏堆栈。

请求各位高手指教啊!我郁闷一个上午了,都没有找到解决方法。

...全文
933 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
yinxue2010 2011-07-18
  • 打赏
  • 举报
回复
谢谢你 “sdl2005lyx” 我的C#调用C 动态库DLL 是第三方的是一个准标的,不能被修改。
这个问题我通过5天的攻关,终于解决了。关键是 C# 中 public struct WFSRESULT
这个结构的定义要改变一下:
将原来的
[StructLayout(LayoutKind.Sequential)]
public struct WFSRESULT
{
改为:(关键)
[StructLayout(LayoutKind.Explicit)]
public unsafe struct _wfs_result
{
这种方式的定义就可以了。
sdl2005lyx 2011-07-18
  • 打赏
  • 举报
回复
解决了就好!
yinxue2010 2011-07-15
  • 打赏
  • 举报
回复
sdl2005lyx 有联系方式吗? 我的QQ:172970951 MSN:yinxuexue07@hotmail.com
yinxue2010 2011-07-15
  • 打赏
  • 举报
回复
sdl2005lyx

谢谢,是也在做XFS 的开发吗?
yinxue2010 2011-07-15
  • 打赏
  • 举报
回复
mjp1234airen4385
我按你提供的方式,将public IntPtr lpBuffer 定义为和_wfs_idc_status 一样大小的byte[],但是从获取的值中解析出来的值看不明白 返回的如下类式值
"?\0\0\0\0\0\0\0\b\0?\f\0\0\0\0\0\0\0\0\0\0\0\0\0\0讪?毀?\0\0\0\0\0\0\0\0\0\0?\f.€嶴ystem.Configuration.ConfigurationPermissionAttribute, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3aT\fUnrestricted\0\0?\fP?4鄩\0\0?\b\0\0\0\0\0\0\0?\0\0\0\0\0\0\0\0"
sdl2005lyx 2011-07-15
  • 打赏
  • 举报
回复
关键是,_wfs_idc_status的字符指针是变长的,不能实现确定byte[]大小!
Lz,我正在帮你写程序测试,报你一样的问题,正在调试,挑好了,发你!
yinxue2010 2011-07-15
  • 打赏
  • 举报
回复
public IntPtr lpBuffer;//返回的指针,指向_wfs_idc_status 这个结构

这个要定义成一个结构体的。而不是一个指针。
或者定义为和_wfs_idc_status 一样大小的byte[],
到时候,你可以把这个内存地址转换为指针,指针再转化为结构体。
你就可以读取到其中的值了。

怎么将byte[] 这个内存地址转换为指针
yinxue2010 2011-07-15
  • 打赏
  • 举报
回复
C 的原型结构体 是不能改动的,是第三方的。
mjp1234airen4385 2011-07-15
  • 打赏
  • 举报
回复
或者定义为和_wfs_idc_status 一样大小的byte[],
到时候,你可以把这个内存地址转换为指针,指针再转化为结构体。
你就可以读取到其中的值了。
或者直接读取byte[]的也可以,就是要根据对应的结构体每一个字段大小,
读取相应的byte[]长度,然后转化为对应的结构体字段数据类型,就OK。

这个方法不能说是万能的,但是大部分情况下,也是好用的。
因为定义和C++对应的结构体,有时候非常麻烦,不断的改变数据类型尝试也不成功。

如果使用byte[]定义,只要定义的大小和C++中的结构体一样,就可以读取到数据。
读取到数据后,再按照C++中的几个他做处理,比如整数、字符串等,花点时间,调试几次,就可以了。
mjp1234airen4385 2011-07-15
  • 打赏
  • 举报
回复
public IntPtr lpBuffer;//返回的指针,指向_wfs_idc_status 这个结构

这个要定义成一个结构体的。而不是一个指针。
或者定义为和_wfs_idc_status 一样大小的byte[],
到时候,你可以把这个内存地址转换为指针,指针再转化为结构体。
你就可以读取到其中的值了。
或者直接读取byte[]的也可以,就是要根据对应的结构体每一个字段大小,
读取相应的byte[]长度,然后转化为对应的结构体字段数据类型,就OK。
sdl2005lyx 2011-07-15
  • 打赏
  • 举报
回复
问题在这个地方:
LPSTR lpszExtra; ==>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string lpszExtra;

LPSTR是字符指针,4个自己,而[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string lpszExtra;是256个字节,这样两边结构体大小不一致!

解决办法:
改变C结构:
char lpszExtra[256];

yinxue2010 2011-07-15
  • 打赏
  • 举报
回复
谢谢大家的回复,我将的问题在重新描述一下吧:
详细描述:
C 的原型结构体
typedef struct _wfs_result
{
REQUESTID RequestID;
HSERVICE hService;
SYSTEMTIME tsTimestamp;//这个对应下面的SYSTEMTIME 结构
HRESULT hResult;
union {
DWORD dwCommandCode;
DWORD dwEventID;
} u;
LPVOID lpBuffer; //返回的指针,指向WFSIDCSTATUS 这个结构
} WFSRESULT, * LPWFSRESULT;

typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME;

typedef struct _wfs_idc_status
{
WORD fwDevice;
WORD fwMedia;
WORD fwRetainBin;
WORD fwSecurity;
USHORT usCards;
WORD fwChipPower;
LPSTR lpszExtra;
} WFSIDCSTATUS, * LPWFSIDCSTATUS;


C#对应定义的结构体:
[StructLayout(LayoutKind.Sequential)]
public unsafe struct _wfs_result
{
public UInt32 RequestID;
public ushort hService;
public _SYSTEMTIME tsTimestamp;//对应下面的 _SYSTEMTIME
public int hResult;
public AnonymousStruct U;//对应下面的 AnonymousStruct 结构
public IntPtr lpBuffer;//返回的指针,指向_wfs_idc_status 这个结构
}

[StructLayout(LayoutKind.Sequential)]
public unsafe struct _SYSTEMTIME
{
public UInt16 wYear;
public UInt16 wMonth;
public UInt16 wDayOfWeek;
public UInt16 wDay;
public UInt16 wHour;
public UInt16 wMinute;
public UInt16 wSecond;
public UInt16 wMilliseconds;
}

[StructLayout(LayoutKind.Explicit)]
public unsafe struct AnonymousStruct
{
[FieldOffset(0)]
public UInt32 dwCommandCode;
[FieldOffset(0)]
public UInt32 dwEventID;
}

[StructLayout(LayoutKind.Sequential)]
public unsafe struct _wfs_idc_status
{
public UInt16 fwDevice;
public UInt16 fwMedia;
public UInt16 fwRetainBin;
public UInt16 fwSecurity;
public ushort usCards;
public UInt16 fwChipPower;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string lpszExtra;
}

通过Windows消息机制处理,我从WinForm的Message.LParam中获得了结构体(_wfs_result)的指针并将其转化为了_wfs_result的一个变量,通过这个变量可以正常访问其中定义的字段,唯有:“public IntPtr lpBuffer; //指向结构体(_wfs_idc_status)的指针”使用失败,该如何定义和操作结构体才能正确使用该指针呢?

补充说明:我用WinForm窗体来接收C++编写的DLL发送过来的消息,DLL导出函数中将WinForm窗体句柄传给导出函数,其会将消息传送给WinForm。经测试证明:DLL传过来的消息Message.LParam指针可以转化为对应的结构体,就是该结构体内的指向另结构体的指针出错,不能转化为对于的结构体,但值有的。为什么呢?该如何解决这类问题?


产生错误的代码: Status = (STATUS)Marshal.PtrToStructure(Result.lpBuffer, typeof(STATUS)); 其中Status为STATUS类型的结构体,Result为RESULT类型的结构体变量。
错误提示: 检测到FatalExecutionEngineError 运行时遇到了错误。此错误的地址为 0x661ffc37,在线程 0xe0c 上。错误代码为 0xc0000005。此错误可能是 CLR 中的 bug,或者是用户代码的不安全部分或不可验证部分中的 bug。此 bug 的常见来源包括用户对 COM-interop 或 PInvoke 的封送处理错误,这些错误可能会损坏堆栈。
sdl2005lyx 2011-07-15
  • 打赏
  • 举报
回复
C#互操作:
[StructLayout(LayoutKind.Sequential)]
public struct WFSRESULT
{
public UInt32 RequestID;
public ushort hService;
public _SYSTEMTIME tsTimestamp;//对应下面的 _SYSTEMTIME
public int hResult;
public AnonymousStruct U;//对应下面的 AnonymousStruct 结构
public IntPtr lpBuffer;//返回的指针,指向_wfs_idc_status 这个结构
}

[StructLayout(LayoutKind.Sequential)]
public struct _SYSTEMTIME
{
public UInt16 wYear;
public UInt16 wMonth;
public UInt16 wDayOfWeek;
public UInt16 wDay;
public UInt16 wHour;
public UInt16 wMinute;
public UInt16 wSecond;
public UInt16 wMilliseconds;
}
[StructLayout(LayoutKind.Explicit)]
public struct AnonymousStruct
{
[FieldOffset(0)]
public UInt32 dwCommandCode;
[FieldOffset(0)]
public UInt32 dwEventID;
}

[StructLayout(LayoutKind.Sequential)]
public struct _wfs_idc_status
{
public UInt16 fwDevice;
public UInt16 fwMedia;
public UInt16 fwRetainBin;
public UInt16 fwSecurity;
public ushort usCards;
public UInt16 fwChipPower;
public string lpszExtra;
}

[DllImport(@"E:\pvcs\utscada\Debug\ExamDll.dll", EntryPoint = "GetStauts")]
public static extern void GetStauts(ref WFSRESULT ret);

WFSRESULT ret=new WFSRESULT();
GetStauts(ref ret);

_wfs_idc_status stat = (_wfs_idc_status)Marshal.PtrToStructure(ret.lpBuffer, typeof(_wfs_idc_status));
sdl2005lyx 2011-07-15
  • 打赏
  • 举报
回复
问题已经找到!这是我测试的程序:
C++:
typedef struct _wfs_idc_status
{
WORD fwDevice;
WORD fwMedia;
WORD fwRetainBin;
WORD fwSecurity;
USHORT usCards;
WORD fwChipPower;
LPSTR lpszExtra;
} WFSIDCSTATUS, * LPWFSIDCSTATUS;

typedef struct _wfs_result
{

DWORD RequestID;
WORD hService;
SYSTEMTIME tsTimestamp;//这个对应下面的SYSTEMTIME 结构
HRESULT hResult;
union {
DWORD dwCommandCode;
DWORD dwEventID;
} u;
// LPVOID lpBuffer; //返回的指针,指向WFSIDCSTATUS 这个结构
WFSIDCSTATUS *lpBuffer;
} WFSRESULT, * LPWFSRESULT;

extern "C" void __declspec(dllexport) GetStauts(WFSRESULT* ret);
void API_CALL GetStauts(WFSRESULT* ret)
{
WFSIDCSTATUS* stat = new WFSIDCSTATUS ;
//注意,这里一定要new,用临时结构体变量,出了作用域范围就释放了,C#互操作那边就会异常
memset (stat, 0, (sizeof(WFSIDCSTATUS))*1);
stat->lpszExtra="测试123";
stat->fwChipPower=1;
stat->fwDevice=1;

stat->lpszExtra="123";
stat->fwChipPower=1;
ret->hService=1;
ret->lpBuffer=stat;
}
sdl2005lyx 2011-07-15
  • 打赏
  • 举报
回复
我的邮箱:sdl2002lyx@163.com,QQ公司封闭了,上班不方便,你及时发邮件即可!
isjoe 2011-07-14
  • 打赏
  • 举报
回复
利用Marshal类将第一个struct封送到非托管内存,并返回指针,赋值给第二个结构体指针


c#函数的调用:包含使用指针IntPtr替代结构体数组和读取IntPtr的方法
view plaincopy to clipboardprint?
HSCAN_MSG[] msg1 = new HSCAN_MSG[10];
for (int i = 0; i < msg1.Length; i++)
{
msg1[i] = new HSCAN_MSG();
msg1[i].pData = new byte[8];
}
IntPtr[] ptArray = new IntPtr[1];
ptArray[0] = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(HSCAN_MSG)) * 10);
IntPtr pt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(HSCAN_MSG)));
Marshal.Copy(ptArray, 0, pt, 1);

int count = HSCAN_ReadCANMessage(0x0, 0,pt, 10);

textBoxStatus.Text += "\r\n" + "读取0口:" + count.ToString() + "帧数据";
for (int j = 0; j < 10; j++)
{
msg1[j] =
(HSCAN_MSG)Marshal.PtrToStructure((IntPtr)((UInt32)pt+ j * Marshal.SizeOf(typeof(HSCAN_MSG)))
, typeof(HSCAN_MSG));
textBoxStatus.Text += "\r\n收到0口" + Convert.ToByte(msg1[j].pData[0]).ToString()
+ "|" + Convert.ToByte(msg1[j].pData[1]).ToString()
+ "|" + Convert.ToByte(msg1[j].pData[2]).ToString()
+ "|" + Convert.ToByte(msg1[j].pData[3]).ToString()
+ "|" + Convert.ToByte(msg1[j].pData[4]).ToString()
+ "|" + Convert.ToByte(msg1[j].pData[5]).ToString()
+ "|" + Convert.ToByte(msg1[j].pData[6]).ToString()
+ "|" + Convert.ToByte(msg1[j].pData[7]).ToString();
}
HSCAN_MSG[] msg1 = new HSCAN_MSG[10];
for (int i = 0; i < msg1.Length; i++)
{
msg1[i] = new HSCAN_MSG();
msg1[i].pData = new byte[8];
}
IntPtr[] ptArray = new IntPtr[1];
ptArray[0] = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(HSCAN_MSG)) * 10);
IntPtr pt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(HSCAN_MSG)));
Marshal.Copy(ptArray, 0, pt, 1);

int count = HSCAN_ReadCANMessage(0x0, 0,pt, 10);

textBoxStatus.Text += "\r\n" + "读取0口:" + count.ToString() + "帧数据";
for (int j = 0; j < 10; j++)
{
msg1[j] =
(HSCAN_MSG)Marshal.PtrToStructure((IntPtr)((UInt32)pt+ j * Marshal.SizeOf(typeof(HSCAN_MSG)))
, typeof(HSCAN_MSG));
textBoxStatus.Text += "\r\n收到0口" + Convert.ToByte(msg1[j].pData[0]).ToString()
+ "|" + Convert.ToByte(msg1[j].pData[1]).ToString()
+ "|" + Convert.ToByte(msg1[j].pData[2]).ToString()
+ "|" + Convert.ToByte(msg1[j].pData[3]).ToString()
+ "|" + Convert.ToByte(msg1[j].pData[4]).ToString()
+ "|" + Convert.ToByte(msg1[j].pData[5]).ToString()
+ "|" + Convert.ToByte(msg1[j].pData[6]).ToString()
+ "|" + Convert.ToByte(msg1[j].pData[7]).ToString();
}

threenewbee 2011-07-14
  • 打赏
  • 举报
回复
定义一个结构体,然后用 MemoryCopy API 把数据拷贝过来。
636f6c696e 2011-07-14
  • 打赏
  • 举报
回复
http://www.bianceng.cn/Programming/csharp/200911/11864.htm

110,545

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧