各位大家好!我现在在做一个调用第三方接口的程序!其dll为c++写的,定义如下:
结构体:typedef struct tagDownDepInfo
{
char DPCODE1[6]; //一级部门
char DPCODE2[6]; //二级部门
char DPCODE3[8]; //三级部门
char DPCODE4[8]; //四级部门
char DPNAME1[60]; //
char DPNAME2[60]; //
char DPNAME3[60]; //
char DPNAME4[60]; //
}DOWNDEPINFO, FAR *PDOWNDEPINFO;
函数:int CapGetDepList(DOWNDEPINFO ** pINFO)
我在c#中的写法是:
结构体:    [StructLayout(LayoutKind.Sequential)]
    internal struct tagDownDepInfo
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
        public string DPCODE1; //一级部门
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
        public string DPCODE2; //二级部门
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
        public string DPCODE3; //三级部门
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
        public string DPCODE4; //四级部门
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 60)]
        public string DPNAME1; //
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 60)]
        public string DPNAME2; //
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 60)]
        public string DPNAME3; //
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 60)]
        public string DPNAME4; //
    }
函数:        [DllImport("Change.dll", CharSet = CharSet.Ansi)]
        internal unsafe static extern int CapGetDepList(ref tagDownDepInfo[] downinfo);
调用:            tagDownDepInfo[] downinfo = new tagDownDepInfo[100];//现已知道有100条数据
            int ret = Class1.CapGetDepList(ref downinfo);
结果:返回正确,但downinfo 却变成了1行,这是为什么呢?请大家指教谢谢!在线等!分数不多,请谅解!

解决方案 »

  1.   

    如果是ref  可能在方法里改变了引用..
      

  2.   

    internal struct tagDownDepInfo
        {
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
            public char[] DPCODE1; //一级部门
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
            public char[] DPCODE2; //二级部门 
            ....
      

  3.   

    qqiuzaihui:我用这种方法试过的,但效果一样!
      

  4.   

    import的时候参数加上修饰[MarshalAs(UnmanagedType.LPArray, SizeConst=100)] 
      

  5.   

    guoyichao:按照你的说法做了 ,但运行时出现“无法封送处理“parameter #1”: 无法对 ByRef 数组参数使用 SizeParamIndex”的错误。这是为什么呢?
      

  6.   

    int CapGetDepList(DOWNDEPINFO ** pINFO) 
    这个定义是说里面的DOWNDEPINFO是CapGetDepList分配的还是外面分配的?
    如果内部分配 定义ok
    不是 应改成int CapGetDepList(DOWNDEPINFO *pINFO)
    看你程序的用法,ms是下面这种不说这个 你要知道这点 C#的数组的内存是manage的,而c++不是,所以当用ref传出去时,不知道传出去实际数组指针的大小,所以,internal unsafe static extern int CapGetDepList(ref tagDownDepInfo[] downinfo); 这种写法有点问题的
    一般,遇到ref数组之类的 都得用IntPtr 如果数组是传入的,嗯,这有点麻烦,要首先申请一块对应的内存,调用Marshal.AllocHGlobal分配相应的内存,大小可以用Marshal.sizeof 自己调用Marshal.Copy 一个一个拷进去(我就是这么用的,没找到好的方法)用后释放内存
    所以 我猜你的dll导入函数定义应为
    [DllImport("Change.dll", CharSet = CharSet.Ansi)] 
            private static extern int CapGetDepList(IntPtr p);
    用Marshal.PtrToStructure获得执行结果
    还有传入指针,而没有大小,是一个不怎么好的函数定义方式
      

  7.   

     chenchangxiong:我按你的写法:
    声明:        [DllImport("Change.dll", CharSet = CharSet.Ansi)]
            internal unsafe static extern int CapGetDepList(IntPtr downinfo);
    调用:            IntPtr p1 = new IntPtr();
                p1 = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(tagDownDepInfo))*i);
                int ret = Class1.CapGetDepList(p1);
    出现:“尝试读取或写入受保护的内存。这通常指示其他内存已损坏。”的异常了!
      

  8.   

    还有这个定义是说里面的DOWNDEPINFO是CapGetDepList分配的还是外面分配的? 是外面分配的!
      

  9.   

    你dll里面的指针是不是越界了 我搭了个简单的框架试了下 没问题的
      

  10.   

    int CapGetDepList(DOWNDEPINFO *pINFO) 
    还有 把dll里的函数定义改成上面的形式
      

  11.   

    谢谢!不好意思,刚远程有点别的事情去了,
    这个函数是别人的,我没办法改的呢!只能按照他的说明调用,他这个是先通过别的函数取出有多少比数据,然后定义同样大小的一个结构数组传进去,我付上他的c++例子吧:
                  DOWNDEPINFO *pInfo=new DOWNDEPINFO[cou];
    memset(pInfo, 0, sizeof(DOWNDEPINFO)*cou);
    int nFlag=ccFun.CapGetDepList(&pInfo);
    这个是可以的呢!在c#中就不明白了!还在郁闷中…………
      

  12.   


    这。
    一定要这么做就定义IntPtr的数组啊
    dllimport的声明还是
    [DllImport("Change.dll", CharSet = CharSet.Ansi)] 
            private static extern int CapGetDepList(IntPtr p); 
    可能的一些步骤为
     IntPtr[] ptArray = new IntPtr[1];
                ptArray[0] = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(tagDownDepInfo)) * 100);
                IntPtr pt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)) * 1);
                Marshal.Copy(ptArray, 0, pt, 1);
    然后
    CapGetDepList(pt);
    拿到数据
    ps:你可以说下写这个接口的人,定义个数组 尽然没有数组大小的参数,还有只需要1维的 搞个2维的,
    那2维变3维 啊哈
      

  13.   

    谢谢chenchangxiong的回复,这两天干别的事情去了,没看!我用你的调用成功了,但我怎么样从这个指针中取出这个结构体数组的数据呢?告诉我了我就要结贴了!
      

  14.   


    for (int i = 0; i < size; i++)
                {
                    tagDownDepInfo tagInfo = (tagDownDepInfo)Marshal.PtrToStructure((IntPtr)((UInt32)ptArray[0] + i * s), typeof(tagDownDepInfo));
                }
                    Marshal.FreeHGlobal(ptArray[0]);
                    Marshal.FreeHGlobal(pt);试试上面的取数据方法
      

  15.   

    chenchangxiong:太谢谢你了,让我学习了c#中指针的使用。数据取到了。结贴!
      

  16.   

    多谢chenchangxiong大师, 我在项目中遇到了同样的问题, 您的方案非常有效, 在此感谢了!!
      

  17.   

    恩。感谢、我也用到了
    对此有的点想法不知道是否正确
    1.当dll中的参数只是结构体时候可以选择传入参数 ref structureParam obj
    2.当dll中参数是结构体指针或指向结构体数组的指针的时候选择intptr ptr传入参数
    是这个意思吧?
      

  18.   

    C#调用c++dll文件是一件很麻烦的事情,首先面临的是数据类型转换的问题,相信经常做c#开发的都和我一样把学校的那点c++底子都忘光了吧(语言特性类)。网上有一大堆得转换对应表,也有一大堆的转换实例,但是都没有强调一个更重要的问题,就是c#数据类型和c++数据类型占内存长度的对应关系。如果dll文件中只包含一些基础类型,那这个问题可能可以被忽略,但是如果是组合类型(这个叫法也许不妥),如结构体、类类型等,在其中的成员变量的长度的申明正确与否将决定你对dll文件调用的成败。如有以下代码,其实不是dll文件的源码,而是厂商给的c++例子代码c++中的结构体申明 typedef struct{ unsigned char Port; unsigned long Id; unsigned char Ctrl; unsigned char pData[8];}HSCAN_MSG;c++中的函数申明(一个c++程序引用另一个c++的dll文件)extern "C" int _stdcall HSCAN_SendCANMessage(unsigned char nDevice,unsigned char nPort,HSCAN_MSG *msg,int nLength); c++中的调用:....HSCAN_MSG msg[100]; ..... HSCAN_SendCANMessage(m_nDevice,m_nPort,msg,nFrames);由上述代码可见,msg是个结构体的数组。下面是我的c#的代码c#结构体申明:(申明成) [StructLayout(LayoutKind.Sequential)]        public struct  HSCAN_MSG        {    // UnmanagedType.ByValArray, [MarshalAs(UnmanagedType.U1)]这个非常重要,就是申明对应类型和长度的            [MarshalAs(UnmanagedType.U1)]            public byte Port;            [MarshalAs(UnmanagedType.U4)]            public uint nId;            [MarshalAs(UnmanagedType.U1)]            public byte nCtrl;            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]            public byte[] pData;        };   c#函数申明[DllImport("HS2106API.dll")]        public static extern int HSCAN_SendCANMessage(             byte nDevice, byte nPort, HSCAN_MSG[] pMsg, int nLength); C#函数调用 HSCAN_MSG[] msg = new HSCAN_MSG[1]; //发送缓冲区大小可根据需要设置;            for (int yy = 0; yy < msg.Length; yy++)            {                msg[yy] = new HSCAN_MSG();            }    //...结构体中的成员的实例化略    HSCAN_SendCANMessage(0x0, 0x0, msg, 1)  那些只能用指针不能用结构体和类的地方  c++中的结构体申明 typedef struct{ unsigned char Port; unsigned long Id; unsigned char Ctrl; unsigned char pData[8];}HSCAN_MSG; c++中的函数申明(一个c++程序引用另一个c++的dll文件)
    extern "C" int _stdcall HSCAN_SendCANMessage(unsigned char nDevice,unsigned char nPort,HSCAN_MSG *msg,int nLength); 
    c#中的结构体申明:
    [StructLayout(LayoutKind.Sequential)]
            public struct HSCAN_MSG
            {
                [MarshalAs(UnmanagedType.U1)]
                public byte Port;
                /// <summary>
                /// 节点标识,nEFF=1 时(扩展帧),为29 位nEFF=0(标准帧)时,为11 位;
                /// </summary>
                [MarshalAs(UnmanagedType.U4)]
                public uint nId;
                [MarshalAs(UnmanagedType.U1)]
                public byte nCtrl;
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
                public byte[] pData;
            }; 
     
    c#函数的调用:包含使用指针IntPtr替代结构体数组和读取IntPtr的方法
    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();
                    }
      

  19.   

    chenchangxiong
    感谢你,感谢上面所有的人,感谢...
    折腾了我一天了。