Using WIA for scanning

I was playing around this morning with scanning images and put together an adapter class that uses the Windows automation library (WIAAUT.DLL) which is part of the WIA automation SDK -- WIA means Windows Image Acquisition.

Here are the imports I used for the code file:

 

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using WIA;

 

I created a simple enumeration for some of the more common errors that I expected.

public enum WiaScannerError : uint
{
     LibraryNotInstalled = 0x80040154,
     OutputFileExists = 0x80070050,
     ScannerNotAvailable = 0x80210015,
     OperationCancelled = 0x80210064
}

Instead of throwing a COMException I created a special exception class that provides an error code from the aforementioned enumeration.

[Serializable]
public class WiaOperationException : 
Exception
{
     private WiaScannerError _errorCode;

     public WiaOperationException(WiaScannerError errorCode)
          : base()
     {
          ErrorCode = errorCode;
     }

     public WiaOperationException(string message, WiaScannerError errorCode) 
          : base(message)
     {
          ErrorCode = errorCode;
     }

     public WiaOperationException(string message, Exception innerException)
          : base(message, innerException)
     {
          COMException comException = innerException as COMException;

          if (comException != null)
               ErrorCode = (WiaScannerError)comException.ErrorCode;
     }

     public WiaOperationException(string message, Exception innerException, WiaScannerError errorCode)
          : base(message, innerException)
     {
          ErrorCode = errorCode;
     }

     public WiaOperationException(System.Runtime.Serialization.SerializationInfo info, 
          System.Runtime.Serialization.StreamingContext context)
               : base(info, context)
     {
          info.AddValue("ErrorCode", (uint)_errorCode);
     }

     public WiaScannerError ErrorCode
     {
          get return _errorCode; }
          protected set { _errorCode = value; }
     }

     public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, 
          System.Runtime.Serialization.StreamingContext context)
     {
          base.GetObjectData(info, context);
          ErrorCode = (WiaScannerError)info.GetUInt32("ErrorCode");
     }
}

The actual class I created is just for scanners, but you can adapt it to any device that supports WIA.  I've pulled out comments and removed some extra stuff like providing a friendly message for errors for the sake of brevity.

public sealed class WiaScannerAdapter : IDisposable
{
     private CommonDialogClass _wiaManager;
     private bool _disposed; 
// indicates if Dispose has been called

     public WiaScannerAdapter()
     {
     }

     ~WiaScannerAdapter()
     {
          Dispose(false);
     }

     private CommonDialogClass WiaManager
     {
          get return _wiaManager; }
          set { _wiaManager = value; }
     }

     [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
     public Image ScanImage(ImageFormat outputFormat, string fileName)
     {
          if (outputFormat == null)
               throw new ArgumentNullException("outputFormat");

          FileIOPermission filePerm = 
               new FileIOPermission(FileIOPermissionAccess.AllAccess, fileName));
          filePerm.Demand();

          ImageFile imageObject = null;

          try
          
{
               if (WiaManager == null)
                    WiaManager = new CommonDialogClass();

               imageObject =
                    WiaManager.ShowAcquireImage(WiaDeviceType.ScannerDeviceType,
                         WiaImageIntent.ColorIntent, WiaImageBias.MinimizeSize, 
                         outputFormat.Guid.ToString("B"), falsetruetrue);

               imageObject.SaveFile(fileName);
               return Image.FromFile(fileName);
          }
          catch (COMException ex)
          {
               string message = “Error scanning image“;
               throw new WiaOperationException(message, ex);
          }
          finally
          
{
               if (imageObject != null)
                    Marshal.ReleaseComObject(imageObject);
          }
     }

     public void Dispose()
     {
          Dispose(true);
          GC.SuppressFinalize(this);
     }

     private void Dispose(bool disposing)
     {
          if (!_disposed)
          {
               if (disposing)
               {
                    // no managed resources to cleanup
               
}

              // cleanup unmanaged resources
              
if (_wiaManager != null)
                   Marshal.ReleaseComObject(_wiaManager);

              _disposed = true;
          }
     }
}

Calling the adapter is really easy.  Here's an example that puts the image into a picturebox:

using (WiaScannerAdapter adapter = new WiaScannerAdapter())
{
     
try
     
{
          Image image = adapter.ScanImage(ImageFormat.Bmp, @"c:\temp\test.bmp");
          pictureBox1.Image = image;
     }
     catch (WiaOperationException ex)
     {
          MessageBox.Show(ex.Message + “ Error Code: “ + ex.ErrorCode);
     }
}