关于复杂结构数组平台调用数据封送的问题

langmafeng 2005-08-12 02:50:36
[StructLayout = LayoutKind.Sequential]
public struct A
{
...
...
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public B[] b
}

[StructLayout = LayoutKind.Sequential]
public struct B
{
...
...
}

[DllImport("...")]
public static extern int Test(..., [In, Out] ref A[] a);

我跟踪到Dll里调试的时候发现结构A中的数组b中的值全部丢掉了,而且在Dll里面为a赋值也都传不出来,请问在调用外部DLL时传递这种较为复杂的结构的数组该如何封送结构?谢谢!
...全文
322 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
langmafeng 2005-08-17
  • 打赏
  • 举报
回复
不好意思,上面是我发贴的时候Copy错了,实际上我测试的时候定义的跟楼上一样:)
算了,反正这个问题用其他方法也勉强能够解决,结贴吧
谢谢各位朋友!
速马 2005-08-16
  • 打赏
  • 举报
回复
[ StructLayout( LayoutKind.Sequential) ]
public struct A
{
public int a;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = xxx, ArraySubType = UnmanagedType.U1)]
public byte[] b;
}
langmafeng 2005-08-16
  • 打赏
  • 举报
回复
查到了一篇英文资料,搞了半天.Net Framework 1.1不支持这种结构的封送
http://www.dotnet247.com/247reference/msgs/29/147905.aspx
然后采用了http://blog.sunmast.com/sunmast/archive/2005/04/19/1739.aspx中的解决方法,但测试后发现该文档中的解决方法还是不能正确的传递“包含了结构数组的结构”的数组,只能传递单个“包含了结构数组的结构”,看来我目前只能用VC++再包一层,对上面的“包含了结构数组的结构”的数组将其分开一个一个的传了。非常感谢Jim3(Jim) !
langmafeng 2005-08-16
  • 打赏
  • 举报
回复
我参考了你的解决方法,但是我在测试时发现在传递“包含了结构数组的结构的数组”的时候仍然无法正确的传入,就是我上面代码中的Test方法,并在C#中将结构A重新定义了:
[ StructLayout( LayoutKind.Sequential) ]
public struct A
{
public int a;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 80, ArraySubType = UnmanagedType.U1)]
public B[] b;
}
在构造A数组的时候我调用了Struct2Bytes方法

不知道 Sunmast 有没有做过类似的测试
Jim3 2005-08-16
  • 打赏
  • 举报
回复
其实一直想搞清楚平台调用时数据封送到底是如何实现的
只是一是没有看到相关的资料,另外要自己去跟踪分析也没有这个耐心

所以只好帮你顶,看看有没有比较了解这方面的人来帮忙
速马 2005-08-16
  • 打赏
  • 举报
回复
我的解决办法就是用等长度的byte[]代替STRUCT[](长度需要自己计算)
然后用我写的那个Struct2Bytes方法传入
flyboy20 2005-08-15
  • 打赏
  • 举报
回复
顶哈
Tony_lau111082 2005-08-15
  • 打赏
  • 举报
回复
要知道你要调用的DLL以前C的格式,才能告诉你C#怎么封装啥
Jim3 2005-08-15
  • 打赏
  • 举报
回复
请参考msdn上面的例子(请留意结构MYPERSON3的使用和说明)

ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.2052/cpguide/html/cpconstructssample.htm

该示例说明如何传递指向另一个结构的结构,如何传递具有嵌入结构的结构,以及如何传递具有嵌入数组的结构

MyPerson3 包含作为嵌入结构的 MyPerson。嵌入到另一个结构中的结构可以通过将嵌入结构的元素直接放置在主结构中而单一化;该结构也可保留为嵌入结构,此示例中即如此处理
Jim3 2005-08-15
  • 打赏
  • 举报
回复
不知道你现在有没有搞定?我把我的理解写出来供你参考

类似嵌套结构有两种处理方式:1 嵌入到另一个结构中的结构可以通过将嵌入结构的元素直接放置在主结构中而单一化 2 该结构也可保留为嵌入结构

用方法1肯定没问题,所以你可以在A中不要B[] b,而是写10个B b1;B b2;...

如果还不行,就完全展开(虽然很麻烦,但总是可行的嘛,呵呵)

方法2的声明中加上ArraySubType试试看
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10,ArraySubType =UnmanagedType.Struct)]
public B[] b

另外你的调用的代码不知道是否有错,最好也贴出来

好运!

langmafeng 2005-08-15
  • 打赏
  • 举报
回复
测试调用代码:
private void button2_Click(object sender, System.EventArgs e)
{
A[] a = new A[2];

a[0].a = 1;
a[0].b = new B[10];

for (int i = 0; i < 10; ++i)
{
a[0].b[i].a = i;
a[0].b[i].b = 10 - i;
}

a[1].a = 2;
a[1].b = new B[10];

for (int i = 0; i < 10; ++i)
{
a[1].b[i].a = 10 - i;
a[1].b[i].b = i;
}

Class1.Test(ref a);
}

private void button3_Click(object sender, System.EventArgs e)
{
A a;

a.a = 5;
a.b = new B[10];

for (int i = 0; i < 10; ++i)
{
a.b[i].a = i;
a.b[i].b = 10 - i;
}

Class1.TestA(ref a);
}

private void button4_Click(object sender, System.EventArgs e)
{
C c;

c.a = 5;
c.b.a = 10;
c.b.b = 10;

Class1.TestC(c);
}

private void button5_Click(object sender, System.EventArgs e)
{
B[] b = new B[10];

for (int i = 0; i < 10; ++i)
{
b[i].a = i;
b[i].b = 10 - i;
}

Class1.TestB(b);
}

private void button6_Click(object sender, System.EventArgs e)
{
B[,] b = new B[10, 10];

for (int i = 0; i < 10; ++i)
{
for (int j = 0; j < 10; ++j)
{
b[i, j].a = i;
b[i, j].b = j;
}
}

Class1.TestBB(b);
}
langmafeng 2005-08-15
  • 打赏
  • 举报
回复
刚才又做了个测试:
C#代码:
using System;
using System.Runtime.InteropServices;

namespace TestDll
{
[ StructLayout( LayoutKind.Sequential) ]
public struct A
{
public int a;
// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10, ArraySubType = UnmanagedType.Struct)]
public B[] b;
}

[ StructLayout( LayoutKind.Sequential) ]
public struct B
{
public int a;
public int b;
}

[ StructLayout( LayoutKind.Sequential) ]
public struct C
{
public int a;
public B b;
}

public class Class1
{
const string DLL_PATH = @"D:\Test\Debug\Test.dll";

[DllImport(DLL_PATH)]
public static extern int Test([In, Out] ref A[] a);

[DllImport(DLL_PATH)]
public static extern int TestA([In, Out] ref A a);

[DllImport(DLL_PATH)]
public static extern int TestB(B[] b);

[DllImport(DLL_PATH)]
public static extern int TestC(C c);

[DllImport(DLL_PATH)]
public static extern int TestBB(B[,] b);
}
}

C++中.h文件的代码:
typedef struct
{
int a;
int b;
} B;

typedef struct
{
int a;
B b[10];
} A;

typedef struct
{
int a;
B b;
} C;

extern "C" __declspec(dllexport) int Test(A *a);

extern "C" __declspec(dllexport) int TestA(A& a);

extern "C" __declspec(dllexport) int TestB(B *b);

extern "C" __declspec(dllexport) int TestC(C c);

extern "C" __declspec(dllexport) int TestBB(B **b);

在.Net环境下启用非托管调试跟踪时发现:
调用TestB、TestC均能正确传值,调用Test、TestA、TestBB时进到C++中时b中的值都乱了,传递结构的多维数组时也不能正确的取到值,如果为A结构中的b添加[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10, ArraySubType = UnmanagedType.Struct)]属性修饰,则在调用Test的时候就出异常了
lyb_abiandbel 2005-08-15
  • 打赏
  • 举报
回复
学习
rtdb 2005-08-15
  • 打赏
  • 举报
回复
强烈建议使用XML封装。
DOTNET下还弄结构,实在是太费力了。
langmafeng 2005-08-15
  • 打赏
  • 举报
回复
To Jim3(Jim):
1、单一化不可行,目前我们系统中的很多地方都是需要与其他公司对接的,我们不可能要求其他公司来修改他们定义的接口中的结构,当然,我们可以在其他公司的Dll之上再自己用VC++再包一层,将结构中的元素单一化,但类似的接口很多,还有不少比上面写的更复杂的结构,结构里嵌结构嵌了四五层,如果这样做的话,工作量很大,而且代码维护起来也很困难。
2、在结构上增加ArraySubType属性的方法我也试过了,系统报错“无法封送类型 B 的字段 b:该类型无法作为结构字段进行封送处理。”
3、调用的代码应该没有问题,结构中所有的字段的值都赋了初始值,跟踪调试的时候在C#中都是正确的
4、MSDN里的那个例子我看过了,但那个传递的只是“具有嵌入结构的结构”,而我们现在要解决的是“具有嵌入结构的结构的数组”
谢谢!

To Tony_lau111082(刘寓)
C++中的结构和函数的定义在2楼
langmafeng 2005-08-14
  • 打赏
  • 举报
回复
谢谢楼上的兄弟:)
Jim3 2005-08-12
  • 打赏
  • 举报
回复
作个记号,有时间再帮你看
langmafeng 2005-08-12
  • 打赏
  • 举报
回复
up
langmafeng 2005-08-12
  • 打赏
  • 举报
回复
对啊,我问的就是该结构体该如何封送啊
Tony_lau111082 2005-08-12
  • 打赏
  • 举报
回复
其实这是因为你结构体没有封装正确的原因
加载更多回复(2)

110,535

社区成员

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

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

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