You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Framework 4.8.1下uEye API录屏触发PInvokeStackImbalance错误

uEye API视频录制在.NET Framework 4.8.1中触发PInvokeStackImbalance错误

问题场景

使用uEye API(DLL)实现相机实时视频录制功能,在.NET 6环境下运行正常,但切换到.NET Framework 4.8.1后,触发Managed Debugging Assistant的PInvokeStackImbalance错误。错误提示:调用PInvoke函数uEyeDotNet!uEye.Tools.Video+ToolsWrapper::InitAviX86导致栈不平衡,原因是托管PInvoke签名与非托管目标签名不匹配,需检查调用约定和参数是否一致。

错误原因分析

PInvoke栈不平衡的核心原因是托管侧的PInvoke签名与非托管DLL的函数签名不匹配,尤其是调用约定或参数类型/顺序不一致:

  • 当前InitAviX86的PInvoke声明使用CallingConvention.Cdecl,但Windows平台多数第三方DLL(包括uEye)默认使用StdCall调用约定。
  • .NET 6对PInvoke栈不平衡的检测更宽松,甚至会自动适配部分不匹配场景;而.NET Framework 4.8.1会严格校验栈状态并抛出错误。
  • 相机句柄hCam在64位环境下是8字节指针,原代码用int(4字节)接收会导致类型不匹配,进一步加剧栈不平衡问题。

解决方案

1. 修正PInvoke调用约定

将InitAviX86的调用约定改为CallingConvention.StdCall(符合uEye API的标准调用约定):

[DllImport("uEye_tools", CallingConvention = CallingConvention.StdCall, EntryPoint = "isavi_InitAVI")]
public unsafe static extern int InitAviX86(int* s32ID, IntPtr hCam);

2. 统一句柄参数类型

相机句柄本质是指针,应使用IntPtr类型适配32/64位环境,避免类型长度不匹配:

public unsafe Status Init(Camera camera, out int s32VideoID)
{
    Status status = Status.Success;
    s32VideoID = 0;
    camera.GetHandle(out IntPtr camHandle); // 改为IntPtr类型接收句柄
    int num = 0;
    try
    {
        fixed (int* s32ID = &s32VideoID)
        {
            num = ((IntPtr.Size != 4)? ToolsWrapper.InitAviX64(s32ID, camHandle) : ToolsWrapper.InitAviX86(s32ID, camHandle));
            status = (Status)num;
            if (status == Status.Success)
            {
                m_bDllFound = true;
                return status;
            }
            return status;
        }
    }
    catch (DllNotFoundException)
    {
        return Status.NoSuccess;
    }
}

3. 校验参数一致性

对照uEye官方文档的isavi_InitAVI函数签名,确保托管侧参数的顺序、数量、类型与非托管侧完全一致:

  • 确认s32ID是输出参数(指针类型),hCam是输入的相机句柄。

相关代码参考

按钮点击事件

private void btnRecord_Click(object sender, EventArgs e)
{
    try
    {
        string filename = DateTime.Now.ToString("yyyy-dd-M--HH-mm-ss") + ".avi";
        camera.Video.Start(procedure_path + "/videos/" + filename);
    }
    catch (Exception)
    {
        throw;
    }
}

DLL中的Start方法

public Status Start(string strFileName)
{
    Status status = Status.Success;
    if (m_VideoHandle.Init(m_CamParent, out s32VideoID) == Status.Success)
    {
        m_bInitialize = true;
    }

    if (m_bInitialize && !m_bRunning)
    {
        new PixelFormat(m_CamParent).Get(out var mode);
        Memory memory = new Memory(m_CamParent);
        memory.GetActive(out int s32MemId);
        memory.GetSize(s32MemId, out var size);
        status = m_VideoHandle.SetImageSize(s32VideoID, mode & (ColorMode)(-16385), size.Width, size.Height);
        int s32Quality = ((m_s32VideoQuality != 0)? m_s32VideoQuality : 75);
        m_VideoHandle.SetQuality(s32VideoID, s32Quality);
        status = m_VideoHandle.Open(s32VideoID, strFileName);
        double f64Value = 0.0;
        if (m_f64VideoFrameRate == 0.0)
        {
            new Timing(m_CamParent).Framerate.Get(out f64Value);
        }
        else
        {
            f64Value = m_f64VideoFrameRate;
        }

        status = m_VideoHandle.SetFramerate(s32VideoID, f64Value);
        status = m_VideoHandle.Start(s32VideoID);
        if (status == Status.Success)
        {
            m_CamParent.EventFrame += onSaveFrame;
            m_bRunning = true;
        }
    }
    else
    {
        status = Status.NoSuccess;
    }

    return status;
}

原DLL导入定义

[DllImport("uEye_tools", CallingConvention = CallingConvention.Cdecl, EntryPoint = "isavi_InitAVI")]
public unsafe static extern int InitAviX86(int* s32ID, int hCam);

原Init方法

public unsafe Status Init(Camera camera, out int s32VideoID)
{
    Status status = Status.Success;
    s32VideoID = 0;
    camera.GetHandle(out var camHandle);
    int num = 0;
    try
    {
        fixed (int* s32ID = &s32VideoID)
        {
            num = ((IntPtr.Size != 4)? ToolsWrapper.InitAviX64(s32ID, camHandle) : ToolsWrapper.InitAviX86(s32ID, camHandle));
            status = (Status)num;
            if (status == Status.Success)
            {
                m_bDllFound = true;
                return status;
            }

            return status;
        }
    }
    catch (DllNotFoundException)
    {
        return Status.NoSuccess;
    }
}

内容的提问来源于stack exchange,提问作者user3281491

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 22:37:47