个性化阅读
专注于IT技术分析

使用C#在Winforms中创建扫描应用程序

本文概述

为了在Visual Studio中使用C#在WinForms中使用扫描仪, 我们将使用WIA API。 Windows图像采集(WIA)有时也称为Windows Imaging Architecture)是Microsoft Windows 2000及更高版本操作系统的Microsoft驱动程序模型和应用程序编程接口(API), 使图形软件能够与诸如扫描仪, 数码相机和Digital的成像硬件进行通信视频设备。

WIA使需要与成像硬件进行交互的应用程序开发人员, 设备制造商和扫描仪用户变得容易。该API集通过提供对以下方面的支持, 使成像应用程序具有静止图像采集硬件功能:

  • 枚举可用的图像采集设备(Windows中已安装的扫描仪)。
  • 同时创建到多个设备的连接。
  • 以标准且可扩展的方式查询设备的属性。
  • 通过使用标准和高性能传输机制来获取设备数据。
  • 跨数据传输维护图像属性。
  • 设备状态通知和扫描事件处理。

在本文中, 你将学习如何使用C#通过WIA操作图像扫描仪。

要求

  • Visual Studio> = 2007(我们将使用Visual Studio社区)
  • 已安装且功能正常的扫描仪

让我们开始吧 !

1.添加WIA参考

使用最新的.NET Framework版本(或旧的C#项目)创建一个新的WinForms项目。然后继续直接从Visual Studio引用Windows Image Acquisition COM组件。组件对象模型(COM)是独立于平台, 分布式, 面向对象的系统, 用于创建可以交互的二进制软件组件。 .NET组件可以调用COM组件并与之交互。

转到位于Visual Studio右上角的解决方案资源管理器, 并右键单击你的项目, 然后单击”添加”>”引用”。

添加参考Visual Studio C#

在紧急窗口中, 选择左侧菜单中的COM选项, 然后搜索Microsoft Windows Image Acquisition Library v2.0, 然后单击OK。

WIA C#COM组件参考

单击确定后, 引用将添加到你的项目中。现在, 你需要将WIA组件的Embed Interop Types属性设置为False。使用Visual Studio转到”解决方案资源管理器”并选择你的项目, 然后在项目中单击树视图组件中的”引用”并搜索WIA。选择WIA参考, 然后在”属性”面板中查找”嵌入互操作类型”选项, 并将此值设置为False:

嵌入互操作类型WIA C#

现在, 你将可以在项目中使用WIA。

2.列出扫描仪设备

要列出设备, 你需要从WIA的DevicesManager对象中检索列表。第一步, 你需要在类顶部的代码中导入WIA组件:

using WIA;

然后只需遍历设备管理器以列出设备:

// Create a DeviceManager instance
var deviceManager = new DeviceManager();

// Loop through the list of devices
for (int i = 1; i <= deviceManager.DeviceInfos.Count; i++)
{
    // Skip the device if it's not a scanner
    if (deviceManager.DeviceInfos[i].Type != WiaDeviceType.ScannerDeviceType)
    {
        continue;
    }

    // Print something like e.g "WIA Canoscan 4400F"
    Console.WriteLine(deviceManager.DeviceInfos[i].Properties["Name"].get_Value());
    // e.g Canoscan 4400F
    //Console.WriteLine(deviceManager.DeviceInfos[i].Properties["Description"].get_Value());
    // e.g \\.\Usbscan0
    //Console.WriteLine(deviceManager.DeviceInfos[i].Properties["Port"].get_Value());
}

Properties对象具有其他属性, 例如Id, Port, Manufacturer和Type, 请访问有关WIA设备信息类的MSDN页面以获取更多信息。

3.使用设备进行扫描

要保存扫描的图像, 你将需要导入以下类型:

using WIA;
using System.IO;

然后, 使用扫描仪的逻辑如下:

  1. 检索要使用的扫描仪的DeviceInfo实例。
  2. 使用DeviceInfo实例连接到扫描仪。
  3. 通过带有连接实例的items属性内具有索引1的元素选择扫描程序。
  4. 使用所选扫描仪的传输方法, 并提供扫描图像的输出格式作为第一个参数。
  5. 将返回的图像数据保存到文件中。

先前的逻辑通过以下代码实现。我们将选择系统上第一个可用的扫描仪, 并将遵循以前的算法:

注意

如果处理不当, 选择扫描仪, 然后使用其余代码启动扫描过程的过程可能会有些棘手。例如, 我们建议你向列表中添加一个列表框项, 并追加一个新项, 该项也显示名称和DeviceInfos对象(你将在本文结尾看到一个示例)。

 // Create a DeviceManager instance
var deviceManager = new DeviceManager();

// Create an empty variable to store the scanner instance
DeviceInfo firstScannerAvailable = null;

// Loop through the list of devices to choose the first available
for (int i = 1; i <= deviceManager.DeviceInfos.Count; i++)
{
    // Skip the device if it's not a scanner
    if (deviceManager.DeviceInfos[i].Type != WiaDeviceType.ScannerDeviceType)
    {
        continue;
    }

    firstScannerAvailable = deviceManager.DeviceInfos[i];

    break;
}

// Connect to the first available scanner
var device = firstScannerAvailable.Connect();

// Select the scanner
var scannerItem = device.Items[1];

// Retrieve a image in JPEG format and store it into a variable
var imageFile = (ImageFile)scannerItem.Transfer(FormatID.wiaFormatJPEG);

// Save the image in some path with filename
var path = @"C:\Users\<username>\Desktop\scan.jpeg";

if (File.Exists(path))
{
    File.Delete(path);
}

// Save image !
imageFile.SaveFile(path);

如果你对其进行测试, 则代码将起作用并且扫描仪将启动, 但是大多数扫描仪上的图像将不完整。这是因为我们没有设置扫描仪的任何常用属性, 你将在接下来的步骤中学习设置。

4.更改WIA属性

WIA有一些可修改的属性, 例如扫描宽度和高度, 颜色模式等。要设置此属性, 我们需要检索WIA.Properties类的属性, 然后设置新值。首先导入所需的类型:

using WIA;
using System.IO;

以下方法AdjustScannerSettings将通过帮助器函数SetWIAProperty设置一些基本属性, 以使其至少在大多数扫描设备中都起作用。请注意, 你需要在AdjustScannerSettings(通过连接实例的items属性内具有索引1的元素选择并分配给变量的扫描仪)方法中提供第一个参数作为扫描仪项目, 并在函数中记录其他参数。

重要

请记住, WIA具有许多可以修改的属性常数, 并且这些常数在不同的扫描设备上可能不可用(例如, 使用常数6147来修改水平分辨率), 请阅读以下MSDN页面以获取更多信息。

/// <summary>
/// Adjusts the settings of the scanner with the providen parameters.
/// </summary>
/// <param name="scannnerItem">Scanner Item</param>
/// <param name="scanResolutionDPI">Provide the DPI resolution that should be used e.g 150</param>
/// <param name="scanStartLeftPixel"></param>
/// <param name="scanStartTopPixel"></param>
/// <param name="scanWidthPixels"></param>
/// <param name="scanHeightPixels"></param>
/// <param name="brightnessPercents"></param>
/// <param name="contrastPercents">Modify the contrast percent</param>
/// <param name="colorMode">Set the color mode</param>
private static void AdjustScannerSettings(IItem scannnerItem, int scanResolutionDPI, int scanStartLeftPixel, int scanStartTopPixel, int scanWidthPixels, int scanHeightPixels, int brightnessPercents, int contrastPercents, int colorMode)
{
    const string WIA_SCAN_COLOR_MODE = "6146";
    const string WIA_HORIZONTAL_SCAN_RESOLUTION_DPI = "6147";
    const string WIA_VERTICAL_SCAN_RESOLUTION_DPI = "6148";
    const string WIA_HORIZONTAL_SCAN_START_PIXEL = "6149";
    const string WIA_VERTICAL_SCAN_START_PIXEL = "6150";
    const string WIA_HORIZONTAL_SCAN_SIZE_PIXELS = "6151";
    const string WIA_VERTICAL_SCAN_SIZE_PIXELS = "6152";
    const string WIA_SCAN_BRIGHTNESS_PERCENTS = "6154";
    const string WIA_SCAN_CONTRAST_PERCENTS = "6155";
    SetWIAProperty(scannnerItem.Properties, WIA_HORIZONTAL_SCAN_RESOLUTION_DPI, scanResolutionDPI);
    SetWIAProperty(scannnerItem.Properties, WIA_VERTICAL_SCAN_RESOLUTION_DPI, scanResolutionDPI);
    SetWIAProperty(scannnerItem.Properties, WIA_HORIZONTAL_SCAN_START_PIXEL, scanStartLeftPixel);
    SetWIAProperty(scannnerItem.Properties, WIA_VERTICAL_SCAN_START_PIXEL, scanStartTopPixel);
    SetWIAProperty(scannnerItem.Properties, WIA_HORIZONTAL_SCAN_SIZE_PIXELS, scanWidthPixels);
    SetWIAProperty(scannnerItem.Properties, WIA_VERTICAL_SCAN_SIZE_PIXELS, scanHeightPixels);
    SetWIAProperty(scannnerItem.Properties, WIA_SCAN_BRIGHTNESS_PERCENTS, brightnessPercents);
    SetWIAProperty(scannnerItem.Properties, WIA_SCAN_CONTRAST_PERCENTS, contrastPercents);
    SetWIAProperty(scannnerItem.Properties, WIA_SCAN_COLOR_MODE, colorMode);
}

/// <summary>
/// Modify a WIA property
/// </summary>
/// <param name="properties"></param>
/// <param name="propName"></param>
/// <param name="propValue"></param>
private static void SetWIAProperty(IProperties properties, object propName, object propValue)
{
    Property prop = properties.get_Item(ref propName);
    prop.set_Value(ref propValue);
}

值得再次说明, 这取决于你对属性的自定义, 但是我们为你提供了一种使用SetWIAProperty方法自定义属性的简单方法。

最后, 要正确开始扫描, 你只需要在初始化扫描器之前执行AdjustScannerSettings方法(混合步骤3和4):

// Create a DeviceManager instance
var deviceManager = new DeviceManager();

// Create an empty variable to store the scanner instance
DeviceInfo firstScannerAvailable = null;

// Loop through the list of devices to choose the first available
for (int i = 1; i <= deviceManager.DeviceInfos.Count; i++)
{
    // Skip the device if it's not a scanner
    if (deviceManager.DeviceInfos[i].Type != WiaDeviceType.ScannerDeviceType)
    {
        continue;
    }

    firstScannerAvailable = deviceManager.DeviceInfos[i];

    break;
}

// Connect to the first available scanner
var device = firstScannerAvailable.Connect();

// Select the scanner
var scannerItem = device.Items[1];

/**
 * Set the scanner settings
 */
int resolution = 150;
int width_pixel = 1250;
int height_pixel = 1700;
int color_mode = 1;
AdjustScannerSettings(scannerItem, resolution, 0, 0, width_pixel, height_pixel, 0, 0, color_mode);

// Retrieve a image in JPEG format and store it into a variable
var imageFile = (ImageFile)scannerItem.Transfer(FormatID.wiaFormatJPEG);

// Save the image in some path with filename
var path = @"C:\Users\<username>\Desktop\scan.jpeg";

if (File.Exists(path))
{
    File.Delete(path);
}

// Save image !
imageFile.SaveFile(path);

5.捕捉异常

WIA函数方法会引发可通过错误代码识别的异常。错误代码列表可以在MSDN网站上的WIA文档中找到。要处理WIA的错误, 请捕获COMException对象。切记之前要导入InteropServices类型:

using System.Runtime.InteropServices;

然后将使用WIA的代码包装在try-catch语句中。你可以使用异常的ErrorCode属性来标识错误, 但请记住将其转换为其uint表示形式, 以便能够将其与MSDN中表的错误代码进行比较。

try
{
    // Some code that uses WIA
    // e.g 
    //
    // var device = firstScannerAvailable.Connect();
    // var scannerItem = device.Items[1];
    // var imageFile = (ImageFile)scannerItem.Transfer(FormatID.wiaFormatJPEG);
}
catch (COMException e)
{ 
    // Convert the error code to UINT
    uint errorCode = (uint)e.ErrorCode;

    // See the error codes
    if (errorCode ==  0x80210006)
    {
        Console.WriteLine("The scanner is busy or isn't ready");
    }
    else if(errorCode == 0x80210064)
    {
        Console.WriteLine("The scanning process has been cancelled.");
    }
    else if(errorCode == 0x8021000C)
    {
        Console.WriteLine("There is an incorrect setting on the WIA device.");
    }
    else if(errorCode == 0x80210005)
    {
        Console.WriteLine("The device is offline. Make sure the device is powered on and connected to the PC.");
    }
    else if(errorCode == 0x80210001)
    {
        Console.WriteLine("An unknown error has occurred with the WIA device.");
    }
}

6.显示扫描进度

要显示扫描仪的进度, 可以使用CommonDialogClass的ShowTransfer方法。 CommonDialog控件是一个运行时不可见的控件, 可以在调用CreateObject时使用” WIA.CommonDialog”作为ProgID来创建, 也可以通过将CommonDialog对象放在窗体上来创建。你可以通过将扫描结果(从ShowTransfer方法返回的对象)转换为ImageFile类型来检索图像。

扫描过程对话框WIA

此方法将显示一个带有进度条的微型对话框, 该进度条指示并在扫描过程中进行更新。它希望将扫描仪项目作为第一个参数, 将扫描图像的格式作为第二个参数, 并将指示是否应显示”取消扫描”按钮的布尔值作为第三个参数。如果用户取消了扫描过程, 请注意添加try-catch语句以防止你的应用程序崩溃。在班级顶部导入以下类型:

using WIA;
using System.Runtime.InteropServices;

然后选择扫描仪并开始扫描过程

// Create a DeviceManager instance
var deviceManager = new DeviceManager();

// Create an empty variable to store the scanner instance
DeviceInfo firstScannerAvailable = null;

// Loop through the list of devices to choose the first available
for (int i = 1; i <= deviceManager.DeviceInfos.Count; i++)
{
    // Skip the device if it's not a scanner
    if (deviceManager.DeviceInfos[i].Type != WiaDeviceType.ScannerDeviceType)
    {
        continue;
    }

    firstScannerAvailable = deviceManager.DeviceInfos[i];

    break;
}

// Connect to the first available scanner
var device = firstScannerAvailable.Connect();

// Select the scanner
var scannerItem = device.Items[1];

CommonDialogClass dlg = new CommonDialogClass(); 
 
try
{
    object scanResult = dlg.ShowTransfer(scannerItem, WIA.FormatID.wiaFormatPNG, true);

    if (scanResult != null){
        ImageFile image = (ImageFile)scanResult;
        
        // Do the rest of things as save the image 
    }
}
catch (COMException e)
{ 
    // Display the exception in the console.
    Console.WriteLine(e.ToString());

    uint errorCode = (uint)e.ErrorCode;

    // Catch 2 of the most common exceptions
    if (errorCode ==  0x80210006)
    {
        Console.WriteLine("The scanner is busy or isn't ready");
    }else if(errorCode == 0x80210064)
    {
        Console.WriteLine("The scanning process has been cancelled.");
    }
}

通过所有这些基本说明, 你将能够使用C#在WinForms中创建自己的扫描应用程序。

实施实例

我们刚刚编写了一个示例应用程序, 该应用程序在列表框中列出了所有可用的扫描仪设备, 并允许你扫描文件并将其保存在自定义路径中。它实现了扫描进度对话框, 并允许你将图像保存为PNG, JPEG或TIFF的不同格式:

扫描应用程序示例

只需克隆存储库, 使用Visual Studio打开项目并进行测试。可以在此Github存储库中找到源代码。

编码愉快!

赞(0)
未经允许不得转载:srcmini » 使用C#在Winforms中创建扫描应用程序

评论 抢沙发

评论前必须登录!