none
c# 操作DLL:如何使用结构体中指向另一结构体的指针? RRS feed

  • 问题

  • 详细描述:

      主要是由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 的封送处理错误,这些错误可能会损坏堆栈。 

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

    2010年6月22日 8:34

全部回复

  • public IntPtr lpBuffer;  

    改为

    public STATUS lpBuffer;  

    试试。


    family as water
    2010年6月22日 8:49
  • 我建议先用代码查看 lpBuffer 的值 (IntPtr),看看是不是这个 IntPtr 导致的问题。另外,错误 0xc0000005 您可以用 Marshal.GetExceptionForHR() 看看它到底是什么。


    Mark Zhou
    2010年6月22日 9:00
  • 呵呵,多谢回答,虽然并未解决!我再试试。

    2010年6月22日 9:19
  • 方法一-------------------------------
       第一个:

           [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.ByValTStr, SizeConst = 255)]
         //需固定长度,或者看方法二:定义为Inptr,后根据已知字符串长度的变量值转
                public string lpszExtra;
            }

           第二个:

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

            }

    Marshal.PtrToStructure必须知道根据Ptr首地址和Structure类型的固定长度

     Status = (STATUS)Marshal.PtrToStructure(Result.lpBuffer, typeof(STATUS));
    这样应该是OK的

    或者
    方法二-------------------------------
    给STATUS新增一个字符串长度字段

        第一个:

           [StructLayoutAttribute(LayoutKind.Sequential)]
            public struct STATUS
            {
                public ushort fwDevice;
                public ushort fwMedia;
                public ushort fwRetainBin;
                public ushort fwSecurity;
                public ushort usCards;
                public ushort fwChipPower;
                public int lpszExtraLen;   //增加长度变量
                public System.IntPtr lpszExtra;
            }

           第二个:

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

            }

          string lpszExtraInStatus=Marshal.PtrToStringAnsi(Result.lpBuffer.lpszExtra,Result.lpBuffer.lpszExtraLen)
         应该就可以得到你要的结果    

     

    2010年6月22日 16:01
  • 问题是:那两个结构体内容不能更改,因为那两个结构体是对应C++上的结构体,在已完成的很多C++非托管代码中都用到这两个结构体。那么,在不改变结构体内容时该如何做才能达到相应的效果呢?

    2010年6月23日 0:56
  • 问题是:那两个结构体内容不能更改,因为那两个结构体是对应C++上的结构体,在已完成的很多C++非托管代码中都用到这两个结构体。那么,在不改变结构体内容时该如何做才能达到相应的效果呢?

    public IntPtr lpBuffer;  

    通常应该写成

    public STATUS lpBuffer;  就如同c++里面一个字符串指针,在c#可以直接写成string一样。这个就是类似一个指针的意思。你先把大家提供的方法尝试一遍,ok?


    family as water
    2010年6月23日 1:17
  • 大哥,你测试过吗?我这可不行,如果我直接将public IntPtr lpBuffer写成public STATUS lpBuffer是没有错误,但不正确lpBuffer只有第一个字段有值且就是之前的IntPtr类型值。

    2010年6月23日 6:56
  • 0xc0000005就是Access Violation嘛,经典错误号码啊……九成你的lpBuffer是0,你可以看看是不是。
    2010年6月23日 7:16
  • 非0,从值上看应该是一个正确的指针。

    2010年6月24日 0:51
  • 错误的地址有可能是代码的地址
    2010年6月28日 13:48