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

本文概述

  • 要求
  • 1.添加WIA参考
  • 2.列出扫描仪设备
  • 3.使用设备进行扫描
  • 4.更改WIA属性
  • 5.捕捉异常
  • 6.显示扫描进度
  • 实施实例
为了在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右上角的解决方案资源管理器, 并右键单击你的项目, 然后单击” 添加” > ” 引用” 。
使用C#在Winforms中创建扫描应用程序

文章图片
在紧急窗口中, 选择左侧菜单中的COM选项, 然后搜索Microsoft Windows Image Acquisition Library v2.0, 然后单击OK。
使用C#在Winforms中创建扫描应用程序

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

文章图片
现在, 你将可以在项目中使用WIA。
2.列出扫描仪设备 要列出设备, 你需要从WIA的DevicesManager对象中检索列表。第一步, 你需要在类顶部的代码中导入WIA组件:
using WIA;

然后只需遍历设备管理器以列出设备:
// Create a DeviceManager instancevar deviceManager = new DeviceManager(); // Loop through the list of devicesfor (int i = 1; i < = deviceManager.DeviceInfos.Count; i++){// Skip the device if it's not a scannerif (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 instancevar deviceManager = new DeviceManager(); // Create an empty variable to store the scanner instanceDeviceInfo firstScannerAvailable = null; // Loop through the list of devices to choose the first availablefor (int i = 1; i < = deviceManager.DeviceInfos.Count; i++){// Skip the device if it's not a scannerif (deviceManager.DeviceInfos[i].Type != WiaDeviceType.ScannerDeviceType){continue; }firstScannerAvailable = deviceManager.DeviceInfos[i]; break; }// Connect to the first available scannervar device = firstScannerAvailable.Connect(); // Select the scannervar scannerItem = device.Items[1]; // Retrieve a image in JPEG format and store it into a variablevar imageFile = (ImageFile)scannerItem.Transfer(FormatID.wiaFormatJPEG); // Save the image in some path with filenamevar 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 instancevar deviceManager = new DeviceManager(); // Create an empty variable to store the scanner instanceDeviceInfo firstScannerAvailable = null; // Loop through the list of devices to choose the first availablefor (int i = 1; i < = deviceManager.DeviceInfos.Count; i++){// Skip the device if it's not a scannerif (deviceManager.DeviceInfos[i].Type != WiaDeviceType.ScannerDeviceType){continue; }firstScannerAvailable = deviceManager.DeviceInfos[i]; break; }// Connect to the first available scannervar device = firstScannerAvailable.Connect(); // Select the scannervar 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 variablevar imageFile = (ImageFile)scannerItem.Transfer(FormatID.wiaFormatJPEG); // Save the image in some path with filenamevar 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 UINTuint errorCode = (uint)e.ErrorCode; // See the error codesif (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类型来检索图像。
使用C#在Winforms中创建扫描应用程序

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

然后选择扫描仪并开始扫描过程
// Create a DeviceManager instancevar deviceManager = new DeviceManager(); // Create an empty variable to store the scanner instanceDeviceInfo firstScannerAvailable = null; // Loop through the list of devices to choose the first availablefor (int i = 1; i < = deviceManager.DeviceInfos.Count; i++){// Skip the device if it's not a scannerif (deviceManager.DeviceInfos[i].Type != WiaDeviceType.ScannerDeviceType){continue; }firstScannerAvailable = deviceManager.DeviceInfos[i]; break; }// Connect to the first available scannervar device = firstScannerAvailable.Connect(); // Select the scannervar 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 exceptionsif (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的不同格式:
使用C#在Winforms中创建扫描应用程序 只需克隆存储库, 使用Visual Studio打开项目并进行测试。可以在此Github存储库中找到源代码。
【使用C#在Winforms中创建扫描应用程序】编码愉快!

    推荐阅读