如何在.NET 7 WinForms应用中使用MediaCapture.StartPreviewAsync()?
在.NET 7 WinForms中使用MediaCapture.StartPreviewAsync()的解决方案
你遇到的0xC00D36BB错误确实是因为WinForms没有UWP的CaptureElement控件——UWP中该控件会自动关联MediaCapture的预览输出目标,而WinForms缺少这个绑定机制,直接调用StartPreviewAsync()会因为没有预览目标而报错。
下面给出具体实现方案,核心思路是通过MediaFrameReader手动捕获预览帧,再将帧转换为WinForms可用的Bitmap显示在PictureBox控件中:
步骤1:准备项目与权限
- 确保项目已引用Windows Runtime相关组件(.NET 7 WinForms默认已包含,若缺失可安装
Microsoft.Windows.SDK.ContractsNuGet包) - 添加摄像头权限:
- 若使用MSIX打包,在
Package.appxmanifest中添加摄像头能力:<Capabilities> <DeviceCapability Name="webcam" /> </Capabilities> - 若为非打包应用,在
app.manifest中添加:<deviceCapability xmlns="http://schemas.microsoft.com/appx/2013/manifest"> <Device Name="webcam" /> </deviceCapability>
- 若使用MSIX打包,在
步骤2:实现核心代码
在WinForms窗体中添加一个PictureBox控件(命名为pictureBoxPreview),然后编写以下代码:
类级变量定义
using Windows.Media.Capture; using Windows.Media.Frames; using Windows.Media.MediaProperties; using Windows.Graphics.Imaging; using System.Drawing.Imaging; using System.Runtime.InteropServices; public partial class MainForm : Form { private MediaCapture _mediaCapture; private MediaFrameReader _frameReader; private readonly object _lockObj = new object(); private Bitmap _currentBitmap;
初始化MediaCapture与预览
private async Task InitializeMediaCaptureAsync() { _mediaCapture = new MediaCapture(); var initSettings = new MediaCaptureInitializationSettings { StreamingCaptureMode = StreamingCaptureMode.Video, PhotoCaptureSource = PhotoCaptureSource.VideoPreview }; // 初始化MediaCapture await _mediaCapture.InitializeAsync(initSettings); // 获取视频预览的帧源 var previewFrameSource = _mediaCapture.FrameSources .Where(f => f.Value.Info.MediaStreamType == MediaStreamType.VideoPreview) .First().Value; // 选择支持的BGRA8格式(方便转WinForms Bitmap) var preferredFormat = previewFrameSource.SupportedFormats .FirstOrDefault(f => f.Subtype == MediaEncodingSubtypes.Bgra8) ?? previewFrameSource.SupportedFormats.First(); await previewFrameSource.SetFormatAsync(preferredFormat); // 创建帧读取器并绑定帧到达事件 _frameReader = await _mediaCapture.CreateFrameReaderAsync(previewFrameSource); _frameReader.FrameArrived += OnPreviewFrameArrived; // 启动帧读取器和预览 await _frameReader.StartAsync(); await _mediaCapture.StartPreviewAsync(); }
处理预览帧并更新UI
private void OnPreviewFrameArrived(MediaFrameReader sender, MediaFrameArrivedEventArgs args) { // 获取最新帧 using var frame = sender.TryAcquireLatestFrame(); if (frame == null) return; using var softwareBitmap = frame.VideoMediaFrame.SoftwareBitmap; if (softwareBitmap == null) return; // 转换为BGRA8格式(忽略Alpha通道) using var convertedBitmap = SoftwareBitmap.Convert(softwareBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore); // 线程安全更新Bitmap lock (_lockObj) { _currentBitmap?.Dispose(); _currentBitmap = ConvertSoftwareBitmapToBitmap(convertedBitmap); } // 跨线程更新PictureBox pictureBoxPreview.Invoke((Action)(() => { pictureBoxPreview.Image?.Dispose(); pictureBoxPreview.Image = _currentBitmap; })); } private Bitmap ConvertSoftwareBitmapToBitmap(SoftwareBitmap softwareBitmap) { var bitmap = new Bitmap(softwareBitmap.PixelWidth, softwareBitmap.PixelHeight, PixelFormat.Format32bppArgb); var bitmapData = bitmap.LockBits( new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.WriteOnly, bitmap.PixelFormat); // 将SoftwareBitmap数据复制到Bitmap内存 softwareBitmap.CopyToBuffer(Windows.Storage.Streams.Buffer.CreateFromMemory(bitmapData.Scan0, bitmapData.Stride * bitmapData.Height)); bitmap.UnlockBits(bitmapData); return bitmap; }
资源清理
private async void MainForm_FormClosing(object sender, FormClosingEventArgs e) { // 停止并清理帧读取器 if (_frameReader != null) { await _frameReader.StopAsync(); _frameReader.FrameArrived -= OnPreviewFrameArrived; _frameReader.Dispose(); } // 停止预览并清理MediaCapture if (_mediaCapture != null) { await _mediaCapture.StopPreviewAsync(); _mediaCapture.Dispose(); } // 释放Bitmap资源 lock (_lockObj) { _currentBitmap?.Dispose(); } }
步骤3:启动预览
在窗体加载事件中调用初始化方法:
private async void MainForm_Load(object sender, EventArgs e) { try { await InitializeMediaCaptureAsync(); } catch (Exception ex) { MessageBox.Show($"初始化失败:{ex.Message}"); } }
关键说明
- 不再依赖
CaptureElement,而是通过MediaFrameReader直接捕获预览帧,这样MediaCapture.StartPreviewAsync()就有了合法的输出目标,不会再报0xC00D36BB错误。 - 必须保证UI更新在主线程执行,所以用
Control.Invoke跨线程更新PictureBox。 - 所有非托管资源(如
MediaCapture、Bitmap)必须在窗体关闭时正确释放,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Gabriel Frigo
相关产品推荐
相关产品推荐

