using Cognex.VisionPro; using Cognex.VisionPro.ImageFile; using Prism.Events; using Prism.Ioc; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Forms.Integration; using TeamAAS_VP.Core; using TeamAAS_VP.Enums; using TeamAAS_VP.Events; using TeamAAS_VP.Interfaces; using TeamAAS_VP.Models; using TeamAAS_VP.Resources.Languages; using TeamAAS_VP.ViewModels.Home; namespace TeamAAS_VP.Views.Home { /// /// Interaction logic for ShowVisionRender /// public partial class ShowVisionRender : UserControl { private ShowVisionRenderViewModel viewModel; // key=ShowRender.Id private readonly Dictionary vmRenders; private readonly Dictionary Titles; private readonly IEventAggregator _eventAggregator; private readonly ISystemDatabaseService _systemDatabaseService; private readonly IProductService _productService; // ======================= 【新增】保存任务控制:防闪退/防资源泄露 ======================= private CancellationTokenSource _saveImageCts = new CancellationTokenSource(); private readonly SemaphoreSlim _saveSemaphore = new SemaphoreSlim(2, 2); public ShowVisionRender() { InitializeComponent(); vmRenders = new Dictionary(); Titles = new Dictionary(); viewModel = DataContext as ShowVisionRenderViewModel; _eventAggregator = viewModel._eventAggregator; _systemDatabaseService = ((Prism.PrismApplicationBase)App.Current).Container.Resolve(); _productService = ((Prism.PrismApplicationBase)App.Current).Container.Resolve(); viewModel.UpdateLayout += ViewModel_UpdateLayout; _eventAggregator.GetEvent().Subscribe(UpdateRenderModuleSource); _eventAggregator.GetEvent().Subscribe(ProductChanged); // 【可选】控件卸载时取消所有保存任务,避免页面关闭后仍在截图/保存导致闪退 this.Unloaded += ShowVisionRender_Unloaded; } private void ShowVisionRender_Unloaded(object sender, RoutedEventArgs e) { try { _saveImageCts.Cancel(); _saveImageCts.Dispose(); } catch { } } /// /// 更新某个工位/模块的显示内容(图像/图形/Record/保存) /// private void UpdateRenderModuleSource(ShowRender render) { this.gridRender.Dispatcher.BeginInvoke(new Action(() => { foreach (var item in vmRenders) { if (item.Value?.Child is CogRecordDisplay disp) { // 通过 Tag(产品名) 匹配当前需要更新的窗口 if (disp.Tag != null && disp.Tag.ToString() == render.ProceductName) { disp.Record = null; disp.Image = render.Image; disp.StaticGraphics.Clear(); if (render.Graphic != null) disp.StaticGraphics.AddList(render.Graphic, ""); if (render.Record != null) disp.Record = render.Record; // ======================= 【关键优化】保存图像走安全入口 ======================= RequestSaveImage(render, disp); break; } } } foreach (var item in vmRenders) { if (((Cognex.VisionPro.CogRecordDisplay)item.Value.Child).Tag!=null && ((Cognex.VisionPro.CogRecordDisplay)item.Value.Child).Tag.ToString() == render.CameraName) { ((Cognex.VisionPro.CogRecordDisplay)item.Value.Child).Record = null; ((Cognex.VisionPro.CogRecordDisplay)item.Value.Child).Image = render.Image; ((Cognex.VisionPro.CogRecordDisplay)item.Value.Child).StaticGraphics.Clear(); if (render.Graphic!=null) ((Cognex.VisionPro.CogRecordDisplay)item.Value.Child).StaticGraphics.AddList(render.Graphic, ""); if (render.Record != null) ((Cognex.VisionPro.CogRecordDisplay)item.Value.Child).Record = render.Record; if (render.IsSaveImage) { SaveImage(render.SaveImageModel, render.SaveImagePathModel, render.SavePath, render.Image, (Bitmap)((Cognex.VisionPro.CogRecordDisplay)item.Value.Child).CreateContentBitmap(Cognex.VisionPro.Display.CogDisplayContentBitmapConstants.Custom), render.Result, render.IsCompress, render.ImgPara); } break; } } /* foreach (var item in gridRender.Children) { if (item is WindowsFormsHost && ((WindowsFormsHost)item).Child is Cognex.VisionPro.CogRecordDisplay) { if (((Cognex.VisionPro.CogRecordDisplay)((WindowsFormsHost)item).Child).Tag != null && ((Cognex.VisionPro.CogRecordDisplay)((WindowsFormsHost)item).Child).Tag.ToString() == render.CameraName) { ((Cognex.VisionPro.CogRecordDisplay)((WindowsFormsHost)item).Child).Image = render.Image; ((Cognex.VisionPro.CogRecordDisplay)((WindowsFormsHost)item).Child).StaticGraphics.Clear(); ((Cognex.VisionPro.CogRecordDisplay)((WindowsFormsHost)item).Child).StaticGraphics.AddList(render.Graphic, ""); if (render.Record != null) ((Cognex.VisionPro.CogRecordDisplay)((WindowsFormsHost)item).Child).Record = render.Record; if (render.IsSaveImage) { SaveImage(render.SaveImageModel, render.SaveImagePathModel, render.SavePath, render.Image, (Bitmap)((Cognex.VisionPro.CogRecordDisplay)((WindowsFormsHost)item).Child).CreateContentBitmap(Cognex.VisionPro.Display.CogDisplayContentBitmapConstants.Custom), render.Result, render.IsCompress); } break; } } else if (item is Grid) { foreach (var item1 in ((Grid)item).Children) { if (item1 is WindowsFormsHost && ((WindowsFormsHost)item1).Child is Cognex.VisionPro.CogRecordDisplay) { if (((Cognex.VisionPro.CogRecordDisplay)((WindowsFormsHost)item1).Child).Tag != null && ((Cognex.VisionPro.CogRecordDisplay)((WindowsFormsHost)item1).Child).Tag.ToString() == render.CameraName) { ((Cognex.VisionPro.CogRecordDisplay)((WindowsFormsHost)item1).Child).Image = render.Image; ((Cognex.VisionPro.CogRecordDisplay)((WindowsFormsHost)item1).Child).StaticGraphics.Clear(); ((Cognex.VisionPro.CogRecordDisplay)((WindowsFormsHost)item1).Child).StaticGraphics.AddList(render.Graphic, ""); if (render.Record != null) ((Cognex.VisionPro.CogRecordDisplay)((WindowsFormsHost)item).Child).Record = render.Record; if (render.IsSaveImage) { SaveImage(render.SaveImageModel, render.SaveImagePathModel, render.SavePath, render.Image, (Bitmap)((Cognex.VisionPro.CogRecordDisplay)((WindowsFormsHost)item).Child).CreateContentBitmap(Cognex.VisionPro.Display.CogDisplayContentBitmapConstants.Custom), render.Result, render.IsCompress); } break; } } } break; } }*/ })); } /// /// 【核心】根据 obj.Count 自动生成 1~16 个显示格子(标题+CogRecordDisplay) /// private async void ViewModel_UpdateLayout(System.Collections.ObjectModel.ObservableCollection obj) { // ======================= 【关键优化】布局刷新前先取消旧保存任务 ======================= try { _saveImageCts.Cancel(); _saveImageCts.Dispose(); } catch { } _saveImageCts = new CancellationTokenSource(); System.Windows.Media.FontFamily font = Application.Current.Resources["DefaultFont"] as System.Windows.Media.FontFamily; // 1) 清理旧控件 if (vmRenders.Count > 0) { foreach (var render in vmRenders) { // WindowsFormsHost.Dispose() 会销毁 WinForms 句柄,防止资源泄漏 render.Value?.Dispose(); } } vmRenders.Clear(); Titles.Clear(); gridRender.Children.Clear(); gridRender.RowDefinitions.Clear(); gridRender.ColumnDefinitions.Clear(); // 2) 判空 if (obj == null || obj.Count == 0) return; // 3) 限制最大 16(防止越界) int count = Math.Min(obj.Count, 16); // 4) 计算行列(最大 4*4) GetGridSize(count, out int rows, out int cols); // 5) 创建主Grid的行列 for (int r = 0; r < rows; r++) gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) }); for (int c = 0; c < cols; c++) gridRender.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) }); gridRender.ShowGridLines = true; // 6) 逐个创建子格子(每个子格子两行:标题/画面) for (int i = 0; i < count; i++) { var sr = obj[i]; // (1) 创建显示控件(WindowsFormsHost + CogRecordDisplay) var disp = new CogRecordDisplay() { Tag = sr.ProceductName, }; disp.HandleCreated += (s, e) => { try { ConfigureCogDisplay(disp); // 句柄创建后再配置,最稳 // 绑定 WinForms 双击事件 //disp.MouseDoubleClick += (r, w) => //{ // try // { // OnDisplayDoubleClick(disp); // } // catch (Exception ex) // { // LogHelper.WriteLogError("双击预览弹窗出错", ex); // } //}; } catch (Exception ex) { LogHelper.WriteLogError("ConfigureCogDisplay 失败", ex); } }; var host = new WindowsFormsHost() { Tag = sr.ProceductName, Child = disp }; vmRenders.Add(sr.Id, host); // (2) 创建标题 var title = new TextBlock() { Text = sr.ProceductName, HorizontalAlignment = System.Windows.HorizontalAlignment.Center, FontWeight = FontWeights.Bold, FontFamily = font, FontSize = 12, Foreground = System.Windows.Media.Brushes.Black }; Titles.Add(sr.Id, title); // (3) 子Grid(标题 + 显示) var cell = new Grid(); cell.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Auto) }); // 标题 cell.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) }); // 图像 cell.Children.Add(title); cell.Children.Add(host); Grid.SetRow(title, 0); Grid.SetColumn(title, 0); Grid.SetRow(host, 1); Grid.SetColumn(host, 0); // (4) 放到主 gridRender 中 int row = i / cols; int col = i % cols; gridRender.Children.Add(cell); Grid.SetRow(cell, row); Grid.SetColumn(cell, col); } } /// /// 根据数量返回最合适的 rows/cols(最大 4*4) /// private void GetGridSize(int count, out int rows, out int cols) { rows = 1; cols = 1; if (count <= 1) { rows = 1; cols = 1; return; } if (count <= 2) { rows = 1; cols = 2; return; } if (count <= 4) { rows = 2; cols = 2; return; } if (count <= 6) { rows = 2; cols = 3; return; } if (count <= 9) { rows = 3; cols = 3; return; } if (count <= 12) { rows = 3; cols = 4; return; } rows = 4; cols = 4; } /// /// 统一配置 VisionPro 显示属性 /// private void ConfigureCogDisplay(CogRecordDisplay disp) { disp.HorizontalScrollBar = false; disp.VerticalScrollBar = false; disp.AutoFit = true; disp.AutoFitWithGraphics = true; disp.BackColor = System.Drawing.SystemColors.ActiveCaption; } // ======================= 【新增】安全保存入口:解决闪退 ======================= private void RequestSaveImage(ShowRender render, CogRecordDisplay disp) { if (render == null) return; if (disp == null) return; if (!render.IsSaveImage) return; // Original 模式不需要 CreateContentBitmap(你的 SaveImage 里 Original 只写 ICogImage) bool needRecordedBitmap = render.SaveImageModel == ProcedureSaveImageModel.Recorded || render.SaveImageModel == ProcedureSaveImageModel.OriginalAndRecorded; _saveImageCts = new CancellationTokenSource(); var token = _saveImageCts.Token; // 必须在控件的 UI 线程上执行 CreateContentBitmap disp.BeginInvoke(new Action(() => { Bitmap clonedBitmap = null; try { token.ThrowIfCancellationRequested(); if (needRecordedBitmap) { // UI线程创建 Bitmap uiBitmap = (Bitmap)disp.CreateContentBitmap( Cognex.VisionPro.Display.CogDisplayContentBitmapConstants.Custom); // 立刻 Clone,后台线程只用 Clone(防止控件刷新/释放导致崩溃) clonedBitmap = (Bitmap)uiBitmap.Clone(); // UI线程立刻释放 uiBitmap.Dispose(); } // 后台保存(限并发,防止GDI/内存瞬时飙升) Task.Run(async () => { await _saveSemaphore.WaitAsync(token); try { token.ThrowIfCancellationRequested(); SaveImage( render.SaveImageModel, render.SaveImagePathModel, render.SavePath, render.Image, clonedBitmap, render.Result, render.IsCompress ); } catch (OperationCanceledException) { // 取消属于正常情况,不记录 } catch (Exception ex) { LogHelper.WriteLogError("后台保存图像任务出错!", ex); } finally { // 确保释放 Clone 的 Bitmap,避免 GDI 泄漏导致闪退 try { clonedBitmap?.Dispose(); } catch { } _saveSemaphore.Release(); } }, token); } catch (OperationCanceledException) { try { clonedBitmap?.Dispose(); } catch { } } catch (Exception ex) { try { clonedBitmap?.Dispose(); } catch { } LogHelper.WriteLogError("RequestSaveImage(UI截图阶段) 出错!", ex); } })); } /// /// 保存图像(保持你原有逻辑不变,补充 Dispose 兜底,防止压缩分支不释放导致闪退) /// /// 要保存图像的模式 /// 图像保存路径类型 /// 保存图像的文件夹路径 /// 原图像 /// 渲染图像 /// 拍照结果 /// 是否压缩 private void SaveImage(ProcedureSaveImageModel saveImageModel, ProcedureSaveImagePathModel saveImagePathModel, string path, ICogImage image, Bitmap reimage, bool result, bool isCompress, ImageParameters imgPara) private void SaveImage(ProcedureSaveImageModel saveImageModel, ProcedureSaveImagePathModel saveImagePathModel, string path, ICogImage image, Bitmap reimage, bool result, bool isCompress) { if (string.IsNullOrEmpty(path) || string.IsNullOrWhiteSpace(path) || !Path.IsPathRooted(path)) { _eventAggregator.GetEvent() .Publish(new Models.MessageStruct() { Message = "保存图像路径为无效的路径!", level = MessageLevel.Alarm }); return; } Task.Run(() => { try { DateTime now = DateTime.Now; if (saveImageModel == ProcedureSaveImageModel.Original) { string Originalpath = $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_OkNG_Time) { Originalpath = result ? $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{now:yyyy-MM-dd HH_mm_ss}.bmp" : $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; } else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Ok_Time) { if (result) Originalpath = $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; else return; } else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Ng_Time) { if (!result) Originalpath = $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; else return; } else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Formula_ProductSN_Time) { // 当前产品配方名(过滤非法字符) string formulaName = _productService.GetCurrentProduct().Name; foreach (char c in Path.GetInvalidFileNameChars()) formulaName = formulaName.Replace(c, '_'); // 产品SN(过滤非法字符) string productSN = _productService.CurrentProductCode; foreach (char c in Path.GetInvalidFileNameChars()) productSN = productSN.Replace(c, '_'); string resultStr = result ? "OK" : "NG"; Originalpath = $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\{formulaName}\\{productSN}\\{productSN}-{now:yyyy-MM-dd HH_mm_ss}-{resultStr}.bmp"; } if (!Directory.Exists(Path.GetDirectoryName(Originalpath))) Directory.CreateDirectory(Path.GetDirectoryName(Originalpath)); using (CogImageFileBMP _cogImageFileTool = new CogImageFileBMP()) { _cogImageFileTool.Open(Originalpath, CogImageFileModeConstants.Write); _cogImageFileTool.Append(image); _cogImageFileTool.Close(); } } else if (saveImageModel == ProcedureSaveImageModel.Recorded) { string Recordedpath = $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_OkNG_Time) { Recordedpath = result ? $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{now:yyyy-MM-dd HH_mm_ss}.bmp" : $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; } else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Ok_Time) { if (result) Recordedpath = $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; else return; } else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Ng_Time) { if (!result) Recordedpath = $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; else return; } else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Formula_ProductSN_Time) { string formulaName = _productService.GetCurrentProduct().Name; foreach (char c in Path.GetInvalidFileNameChars()) formulaName = formulaName.Replace(c, '_'); string productSN = _productService.CurrentProductCode; foreach (char c in Path.GetInvalidFileNameChars()) productSN = productSN.Replace(c, '_'); string resultStr = result ? "OK" : "NG"; Recordedpath = $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\{formulaName}\\{productSN}\\{productSN}-{now:yyyy-MM-dd HH_mm_ss}-{resultStr}.bmp"; } if (!Directory.Exists(Path.GetDirectoryName(Recordedpath))) Directory.CreateDirectory(Path.GetDirectoryName(Recordedpath)); if (isCompress) { ImageHelper.CompressImage(reimage, Recordedpath, 100); } else { reimage.Save(Recordedpath); } } else if (saveImageModel == ProcedureSaveImageModel.OriginalAndRecorded) { string Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; string Recordedpath = $"{path}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_OkNG_Time) { if (result) { Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; Recordedpath = $"{path}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; } else { Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; Recordedpath = $"{path}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; } } else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Ok_Time) { if (result) { Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; Recordedpath = $"{path}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; } else { return; } } else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Ng_Time) { if (!result) { Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; Recordedpath = $"{path}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; } else { return; } } else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Formula_ProductSN_Time) { string formulaName = _productService.GetCurrentProduct().Name; foreach (char c in Path.GetInvalidFileNameChars()) formulaName = formulaName.Replace(c, '_'); string productSN = _productService.CurrentProductCode; foreach (char c in Path.GetInvalidFileNameChars()) productSN = productSN.Replace(c, '_'); string resultStr = result ? "OK" : "NG"; Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\{formulaName}\\{productSN}\\{productSN}-{now:yyyy-MM-dd HH_mm_ss}-{resultStr}.bmp"; Recordedpath = $"{path}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\{formulaName}\\{productSN}\\{productSN}-{now:yyyy-MM-dd HH_mm_ss}-{resultStr}.bmp"; } if (!Directory.Exists(Path.GetDirectoryName(Originalpath))) Directory.CreateDirectory(Path.GetDirectoryName(Originalpath)); using (CogImageFileBMP _cogImageFileTool = new CogImageFileBMP()) { _cogImageFileTool.Open(Originalpath, CogImageFileModeConstants.Write); _cogImageFileTool.Append(image); _cogImageFileTool.Close(); } if (!Directory.Exists(Path.GetDirectoryName(Recordedpath))) Directory.CreateDirectory(Path.GetDirectoryName(Recordedpath)); if (isCompress) { ImageHelper.CompressImage(reimage, Recordedpath, 100); } else { reimage.Save(Recordedpath); } } } catch (Exception ex) { LogHelper.WriteLogError("保存流程运行结果的图片时出错!", ex); } finally { // ======================= 【关键修复】无论是否压缩都释放Bitmap ======================= try { reimage?.Dispose(); } catch { } } }); } /// /// 切换产品时清空主页显示(保持你原有逻辑) /// private void ProductChanged(ProductModel product) { this.gridRender.Dispatcher.BeginInvoke(new Action(() => { foreach (var item in vmRenders) { try { if (item.Value?.Child is CogRecordDisplay disp) { if (disp.Tag != null) { disp.Image = null; disp.StaticGraphics.Clear(); // 你原来 break 只清一次;这里保留你的行为 break; } } } catch (Exception ex) { LogHelper.WriteLogError(Lang.切换产品清除主页图像时出错, ex); } } })); } private void OnDisplayDoubleClick(CogRecordDisplay disp) { if (disp == null) return; // 必须在 WinForms/ActiveX 线程截图 disp.BeginInvoke(new Action(() => { Bitmap bmp = null; try { if (disp.IsDisposed || !disp.IsHandleCreated) return; // 优先使用显示内容截图(带图形/record叠加) try { var uiBmp = (Bitmap)disp.CreateContentBitmap( Cognex.VisionPro.Display.CogDisplayContentBitmapConstants.Custom); bmp = (Bitmap)uiBmp.Clone(); uiBmp.Dispose(); } catch { // 如果 ActiveX 状态不允许截图,退化:尝试从 Image 转 Bitmap bmp = TryConvertCogImageToBitmap(disp.Image); } if (bmp == null) return; // 回到 WPF UI 线程弹窗 this.Dispatcher.BeginInvoke(new Action(() => { //var win = new ImagePreviewWindow(bmp, $"预览:{disp.Tag ?? "Display"}"); //win.Owner = Window.GetWindow(this); // 让弹窗归属当前窗口 //win.Show(); })); } catch (Exception ex) { LogHelper.WriteLogError("OnDisplayDoubleClick 截图/弹窗出错", ex); } finally { // 这里 bmp 不要 Dispose,因为传给窗口了 // 窗口关闭时会释放 } })); } /// /// 将 VisionPro 的 ICogImage 尝试转换成 Bitmap(作为 CreateContentBitmap 失败时的兜底) /// private Bitmap TryConvertCogImageToBitmap(ICogImage cogImage) { try { if (cogImage == null) return null; // VisionPro 常用转换:CogImage8Grey / CogImage24PlanarColor / CogImage24PackedColor 等 // 这里给通用兜底:用 CogImageFileBMP 落地到内存流,再读回 Bitmap using (var ms = new MemoryStream()) { // 需要引用 Cognex.VisionPro.ImageFile using (var bmpFile = new CogImageFileBMP()) { // CogImageFileBMP 只能写文件路径,不直接写流 // 所以用临时文件方式最稳(如你不想落盘,可以另写转换器) var tmp = Path.Combine(Path.GetTempPath(), $"vp_preview_{Guid.NewGuid():N}.bmp"); try { bmpFile.Open(tmp, CogImageFileModeConstants.Write); bmpFile.Append(cogImage); bmpFile.Close(); var bitmap = (Bitmap)Bitmap.FromFile(tmp); return (Bitmap)bitmap.Clone(); } finally { try { if (File.Exists(tmp)) File.Delete(tmp); } catch { } } } } } catch { return null; } } } }