创建可被PowerShell调用的C# UWP DLL控制USB相机拍照遇问题求助
UWP相机控制DLL无法被PowerShell调用的问题排查与解决
问题背景
我尝试用C#写一个UWP DLL,导入PowerShell后实现USB相机的自动化拍照,但调用时遇到两种错误:
- 用
Add-Type加载时提示:Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. - 用
[Assembly]::LoadFile()加载后,创建实例时提示:Cannot find type [CameraClassLibrary.Camera]: verify that the assembly containing this type is loaded.
附我的C# DLL代码:
using System; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using Windows.Devices.Enumeration; using Windows.Graphics.Imaging; using Windows.Media.Capture; using Windows.Media.MediaProperties; using Windows.Storage; using Windows.Storage.Streams; namespace CameraClassLibrary { public class Camera { private StorageFolder _captureFolder = null; private MediaCapture _mediaCapture; private bool _isInitialized; private bool _isPreviewing; private bool _isRecording; private bool _externalCamera; private async Task InitializeCameraAsync() { Debug.WriteLine("InitializeCameraAsync"); if (_mediaCapture == null) { // 优先获取后置相机,没有则用任意相机 var cameraDevice = await FindCameraDeviceByPanelAsync(Windows.Devices.Enumeration.Panel.Back); if (cameraDevice == null) { Debug.WriteLine("未找到相机设备!"); return; } // 创建MediaCapture实例并绑定事件 _mediaCapture = new MediaCapture(); _mediaCapture.RecordLimitationExceeded += MediaCapture_RecordLimitationExceeded; _mediaCapture.Failed += MediaCapture_Failed; var settings = new MediaCaptureInitializationSettings { VideoDeviceId = cameraDevice.Id }; // 初始化MediaCapture try { await _mediaCapture.InitializeAsync(settings); _isInitialized = true; } catch (UnauthorizedAccessException) { Debug.WriteLine("应用被拒绝访问相机"); } // 初始化成功后判断相机类型 if (_isInitialized) { if (cameraDevice.EnclosureLocation == null || cameraDevice.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Unknown) { _externalCamera = true; } else { _externalCamera = false; } } } } private async Task TakePhotoAsync(string Filename) { await InitializeCameraAsync(); var stream = new InMemoryRandomAccessStream(); Debug.WriteLine("正在拍照..."); await _mediaCapture.CapturePhotoToStreamAsync(ImageEncodingProperties.CreateJpeg(), stream); try { var file = await _captureFolder.CreateFileAsync(Filename + ".jpg", CreationCollisionOption.GenerateUniqueName); Debug.WriteLine("拍照完成! 保存路径: " + file.Path); await ReencodeAndSavePhotoAsync(stream, file); Debug.WriteLine("照片已保存!"); } catch (Exception ex) { Debug.WriteLine("拍照时发生异常: " + ex.ToString()); } } private async Task CleanupCameraAsync() { Debug.WriteLine("清理相机资源"); if (_isInitialized) { if (_isRecording) { await StopRecordingAsync(); } _isInitialized = false; } if (_mediaCapture != null) { _mediaCapture.RecordLimitationExceeded -= MediaCapture_RecordLimitationExceeded; _mediaCapture.Failed -= MediaCapture_Failed; _mediaCapture.Dispose(); _mediaCapture = null; } } private static async Task ReencodeAndSavePhotoAsync(IRandomAccessStream stream, StorageFile file) { using (var inputStream = stream) { var decoder = await BitmapDecoder.CreateAsync(inputStream); using (var outputStream = await file.OpenAsync(FileAccessMode.ReadWrite)) { var encoder = await BitmapEncoder.CreateForTranscodingAsync(outputStream, decoder); await encoder.FlushAsync(); } } } private static async Task<DeviceInformation> FindCameraDeviceByPanelAsync(Windows.Devices.Enumeration.Panel desiredPanel) { var allVideoDevices = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture); DeviceInformation desiredDevice = allVideoDevices.FirstOrDefault(x => x.EnclosureLocation != null && x.EnclosureLocation.Panel == desiredPanel); return desiredDevice ?? allVideoDevices.FirstOrDefault(); } private async void MediaCapture_RecordLimitationExceeded(MediaCapture sender) { await StopRecordingAsync(); } private async void MediaCapture_Failed(MediaCapture sender, MediaCaptureFailedEventArgs errorEventArgs) { Debug.WriteLine("MediaCapture失败: (0x{0:X}) {1}", errorEventArgs.Code, errorEventArgs.Message); await CleanupCameraAsync(); } private async Task StopRecordingAsync() { Debug.WriteLine("停止录制..."); _isRecording = false; await _mediaCapture.StopRecordAsync(); Debug.WriteLine("录制已停止!"); } public async void TakePhoto(string Filename) { await TakePhotoAsync(Filename); } } }
问题根源与解决办法
1. UWP DLL无法被PowerShell直接加载的本质原因
PowerShell基于.NET Framework/.NET Core运行,而UWP DLL是WinRT组件,二者运行环境不兼容。普通的Add-Type或Assembly.LoadFile无法直接加载WinRT组件,这是核心错误原因。
2. 可行的修复方向
方向一:改用.NET 6+类库,通过WinRT互操作访问相机
- 新建**.NET 6+类库项目**(不要选UWP)
- 安装NuGet包:
Microsoft.Windows.CsWinRT和Microsoft.Windows.SDK.Contracts,这两个包能让.NET类库调用WinRT API - 迁移现有代码到新项目中,调整异步方法的调用逻辑
方向二:修复现有代码中的致命问题(即使环境兼容也需处理)
现有代码还有几个必改的错误:
_captureFolder未初始化:TakePhotoAsync中调用_captureFolder.CreateFileAsync时,这个变量是null,会直接抛出空引用异常。可以在InitializeCameraAsync中初始化它,比如指向系统图片库:_captureFolder = await KnownFolders.PicturesLibrary.CreateFolderAsync("CameraCaptures", CreationCollisionOption.OpenIfExists);async void的公共方法:TakePhoto是async void,PowerShell无法追踪异步操作的完成状态,会导致相机操作还没完成,PowerShell就结束了。改成返回Task:public Task TakePhoto(string Filename) { return TakePhotoAsync(Filename); }- 权限问题:确保PowerShell有相机访问权限(打开Windows设置-隐私和安全性-相机,允许PowerShell访问相机)
3. 调整后的PowerShell调用方式
如果用方向一的.NET类库,调用代码如下:
# 加载编译好的DLL Add-Type -Path ".\CameraClassLibrary.dll" # 创建相机实例并调用拍照方法(用Wait等待异步完成) $camera = New-Object CameraClassLibrary.Camera $camera.TakePhoto("test_photo").Wait()
内容的提问来源于stack exchange,提问作者Gengar
相关产品推荐
相关产品推荐

