对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错误。

对PInvoke的调用使堆栈不平衡.即使使用Cdecl, dll导入也失败

看看这些答案

  • 物理磁盘大小不正确(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);

注意,我没有尝试调用约定,您可能需要用实际代码进行测试。