Introduction
In this article I
will show how to acquire pictures from imaging peripherals like scanners,
webcams and so on, by calling some API functions, provided by the EZTW32.DLL
library, and Visual Basic 2005.
To perform this
operations we're going to create a wrapper that will expose methods based on the
above mentioned API functions. Another purpose of this article is to show how to
create CLS-Compliant assemblies and classes covering some code writing
guidelines and some executable settings. Don't worry if any of the words above
sound new. In this article you will find the right explanation about everything.
But we won't stop at
this. We will also see how to use an assembly analysis tool to validate our
executable. We will moreover talk about XML comments, a new feature of VB IDE.
To continue reading
this article, download the source code from the link above. The archive contains
two projects: the class library and a Windows Forms sample, called Esempio2005
(in Italian Esempio means Sample).
If yet not, install the Microsoft
.NET Framework 2.0. And obviously you need to check if the EZTW32.DLL is
already present on your machine. You should find this library in the System
directory (e.g.: "C:\Windows\System32"). If you have one or more imaging
peripherals installed, you should already have this library. If this is not your
case, you can download the library from the following URL: http://www.dosadi.com/eztwain1.htm.
On this web site you
can find different versions of the library, but only the one linked above is
freeware. Its last updated was in 1999 but for our purpose it is really good.
Acquiring pictures via Visual Basic 2005
code
The best project type
that fits our purpose is a Class Library, so that we will obtain a compiled Dll.
Give the project the name you like more, I used EZTwainLibrary.
Just one code file is
what we need. But before showing the code I think it's important to tell
something more about the Framework and the Common Language Specifications (by
now just CLS).
Everything that
follows has the purpose to fit as more as possible the CLS. According to the
CLS, API declarations, called pinvokes in .NET, must be declared in a separated
class. This class must have friend accessibility and its name must be one of the
following conventional identifiers: NativeMethods, SafeNativeMethods,
UnsafeNativeMethods.
This means that we
need to create two classes in the same file. First of all we have to declare a
new namespace, called EZTwainLibrary as in the sample code. This namespace
contains the main class, EZTwain, with public accessibility, which implements
methods and properties for interfacing with imaging peripherals.
A second class is
called NativeMethods and contains the API declarations for using the EZTW32.DLL.
Let's begin to write the code, line by line. First of all a pair of Imports
directives:
Imports System
Imports System.Runtime.InteropServices
Next step is to mark
the assembly as CLS-Compliant.
Some features of Common Language
Specifications
As mentioned before,
one of this article's purposes is to show how to compile a CLS-Compliant
assembly. This means that we have to write compliant code from the beginning.
Next steps show what and how to do it.
-
According to the
CLS an assembly should be marked as CLS-Compliant. This assures a full
compatibility with the .NET platform and can be done by using two
attributes: Assembly and CLSCompliant.
-
EZTwain class name: we
could choose any other fantasy name but letters capitalization is not
random. In fact, always according to the CLS, class names are pascal-cased;
if composed of more than one word, the first letter of every word is upper
case while the others are lower case, as in EZ and Twain. But there is an
exception: when the first word is made of two characters, as in our case,
they must be both upper case.
- Why NativeMethods:
as seen before, according to .NET specifications, all pinvokes must reside
in a class identified with one of the conventional names mentioned above
Now it's time to mark the assembly as
CLS-Compliant. The following code must be put after Imports directives:
'Marks the assembly
as CLS-Compliant
<Assembly:
CLSCompliant(True)>
Then a new namespace, that will contain the two
classes:
Namespace EZTwainLibrary
End Namespace
Namespace notation is the same as for classes.
Then comes the implementation of NativeMethods
class, which declares EZTW32.DLL APIs. You can see the code inside the
EzTwain.Vb file, in the downloadable archive. Please, check out the
documentations link at the bottom of this article. By visiting these web pages
you will find very detailed informations about what used APIs do.
As you can see in the source code, NativeMethods
has Friend accessibility since it must be reached only from within the project
and not outside, and the same applies to its methods. These are declared as
shared because they will be used by a class that will implement all shared
methods and properties.
CLS also state that a class made of shared
methods only, must contain an empty and Private constructor.
We can now implement the EZTwain class, that makes the real job. You can observe
the source code in the archive you downloaded before.
Pay attention to the following points:
- The class has been marked as CLS-Compliant
through the CLSCompliant attribute and declared as Public because it will
expose methods and properties to the external world;
- It has been also declared as NotInheritable,
because CLS state that if a class is implementing only shared methods, it
should not be inherited. The equivalent of NotInheritable in C# is the
sealed keyword. It's useful to know this C# keyword because some analysis
tools, like Microsoft FxCop, throw exceptions returning messages containing
sealed rather than NotInheritable.
- Methods and properties identifiers are
pascal-cased; the first letter is upper case, the others are lower case as
in AcquireToClipboard;
- Parameters are camel-cased; as in
outputFileName. The first letter of the first word is lower case, the first
letter of the othe words is upper case;
- There are some identifiers, called discrete,
that are recognized by the compiler (since they are used in the base class
library) when used by the developer in his own methods, e.g.: Filename. If
we want to use a discrete identifier, we have to define it in a different
way. Simply it's right to use FileName rather than Filename;
- As in NativeMethods class, EZTwain
implements an empty and Private constructor too, since it exposes only
shared methods;
- The Visual Basic compiler understands
whether we create functions that make a job of Get/Set. In case of Get,
properties are preferred to methods when properties implementation is not
very complex and they do not use too many resources;
- In VB 2005, methods should return values via
the Return keyword rather than assigning a value to the function, even if
this does not throw exceptions;
- CLS state that we should avoid namespaces
with less than five elements. In our case it was necessary to leave that
namespace, because it is just a sample.
The following is a small example of how EZTwain
could be used for acquiring images:
Sub Scan()
EZTWain.SelectTwainSource()
Imports EZTwainLibrary
EZTwain.AcquireToFilename(Me.Handle,
"C:\MyImage.Bmp"
End Sub
Inside the
downloadable source code you will find a sample Windows Forms application
(called Esempio2005) which shows some of our assembly capabilities.
Writing methods and properties in a
.NET-style
Now it's the moment
for a walkthrough inside two methods exposed by our EZTwain class:
IsTwainInstalled and AcquireToNative methods.
IsTwainInstalled,
which determines whether EZTW32.DLL is installed or not, calls a method
implemented by the new My namespace.
This is not the best place to talk about this revolutionary namespace but here I
can say that My implements further classes which provide full access to many
important situations, like current-user, file-system, network connections. To
get the same functionalities in VB 6 we had to write hundreds lines of code,
very often referring to Windows API. Now everything is easier.
AcquireNative is
declared Overloads. This operator lets the developer define more instances of
the same method, but with different parameters and implementations. In our case,
the first overload acquires an image from a scanner, saves it to disk given the
user-specified filename handling the calling window. Returns an integer value,
the same returned by its related API. The second overload acquires an image from
a scanner and saves to a new temporary file, handling the active window. Returns
a string containing the full pathname of the new temporary file, so that the
user can manage the new image file.
The Version property
uses a System.Globalization.CultureInfo.InvariantCulture parameter during the
conversion to string of the library version value. CLS state that when you
convert a numeric value as an integer or a double to a string, calling the
.ToString method, it's important to specify a valid string format depending on
the client operanting system culture.
This can be done
implementing the IFormatProvider interface,
which provides methods for strings formatting. In our code sample we used the
System.Globalization.CultureInfo.InvariantCulture type to return a
culture-indipendent string.
XML comments
Being superior to its
predecessors Visual Basic 2005 lets you add XML-formatted comments while writing
code. If you know Visual Basic .NET 2003, you can remember how this was
possible only installing an IDE add-in called VB-Commenter,
while this feature was always part of Visual C# development. Finally this
feature has been implemented directly into Visual Basic 2005 IDE. Documenting
source code through XML comments can be very useful if you develop Dlls instead
of stand-alone applications. In fact there are some programs, like NDoc,
which can create compiled help files starting from XML comments, building
compiled files very similar to Visual Studio documentation, where every
namespace or class is shown describing its methods, properties and so on.
To insert XML
comments, just put the cursor on the line before the code you want to describe
and write three apostrophes. The IDE will automatically add a collapsible XML
block, where you can add comments about what the coming code is implementing as
shown in the following picture:
Signing the assembly
To complete assembly
creation it's necessary to create a strong name. This means signing the assembly
with a digital sign (password protected or not). This tells the .NET Framework
that the assembly comes from a certified source. In this way the .dll will be
definitively CLS-Compliant.
Do you know what the
Global Assembly Cache (GAC) is?. It is a folder created by the .NET Framework.
In this folder reside all the registered assemblies, depending on .NET version.
If an assembly is in the GAC it does not need to be copied every time into the
folder where resides the application that uses it as reference, since the Common
Language Runtime already know where registered assemblies are located. If
different applications reference the same assembly, you should consider to
install that assembly into the GAC.
Whenever we would
like to install our assembly into the GAC, it's necessary to sign it. Visual
Studio 2005 lets you make this operations from within the IDE. In the previous
versions of Visual Studio .NET this could be done adding some particular lines
of code into the AssemblyInfo.Vb file and running certain command line
utilities.
In Visual Studio 2005
open the "My Project" window. When you get the project properties, select the
"Signing" tab. Apply a flag onto the "Sign the assembly" checkbox, create a new
strong name or choose an existing one, with even a password. The IDE will make
the rest of the job and you will be able to see how the new file appears in the
Solution Explorer.
The source code that
comes with this article has already a strong name file, called EZTwain.Pfx. It's
password is strongname.
Build the solution
After a long job we
are ready to build the solution. Our assembly is now ready to use in our
applications and can be also installed into the Global Assembly Cache.
Assembly analysis
The .NET
Framework SDK 2.0 complains a
very useful tool, called Microsoft FxCop. This tool implements analysis tools to
understand if assemblies are CLS Compliant or not. FxCop is not automatically
installed together with the SDK, but a link to its setup can be find in the
Start menu.
Now let's try to
analyze our assembly via the Add targets command of the Project menu. Then just
click on Analyze. Though we have tried to be the most sharp as possible, you
we'll see how some exceptions are thrown. But this is normal since: parameters
names are made of single words, but FxCop tells that this can happen even if the
developer is sure to have correctly written the code. So this can be ignored.
FxCop shows warnings
about unused locals, but you can see that some of these locals are added by the
IDE into some hidden files so to prevent user modifies. In the other cases it is
due to particular situations concerning API functions.
Since we got a few
warnings, for the reasons I just talked about, we can say that our job is good.
If you want to better understand how analysis results can change, just remove
CLSCompliant attributes or the NotInheritable keyword or change properties to
methods or put all pinvokes into EZTwain class removing the NativeMethods one.
If you repeat the analysis now, you will get many more exceptions.
Red-marked errors are
more dangerous, while blue errors can be corrected in a second time...but don't
forget it! For further details about every error just click on each message. If
you are connected to the Internet you will be able to use hyperlinks to be
redirected to the application's web site.
Microsoft FxCop is
hosted on workspace onto the GotDotNet web site, at the following URL:http://www.gotdotnet.com/team/fxcop/ and
can be downloaded stand-alone too, outside the Framework SDK.
Documentation
Here is a small list
of web sites where I found further informations about EZTW32.DLL API:
It's really important to visit the PowerBasic
Support Forum above because inside the provided .bas module you can find very
detailed comments about what EZTW32.DLL APIs do, so to better understand also my
source code.
Conclusion
In this article we
learnt some important features about .NET Framework, Common Language
Specifications and we saw how to analyze an assembly to get it .NET compliant.