当前位置: 代码迷 >> C# >> c#调用c++dll,出错(未能封送类型,因为嵌入数组实例的长度与布局中声明的长度不匹配)
  详细解决方案

c#调用c++dll,出错(未能封送类型,因为嵌入数组实例的长度与布局中声明的长度不匹配)

热度:932   发布时间:2016-04-28 08:32:56.0
c#调用c++dll,报错(未能封送类型,因为嵌入数组实例的长度与布局中声明的长度不匹配)
c++函数原型:

int __stdcall misposTrans(void* input, void* output)

c++结构原型:

typedef struct
{
   char TransType[2];    //交易代码
   char CardNo[19];             //卡号
     char Amount[12];              //交易金额
     char Tip[12];               //小费金额(暂时不用,空格补齐)
}} ST_ICBC_MIS;


c#调用原型:
//C++ 的char对应C#的Byte类型,不是char,在C#中char是16位的。

        [DllImport("KeeperClient.dll", EntryPoint = "misposTrans", CallingConvention = CallingConvention.StdCall)]
        public static extern int misposTrans(ref ST_ICBC_MIS input, out ST_ICBC_MIS output);

c#结构:

 public struct ST_ICBC_MIS
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] //这里的2就是数组长度
        public Byte[] TransType;    //交易代码
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 19)]
        public Byte[] CardNo;        //卡号
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
        public Byte[] Amount;        //交易金额
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
        public Byte[] Tip;            //小费金额(暂时不用,空格补齐)



C#调用过程:
  
  
 ST_ICBC_MIS strInput = new ST_ICBC_MIS();//初始化结构
 ST_ICBC_MIS strOutput= new ST_ICBC_MIS();//初始化结构
strInput.TransType = new Byte[2];
strInput.CardNo = new Byte[19] ;
 strInput.Amount = new Byte[12] ;
 strInput.Tip = new Byte[12] ;

 Byte[] buf = new Byte[2]{0, 9};//赋值
strInput.TransType = buf;
 int i = IcbcMis.misposTrans(ref strInput, out strOutput);

这里报错!

{"未能封送类型,因为嵌入数组实例的长度与布局中声明的长度不匹配。"}


请指正!!!万分感谢!
------解决思路----------------------
 [DllImport("KeeperClient.dll", EntryPoint = "misposTrans", CallingConvention = CallingConvention.StdCall)]
 public static extern int misposTrans(ref ST_ICBC_MIS input, out ST_ICBC_MIS output);
将out改为ref试一试,依稀记得out和C++里**对应
还不行的话,
传送非托管参数,并固定住别让GC回收了
ST_ICBC_MIS strInput = new ST_ICBC_MIS();
GCHandle un_strInput = GCHandle.Alloc(strInput, GCHandleType.Pinned); 
ST_ICBC_MIS strOutput = new ST_ICBC_MIS();
GCHandle un_strOutput = GCHandle.Alloc(strOutput, GCHandleType.Pinned); 

调用:
int i = IcbcMis.misposTrans(ref un_strInput.AddrOfPinnedObject(), ref un_strOutput.AddrOfPinnedObject());
输出参数out还是ref各试一次吧
……
un_strInput.Free();
un_strOutPut.Free();


------解决思路----------------------
引用:
 [DllImport("KeeperClient.dll", EntryPoint = "misposTrans", CallingConvention = CallingConvention.StdCall)]
 public static extern int misposTrans(ref ST_ICBC_MIS input, out ST_ICBC_MIS output);
将out改为ref试一试,依稀记得out和C++里**对应
还不行的话,
传送非托管参数,并固定住别让GC回收了
ST_ICBC_MIS strInput = new ST_ICBC_MIS();
GCHandle un_strInput = GCHandle.Alloc(strInput, GCHandleType.Pinned); 
ST_ICBC_MIS strOutput = new ST_ICBC_MIS();
GCHandle un_strOutput = GCHandle.Alloc(strOutput, GCHandleType.Pinned); 

调用:
int i = IcbcMis.misposTrans(ref un_strInput.AddrOfPinnedObject(), ref un_strOutput.AddrOfPinnedObject());
输出参数out还是ref各试一次吧
……
un_strInput.Free();
un_strOutPut.Free();
更正下,调用时,实参前都不加ref
------解决思路----------------------
C++要4字节的指针,你传了整个的结构体,内存匹配不上吧(使用ref和out没尝试过,不知道会不会转成指针,如果转成了指针就忽略后面说的吧)。我有两个没试过的想法,LZ可以试试:
1. 在C#结构体上添加[MarshalAs(UnmanagedType.LPStruct)]
2. 函数声明为
[DllImport("KeeperClient.dll", EntryPoint = "misposTrans", CallingConvention = CallingConvention.StdCall)]
        public static extern int misposTrans(IntPtr input, IntPtr output);

使用Marshal.StructureToPtr将结构体转换成IntPtr传入

------解决思路----------------------
你现在的问题是代码的逻辑太混乱了
你先alloc了一个ptr,然后把strInput转换给了ptr,最后释放的ptr是指向strInput的,最初分配的那块内存已经泄露了
------解决思路----------------------
结构的定义使用 structLayout 属性。

不要再为结构里面的数组字段赋值了。
这也字段直接包含在 结构里面。
  相关解决方案