首页
看点啥
插画图片
首页 科技看点 C#调用摄像头拍照的三种API方案实用指南

C#调用摄像头拍照的三种API方案实用指南

2026-10-27 0

平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“C#调用摄像头拍照的三种API方案”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。

在 C# 中编写摄像头拍照程序,主要依赖以下三类 API 技术栈:Windows API (avicap32.dll)、DirectShow/Media Foundation 以及 第三方封装库 (如 AForge.NET/Emgu CV)。不同方案适用来不同的开发场景,从底层控制到更快应用开发各有优劣。

核心 API 技术方案对比

技术方案核心 API/库实现原理优点缺点适用场景
Windows APIavicap32.dll (P/Invoke)在这个场景下,借助 P/Invoke 调用 capCreateCaptureWindow 等函数新建捕获窗口同时发送消息控制。无需额外引用,系统原生兼容,轻量级。代码繁琐,仅兼容基础功能,难以处理高清或复杂编码。轻松拍照、老旧系统兼容、无外部依赖需求。
DirectShow / WMFDirectShow.net, Windows.Media.Capture落到代码里,借助 COM 组件构建过滤器图(Filter Graph)或直接调用 UWP 接口访问媒体流。功能强大,兼容视频流精细控制、多设备切换、高清采集。结合项目来看,学习曲线陡峭,DirectShow 需注册 COM 组件,WMF 主要限于 UWP/.NET Core。专业视频 监控、直播推流、需录制视频的场景。
第三方库AForge.NET, Emgu CV, OpenCvSharp基于底层 API 封装的高级 .NET 类库,提供面向对象的操作接口。开发效率极高,内置图像处理和识别算法,代码简洁。需引入外部 DLL 依赖,增加发布包体积。更快原型开发、涉及图像处理(如二维码识别)的项目。

方案一:采用 Windows API (avicap32.dll) 实现

结合项目来看,这是最轻量级的方法,直接借助 P/Invoke 调用系统动态链接库。适合仅需轻松拍照功能的 WinForm 程序。

1. API 声明与常量定义

首先定义所需的 API 函数和消息常量。

using System;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Windows.Forms;

public class CameraAPI
{
    // 定义 avicap32.dll 中的核心函数
    [DllImport("avicap32.dll", EntryPoint = "capCreateCaptureWindowA", CharSet = CharSet.Ansi)]
    public static extern IntPtr capCreateCaptureWindowA(
        string lpszWindowName, int dwStyle, int x, int y, int nWidth, int nHeight,
        IntPtr hwndParent, int nID);

    [DllImport("user32.dll", EntryPoint = "SendMessageA", CharSet = CharSet.Ansi)]
    public static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);

    [DllImport("user32.dll", EntryPoint = "SetWindowPos")]
    public static extern bool SetWindowPos(IntPtr hwnd, int hWndInsertAfter, int x, int y, int cx, int cy, int wFlags);

    // 定义控制消息常量
    public const int WM_USER = 0x0400;
    public const int WS_CHILD = 0x40000000;
    public const int WS_VISIBLE = 0x10000000;
    public const int SWP_NOMOVE = 0x2;
    public const int SWP_NOZORDER = 0x4;
    
    public const int WM_CAP_START = WM_USER;
    public const int WM_CAP_SET_CALLBACK_VIDEOSTREAM = WM_CAP_START + 6;
    public const int WM_CAP_SET_CALLBACK_FRAME = WM_CAP_START + 5;
    public const int WM_CAP_DRIVER_CONNECT = WM_CAP_START + 10;
    public const int WM_CAP_SET_PREVIEW = WM_CAP_START + 50;
    public const int WM_CAP_SET_PREVIEWRATE = WM_CAP_START + 52;
    public const int WM_CAP_GRAB_FRAME = WM_CAP_START + 60;
    public const int WM_CAP_COPY = WM_CAP_START + 54; // 复制到剪贴板
}

2. 初始化与拍照逻辑

结合项目来看,在 WinForm 中新建一个 Panel 作为容器,将摄像头窗口嵌入其中,同时借助发送消息控制拍照。

public partial class MainForm : Form
{ private IntPtr _hWndCamera; // 摄像头窗口句柄
    private const int CAMERA_INDEX = 0; // 摄像头索引,通常为 0

    public MainForm()
    {
        InitializeComponent();
        InitCamera();
    }

    private void InitCamera()
    {
        // 1. 创建捕获窗口,父窗口为 panel1
        _hWndCamera = CameraAPI.capCreateCaptureWindowA(
            "WebCam",
            CameraAPI.WS_CHILD | CameraAPI.WS_VISIBLE,
            0, 0, panel1.Width, panel1.Height,
            panel1.Handle, 0);

        if (_hWndCamera != IntPtr.Zero)
 {
            // 2. 连接驱动
            CameraAPI.SendMessage(_hWndCamera, CameraAPI.WM_CAP_DRIVER_CONNECT, CAMERA_INDEX, 0);
            // 3. 设置预览模式
            CameraAPI.SendMessage(_hWndCamera, CameraAPI.WM_CAP_SET_PREVIEW, 1, 0);
            // 4. 设置帧率 (66ms ≈ 15fps)
            CameraAPI.SendMessage(_hWndCamera, CameraAPI.WM_CAP_SET_PREVIEWRATE, 66, 0);
        }
        else
        {
            MessageBox.Show("无法启动摄像头,请检查设备连接。");
        }
    }

    private void btnTakePhoto_Click(object sender, EventArgs e)
    {
        if (_hWndCamera == IntPtr.Zero) return;

        // 1. 抓取当前帧到剪贴板
        CameraAPI.SendMessage(_hWndCamera, CameraAPI.WM_CAP_GRAB_FRAME, 0, 0);
        CameraAPI.SendMessage(_hWndCamera, CameraAPI.WM_CAP_COPY, 0, 0);

        // 2. 从剪贴板获取图像并保存
        if (Clipboard.ContainsImage())
        {
            Image photo = Clipboard.GetImage();
            string savePath = $"photo_{DateTime.Now:yyyyMMdd_HHmmss}.bmp";
            photo.Save(savePath, System.Drawing.Imaging.ImageFormat.Bmp);
            MessageBox.Show($"拍照成功!已保存至:{savePath}");
            
            // 可选:在 PictureBox 中显示
            pictureBox1.Image = photo;
        }
    }

    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        // 释放资源:断开驱动连接
        if (_hWndCamera != IntPtr.Zero)
        {
            CameraAPI.SendMessage(_hWndCamera, CameraAPI.WM_CAP_DRIVER_CONNECT, 0, 0);
            _hWndCamera = IntPtr.Zero;
        }
        base.OnFormClosing(e);
    }
}

方案二:采用AForge.NET库(建议用来现代开发)

落到代码里,AForge.NET 是 C# 中经典的开源计算机视觉库,它封装了 DirectShow 的复杂性,提供了更简洁的 VideoCaptureDevice 类,且便于后续进行图像处理(如二维码识别)。

1. 环境准备

需借助 NuGet 安装 AForge.Video 和 AForge.Video.DirectShow 包。

2. 核心代码实现

此方案无需手动处理 API 消息,直接借助事件订阅拿到视频帧。

using AForge.Video;
using AForge.Video.DirectShow;
using System;
using System.Drawing;
using System.Windows.Forms;

public class AForgeCameraExample : Form
{
    private VideoCaptureDevice _videoSource;
    private FilterInfoCollection _videoDevices;
    private Bitmap _currentFrame; public AForgeCameraExample()
    {
        InitializeComponent();
        LoadDevices();
    }

    // 加载可用摄像头列表
    private void LoadDevices()
    {
        _videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
        if (_videoDevices.Count == 0)
        {
            MessageBox.Show("未找到摄像头设备。");
            return;
        }
        // 选择第一个摄像头
        _videoSource = new VideoCaptureDevice(_videoDevices[0].MonikerString);
        _videoSource.NewFrame += VideoSource_NewFrame; // 订阅新帧事件
    }

    // 启动摄像头
    private void btnStart_Click(object sender, EventArgs e)
    {
        if (_videoSource != null && !_videoSource.IsRunning)
        {
            _videoSource.Start();
        }
    }

    // 处理新帧事件(用于预览)
    private void VideoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
    {
        // 克隆图像以避免线程冲突
        _currentFrame = (Bitmap)eventArgs.Frame.Clone();
        
        // 更新 UI (需跨线程调用)
        if (pictureBox1.InvokeRequired)
        {
            pictureBox1.Invoke(new Action(() => pictureBox1.Image = _currentFrame));
        }
        else
        {
            pictureBox1.Image = _currentFrame;
        }
    }

    // 拍照逻辑
    private void btnTakePhoto_Click(object sender, EventArgs e)
    {
        if (_currentFrame != null)
        {
            string path = $"capture_{DateTime.Now:yyyyMMdd_HHmmss}.jpg";
            // 保存高质量 JPG
            _currentFrame.Save(path, System.Drawing.Imaging.ImageFormat.Jpeg);
            MessageBox.Show($"照片已保存:{path}");
            
            // 此处可直接对 _currentFrame 进行 QR 码解析等处理
        }
        else
        {
            MessageBox.Show("暂无图像数据,请先启动摄像头。");
        }
    }

    // 停止并释放
    private void btnStop_Click(object sender, EventArgs e)
    {
        if (_videoSource != null && _videoSource.IsRunning)
        {
            _videoSource.SignalToStop();
            _videoSource.WaitForStop();
        }
    }
}

关键技术点说明

  1. 权限管理:在 Windows 10/11 环境下,应用程序首次访问摄像头可能需用户在系统设置中授予“隐私 - 相机”权限,否则 API 调用会失败或得到黑屏。
  2. 线程安全:视频流通常在独立线程中运行(如 AForge 的 NewFrame 事件),在更新 WinForm 控件(如 PictureBox)时必须采用 Invoke 或 BeginInvoke 避免跨线程异常。
  3. 资源释放:无论是 API 句柄还是 DirectShow 对象,必须在程序退出或窗口关闭时显式断开连接(如 WM_CAP_DRIVER_CONNECT 设为 0 或调用 SignalToStop),否则摄像头指示灯可能常亮且无法被其他程序占用。

从实现思路看,总的来说,C#调用摄像头拍照API方案适合结合实际项目边做边理解。先抓住核心思路,再逐步补上细节和边界处理,最后效果会更稳定,也更容易复用。

喜欢(0)

上一篇

MySQL主从复制完整配置及实践指南实用指南

MySQL主从复制完整配置及实践指南实用指南

下一篇

C# 并行和多线程编程——认识和使用Task实用指南

猜你喜欢