我非常接近我的问题的解决方案,但我需要一些关于最后润色的指导,以使一切正常.在过去的一周里,我学到了很多东西
作为参考,我上周就同一个话题提出了类似的问题,但由于我身边的巨大疏忽,我提出了错误的问题.
我正在尝试使用非托管c ++ dll,它是与连接设备进行通信的API.我已经成功创建了包装器,以及大多数其他函数调用,但最后一个让我发疯.
对于一些背景信息(可能不需要回答这个问题 - 并记住我的基本思维过程当时存在缺陷)在这里:用指针调用未托管代码(更新)
在我原来的问题中,我问的是为包含struct(2)数组的struct(1)创建一个IntPtr ....实际上,struct(1)根本不包含一个数组,它包含一个指针数组.
以下是我试图实现的API的文档作为参考:
extern “C” long WINAPI PassThruIoctl
(
unsigned long ChannelID,
unsigned long IoctlID,
void *pInput,
void *pOutput
)
// *pInput Points to the structure SCONFIG_LIST, which is defined as follows:
// *pOutput is not used in this function and is a null pointer
typedef struct
{
unsigned long NumOfParams; /* number of SCONFIG elements */
SCONFIG *ConfigPtr; /* array of SCONFIG */
} SCONFIG_LIST
// Where:
// NumOfParms is an INPUT, which contains the number of SCONFIG elements in the array pointed to by ConfigPtr.
// ConfigPtr is a pointer to an array of SCONFIG structures.
// The structure SCONFIG is defined as follows:
typedef struct
{
unsigned long Parameter; /* name of parameter */
unsigned long Value; /* value of the parameter */
} SCONFIG
Run Code Online (Sandbox Code Playgroud)
这是我的结构定义,因为我目前已定义它们
[StructLayout(LayoutKind.Sequential)] // Also tried with Pack=1
public struct SConfig
{
public UInt32 Parameter;
public UInt32 Value;
}
[StructLayout(LayoutKind.Sequential)] // Also tried with Pack=1
public struct SConfig_List
{
public UInt32 NumOfParams;
public IntPtr configPtr;
public SConfig_List(UInt32 nParams, SConfig[] config)
{
this.NumOfParams = nParams;
// I have tried these 2 lines together
IntPtr temp = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MyNameSpace.SConfig)) * (int)nParams);
this.configPtr = new IntPtr(temp.ToInt32());
// I have tried this by itself
// this.configPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MyNameSpace.SConfig)) * (int)nParams);
// and this line
// this.configPtr = Marshal.AllocHGlobal(sizeof(SConfig)*(int)nParams); // this only complies with unsafe struct
}
}
Run Code Online (Sandbox Code Playgroud)
下面是代码片段,它们将这些代码设置为变量并调用与API接口的函数
SConfig[] arr_sconfig;
arr_sconfig = new SConfig[1];
arr_sconfig[0].Parameter = 0x04;
arr_sconfig[0].Value = 0xF1;
SConfig_List myConfig = new SConfig_List(1, arr_sconfig);
m_status = m_APIBox.SetConfig(m_channelId, ref myConfig);
Run Code Online (Sandbox Code Playgroud)
最后,这是将此信息传递给dll的函数:
public APIErr SetConfig(int channelId, ref SConfig_List config)
{
unsafe
{
IntPtr output = IntPtr.Zero; // Output not used, just a null pointer for this function
// These 2 lines of code cause API dll to yell about invalid pointer (C# is happy but it doesnt work with dll)
// IntPtr temp = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(J_2534_API.SConfig_List)));
// IntPtr input = new IntPtr(temp.ToInt32());
// The following 2 lines only compile with unsafe - but API dll "likes" the pointer - but I am not getting desired results
// The dll is properly getting the Number of Parameters (NumOfParams), but the data within the array is not being
// referenced correctly
IntPtr input = Marshal.AllocHGlobal(sizeof(SConfig_List)); // Only works with unsafe
Marshal.StructureToPtr(config, input, true);
APIErr returnVal = (APIErr)m_wrapper.Ioctl(channelId, (int)Ioctl.SET_CONFIG, input, output);
return returnVal;
}
}
Run Code Online (Sandbox Code Playgroud)
在我意识到我对基本思想的巨大疏忽之前,我从来没有让C#感到高兴,要么我的语法错误而且代码无法编译,要么它会编译但是会产生运行时错误(甚至从不调用外部dll )
这些问题都在我身后.代码现在编译良好,并且执行时没有任何运行时错误.另外,我使用的DLL有一个日志记录功能,所以我可以看到我实际上正在调用正确的函数.我甚至正确地传递了一些数据.函数正在正确读取NumOfParams变量,但结构数组似乎是垃圾数据.
我在这里阅读了一篇非常有用的文章:http://limbioliong.wordpress.com/2012/02/28/marshaling-a-safearray-of-managed-structures-by-pinvoke-part-1/
我一直在阅读MSDN,但到目前为止,我还没有能够遇到使这个问题有效的代码的神奇组合,所以我再次寻求帮助.
我很确定我的问题是我没有正确设置IntPtr变量,并且它们没有指向内存中的正确区域.
我尝试过各种不安全和安全的代码组合.此外,我知道此时我没有明确释放内存,因此指针也是有帮助的.在我的研究中,这里有一些可能有用的想法,我似乎无法让它们恰到好处
[MarshalAs(UnmanagedType.LPWStr)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst=...)]
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst=100)]
Run Code Online (Sandbox Code Playgroud)
最后一个问题:我假设因为c ++声明是无符号长的,那么UInt32是C#中的正确类型?
代码段中的SConfig_List构造函数有很多问题.最大的问题是它为数组分配内存但完全忘记了复制结构.所以本机代码可以获得指针,但是会看到未初始化的内存.你可以像这样解决它:
public SConfig_List(SConfig[] config) {
this.NumOfParams = config.Length;
int size = Marshal.SizeOf(config[0]);
IntPtr mem = this.configPtr = Marshal.AllocHGlobal(size * config.Length);
for (int ix = 0; ix < config.Length; ++ix) {
Marshal.StructureToPtr(config[ix], mem, false);
mem = new IntPtr((long)mem + size);
}
}
Run Code Online (Sandbox Code Playgroud)
务必不能忘记调用Marshal.FreeHGlobal()之后再次调用完成,否则你会泄漏内存.
避免编组SConfig_List的最简单方法是为C函数提供更好的声明:
[DllImport(...)]
private static extern ApiErr PassThruIoctl(
int channelID,
uint ioctlID,
ref SConfig_List input,
IntPtr output);
Run Code Online (Sandbox Code Playgroud)
这使得一个不错的包装方法看起来像这样:
public APIErr SetConfig(int channelId, SConfig[] config) {
var list = new SConfig_List(config);
var retval = PassThruIoctl(channelId, Ioctl.SET_CONFIG, ref list, IntPtr.Zero);
Marshal.FreeHGlobal(list.configPtr);
return retval;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
7706 次 |
| 最近记录: |