You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

创建可被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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 14:48:11