【问题标题】:A call to PInvoke has unbalanced the stack... dll import fails even with Cdecl对 PInvoke 的调用使堆栈不平衡...即使使用 Cdecl,dll 导入也会失败
【发布时间】:2013-03-12 04:02:33
【问题描述】:

啊哈,这个问题完全是堆栈溢出,所以我已经开始添加 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(如预期的那样)我也会收到相同的错误。

有什么想法吗?我猜这个结构会以某种方式干扰,但使用这样的导入对我来说不是一项常见的任务,所以这是一个盲目的猜测。是时候询问谷歌并深入了解这里了。

编辑:如果这有用,这里有两个结构作为参考传递:

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++ 函数不是特别流利,但签名似乎很好。

编辑我发现如果我只是从我的 bin 中构建并运行它,它就可以正常工作。如果我尝试通过 Visual Studio 在调试模式下运行,则会失败并出现此 PInvoke 错误。

【问题讨论】:

  • 我没有意识到 DllImport 可以做重载解析。 stParamInt 和 stParamFloat 是如何定义的,您是否在两种平面格式(32 对 64)上使用相同的位数?
  • 这段代码的作者不介意你,但我知道我们使用它的应用程序的当前版本(大约 4 个不同的应用程序)都是从 .net 3.5 SP1 运行的 32 位应用程序。 .net 4.0 项目也是 32 位的,因为他没有重做任何东西来支持 64 位机器。
  • 您的 pinvoke 声明没有明显错误。 C 代码本身可能使堆栈不平衡。单步执行 CONFIG_GetSetConfigParam() 时,请使用调试器并注意 ESP 寄存器的值。调试 + Windows + 寄存器。
  • @Mohgeroth:请检查提供的答案,如果解决了您的问题,请标记为答案。
  • 这并没有解决问题,所以它没有被标记为答案。

标签: c# .net-4.0 dllimport


【解决方案1】:

看看这些答案

您可以使用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);

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多