C# 调用 VC++的DLL是结构指针问题,求助
原本是用VC的Dll 近期要用C#来写,VC 原版如下:
#define MAX_JUKEBOX_NAME 30
typedef struct
{
UINT jbxId;
UINT flags;
UINT numDrives;
UINT numSlots;
UINT numMagazines;
CHAR name [MAX_JUKEBOX_NAME];
} JBMSDK_JUKEBOX_T, *PJBMSDK_JUKEBOX_T;
UINT DLLENTRY
jbmGetJukeboxByNr (
UINT jbxNr,
ref PJBMSDK_JUKEBOX_T jukebox
);
改C#后 代码如下
myClass.jbmClass.cs中如下:
public const uint MAX_JUKEBOX_NAME=30;
[StructLayout(LayoutKind.Sequential)]
public struct JBMSDK_JUKEBOX_T
{
public uint jbxId;
public uint flags;
public uint numDrives;
public uint numSlots;
public uint numMagazines;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)MAX_JUKEBOX_NAME)]
public char[] name;
}
[DllImport("jbmSdk.dll")]
public static extern uint jbmGetJukeboxById (
uint jbxId,
IntPtr jukebox //PJBMSDK_JUKEBOX_T
);
ShowConnectStat.cs中:
public ShowConnectStat_Form()
{
InitializeComponent();
uint jbxNr = 0;
myClass.jbmClass.JBMSDK_JUKEBOX_T jukeBox_t = new myClass.jbmClass.JBMSDK_JUKEBOX_T();
IntPtr intP_jukeBox_t = Marshal.AllocHGlobal(Marshal.SizeOf(jukeBox_t));
try
{
Marshal.StructureToPtr(jukeBox_t, intP_jukeBox_t, false);
myClass.jbmClass.jbmGetJukeboxByNr(jbxNr, ref intP_jukeBox_t);
label6.Text = jukeBox_t.numDrives.ToString();
}
finally
{
Marshal.FreeHGlobal(intP_jukeBox_t);
}
}
结果内存无法对齐,
VC中的的结果应该是: 现在C#中
Jukebox data: jbxid: 8 flage:45
Flags: 1 jbxid:1
Drives: 1 其他字段都是空。
Slots: 45
Magazines: 3
Jukebox name: DISC1000
采用对齐方式后
[StructLayout(LayoutKind.Explicit)]
public struct JBMSDK_JUKEBOX_T
{
[FieldOffset(0)]
public uint jbxId;
[FieldOffset(4)]
public uint flags;
[FieldOffset(8)]
public uint numDrives;
[FieldOffset(12)]
public uint numSlots;
[FieldOffset(16)]
public uint numMagazines;
[FieldOffset(20)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)MAX_JUKEBOX_NAME)]
public char[] name;
}
flage:1 jbxid:1 其他字段还是空,请告诉帮忙
--------------------编程问答-------------------- 有过很多次这样的用法,从来没出现过问题,估计是C#结构设置的问题 --------------------编程问答-------------------- 这样可以???我第一次用VC DLL!还有其他办法吗? --------------------编程问答-------------------- VC中的的结果应该是:
jbxid: 8
Flags: 1 Drives: 1
Slots: 45
Magazines: 3
Jukebox name: DISC1000
现在C#中
flage:45
jbxid:1
其他字段都是空。
--------------------编程问答-------------------- 精确控制对齐,建议
[StructLayout(LayoutKind.Explicit)]
public struct JBMSDK_JUKEBOX_T
{
[FieldOffset(0)]
public uint jbxId;
[FieldOffset(4)]
public uint flags;
[FieldOffset(8)]
public uint numDrives;
[FieldOffset(12)]
public uint numSlots;
[FieldOffset(16)]
public uint numMagazines;
[FieldOffset(20)]
unsafe public fixed byte Name[MAX_JUKEBOX_NAME];
}
vc中CHAR代表8位ansi字符,所在在.net中,需要使用byte进行封装,使用时候,使用
时需要Encoding.ASCII.GetString()方法转换为字符串
对于dll导出函数的调用方法,需要楼主将vc的dll导出函数声明贴出来进行参考才行 --------------------编程问答-------------------- vc中导出函数声明为cdecl时,函数声明的参数按照从右到左的顺序入栈,且由调用者清理堆栈,
声明stdcall导出时,函数声明的参数按照从左到右的顺序入栈,由函数内部负责清理堆栈,
fastcall代表使用寄存器ecx,edx作为第一个,和第二个参数,他不需要堆栈操作,但.net中已经不支持这样的调用。
所以我建议在.net中调用非托管代码时,尽量使用stdcall类型的导出函数(如果需要用可变大小参数除外,当然如果有也可以用指针)。你可以在指定的引入dll导出函数声明中这样写
(针对stdcall)
[DllImport("jbmSdk.dll",CharSet = CharSet.Auto)]
public static extern uint jbmGetJukeboxById (
uint jbxId,
IntPtr jukebox //PJBMSDK_JUKEBOX_T
);
(针对cdecl,不建议,没变参,干嘛要用cdecl
[DllImport("jbmSdk.dll",CharSet = CharSet.Auto,CallingConvention=CallingConvention.Cdecl)]
public static extern uint jbmGetJukeboxById (
uint jbxId,
IntPtr jukebox //PJBMSDK_JUKEBOX_T
); --------------------编程问答-------------------- 方法是对的,至于原因只要看看数据在内存中的结构具体是什么,再调整一下对其就好了。
vs自带内存查看功能,直接查看一下指针所指向的内存,分析一下即可。 --------------------编程问答-------------------- mark
补充:.NET技术 , C#