将复杂的结构(带有内部结构数组)从 C# 传递到 C++
Passing a complex Struct (with inner array of struct) from C# to C++
我正在研究扫描仪的驱动程序。从供应商那里得到 dll 和头文件,除了 PDF 手册,用本机 C++ 编写(没有源代码)。需要在 C# 项目中使用它,但我在结构方面遇到问题(尝试读取或发送它们)。
我使用命令提示符获得了 dll 方法,在网站上对它们进行了分解(因为它有 +100)。当然,我不会全部使用,只使用我需要的那些。使用原始数据类型的那些没有问题,事实上,使扫描仪转向 on/off、扫描等。
我的主要问题如下:我需要设置一些参数,因为使用默认参数我没有获得所需的信息(实际上这是我需要的最重要的东西)。唯一的方法是使用包含 2 个参数的方法:ID(只是一个 int)和设置(一个结构)。该结构在内部不是一个,而是两个不同的结构实例(其中一个是数组,在另一种结构中作为参数之一)。换句话说,需要使用 4 种不同的结构。
我按照 .h 文件中提供的模板声明了所有结构,并导入了方法。当我尝试进行测试时,它一直给我一个错误。我相信问题是结构数组。我尝试了一个正常的传递,封送处理,使用一个 pin 到数组,改变数据类型,添加一个 "MarshalAs" 和所有 bool vars...似乎没有任何效果。
几天来一直在努力解决这个问题。不知道我做错了什么(因为这是我第一次导入方法)。我阅读了有关 C++/Cli 的内容,但也从未使用过它。
见下面的代码(由于信息保密,我稍微修改了一下)
.h 文件 (C++) 中是这样定义的
// The structs (won't add all parameters, but are basically the same type)
typedef struct _ImParam
{
UINT Format;
UINT Resolution;
UINT ColorDepth;
} IM_PARAM;
typedef struct _sValues
{
UINT Xpos;
UINT Ypos;
UINT Width;
UINT Height;
BOOL Milli;
} S_VALUES;
typedef struct _sProperties
{
BOOL Enable;
S_VALUES Properties;
} S_PROPERTIES;
typedef struct _DevParam
{
BOOL Enable;
UINT Font;
char Symbol;
IM_PARAM Image1;
IM_PARAM Image2;
S_PROPERTIES Properties[10];
UINT FeedMode;
} DevParam;
// more code, comments, etc. etc.
// This is how is defined
BOOL SetParameters( DWORD ID, DevParams DParam )
这就是我在 C# 中构建结构的方式
[StructLayout(LayoutKind.Sequential)]
public struct ImParam
{
public uint Format;
public uint Resolution;
public uint ColorDepth;
public ImParam(uint n)
{
Format = n;
Resolution = 300;
ColorDepth = 256;
}
};
[StructLayout(LayoutKind.Sequential)]
public struct sValues
{
public uint Xpos;
public uint Ypos;
public uint Width;
public uint Height;
public bool Milli;
public sValues(uint n)
{
Xpos = n;
Ypos = n;
Width = n;
Height = n;
Milli = false;
}
};
[StructLayout(LayoutKind.Sequential)]
public struct sProperties
{
public bool Enable;
public sValues Properties;
public sProperties(int n)
{
Enable = false;
Front = false;
Properties = new sValues(n);
}
};
// Commented code is from another attemp
[StructLayout(LayoutKind.Sequential)]
public struct DevParam
{
public bool Enable;
public uint Font;
public char Symbol;
public ImParam Image1;
public ImParam Image2;
public IntPtr Properties;
//public sProperties[] Properties;
public uint FeedMode;
public DeviceParameters(IntPtr SnP) //(int n)
{
Enable = true;
Font = 0;
Symbol = '?';
Image1 = new ImParam(3);
Image2 = new ImParam(3);
Properties = SnP;
/*Properties = new sProperties[n];
*for(int i = 0; i < n; i++)
* Properties[i] = new sProperties(0);*/
FeedMode = 1;
}
};
// .dll file path definition, some methods imported, etc. etc.
[DllImport(path, EntryPoint = "?SetParameters@@YGHKU_DevParam@@@Z")]
public static extern bool SetParameters(int ID, DevParam dParam);
这是我调用它的时候(添加注释代码以向您展示我的尝试)
static void Main(string[] args)
{
bool res = false;
int ID;
sProperties[] SnP = new sProperties[10];
for (int i = 0; i < 10; i++)
SnP[i] = new sProperties(0);
try
{
// Some code to turn on scanner, get ID value and such
/* Attemp1: Passing the struct normaly.
* Result: ArgumentException [HRESULT: 0x80070057 (E_INVALIDARG))]
* try
* {
* DevParam dParam = new DevParam(10);
* res = Class1.SetParameters(ID, dParam);
* Console.WriteLine(res);
* }
* catch (Exception e) { Console.WriteLine(e); }*/
/* Attemp2: Marshaling each element of the array.
* Result: The managed PInvoke signature doesnt mach the destination one
* int S = Marshal.SizeOf(typeof(sProperties));
* DevParam dParam = new DevParam(Marshal.AllocHGlobal(SnP.Length*S));
* IntPtr ptr = dParam.Properties;
* for (int i = 0; i < SnP.Length; i++)
* {
* Marshal.StructureToPtr(SnP[i], ptr, false);
* ptr += S;
* }
* try
* {
* res = Class1.SetDevParam(ID, dParam);
* Console.WriteLine(res);
* }
* finally { Marshal.FreeHGlobal(dParam.sProperties); }*/
/* Attemp3: Adding a Pin Pointer to struct
* Result: Exception (Object has no primitive data and it can't
* be transfered into bits blocks) */
GCHandle SpHandle = GCHandle.Alloc(SnP, GCHandleType.Pinned);
try
{
DevParam dParam = new DevParam(SpHandle.AddrOfPinnedObject());
res = Class1.SetParameters(ID, dParam);
Console.WriteLine(res);
}
catch (Exception e) { Console.WriteLine(e); }
finally { SpHandle.Free(); }
// More code for testing other methods and blahblahblah
}
catch (Exception e) { Console.WriteLine(e); }
Console.WriteLine("Finished");
Console.ReadKey();
}
我期待什么?只获取一个布尔结果以查看方法是否成功执行(当然,如果为真,扫描器应该已经定义了新参数)
我得到了什么?一堆例外。
拜托,任何帮助都会很棒。提前致谢。
PD:抱歉这么久post。
PD2:我很不稳定,所以请尝试解释一下 "for dummies"
谢谢汉斯。似乎成功了!
刚刚按照建议修改了结构:
[StructLayout(LayoutKind.Sequential)]
public struct DevParam
{
public bool Enable;
public uint Font;
public char Symbol;
public ImParam Image1;
public ImParam Image2;
//public IntPtr Properties;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public sProperties[] Properties;
public uint FeedMode;
public DeviceParameters(int n) //(IntPtr SnP)
{
Enable = true;
Font = 0;
Symbol = '?';
Image1 = new ImParam(3);
Image2 = new ImParam(3);
//Properties = SnP;
Properties = new sProperties[n];
for(int i = 0; i < n; i++)
Properties[i] = new sProperties(0);
FeedMode = 1;
}
};
并使用了 "Attemp1" 代码。
我正在研究扫描仪的驱动程序。从供应商那里得到 dll 和头文件,除了 PDF 手册,用本机 C++ 编写(没有源代码)。需要在 C# 项目中使用它,但我在结构方面遇到问题(尝试读取或发送它们)。
我使用命令提示符获得了 dll 方法,在网站上对它们进行了分解(因为它有 +100)。当然,我不会全部使用,只使用我需要的那些。使用原始数据类型的那些没有问题,事实上,使扫描仪转向 on/off、扫描等。
我的主要问题如下:我需要设置一些参数,因为使用默认参数我没有获得所需的信息(实际上这是我需要的最重要的东西)。唯一的方法是使用包含 2 个参数的方法:ID(只是一个 int)和设置(一个结构)。该结构在内部不是一个,而是两个不同的结构实例(其中一个是数组,在另一种结构中作为参数之一)。换句话说,需要使用 4 种不同的结构。
我按照 .h 文件中提供的模板声明了所有结构,并导入了方法。当我尝试进行测试时,它一直给我一个错误。我相信问题是结构数组。我尝试了一个正常的传递,封送处理,使用一个 pin 到数组,改变数据类型,添加一个 "MarshalAs" 和所有 bool vars...似乎没有任何效果。
几天来一直在努力解决这个问题。不知道我做错了什么(因为这是我第一次导入方法)。我阅读了有关 C++/Cli 的内容,但也从未使用过它。
见下面的代码(由于信息保密,我稍微修改了一下)
.h 文件 (C++) 中是这样定义的
// The structs (won't add all parameters, but are basically the same type)
typedef struct _ImParam
{
UINT Format;
UINT Resolution;
UINT ColorDepth;
} IM_PARAM;
typedef struct _sValues
{
UINT Xpos;
UINT Ypos;
UINT Width;
UINT Height;
BOOL Milli;
} S_VALUES;
typedef struct _sProperties
{
BOOL Enable;
S_VALUES Properties;
} S_PROPERTIES;
typedef struct _DevParam
{
BOOL Enable;
UINT Font;
char Symbol;
IM_PARAM Image1;
IM_PARAM Image2;
S_PROPERTIES Properties[10];
UINT FeedMode;
} DevParam;
// more code, comments, etc. etc.
// This is how is defined
BOOL SetParameters( DWORD ID, DevParams DParam )
这就是我在 C# 中构建结构的方式
[StructLayout(LayoutKind.Sequential)]
public struct ImParam
{
public uint Format;
public uint Resolution;
public uint ColorDepth;
public ImParam(uint n)
{
Format = n;
Resolution = 300;
ColorDepth = 256;
}
};
[StructLayout(LayoutKind.Sequential)]
public struct sValues
{
public uint Xpos;
public uint Ypos;
public uint Width;
public uint Height;
public bool Milli;
public sValues(uint n)
{
Xpos = n;
Ypos = n;
Width = n;
Height = n;
Milli = false;
}
};
[StructLayout(LayoutKind.Sequential)]
public struct sProperties
{
public bool Enable;
public sValues Properties;
public sProperties(int n)
{
Enable = false;
Front = false;
Properties = new sValues(n);
}
};
// Commented code is from another attemp
[StructLayout(LayoutKind.Sequential)]
public struct DevParam
{
public bool Enable;
public uint Font;
public char Symbol;
public ImParam Image1;
public ImParam Image2;
public IntPtr Properties;
//public sProperties[] Properties;
public uint FeedMode;
public DeviceParameters(IntPtr SnP) //(int n)
{
Enable = true;
Font = 0;
Symbol = '?';
Image1 = new ImParam(3);
Image2 = new ImParam(3);
Properties = SnP;
/*Properties = new sProperties[n];
*for(int i = 0; i < n; i++)
* Properties[i] = new sProperties(0);*/
FeedMode = 1;
}
};
// .dll file path definition, some methods imported, etc. etc.
[DllImport(path, EntryPoint = "?SetParameters@@YGHKU_DevParam@@@Z")]
public static extern bool SetParameters(int ID, DevParam dParam);
这是我调用它的时候(添加注释代码以向您展示我的尝试)
static void Main(string[] args)
{
bool res = false;
int ID;
sProperties[] SnP = new sProperties[10];
for (int i = 0; i < 10; i++)
SnP[i] = new sProperties(0);
try
{
// Some code to turn on scanner, get ID value and such
/* Attemp1: Passing the struct normaly.
* Result: ArgumentException [HRESULT: 0x80070057 (E_INVALIDARG))]
* try
* {
* DevParam dParam = new DevParam(10);
* res = Class1.SetParameters(ID, dParam);
* Console.WriteLine(res);
* }
* catch (Exception e) { Console.WriteLine(e); }*/
/* Attemp2: Marshaling each element of the array.
* Result: The managed PInvoke signature doesnt mach the destination one
* int S = Marshal.SizeOf(typeof(sProperties));
* DevParam dParam = new DevParam(Marshal.AllocHGlobal(SnP.Length*S));
* IntPtr ptr = dParam.Properties;
* for (int i = 0; i < SnP.Length; i++)
* {
* Marshal.StructureToPtr(SnP[i], ptr, false);
* ptr += S;
* }
* try
* {
* res = Class1.SetDevParam(ID, dParam);
* Console.WriteLine(res);
* }
* finally { Marshal.FreeHGlobal(dParam.sProperties); }*/
/* Attemp3: Adding a Pin Pointer to struct
* Result: Exception (Object has no primitive data and it can't
* be transfered into bits blocks) */
GCHandle SpHandle = GCHandle.Alloc(SnP, GCHandleType.Pinned);
try
{
DevParam dParam = new DevParam(SpHandle.AddrOfPinnedObject());
res = Class1.SetParameters(ID, dParam);
Console.WriteLine(res);
}
catch (Exception e) { Console.WriteLine(e); }
finally { SpHandle.Free(); }
// More code for testing other methods and blahblahblah
}
catch (Exception e) { Console.WriteLine(e); }
Console.WriteLine("Finished");
Console.ReadKey();
}
我期待什么?只获取一个布尔结果以查看方法是否成功执行(当然,如果为真,扫描器应该已经定义了新参数)
我得到了什么?一堆例外。
拜托,任何帮助都会很棒。提前致谢。
PD:抱歉这么久post。 PD2:我很不稳定,所以请尝试解释一下 "for dummies"
谢谢汉斯。似乎成功了!
刚刚按照建议修改了结构:
[StructLayout(LayoutKind.Sequential)]
public struct DevParam
{
public bool Enable;
public uint Font;
public char Symbol;
public ImParam Image1;
public ImParam Image2;
//public IntPtr Properties;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public sProperties[] Properties;
public uint FeedMode;
public DeviceParameters(int n) //(IntPtr SnP)
{
Enable = true;
Font = 0;
Symbol = '?';
Image1 = new ImParam(3);
Image2 = new ImParam(3);
//Properties = SnP;
Properties = new sProperties[n];
for(int i = 0; i < n; i++)
Properties[i] = new sProperties(0);
FeedMode = 1;
}
};
并使用了 "Attemp1" 代码。