对PInvoke的调用使堆栈不平衡.即使使用Cdecl, dll导入也失败
本文关键字:dll Cdecl 导入 失败 PInvoke 调用 堆栈 不平衡 | 更新日期: 2023-09-27 18:02:41
啊哈,这个问题都是关于堆栈溢出的,所以我已经添加了CallingConvention = CallingConvention。Cdecl在我导入的其他库中工作得很好,但在这种情况下,没有任何变化,它仍然失败,并显示相同的错误信息。
它的原始代码来自于一个。net 3.5项目,并且工作得非常好:
[DllImport("Compiled DSP.dll")]
private static extern int fnGetConfigParam(int nID, ref stParamInt pstParam);
[DllImport("Compiled DSP.dll")]
private static extern int fnGetConfigParam(int nID, ref stParamFloat pstParam);
新项目是一个尝试调用相同库的。net 4.0项目。我已经添加了调用约定:
[DllImport("Compiled DSP.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int fnGetConfigParam(int nID, ref stParamInt pstParam);
[DllImport("Compiled DSP.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int fnGetConfigParam(int nID, ref stParamFloat pstParam);
这两个ref类型都是结构体。当我尝试运行与3.5项目调用此函数相同的代码时,我得到PInvoke错误,即使我将调用更改为StdCall(如预期的那样),我也会得到相同的错误。
任何想法吗?我猜这个结构体在某种程度上是干扰的,但像这样使用导入对我来说不是一个常见的任务,所以这是一个盲目的猜测。是时候问问谷歌,深入研究一下了。
EDIT:如果这是有用的,这里是作为引用传递的两个结构体:
stParamInt:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct stParamInt
{
public uint unID;
public int nValue;
public int nValueMin;
public int nValueMax;
public int nValueDef;
public int nUnitsType;
public int nUnits;
public byte[] GetBytes()
{
byte[] result = new byte[0];
List<byte> buf = new List<byte>();
buf.AddRange(BitConverter.GetBytes(unID));
buf.AddRange(BitConverter.GetBytes(nValue));
buf.AddRange(BitConverter.GetBytes(nValueMin));
buf.AddRange(BitConverter.GetBytes(nValueMax));
buf.AddRange(BitConverter.GetBytes(nValueDef));
buf.AddRange(BitConverter.GetBytes(nUnitsType));
buf.AddRange(BitConverter.GetBytes(nUnits));
result = buf.ToArray();
return result;
}
public stParamInt(byte[] buf)
{
unID = BitConverter.ToUInt32(buf, 0);
nValue = BitConverter.ToInt32(buf, 4);
nValueMin = BitConverter.ToInt32(buf, 8);
nValueMax = BitConverter.ToInt32(buf, 12);
nValueDef = BitConverter.ToInt32(buf, 16);
nUnitsType = BitConverter.ToInt32(buf, 20);
nUnits = BitConverter.ToInt32(buf, 24);
}
};
stParamFloat:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct stParamFloat
{
public uint unID;
public float fValue;
public float fValueMin;
public float fValueMax;
public float fValueDef;
public int nUnitsType;
public int nUnits;
public byte[] GetBytes()
{
byte[] result = new byte[0];
List<byte> buf = new List<byte>();
buf.AddRange(BitConverter.GetBytes(unID));
buf.AddRange(BitConverter.GetBytes(fValue));
buf.AddRange(BitConverter.GetBytes(fValueMin));
buf.AddRange(BitConverter.GetBytes(fValueMax));
buf.AddRange(BitConverter.GetBytes(fValueDef));
buf.AddRange(BitConverter.GetBytes(nUnitsType));
buf.AddRange(BitConverter.GetBytes(nUnits));
result = buf.ToArray();
return result;
}
public stParamFloat(byte[] buf)
{
unID = BitConverter.ToUInt32(buf, 0);
fValue = BitConverter.ToSingle(buf, 4);
fValueMin = BitConverter.ToSingle(buf, 8);
fValueMax = BitConverter.ToSingle(buf, 12);
fValueDef = BitConverter.ToSingle(buf, 16);
nUnitsType = BitConverter.ToInt32(buf, 20);
nUnits = BitConverter.ToInt32(buf, 24);
}
};
编辑我设法挖掘出相关dll中的签名,它在c++非托管代码中具有以下方法签名:
COMPILEDDSP_API int fnGetConfigParam(int nID, struct stPint *pstParam)
{
return CONFIG_GetSetConfigParam(GET_PARAM, nID, pstParam, &g_stActiveCfg, sizeof(struct stActiveConfig)/sizeof(struct stPint), NULL);
}
不幸的是,我对标准c++函数不是特别流利,但签名似乎很好。
EDIT我发现如果我只是构建并从我的bin运行它,它的工作非常好。如果我尝试通过visual studio在调试模式下运行,它会失败,并出现这个PInvoke错误。
看看这些答案
-
物理磁盘大小不正确(IoCtlDiskGetDriveGeometry)
-
如何从c#
传递结构作为C dll中的指针
可以用IntPtr
传递ref
结构,导入将是
[DllImport("Compiled DSP.dll")]
private static extern int fnGetConfigParam(int nID, IntPtr pstParam);
和测试
var sizeParamInt=Marshal.SizeOf(typeof(stParamInt));
var marshalParamInt=Marshal.AllocHGlobal(sizeParamInt);
fnGetConfigParam(123, marshalParamInt);
var paramInt=(stParamInt)Marshal.PtrToStructure(marshalParamInt, typeof(stParamInt));
Marshal.FreeHGlobal(marshalParamInt);
注意,我没有尝试调用约定,您可能需要用实际代码进行测试。