.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
相关产品推荐
相关产品推荐

