【C#】对“xxxx::Invoke”类型的已垃圾回收委托进行了回调。这可能会导致应用程序崩溃、损坏和数据丢失。向非托管代码传递委托时,托管应用程序必须让这些委托保持活动状态,直到确信不会再次调用。

本文介绍了一种常见的.NET应用程序异常——已垃圾回收委托进行了回调的问题及其解决方案。该问题通常发生在托管代码调用非托管代码的过程中,文章通过具体实例展示了如何使用GC.KeepAlive()方法来避免委托被过早回收。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在做项目的时候,出现了几次 对“xxxx::Invoke”类型的已垃圾回收委托进行了回调。这可能会导致应用程序崩溃、损坏和数据丢失。向非托管代码传递委托时,托管应用程序必须让这些委托保持活动状态,直到确信不会再次调用它们。异常。

这种错误,一般出现在托管代码调用非托管代码的过程中,如C#程序要调用c++的某个函数,而这个函数正好有个callback函数。当C#调用完之后,GC有可能会把这个callback给回收掉,如果C++接下来的某个函数中也同样使用了这个callback,就会抛出上述异常。因为C++是非托管的,GC又不知道它接下来还要使用callback所以就把它回收掉了。

这个时候,微软就建议我们使用GC.KeepAlive()方法解决,参考官网例子

KeepAlive 方法的目的是确保对对象的引用存在,该对象有被垃圾回收器过早回收的危险。

  • 这种现象可能发生的一种常见情形是,当在托管代码或数据中已没有对该对象的引用,但该对象仍然在非托管代码(如 Win32 API、非托管 DLL 或使用 COM 的方法)中使用。
  • 另一种过早发生垃圾回收的情形是,在一个方法中创建并使用一个对象。此时,当对对象的某个成员的调用仍在执行时,可能会对该对象进行回收,如第一个代码示例所示。此方法引用 obj,从而使该对象从例程开始到调用此方法的那个位置(按执行顺序)均不符合进行垃圾回收的条件。在 obj 必须可用的指令范围的结尾(而不是开头)编写此方法的代码。
    KeepAlive 方法除了延长作为参数传递的对象的生存期之外,不会执行任何操作,也会不产生任何其他副作用。

MSDN上有官方例子可以参考下。

在这里贴出我的代码:

private InteropHelper.MKDriverAcceptGatewayConnectCallback connectCallback;
private InteropHelper.MKDriverAcceptGatewayErrorCallback errorCallback;
public void cbMKDriverAcceptGatewayConnect(IntPtr dref, IntPtr sref, IntPtr addr, int socklen)
{
	//... 
}
public void cbMKDriverAcceptGatewayErrorCallback(IntPtr dref, int error)
{
	//...
}
private void XtraForm1_Load(object sender, EventArgs e)
{
	connectCallback = new InteropHelper.MKDriverAcceptGatewayConnectCallback(cbMKDriverAcceptGatewayConnect);
	errorCallback = new InteropHelper.MKDriverAcceptGatewayErrorCallback(cbMKDriverAcceptGatewayErrorCallback);
	drvRef = InteropHelper.MKDriverCreate();

	if (drvRef == IntPtr.Zero)
	{
		MessageBox.Show("加载接口失败");
	}
	else
	{
		InteropHelper.MKDriverSetCb(drvRef, connectCallback, errorCallback);
		GC.KeepAlive(connectCallback);//关键的两句
		GC.KeepAlive(errorCallback);//关键的两句
		//如果没有上面的这两句KeepAlive,MKDriverSetCb调用完之后,两个callback就被GC.Collect掉了,以后如果还有调用的话就会抛异常。
		timer1.Enabled = true;
	}
}
private void timer1_Tick(object sender, EventArgs e)
{
	//间隔10ms
	if (drvRef != IntPtr.Zero)
	{
		if (InteropHelper.MKDriverDispatch(drvRef, 0, 0) != 0)
		{
			//就是因为上面Load方法内两句GC.KeepAlive没有加,所以在这里会不定时抛出异常。谁知道搞C++的那帮人在MKDriverDispatch里也调用了callback。
			GMessageBox.Wrong("接口授权过期");
			return;
		}
	}
}

欢迎大家,评论交流。

<think>好的,用户问的是如何用C#对VisionMaster进行二次开发,获取当前执行的流程以及采集的图片。首先,我需要回忆一下VisionMaster的基本架构。VisionMaster是海康威视的机器视觉软件,可能VisionPro类似,但具体的API可能有所不同。用户之前问过关于VisionPro的问题,这次换成了VisionMaster,可能需要调整方法。 首先,用户的需求是获取当前执行的流程采集的图片。这意味着需要了解VisionMaster的SDK中流程管理的部分,以及图像采集的接口。流程可能指的是视觉任务的工作流程,比如步骤或模块的执行顺序。采集的图片可能来自相机或其他输入源。 接下来,我需要确定如何通过C#访问这些信息。可能需要引用海康的VisionMaster的COM库或者.NET库,然后在代码中实例化相关的管理类。比如,可能存在一个全局的流程管理器,可以获取当前活动的流程实例。 然后,关于图像采集,可能需要配置采集设备,比如相机,然后通过回调或事件来获取图像数据。用户可能需要注册图像到达的事件,然后在事件处理函数中将图像保存或传递到其他模块。 另外,用户可能需要注意线程安全,尤其是在UI更新,需要使用Invoke来避免跨线程问题。同,资源管理也很重要,比如及释放图像对象,防止内存泄漏。 还需要考虑异常处理,比如相机连接失败或流程执行错误的情况,如何捕获并处理这些异常,保证程序的稳定性。 可能还需要一些代码示例,比如如何初始化流程管理器,如何绑定图像采集事件,如何获取当前流程的状态等。这些代码示例需要符合C#的语法,并且正确使用VisionMaster的API。 此外,用户可能想知道如何动态监控流程的执行,比如流程开始、结束或步骤变更的事件,这样可以在流程执行获取相关信息。 最后,要确保回答结构清晰,分步骤说明,每个步骤包含关键代码注意事项,帮助用户逐步实现功能。</think>$\boxed{基于VisionMaster的流程与图像获取实现步骤}$ $\boxed{1.\ 环境准备}$ - 引用海康VisionMaster SDK组件(以VM 4.2为例) ```csharp using Mv.VisionMaster.Module.Api; // 核心API using Mv.VisionMaster.Module.Camera; // 相机模块 using Mv.VisionMaster.Module.Process; // 流程管理 ``` $\boxed{2.\ 获取当前执行流程}$ ```csharp // 获取全局流程管理器 IProcessManager processMgr = MVProcessManager.Instance; // 获取当前激活的流程 Process currentProcess = processMgr.CurrentProcess; // 获取流程执行状态(需事件监听) private void MonitorProcessStatus() { // 绑定流程状态变更事件 currentProcess.ProcessStateChanged += (sender, e) => { Console.WriteLine($"流程状态: {e.NewState}"); // 包含Running/Stopped/Error }; // 获取流程参数(示例:曝光间) double exposureTime = currentProcess.GetParam<double>("CameraModule.ExposureTime"); } ``` $\boxed{3.\ 采集图像获取方法}$ ```csharp // 通过相机模块获取图像 ICameraModule cameraModule = currentProcess.GetModule<ICameraModule>("Camera_1"); // 注册图像回调(硬触发模式) cameraModule.ImageAcquired += (imageData) => { // 转换为Bitmap格式 Bitmap bmp = imageData.ToBitmap(); // 保存或显示图像 this.Invoke(() => pictureBox.Image = bmp); }; // 主动获取最新图像(软触发) try { ImageData latestImage = cameraModule.GetLastImage(); File.WriteAllBytes("capture.bmp", latestImage.RawData); } catch (MVException ex) { MessageBox.Show($"采集失败: {ex.ErrorCode}-{ex.Message}"); } ``` $\boxed{4.\ 流程执行监控(高级)}$ ```csharp // 获取流程步骤树 var stepTree = currentProcess.GetStepTree(); // 遍历所有步骤 foreach (var step in stepTree.AllSteps) { if (step.IsActive) // 当前正在执行的步骤 { Console.WriteLine($"正在执行: {step.Name} ({step.Type})"); } } // 实获取流程结果数据 var resultSet = currentProcess.GetResultData(); if (resultSet.TryGetValue("MeasureResult", out object measureValue)) { double measurement = Convert.ToDouble(measureValue); } ``` $\boxed{5.\ 核心代码封装}$ ```csharp public class VisionMasterController : IDisposable { private IProcessManager _processMgr; private ICameraModule _camera; public VisionMasterController() { _processMgr = MVProcessManager.Instance; _processMgr.ProcessSwitched += OnProcessSwitched; } private void OnProcessSwitched(object sender, ProcessSwitchEventArgs e) { // 新流程激活重新绑定相机 if (e.NewProcess != null) { _camera = e.NewProcess.GetModule<ICameraModule>("MainCamera"); _camera.ImageAcquired += OnImageAcquired; } } private void OnImageAcquired(ImageData image) { // 图像处理回调(注意跨线程) this.ImageReceived?.Invoke(this, image.ToBitmap()); } public event EventHandler<Bitmap> ImageReceived; public void Dispose() { _camera.ImageAcquired -= OnImageAcquired; } } ``` $\boxed{关键注意事项}$ 1. **SDK版本匹配**:确保安装的VM SDK版本与工程引用的DLL版本严格一致 2. **COM组件初始化**:在程序启动调用`MVStartup.Initialize()` 3. **图像生命周期**:使用`image.Lock()`/`image.Unlock()`管理内存锁 4. **异常处理**:捕获`MVException`处理硬件超(0x8300xxxx)等错误 5. **多线程同步**:所有UI操作必须通过`Control.BeginInvoke` $\boxed{调试技巧}$ ```csharp // 查看所有可用模块 var modules = currentProcess.GetAllModules(); foreach (var module in modules) { Console.WriteLine($"{module.Name} ({module.Type})"); } // 导出流程配置(XML格式) string configXml = currentProcess.ExportConfiguration(); File.WriteAllText("process_config.xml", configXml); ``` $\boxed{扩展应用——动态流程控制}$ ```csharp // 动态加载流程文件 public void LoadProcess(string filePath) { if (_processMgr.LoadProcess(filePath)) { _processMgr.StartProcess(); // 自动启动新流程 } } // 执行指定步骤(示例:单独运行定位算法) var alignmentStep = currentProcess.GetStep("Step_Alignment"); if (alignmentStep.CanExecute) { alignmentStep.Execute(); var result = alignmentStep.GetResult(); } ```
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JimCarter

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值