Here's all my WIA notes - you can find samples showing how to list the code numbers.
From Microsoft download the Wia 2.0 SDK (WiaAutSDK.zip) which contains the Wiaaut.DLL.
The WIA download is this link:
http://www.microsoft.com/downloads/info.aspx?na=46&p=3&SrcDisplayLang=en&SrcCategoryId=&SrcFamilyId=a332a77a-01b8-4de6-91c2-b7ea32537e29&genscs=&u=http%3a%2f%2fdownload.microsoft.com%2fdownload%2fWinXPHome%2fUtility%2f2.0%2fWXP%2fEN-US%2fWIAAutSDK.zip
Next go to Project > AddReference > Com and then BROWSE to Wiaaut.Dll. Select it, choose OK, and thenceforth you'll see the WIA {} in Intellisense. (As far as I know, it is not necessary to to put the DLL in the system32 directory for purposes of running the RegSvr32. I believe that .Net can register the DLL without usage of the RegSvr32 command).
Microsft says XP service pack 1 is necessary for WIA 2.0 (Windows Image Acquisition 2.0) but I was successful without any service packs. The easiest way to tell if a scanner supports WIA 2.0 is to try using the Scanner and Camera Wizard at Start > Programs > Acessories. That Wizard is based on Wia 2.0. If the wizard cannot detect or cannot drive your scanner, chances are the scanner won't respond to Wia 2.0 code blocks. To this there is, however, one caveat, namely there can be a temporary disconnectivity remedied by rebooting. Suppose for instance your Wia 2.0 code suddenly stops working, so you try the wizard only to find that it can no longer detect your scanner. Try rebooting. I don't recall having this problem with Twain drivers. Also, I've heard rumor that RealPlayer can make the wizard unusable.
Warning! Warning! WIA can be imported as an activeX objectwhich appears in the code as AxCommonDialog1 (the Ax stands for ActiveX). I seem to recall having trouble with the Ax version of the CommonDialog. The point is this. Instead of dropping an AxCommonDialog onto the form, you might have better luck just creating the CommonDialog in code like this:
Dim dlg as New Wia.CommonDialog
The CommonDialog object presents dialog boxes to the user such as the Scanner and Camera Wizard but can drive scanning without showing the wizard as shown below (this is possibly the shortest code for scanning).
Dim Dialog1 As New WIA.CommonDialog
Dim Scanner As WIA.Device = Dialog1.ShowSelectDevice(WIA.WiaDeviceType.ScannerDeviceType, False, False)
Dim Img As WIA.ImageFile = Scanner.Items(1).Transfer() 'Whereas ShowTransfer() shows the progress bar.
Img.SaveFile("C:\MyImage.Tif")
The above code scans using the default values for the default scanner. The first line of the above code was this:
Dim Dialog1 As New WIA.CommonDialog
After this first line, you could have inserted the following lines to lock in some specific values. The values are identified by a four-digit PropertyID that typically begins with the number 6 such as 6146.
Dim DPI As Integer = 150 'dots per inch
Scanner.Items(1).Properties("6146").Value = 4 '4 is Black-white,gray is 2, color 1 (Color Intent)
Scanner.Items(1).Properties("6147").Value = DPI 'dots per inch/horizontal
Scanner.Items(1).Properties("6148").Value = DPI 'dots per inch/vertical
Scanner.Items(1).Properties("6149").Value = 0 'x point where to start scan
Scanner.Items(1).Properties("6150").Value = 0 'y-point where to start scan
Scanner.Items(1).Properties("6151").Value = 8.5 * DPI 'horizontal exent 'many scanners have max size of 8.5 by 11
Scanner.Items(1).Properties("6152").Value = 11 * DPI 'vertical extent
You MUST use Scanner.Items(1) in your code if you intend to automate scanning (note that WIA indexes are 1-based rather than 0-based). If you are not automating scanning, that is, if you merely intend to show the user a scan-dialog, there is no need for Scanner.Items(1). But precisely what is Scanner.Items(1)? On the one hand it appears to be an image-awaiting-transfer since we wrote:
Dim Img As WIA.ImageFile = Scanner.Items(1).Transfer() 'And yet, this commmand is what initiates scanning!
How can Scanner.Items(1) already be an image prior to scanning? In a momement I will submit a theory as to how Scanner.Items(1) operates, since Microsoft's WIA manual is unclear on this issue. With cameras, things seem a bit more clear. Each item in Camera.Items such as Camera.Items(1)does indeed seem to be an image-awaiting-transfer. In other words the command:
Camera.Items(1).Transfer()
doesn't INITIATE imaging (like it typically does with scanners) but rather transfers an already existing image, as one would intuitively expect. Thus with Cameras, typically an item is an image. How then do we tell a camera to initiate imaging, that is, to take a picture? Apparently like this:
Dim Camera As WIA.Device = (New WIA.CommonDialog).ShowSelectDevice
If Camera.Type = WIA.WiaDeviceType.CameraDeviceType Then 'verifies this IS a camera
Dim Item As WIA.Item = Camera.ExecuteCommand(WIA.CommandID.wiaCommandTakePicture)
Dim Image as Wia.ImageFile = Item.Transfer 'because this item is an image.
End If
The command Camera.Items(1).Transfer merely copies the image to the computer instead of completely erasing it from the camera, whereas Scanner.Items(1).Transfer actually moves the image, that is, erases it from the scanner. Here's our theory as to how the command Scanner.Items(1).Transfer works. If there is no image scanned as yet, it initiates scanning and then transfers the first image to the computer. Thus if the paper is scanned in duplex, only one of the two images gets transferred. The second image will ALSO be Scanner.Items(1) since the first image is gone. Thus, in a loop, you simply need to keep calling Scanner.Items(1).Transfer. Even if it were possible for a Scanner to have, say, 10 images in its buffer at once, you could keep calling Scanner.Items(1).Transfer until the buffer becomes completely empty.
Again, the command Scanner.Items(1).Transfer is necessary only for automating scanning. You don't need it if you are merely summoning the Scanner and Camera Wizard as follows:
Dim CommonDialog1 As New WIA.CommonDialog
Dim Scanner As WIA.Device = CommonDialog1.ShowSelectDevice
CommonDialog1.ShowAcquisitionWizard(Scanner) 'shows the Scanner and Camera wizard.
The above code doesn't seem to give the programmer a handle on the resulting image, whereas the following code shows the wizard AND returns the image back to the programmer.
Dim CommonDialog1 As New WIA.CommonDialog
Dim Image As WIA.ImageFile = CommonDialog1.ShowAcquireImage(WIA.WiaDeviceType.ScannerDeviceType, WIA.WiaImageIntent.TextIntent, WIA.WiaImageBias.MaximizeQuality, , False, False)
Image.SaveFile("C:\MyFile.Tif")
An Item doesn't have to pertain to images. In WIA 2.0, an Item can be virtually any WIA 2.0 object although, as we have seen, Scanera.Items and Camera.Items usually DO pertain to images. To verify that a given Item is an image, or to find out what kind of Item it is, make use of the various ItemFlags. A non-programmer like me would think that Microsoft would set up the test like this:
If Item.Type = enumItemTypes.Image Then 'do whatever
Instead bitwise operations are used. Instead of a "Type" property there is an ItemFlags bitmask property on which you have to perform a bitwise operation to get the above result. That is to say, to verify it's an image, apply a Logical-AND to two bitmasks, namely the ItemsFlag bitmask and the image-verification bitmask like this:
If (Item.Properties("Item Flags") AND WIA.ImageItemFlag) = Wia.ImageItemFlag Then 'it is indeed an image.
Here are the other flags which depend on a logical AND for verification. That is to say, if you detect using the above line of code that this Item is not an image, you can apply the same type of test (logical AND) on each of the following flags until you find out what type of Item it is:
WIA.FreeItemFlag - The item is uninitialized or has been deleted.
WIA.ImageItemFlag - The item is an image file. Only valid for items that also have the FileItemFlag flag set.
WIA.FileItemFlag - The item is a file.
WIA.FolderItemFlag - The item is a folder.
WIA.RootItemFlag - Identifies the root item in the device.
WIA.AnalyzeItemFlag - This item supports the Analyze method.
WIA.AudioItemFlag - The item is an audio file. Only valid for items that also have the FileItemFlag flag set.
WIA.DeviceItemFlag - The item represents a connected device.
WIA.DeletedItemFlag - The item is marked as deleted.
WIA.DisconnectedItemFlag - The item represents a disconnected device.
WIA.HPanoramaItemFlag - The item represents a horizontal panoramic image.
WIA.VPanoramaItemFlag - The item represents a vertical panoramic image.
WIA.BurstItemFlag - Images in this folder were taken in a continuous time sequence. Only valid for items that also have the FolderItemFlag flag set.
WIA.StorageItemFlag - The item represents a storage medium.
WIA.TransferItemFlag - The item can be transferred.
WIA.GeneratedItemFlag - This item was created and does not correspond to an item in a device.
WIA.HasAttachmentsItemFlag - The item has file attachments.
WIA.VideoItemFlag - The item represents streaming video.
WIA.RemovedItemFlag - The item has been removed from the device.
Be careful about depending on WIA 2.0 for duplex. Do a lot of testing. I found that if I scanned duplex using Wia 2.0 and then did a Twain scan or selected a Twain scanner in between two WIA sessions, when I returned to Wia 2.0 it would be in simplex mode instead of duplex. THAT'S A BIG GOTCHA! For each scanner individually, try to find a WIA-based property (see the samples later one) that LOCKS the scanner into dupliex and then be sure to have your code re-initiate this lock every time scanning is paused or interrupted.
Realize that scanners vary widely. If you are writing code for a company with several different scanners, you might need to, for best results, detect each scanner by name or ID# (as shown later) in order to initialize each scanner according to its unique properties. For example the values that lock one scanner into duplex may be different than the values needed to lock another scanner into duplex. In my case, value 5 for the Document Handling Select property (property 3088) locked in duplexing on the Xerox 252/262 scanners. Valid values for the Xerox are 0, 1, 4,5, 8, 9, 12, 13. Value 1 seemed to lock in simplex.
Dim DefaultDevice As WIA.Device = (new Wia.CommonDialog).ShowSelectDevice(WIA.WiaDeviceType.ScannerDeviceType,False, False)
Dim ThisProperty As WIA.Property
For Each ThisProperty In DefaultDevice.Properties
If ThisProperty.PropertyID = "3088" Then 'Document-Handling-Select property.
Try
ThisProperty.Value = 5 'This is my effort to enforce duplex. I
'don't know if this value works for non-Xerox scanners.
Catch Ex As Exception
End Try
Exit For
End If
Next
Be aware that the line of code:
Dim DefaultDevice as Wia.Device = Dialog1.ShowSelectDevice(WIA.WiaDeviceType.ScannerDeviceType, False, False)
will throw an error if the default device is not Wia2.0 compliant.
To find out the name or ID# of the scanners at your company so that your code can begin to select devices by name or ID#, use this code:
Dim DefaultDevice As WIA.Device = (New WIA.CommonDialog).ShowSelectDevice(WIA.WiaDeviceType.ScannerDeviceType, False, False)
Dim Name as String = DefaultDevice.Properties("Name").Value
Dim ID as String = DefaultDevice.DeviceID
If you installed the scanner driver three times, the Name string may have the suffix #3 appended to it, and the ID string may have the suffix \0003 appended to it.Now, having this knowledge, suppose you wanted to connect by DeviceID. For example my Device ID is as follows (note I had to include the suffix to get this to work):
Dim Id As String = "{6BDD1FC6-810F-11D0-BEC7-08002BE2092F}\0005"
Dim DevMgr As New WIA.DeviceManager
Dim Scanner As WIA.Device = DevMgr.DeviceInfos(Id).Connect()
Dim Item As WIA.Item = Scanner.Items(1) 'the needed ITEM object.
Dim Img As WIA.ImageFile = Item.Transfer 'scans the image.
Img.SaveFile("C:\MyImage.Tif")
If I wanted to avoid having to use the suffix, I could have looped through all installed WIA devices trying to connect to one that has the main part of the string:
Dim TargetId As String = "{6BDD1FC6-810F-11D0-BEC7-08002BE2092F}"
Dim DevMgr As New WIA.DeviceManager
Dim Scanner As WIA.Device
For Each DeviceInfo As WIA.DeviceInfo In DevMgr.DeviceInfos 'loops through all Wia devices.
If DeviceInfo.DeviceID.IndexOf(TargetId) > -1 Then 'this device/driver contains main part of target ID
Try
Scanner = DevMgr.DeviceInfos(DeviceInfo.DeviceID).Connect()
Exit For
Catch ex As Exception
End Try
End If
Next
If IsNothing(Scanner) Then
MsgBox("The target scanner was not found.")
Else
Dim Img As WIA.ImageFile = Scanner.Items(1).Transfer 'scans the next piece of paper
Img.SaveFile("C:\MyImage.Tif")
End If
Similarly, if I had previously detected the name of my desired scanner, I could loop through all WIA devices trying to force a connection to a scanner of that name:
Dim TargetName As String = "MEM48U" 'the name of desired scanner.
Dim DevMgr As New WIA.DeviceManager
Dim Scanner As WIA.Device
For Each DeviceInfo As WIA.DeviceInfo In DevMgr.DeviceInfos
Dim CurrentName As String = DeviceInfo.Properties("Name").Value
If CurrentName.IndexOf(TargetName) > -1 Then 'this device/driver contains target name
Try
Scanner = DevMgr.DeviceInfos(DeviceInfo.DeviceID).Connect()
Exit For
Catch ex As Exception
End Try
End If
Next
If IsNothing(Scanner) Then
MsgBox("The target scanner was not found.")
Else
Dim Img As WIA.ImageFile = Scanner.Items(1).Transfer 'scans the image
Img.SaveFile("C:\MyImage.Tif")
End If
Don't forget to dispose of your COM objects. Here are two examples:
System.Runtime.InteropServices.Marshal.ReleaseComObject(Scanner)
System.Runtime.InteropServices.Marshal.ReleaseComObject(DevMgr)
By the way, the "cancelError" option on some of these commands pertains to when a user hits the cancel button. This option refers to whether or not Windows should generate an error.
COMPRESSING THE IMAGE
Generally, to save disk space you should compress the acquired image (the Wia.ImageFile object). You can do this before or after you save the image. If you previously archived it, you may have to first reload it using the Wia.ImageFile.Load(strPath) method and then resave it after compression. In any case, compression is a matter of creating an ImageProcess object:
Dim ImageProcess1 As New WIA.ImageProcess 'instanciates a Process-The-Image Object
'Add the"Convert" filter to define this ImageProcess as file conversion.
'This sets up to convert the image to a compressed format
ImageProcess1.Filters.Add(ImageProcess1.FilterInfos("Convert").FilterID)
'Next line opts to convert the file to TIF format
ImageProcess1.Filters(1).Properties("FormatID").Value = WIA.FormatID.wiaFormatTIFF
'CCITT4' is a popular compression format,typically used for faxing as far as I know.
ImageProcess1.Filters(1).Properties("Compression").Value = "CCITT4"
Img = ImageProcess1.Apply(Img)
To compress a JPEG, the code is similar to the above.
Dim ImageProcess1 As New WIA.ImageProcess
ImageProcess1.Filters.Add(ImageProcess1.FilterInfos("Convert").FilterID)
ImageProcess1.Filters.Add IP.FilterInfos("Convert").FilterID
ImageProcess1.Filtes(1).Properties("FormatID").Value = wiaFormatJPEG 'converts to JPEG format.
ImageProcess1.Filters(1).Properties("Quality").Value = 5 'a common JPEG compression standard
Img = ImageProcess1.Apply(Img)
SCRIPTING
You don't need dot Net to use Wia 2.0. To use it in scripting, invoke the CreateObject method to instanciate the various objects of the WIA 2.0 object model. For example:
Dim ImageProcess1
Set ImageProcess1 = CreateObject("WIA.ImageProcess")
See Microsoft's WIA manual located in the WIA 2.0 SDK for many more examples of scripting.
Displaying the Image
I did not find a way to load a Wia.ImageFile directly into a dot Net picturebox. An alternative would be to save the file first and then use the following code:
PictureBox1.Image = Image.FromFile("C:\MyImage.Tif")
Printing an Image
An easy, cheapskate way to let the user print an image is the following VB.net code:
Dim Dialog1 As New WIA.CommonDialog
Dialog1.ShowPhotoPrintingWizard("C:\MyImage.Tif")
The downside of the above is inflexibiltiy - that's about all you can do with this printing Wizard. Printing is not WIA's strongpoint but certainly the dot Net GDI+ class will allow you to print images in any way necessary. By the way, you can use the Wia.Vector object to add more pictures to the PhotoPrintingWizard. The vector is apparently a special type of collection specialized for WIA 2.0. Here we add more pictures to the collection (to the vector).
Dim V As New WIA.Vector
V.Add("C:\MyImage1.Tif")
V.Add("C:\MyImage2.Tif")
Dim Dialog1 As New WIA.CommonDialog
Dialog1.ShowPhotoPrintingWizard(V)
Using the Vector Collection-Object for Image Analysis
Once a vector is created, you then add items to the collection using the Vector.Add method which takes two parameters, the second one optional.
Vector1.Add(Value as Variant, Index as Long).
You can clear a vector using Vector.Clear. Annihilates the items. There is a function:
Vector1.Remove (index) as Variant
The above removes one of the items from the Vector (each item in a Vector is of Variant type) and then outputs it as the return value of the function. A vector can also be a collection of pixels useful for counting pixels although WIA is particularly slow at such processes. Here we count the white pixels on a page, based on the fact that the ARGB (alpha-red-green-blue) color value of a white pixel is -1. This could be helpful for detecting blank pages, although a quicker way is to realize that any saved file less than 500 bytes in size is a blank page.
Dim Img As New WIA.ImageFile
Img.LoadFile("C:\MyImage1.Tif")
Dim v As WIA.Vector = Img.ARGBData 'a collection of pixels.
Dim whitecount As Integer
For i As Integer = 1 To v.Count
If Val(v(i)) = -1 Then whitecount += 1
Next
MsgBox("The total number of pixels " & v.Count)
MsgBox("The total number of white pixels is " & whitecount)
MERGING IMAGE FILES INTO A MULTIPAGE TIF
You can use WIA to convert any number of saved image files into a multipage tif. First, I stored the image filenames in a string collection called FilePaths. The FirstImage is the base image. The remaining images are NOT, at first, added to FirstImage. Rather we create an ImageProcess1 to which we add the remaining images and then later we apply ImageProcess1 (the collection of subsequent images) to the FirstImage. To build this collection into ImageProcess1 do the following. For each image, add to ImageProcess1 a Filter object of type Frame and then associate this Frame with the image. Thus for each image do the following:
(1)Add a Filter of type "Frame" to ImageProcess1 like this:
ImageProcess1.Filters.Add(ImageProcess1.FilterInfos("Frame").FilterID) 'adds a filter
Dim Filter as Wia.Filter = ImageProcess1.Filters(ImageProcess1.Filters.Count) 'gives us a handle on this filter
The second line above gives us a handle on the newly added filter. It gets the handle based on the knowledge that the newly added filter will be the last filter index, that is, the index of value Filters.Count. And we specified that this filter is of type Frame.
(2)Associate this newly added Filter/Frame with an actual Wia.ImageFile object as follows;
Dim objImage as Wia.Image
objImage.LoadFile(PathToNextImage)
Filter.Properties("ImageFile").Value = objImage 'uses the filter-handle obtained in step 1.
When all the images have been added to the ImageProcess1, we can then FINALLY apply ImageProcess1 to FirstImage:
objFirstImg = ImageProcess1.Apply(objFirstImg) 'appends all the Frames to the original image.
The complete code is as follows:
Dim FirstImg As New WIA.ImageFile
FirstImg.LoadFile(FilePaths(0)) 'loads first image. For now, ignore this FirstImage
Dim ImageProcess1 As New WIA.ImageProcess 'load remaining images into ImageProcess1
Dim i As Integer
For i = 1 To FilePaths.Count - 1 'skip FilePaths(0) since it was already reserved as FirstImage
Dim Img As New WIA.ImageFile
Img.LoadFile(FilePaths(i))
ImageProcess1.Filters.Add(ImageProcess1.FilterInfos("Frame").FilterID)
Dim Filter as Wia.Filter = ImageProcess1.Filters(ImageProcess1.Filters.Count)
Filter.Properties("ImageFile").Value = Img
Next i
FirstImg = ImageProcess1.Apply(FirstImg) 'applies the image collection to FirstImage
Dim FilePath As String = Me.GetAFilePathToSaveThisImage()
FirstImg.SaveFile(FilePath) 'saves the multipage Tif.
HOW TO SCALE (RESIZE) AN IMAGE
In this case scale/resize an image to a thumbnail size (100 pixels by 100 pixels) as follows:
Dim Img As New WIA.ImageFile
Dim ImageProcess As New WIA.ImageProcess
Img.LoadFile("C:\WINDOWS\Web\Wallpaper\Bliss.bmp")
ImageProcess.Filters.Add(ImageProcess.FilterInfos("Scale").FilterID)
ImageProcess.Filters(1).Properties("MaximumWidth").Value = 100
ImageProcess.Filters(1).Properties("MaximumHeight").Value = 100
Img = ImageProcess.Apply(Img)
Img.SaveFile("C:\Thumb1.tif")
Now, for the fun of it, STAMP the thumbnail on top of the original to show how to overlay one picture over another as a stamp.
Dim Thumb As New WIA.ImageFile
Dim Img As New WIA.ImageFile
Dim ImageProcess As New WIA.ImageProcess
Img.LoadFile("C:\WINDOWS\Web\Wallpaper\Bliss.bmp")
Thumb.LoadFile("C:\Thumb1.tif")
ImageProcess.Filters.Add(ImageProcess.FilterInfos("Stamp").FilterID)
ImageProcess.Filters(1).Properties("ImageFile").Value = Thumb
ImageProcess.Filters(1).Properties("Left").Value = Img.Width - Thumb.Width
ImageProcess.Filters(1).Properties("Top").Value = Img.Height - Thumb.Height
Img = ImageProcess.Apply(Img)
Img.SaveFile("C:\Stamp.Tif")
ROTATING AN IMAGE
Dim Img As New WIA.ImageFile
Dim ImageProcess As New WIA.ImageProcess
Img.LoadFile("C:\WINDOWS\Web\Wallpaper\Bliss.bmp")
ImageProcess.Filters.Add(ImageProcess.FilterInfos("RotateFlip").FilterID)
ImageProcess.Filters(1).Properties("RotationAngle").Value = 90
Img = ImageProcess.Apply(Img)
Img.SaveFile("C:\Rotated.tif")
ENUMERATE EVENTS SUPPORTED BY THE DEVICE
Dim s As String
Dim dev As WIA.Device = (New WIA.CommonDialog).ShowSelectDevice
For i As Integer = 1 To dev.Events.Count
s = s & dev.Events(i).Name & " (" & dev.Events(i).EventID & "): " & dev.Events(i).Description & vbCrLf
Next
MsgBox("The selected device supports the following events:" & vbCrLf & s)
You can use the above information to detect when the user has hit one of the buttons on the machine. You need to use BOTH a RegisterEvent method AND an Addhandler method to get this to work. The following code detected when the user hit the "Scan" button on my Memorex scanner.
Dim Scanner As WIA.Device = (New WIA.CommonDialog).ShowSelectDevice
Dim DevMgr As New WIA.DeviceManager
DevMgr.RegisterEvent(WIA.EventID.wiaEventScanImage, Scanner.DeviceID)
AddHandler DevMgr.OnEvent, AddressOf DeviceManager1_OnEvent
Private Sub handlerDeviceManager1_OnEvent(ByVal EventID As String, ByVal DeviceID As String, ByVal ItemID As String)
If EventID = WIA.EventID.wiaEventScanImage Then MsgBox("User hit the scan button.")
End Sub
The above code worked only because the manufacturer provided driver support for this Event. In other words, even if the WIA manual lists a particular type of event,it won't fire unless the manufacturer deliberately provided driver support for it. With digital cameras, expect manufacturers to provide driver support for the ItemCreated Event whereby you should be able to get a handle on the picture the instant it's taken/created using the following code:
Dim DevMgr As New WIA.DeviceManager
DevMgr.RegisterEvent(WIA.EventID.wiaEventItemCreated, WIA.Miscellaneous.wiaAnyDeviceID) 'ANY device.
AddHandler DevMgr.OnEvent, AddressOf DeviceManager1_OnEvent
Private Sub DeviceManager1_OnEvent(ByVal EventID As String, ByVal DeviceID As String, ByVal ItemID As String)
Dim DevMgr As New WIA.DeviceManager
Dim dev As WIA.Device = DevMgr.DeviceInfos(DeviceID).Connect
'The item captured here is just an image (hence lower-case), not the ITEM crucial to automatation.
Dim item As WIA.Item = dev.GetItem(ItemID)
Dim img As WIA.ImageFile = (New WIA.CommonDialog).ShowTransfer(item) 'transfers the image
img.SaveFile("C:\GotThePicture.tif")
System.Runtime.InteropServices.Marshal.ReleaseComObject(DevMgr)
System.Runtime.InteropServices.Marshal.ReleaseComObject(dev)
End Sub
DEVICE PROPERTIES: NOT READ-ONLY
Most properties of a particular scanner are read-only, so you can't modify them. This code sample tries to find properties that are NOT read-only. To modify a property, access it by it's four-digit property ID number as already shown above. The following code outputs the id numbers for those device properties which are modifiable (those that are no read-only).
Dim Scanner As WIA.Device = (New WIA.CommonDialog).ShowSelectDevice
Dim message As String
For Each P As WIA.Property In Scanner.Items(1).Properties 'The ITEM object
Try
If Not P.IsReadOnly Then
message &= vbCrLf & "ID is " & P.PropertyID & ". The name is " & P.Name & ". And the Current Value is "
message &= CStr(P.Value)
End If
Catch ex As Exception
End Try
Next
MsgBox(message)
GETTING ALL PROPERTIES FOR A DEVICE
Dim s As String
Dim Scanner As WIA.Device = (New WIA.CommonDialog).ShowSelectDevice
For Each p As WIA.Property In Scanner.Properties
s = p.Name & "(" & p.PropertyID & ") = "
If p.IsVector Then
s = s & "[vector of data]"
Else
s = s & (CType(p.Value, Object)).ToString
If p.SubType <> WIA.WiaSubType.UnspecifiedSubType Then
If p.Value <> p.SubTypeDefault Then
s = s & "(Default = " & p.SubTypeDefault & ")"
End If
End If
End If
If p.IsReadOnly Then
s = s & " [READ ONLY]"
Else
Select Case p.SubType
Case WIA.WiaSubType.FlagSubType
s = s & " [ valid flags include:"
For i As Integer = 1 To p.SubTypeValues.Count
s = s & p.SubTypeValues(i)
If i <> p.SubTypeValues.Count Then
s = s & ", "
End If
Next
s = s & " ]"
Case WIA.WiaSubType.ListSubType
s = s & " [ valid values include:"
For i As Integer = 1 To p.SubTypeValues.Count
s = s & p.SubTypeValues(i)
If i <> p.SubTypeValues.Count Then
s = s & ", "
End If
Next
s = s & " ]"
Case WIA.WiaSubType.RangeSubType
s = s & " [ valid values in the range from " & _
p.SubTypeMin & " to " & p.SubTypeMax & _
" in increments of " & p.SubTypeStep & " ]"
Case Else 'UnspecifiedSubType
End Select
End If
MsgBox(s)
Next
*********************
More code for listing device properties. This seems to output same properties as above except with descriptions less detailed, apparently.
Dim dev As WIA.Device = (New WIA.CommonDialog).ShowSelectDevice
For Each Prop As WIA.Property In dev.Properties
Dim S As String = Prop.Name & "(" & Prop.PropertyID & ") = "
If Prop.IsVector Then
S = S & "[vector of data]"
Else
If Prop.Type = WIA.WiaPropertyType.StringPropertyType Then
S = S & """" & Prop.Value & """"
Else : S = S & CType(Prop.Value, Object).ToString
End If
End If
MsgBox(S)
Next
COMMON EXCEPTIONS THROWN
catch (Exception ex)
Dim message as String = ex.Message
if (message.EndsWith("0x80210001."))
message = " General error.";
else
if (message.EndsWith("0x80210002."))
message = " Paper Jam.";
else
if (message.EndsWith("0x80210003."))
message = " Paper Empty.";
else 'does this one work? I think so.
if (message.EndsWith("0x802100153.") or message.EndsWith("0x8021006A"). 'the power is probably off
GET THE PROPERTIES OF AN IMAGE
Dim Img As New WIA.ImageFile
Dim s As String
Dim v As WIA.Vector
Img.LoadFile("C:\WINDOWS\Web\Wallpaper\Autumn.jpg")
s = "Width = " & Img.Width & vbCrLf & _
"Height = " & Img.Height & vbCrLf & _
"Depth = " & Img.PixelDepth & vbCrLf & _
"HorizontalResolution = " & Img.HorizontalResolution & vbCrLf & _
"VerticalResolution = " & Img.VerticalResolution & vbCrLf & _
"FrameCount = " & Img.FrameCount & vbCrLf
If Img.IsIndexedPixelFormat Then s = s & "Pixel data contains palette indexes" & vbCrLf
If Img.IsAlphaPixelFormat Then s = s & "Pixel data has alpha information" & vbCrLf
If Img.IsExtendedPixelFormat Then s = s & "Pixel data has extended color information (16 bit/channel)" & vbCrLf
If Img.IsAnimated Then s = s & "Image is animated" & vbCrLf
If Img.Properties.Exists("40091") Then
v = Img.Properties("40091").Value
s = s & "Title = " & v.String & vbCrLf
End If
If Img.Properties.Exists("40092") Then
v = Img.Properties("40092").Value
s = s & "Comment = " & v.String & vbCrLf
End If
If Img.Properties.Exists("40093") Then
v = Img.Properties("40093").Value
s = s & "Author = " & v.String & vbCrLf
End If
If Img.Properties.Exists("40094") Then
v = Img.Properties("40094").Value
s = s & "Keywords = " & v.String & vbCrLf
End If
If Img.Properties.Exists("40095") Then
v = Img.Properties("40095").Value
s = s & "Subject = " & v.String & vbCrLf
End If
MsgBox(s)
************************
The following code looks pretty powerful. It enumerates the ways that an image can be modified.
Dim Img As New WIA.ImageFile
Dim ImageProcess1 As New WIA.ImageProcess
Dim s As String
For Each filterInfo As WIA.FilterInfo In ImageProcess1.FilterInfos
s = filterInfo.Name & vbCrLf & _
"===================================" & vbCrLf & filterInfo.Description
MsgBox(s)
Next
************************
The following code is similar to the last one but seems to report a bit more information. The sub makes use of the following function
Function ListValues(ByVal v) as String
Dim i 'As Integer
ListValues = ""
For i = 1 To v.Count
ListValues = ListValues & CType((v(i)), Object).ToString
If i <> v.Count Then ListValues = ListValues & ", "
Next
End Function
Private Sub ListFilterInfo()
Dim ImageProcess1 As New WIA.ImageProcess
For Each FilterInfo As WIA.FilterInfo In ImageProcess1.FilterInfos
ImageProcess1.Filters.Add(FilterInfo.FilterID)
Dim Filter As WIA.Filter = ImageProcess1.Filters.Item(1)
Dim s As String
s = Filter.Name & " (" & Filter.FilterID & ")" & vbCrLf & _
"==================================================" & vbCrLf & _
Filter.Description & vbCrLf & _
"==================================================" & vbCrLf
For Each p As WIA.Property In Filter.Properties
s = s & p.Name & " = "
If Not IsNothing(p.Value) Then s &= CType(p.Value, Object).ToString
Select Case p.SubType
Case WIA.WiaSubType.FlagSubType
s = s & " '[valid values formed by using the OR operator with the following bit flags: " & _
ListValues(p.SubTypeValues) & "]" & vbCrLf
Case WIA.WiaSubType.ListSubType
s = s & " '[valid values from the following list: " & _
ListValues(p.SubTypeValues) & "]" & vbCrLf
Case WIA.WiaSubType.RangeSubType
s = s & " '[valid values in the range: Min = " & p.SubTypeMin & _
", Max = " & p.SubTypeMax & ", Step = " & p.SubTypeStep & "]" & _
vbCrLf
Case Else 'UnspecifiedSubType
s = s & vbCrLf
End Select
Next
MsgBox(s)
ImageProcess1.Filters.Remove(1)
Next
End Sub
**************************
The next sample supposeldy lists the valid filetypes for the selected scanner. In my case the output was only one type of file (BMP) although I don't see in what sense I am limited to this.
'Get the ITEM object for my scanner.
Dim Item As WIA.Item = ((New WIA.CommonDialog).ShowSelectDevice(WIA.WiaDeviceType.UnspecifiedDeviceType, True).Items(1))
Dim s As String
For i As Integer = 1 To Item.Formats.Count
Select Case Item.Formats(i)
Case WIA.FormatID.wiaFormatBMP
s = s & "BMP"
Case WIA.FormatID.wiaFormatPNG
s = s & "PNG"
Case WIA.FormatID.wiaFormatGIF
s = s & "GIF"
Case WIA.FormatID.wiaFormatJPEG
s = s & "JPEG"
Case WIA.FormatID.wiaFormatTIFF
s = s & "TIFF"
Case Else
s = s & "Unknown"
End Select
s = s & vbCrLf
Next i
MsgBox(s)
**************
This code doesn't seem relevant to scanning since it merely queries to see if the device can take a picture:
Dim dev As WIA.Device = (new Wia.CommonDialog).ShowSelectDevice
For Each Command as Wia.DeviceCommand In dev.Commands
If Command.CommandID = WIA.CommandID.wiaCommandTakePicture Then
MsgBox("Selected device supports the TakePicture command")
Else : MsgBox("Take Picture NOt Supported")
End If
Next
*****************
If a scanner has multiple images in its memory buffer, the programmer would apparently reference them as ITEM.items as follows.
Dim Count As Integer
Dim dev As WIA.Device = (New WIA.CommonDialog).ShowSelectDevice(WIA.WiaDeviceType.ScannerDeviceType, True)
Dim ITEM As WIA.Item = dev.Items(1)
For Each itm As WIA.Item In ITEM.Items
Dim f As WIA.Property
f = itm.Properties("Item Flags")
If f.Type = WIA.WiaItemFlag.TransferItemFlag Then Count += 1
Next
MsgBox("Selected device has " & Count & " child items that can be transferred")
*************************
A C# example.
The code gets long and ugly in C# because for some reason you have to pass in the values ByRef which means you usually need two extra lines of code (for each line of Wia code) to convert the values to Object. Another reason that C# could be a problem is its lack of support for optional parameters.
private bool WantsToScan;
private Msg.Msg M;
private void Form1_Load(object sender, System.EventArgs e)
{
WantsToScan = true;
while (WantsToScan) ScanAndSaveOnePage();
}
private void ScanAndSaveOnePage()
{
WIA.CommonDialog Dialog1 = new WIA.CommonDialogClass();
WIA.Device Scanner = Dialog1.ShowSelectDevice(WIA.WiaDeviceType.ScannerDeviceType, false, false);
WIA.Item ITEM = Scanner.Items[1];
this.InitializeITEM(Scanner);
WIA.ImageFile Image1;
try
{
Image1 = (WIA.ImageFile)ITEM.Transfer(WIA.FormatID.wiaFormatTIFF);
}
catch (Exception Ex)
{
string Menu;
if (Ex.Message.EndsWith("210003.")) Menu = "Feeder seems to be empty.";
else if (Ex.Message.EndsWith("210002.")) Menu = "Cannot scan. The feeder appears to be jammed.";
else Menu = "Couldn't scan. " + Ex.Message;
Menu += "\r\nEnter 1 to resume scanning.\r\nPress 2 or hit Escape to cancel.";
M = new Msg.Msg(Menu,Msg.Msg.Types.InpBox);
if (M.DialogResult == DialogResult.Cancel) this.WantsToScan = false;
if (M.Reply != "1") WantsToScan = false;
return;
}
WIA.ImageProcess ImageProcess1 = new WIA.ImageProcessClass();
System.Object Object1 = null;
System.Object Object2 = null;
Object1 = (Object)"Convert";
ImageProcess1.Filters.Add(ImageProcess1.FilterInfos.get_Item(ref Object1).FilterID,0);
Object1 = (Object)"FormatID";
Object2 = (Object)WIA.FormatID.wiaFormatTIFF;
ImageProcess1.Filters[1].Properties.get_Item(ref Object1).set_Value(ref Object2);
Object1 = (Object)"Compression";
Object2 = (Object)"CCITT4";
ImageProcess1.Filters[1].Properties.get_Item(ref Object1).set_Value(ref Object2);
Object1 = null;
Object2 = null;
Image1 = ImageProcess1.Apply(Image1);
string DestImagePath = @"c:\MyImage5.tif";
Image1.SaveFile(DestImagePath);
}
private void InitializeITEM(WIA.Device Scanner)
{
Object Object1 = null;
Object Object2 = null;
Int32 DPI = 200;
Object1 = (Object)"6146";
Object2 = (Object)4;
Scanner.Items[1].Properties.get_Item(ref Object1).set_Value(ref Object2);
Object1 = (Object)"6147";
Object2 = (Object)DPI;
Scanner.Items[1].Properties.get_Item(ref Object1).set_Value(ref Object2);
Object1 = (Object)"6148";
Object2 = (Object)DPI;
Scanner.Items[1].Properties.get_Item(ref Object1).set_Value(ref Object2);
Object1 = (Object)"6149";
Object2 = (Object)0;
Scanner.Items[1].Properties.get_Item(ref Object1).set_Value(ref Object2);
Object1 = (Object)"6150";
Object2 = (Object)0;
Scanner.Items[1].Properties.get_Item(ref Object1).set_Value(ref Object2);
Object1 = (Object)"6151";
Object2 = (Object)(8.5*DPI);
Scanner.Items[1];.Properties.get_Item(ref Object1).set_Value(ref Object2);
Object1 = (Object)"6152";
Object2 = (Object)(11.5*DPI);
Scanner.Items[1].Properties.get_Item(ref Object1).set_Value(ref Object2);
Object1 = (Object)"3088"; //Document handling select property. My effort to enforce duplex.
Object2 = (Object)5; //I don't know if this value 5 enforces duplex on non-Xerox scanners
try {Scanner.Items[1].Properties.get_Item(ref Object1).set_Value(ref Object2);}
catch (Exception){}
Object1 = null;
Object2 = null;
}
}
In C#, to supply optional parameters, try passing in this:
System.Reflection.Missing.Value;
Or first convert it to an object and then pass in the object:
object ParameterNotUsed = System.Reflection.Missing.Value;
DEBUGGING WIA
I didn't feel like taking notes here so I'm just pasting in a Microsoft article on debugging WIA:
Enable Logging Of Wiadebug.log File
The information that the WIA service logs in this file can be very helpful during driver development. The logging level is controlled by an entry in the registry. For WIA, this key resides in the following registry key, where Module_name is the name of the appropriate binary module:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\StillImage\Debug\Module_name\DebugFlags
For the WIA service, the appropriate binary module is Wiaservc.dll.
The value in DebugFlags controls the logging level. The following list describes three of the settings:
• 0x00000001: Display error messages.
• 0x00000002: Display warning messages
• 0x00000004: Display trace messages.
The value for DebugFlags is a flag value; that is, different settings may be read together. To turn on logging for errors, warnings, and traces all at once, set DebugFlags to 0x0000007.
If you change the value of DebugFlags, stop the WIA service (stisvc), and then restart it for the changes to take effect.
To stop the Still Image service, open a command prompt, and then run the following command:
net stop stisvc
To start the Still Image service, open a command prompt, and then run the following command:
net start stisvc
NOTE: Excessive logging can lead to a significant decrease in performance. Increase the logging level only when you are attempting to solve a particular problem. After you have corrected the problem, set the logging level to its original level.
Back to the top Back to the top
Troubleshooting
The following scenario describes a typical problem, and describes how you can use the information in the Wiadebug.log file to resolve the problem.
Scenario: You have written a program to test a scanner driver that is under development. For one of the tests, you attempt to set the scanner's dots per inch (dpi) setting to 1200, but you notice that this action produces an error.
The following data is logged in Wiadebug.log:
wiasGetChangedValueLong, validate prop 6147 failed hr: 0x80070057
wiasUpdateScanRect, CheckXResAndUpdate failed (0x80070057)
CDrvWrap::WIA_drvValidateItemProperties, Error calling driver:
drvValidateItemProperties with hr = 0x80070057
NOTE: This behavior is typical if a program writes an invalid value.
These log entries indicate that the driver is reporting that the program wrote an invalid value. It is not clear from this information what the exact problem is. If you increases the WIA logging level to report warnings as well as the errors, the following information is logged in Wiadebug.log: wiasValidateItemProperties, invalid LIST value for :
propID) Horizontal Resolution, value = 1200
Valid values are:
• 75
• 100
• 150
• 200
• 300
• 600
wiasGetChangedValueLong, validate prop 6147 failed hr: 0x80070057
wiasUpdateScanRect, CheckXResAndUpdate failed (0x80070057)
CDrvWrap::WIA_drvValidateItemProperties, Error calling driver:
drvValidateItemProperties with hr = 0x80070057
NOTE: This behavior is typical if the program writes an invalid value.
The output shows that the Horizontal Resolution property is causing the failure. The program is attempting to set the resolution to 1200, but the list of supported resolutions does not include 1200; therefore the WIA service validation helper (wiasValidateItemProperties) rejects the request to set this value.
Now that you have identified the problem, you can determine whether to revise the driver or the program. If the scanner's specifications allow it to support all resolutions between 100 and 1400 dpi, the driver should be able to handle a request for 1200 dpi. If the scanner does not support this setting, change the program so that it does not attempt to set the Horizontal Resolution to a value that is not valid for this property. In this case, the program should check that a value is valid before attempting to set a property to this value.
WIA VIDEO
An article said the following:
For web cams or other video devices, WIA on Windows XP has a great new feature: live video stream overlay! It uses DirectShow to draw the overlay on the graphics card. The IWiaVideo interface can be accessed by importing another COM type library: "WiaVideo 1.0 Type Library" (wiavideo.dll). Unfortunately, the embedded TLB has a bug for methods passing a window handle. (A reader made the following comment): A way around the bug is this code:
WIAVIDEOLib.WiaVideoClass aoeu = new WIAVIDEOLib.WiaVideoClass();
unsafe
{
WIAVIDEOLib._RemotableHandle *handle = (WIAVIDEOLib._RemotableHandle*)picLastImage.Handle.ToPointer();
aoeu.ImagesDirectory = "C:\\temp";
aoeu.CreateVideoByWiaDevID(info.DeviceID, ref *handle, 1, 1);
}
The code to show a real-time and live video stream in a window could be as simple as these two steps:
wiaVideo = new WiaVideoClass();
wiaVideo.CreateVideoByWiaDevID(
wiaDeviceID, window.Handle, 0, 1 );
and to take a snapshot jpeg image from the current video stream is possible with one single method:
// this string will get the filename of the picture...
string jpgFile;
// call IWiaVideo::TakePicture
wiaVideo.TakePicture( out jpgFile );
Check the included video sample for more!