C#/VB.NET
What is C#?
C# Questions

1.1 What is C#?

C# is a programming language designed by Microsoft. It is loosely based on C/C++, and bears a striking similarity to Java. Microsoft describe C# as follows:

"C# is a simple, modern, object oriented, and type-safe programming language derived from C and C++. C# (pronounced 'C sharp') is firmly planted in the C and C++ family tree of languages, and will immediately be familiar to C and C++ programmers. C# aims to combine the high productivity of Visual Basic and the raw power of C++."

You can get the ECMA C# spec in PDF form here, or use Jon Jagger's html version.

1.2 How do I develop C# apps?

The (free) .NET SDK contains the C# command-line compiler (csc.exe). Visual Studio has fully integrated support for C# development. On Linux you can use Mono.

1.3 Does C# replace C++?

There are three options open to the Windows developer from a C++ background:

  • Stick with standard C++. Don't use .NET at all.
  • Use C++ with .NET. Microsoft supply a .NET C++ compiler that produces IL rather than machine code. However to make full use of the .NET environment (e.g. garbage collection), a set of extensions are required to standard C++. In .NET 1.x this extended language is called Managed Extensions for C++. In .NET 2.0 ME C++ has been completely redesigned under the stewardship of Stan Lippman, and renamed C++/CLI.
  • Forget C++ and use C#.

Each of these options has merits, depending on the developer and the application. For my own part, I intend to use C# where possible, falling back to C++ only where necessary. ME C++ (soon to be C++/CLI) is very useful for interop between new .NET code and old C++ code - simply write a managed wrapper class using ME C++, then use the managed class from C#. From experience, this works well.

1.4 Does C# have its own class library?

Not exactly. The .NET Framework has a comprehensive class library, which C# can make use of. C# does not have its own class library.

2. Types

 

2.1 What standard types does C# use?

C# supports a very similar range of basic types to C++, including int, long, float, double, char, string, arrays, structs and classes. However, don't assume too much. The names may be familiar, but many of the details are different. For example, a long is 64 bits in C#, whereas in C++ the size of a long depends on the platform (typically 32 bits on a 32-bit platform, 64 bits on a 64-bit platform). Also classes and structs are almost the same in C++ - this is not true for C#. Finally, chars and strings in .NET are 16-bit (Unicode/UTF-16), not 8-bit like C++.

2.2 Is it true that all C# types derive from a common base class?

Yes and no. All types can be treated as if they derive from object (System.Object), but in order to treat an instance of a value type (e.g. int, float) as object-derived, the instance must be converted to a reference type using a process called 'boxing'. In theory a developer can forget about this and let the run-time worry about when the conversion is necessary, but in reality this implicit conversion can have side-effects that may trip up the unwary.

2.3 So I can pass an instance of a value type to a method that takes an object as a parameter?

Yes. For example:

    class CApplication
{
public static void Main()
{
int x = 25;
string s = "fred";
DisplayMe( x );
DisplayMe( s );
}
static void DisplayMe( object o )
{
System.Console.WriteLine( "You are {0}", o );
}
} 

This would display:

    You are 25
You are fred

 

2.4 What are the fundamental differences between value types and reference types?

C# divides types into two categories - value types and reference types. Most of the intrinsic types (e.g. int, char) are value types. Structs are also value types. Reference types include classes, arrays and strings. The basic idea is straightforward - an instance of a value type represents the actual data, whereas an instance of a reference type represents a pointer or reference to the data.

The most confusing aspect of this for C++ developers is that C# has predetermined which types are represented as values, and which are represented as references. A C++ developer expects to take responsibility for this decision.

For example, in C++ we can do this:

    int x1 = 3;        // x1 is a value on the stack
int *x2 = new int(3)    // x2 is a pointer to a value on the heap

but in C# there is no control:

    int x1 = 3;        // x1 is a value on the stack
int x2 = new int();
x2 = 3;        // x2 is also a value on the stack!

 

2.5 Okay, so an int is a value type, and a class is a reference type. How can int be derived from object?

It isn't, really. When an int is being used as an int, it is a value. However, when it is being used as an object, it is a reference to an integer value (on the managed heap). In other words, when you treat an int as an object, the runtime automatically converts the int value to an object reference. This process is called boxing. The conversion involves copying the int to the heap, and creating an object instance which refers to it. Unboxing is the reverse process - the object is converted back to a value.

    int x = 3;        // new int value 3 on the stack
object objx = x;    // new int on heap, set to value 3 - still have x=3 on stack
int y = (int)objx;    // new value 3 on stack, still got x=3 on stack and objx=3 on heap

 

2.6 Are C# references the same as C++ references?

Not quite. The basic idea is the same, but one significant difference is that C# references can be null . So you cannot rely on a C# reference pointing to a valid object. In that respect a C# reference is more like a C++ pointer than a C++ reference. If you try to use a null reference, a NullReferenceException is thrown.

For example, look at the following method:

    void displayStringLength( string s )
{
Console.WriteLine( "String is length {0}", s.Length );
}        

The problem with this method is that it will throw a NullReferenceException if called like this:

    string s = null;
displayStringLength( s );

Of course for some situations you may deem a NullReferenceException to be a perfectly acceptable outcome, but in this case it might be better to re-write the method like this:

    void displayStringLength( string s )
{
if( s == null )
Console.WriteLine( "String is null" );
else
Console.WriteLine( "String is length {0}", s.Length );
}        

 

2.7 Can I use typedefs in C#?

No, C# has no direct equivalent of the C++ typedef. C# does allow an alias to be specified via the using keyword:

    using IntList = System.Collections.Generic.List<int>;

but the alias only applies in the file in which it is declared. A workaround in some cases is to use inheritance:

    public class IntList : List<int> { }

The pros and cons of this approach are discussed here.

3. Classes and Structs

 

3.1 Structs are largely redundant in C++. Why does C# have them?

In C++, a struct and a class are pretty much the same thing. The only difference is the default visibility level (public for structs, private for classes). However, in C# structs and classes are very different. In C#, structs are value types (instances stored directly on the stack, or inline within heap-based objects), whereas classes are reference types (instances stored on the heap, accessed indirectly via a reference). Also structs cannot inherit from structs or classes, though they can implement interfaces. Structs cannot have destructors. A C# struct is much more like a C struct than a C++ struct.

3.2 Does C# support multiple inheritance (MI)?

No, though it does support implementation of multiple interfaces on a single class or struct.

3.3 Is a C# interface the same as a C++ abstract class?

No, not quite. An abstract class in C++ cannot be instantiated, but it can (and often does) contain implementation code and/or data members. A C# interface cannot contain any implementation code or data members - it is simply a group of method names & signatures. A C# interface is more like a COM interface than a C++ abstract class.

3.4 Are C# constructors the same as C++ constructors?

Very similar, but there are some significant differences. First, C# supports constructor chaining. This means one constructor can call another:

    class Person
{
public Person( string name, int age ) { ... }
public Person( string name ) : this( name, 0 ) {}
public Person() : this( "", 0 ) {}
}

Another difference is that virtual method calls within a constructor are routed to the most derived implementation - see Can I Call a virtual method from a constructor.

Error handling is also somewhat different. If an exception occurs during construction of a C# object, the destuctor (finalizer) will still be called. This is unlike C++ where the destructor is not called if construction is not completed. (Thanks to Jon Jagger for pointing this out.)

Finally, C# has static constructors. The static constructor for a class runs before the first instance of the class is created.

Also note that (like C++) some C# developers prefer the factory method pattern over constructors. See Brad Wilson's article.

3.5 Are C# destructors the same as C++ destructors?

No. They look the same but they are very different. The C# destructor syntax (with the familiar ~ character) is just syntactic sugar for an override of the System.Object Finalize method. This Finalize method is called by the garbage collector when it determines that an object is no longer referenced, before it frees the memory associated with the object. So far this sounds like a C++ destructor. The difference is that the garbage collector makes no guarantees about when this procedure happens. Indeed, the algorithm employed by the CLR garbage collector means that it may be a long time after the application has finished with the object. This lack of certainty is often termed 'non-deterministic finalization', and it means that C# destructors are not suitable for releasing scarce resources such as database connections, file handles etc.

To achieve deterministic destruction, a class must offer a method to be used for the purpose. The standard approach is for the class to implement the IDisposable interface. The user of the object must call the Dispose() method when it has finished with the object. C# offers the 'using' construct to make this easier.

3.6 If C# destructors are so different to C++ destructors, why did MS use the same syntax?

Presumably they wanted C++ programmers to feel at home. I think they made a mistake.

3.7 Are all methods virtual in C#?

No. Like C++, methods are non-virtual by default, but can be marked as virtual.

3.8 How do I declare a pure virtual function in C#?

Use the abstract modifier on the method. The class must also be marked as abstract (naturally). Note that abstract methods cannot have an implementation (unlike pure virtual C++ methods).

3.9 Can I call a virtual method from a constructor/destructor?

Yes, but it's generally not a good idea. The mechanics of object construction in .NET are quite different from C++, and this affects virtual method calls in constructors.

C++ constructs objects from base to derived, so when the base constructor is executing the object is effectively a base object, and virtual method calls are routed to the base class implementation. By contrast, in .NET the derived constructor is executed first, which means the object is always a derived object and virtual method calls are always routed to the derived implementation. (Note that the C# compiler inserts a call to the base class constructor at the start of the derived constructor, thus preserving standard OO semantics by creating the illusion that the base constructor is executed first.)

The same issue arises when calling virtual methods from C# destructors. A virtual method call in a base destructor will be routed to the derived implementation.

3.10 Should I make my destructor virtual?

A C# destructor is really just an override of the System.Object Finalize method, and so is virtual by definition.

4. Exceptions

 

4.1 Can I use exceptions in C#?

Yes, in fact exceptions are the recommended error-handling mechanism in C# (and in .NET in general). Most of the .NET framework classes use exceptions to signal errors.

4.2 What types of object can I throw as exceptions?

Only instances of the System.Exception classes, or classes derived from System.Exception. This is in sharp contrast with C++ where instances of almost any type can be thrown.

4.3 Can I define my own exceptions?

Yes, just derive your exception class from System.Exception.

Note that if you want your exception to cross remoting boundaries you'll need to do some extra work - see http://www.thinktecture.com/Resources/RemotingFAQ/CustomExceptions.html for details.

4.4 Does the System.Exception class have any cool features?

Yes - the feature which stands out is the StackTrace property. This provides a call stack which records where the exception was thrown from. For example, the following code:

    using System;
class CApp
{
public static void Main()
{
try
{
f();
}
catch( Exception e )
{
Console.WriteLine( "System.Exception stack trace = \n{0}", e.StackTrace );
}
}
static void f()
{
throw new Exception( "f went pear-shaped" );
}
}

produces this output:

    System.Exception stack trace =
at CApp.f()
at CApp.Main()

Note, however, that this stack trace was produced from a debug build. A release build may optimise away some of the method calls which could mean that the call stack isn't quite what you expect.

4.5 When should I throw an exception?

This is the subject of some debate, and is partly a matter of taste. However, it is accepted by many that exceptions should be thrown only when an 'unexpected' error occurs. How do you decide if an error is expected or unexpected? This is a judgement call, but a straightforward example of an expected error is failing to read from a file because the seek pointer is at the end of the file, whereas an example of an unexpected error is failing to allocate memory from the heap.

4.6 Does C# have a 'throws' clause?

No, unlike Java, C# does not require (or even allow) the developer to specify the exceptions that a method can throw.

5. Run-time Type Information

 

5.1 How can I check the type of an object at runtime?

You can use the is keyword. For example:

    using System;
class CApp
{
public static void Main()
{
string s = "fred";
long i = 10;
Console.WriteLine( "{0} is {1}an integer", s, (IsInteger(s) ? "" : "not ") );
Console.WriteLine( "{0} is {1}an integer", i, (IsInteger(i) ? "" : "not ") );
}
static bool IsInteger( object obj )
{
if( obj is int || obj is long )
return true;
else
return false;
}
} 

produces the output:

    fred is not an integer
10 is an integer

 

5.2 Can I get the name of a type at runtime?

Yes, use the GetType method of the object class (which all types inherit from). For example:

    using System;
class CTest
{
class CApp
{
public static void Main()
{
long i = 10;
CTest ctest = new CTest();
DisplayTypeInfo( ctest );
DisplayTypeInfo( i );
}
static void DisplayTypeInfo( object obj )
{
Console.WriteLine( "Type name = {0}, full type name = {1}", obj.GetType(), obj.GetType().FullName );
}
}
}

produces the following output:

    Type name = CTest, full type name = CTest
Type name = Int64, full type name = System.Int64

 

5.3 What is the difference between typeof and GetType()?

Apart from the obvious (i.e. typeof operates on a type whereas GetType operates on an object), the main thing to watch out for is that GetType returns the underlying type of the object, which may not be the same as the type of the reference to the object. For example:

    class Base { }
class Derived : Base { }
class Program
{
static void Main()
{
ShowType( new Derived() );
}
static void ShowType( Base b )
{
Console.WriteLine(typeof(Base));
Console.WriteLine(b.GetType());
}
}

gives the following output:

    Base
Derived

 

6. Miscellaneous

 

6.1 How do I do a case-insensitive string comparison?

Use the String.Compare function. Its third parameter is a boolean which specifies whether case should be ignored or not.

    "fred" == "Fred"    // false
System.String.Compare( "fred", "Fred", true ) == 0    // true

For more control over the comparison, e.g. exotic features like width-sensitivity, consider using System.Globalization.CompareInfo.Compare(), e.g.

    CultureInfo.CurrentCulture.CompareInfo.Compare(
"fred", "Fred",
CompareOptions.IgnoreCase |
CompareOptions.IgnoreKanaType |
CompareOptions.IgnoreWidth
);

 

6.2 Does C# support a variable number of arguments?

Yes, using the params keyword. The arguments are specified as a list of arguments of a specific type, e.g. int. For ultimate flexibility, the type can be object. The standard example of a method which uses this approach is System.Console.WriteLine().

6.3 How can I process command-line arguments?

Like this:

    using System;
class CApp
{
public static void Main( string[] args )
{
Console.WriteLine( "You passed the following arguments:" );
foreach( string arg in args )
Console.WriteLine( arg );
}
}

 

6.4 Does C# do array bounds checking?

Yes. An IndexOutOfRange exception is used to signal an error.

6.5 How can I make sure my C# classes will interoperate with other .NET languages?

Make sure your C# code conforms to the Common Language Subset (CLS). To help with this, add the [assembly:CLSCompliant(true)] global attribute to your C# source files. The compiler will emit an error if you use a C# feature which is not CLS-compliant.

6.6 How do I use the 'using' keyword with multiple objects?

You can nest using statements, like this:

    using( obj1 )
{
using( obj2 )
{
...
}
}

However consider using this more aesthetically pleasing (but functionally identical) formatting:

    using( obj1 )
using( obj2 )
{
...
}

 

6.7 What is the difference between == and object.Equals?

For value types, == and Equals() usually compare two objects by value. For example:

    int x = 10;
int y = 10;
Console.WriteLine( x == y );
Console.WriteLine( x.Equals(y) );

will display:

    True
True

However things are more complex for reference types. Generally speaking, for reference types == is expected to perform an identity comparison, i.e. it will only return true if both references point to the same object. By contrast, Equals() is expected to perform a value comparison, i.e. it will return true if the references point to objects that are equivalent. For example:

    StringBuilder s1 = new StringBuilder("fred");
StringBuilder s2 = new StringBuilder("fred");
Console.WriteLine( s1 == s2 );
Console.WriteLine( s1.Equals(s2) );

will display:

    False
True

s1 and s2 are different objects (hence == returns false), but they are equivalent (hence Equals() returns true).

Unfortunately there are exceptions to these rules. The implementation of Equals() in System.Object (the one you'll inherit by default if you write a class) compares identity, i.e. it's the same as operator==. So Equals() only tests for equivalence if the class author overrides the method (and implements it correctly). Another exception is the string class - its operator== compares value rather than identity.

Bottom line: If you want to perform an identity comparison use the ReferenceEquals() method. If you want to perform a value comparison, use Equals() but be aware that it will only work if the type has overridden the default implementation. Avoid operator== with reference types (except perhaps strings), as it's simply too ambiguous.

6.8 How do I enforce const correctness in C#?

You can't - at least not in the same way you do in C++. C# (actually, the CLI) has no real concept of const correctness, For example, there's no way to specify that a method should not modify an argument passed in to it. And there's no way to specify that a method does not modify the object on which it is acting.

To get a feel for the angst this causes among some C++ programmers, read the feedback on this post from Raymond Chen.

There are of course ways of addressing this issue. For example, see Brad Abram's post (and associated feedback) for some ideas on adding optional read-only behaviour to collection classes.

7. C# 2.0

 

7.1 What are the new features in C# 2.0?

Support for all of the new framework features such as generics, anonymous methods, partial classes, iterators and static classes. See the .NET FAQ for more on these features.

Delegate inference is a new feature of the C# compiler which makes delegate usage a little simpler. It allows you to write this:

    Thread t = new Thread(ThreadFunc);

instead of this:

    Thread t = new Thread( new ThreadStart(ThreadFunc) );

Another minor but welcome addition is the explicit global namespace, which fixes a hole in namespace usage in C# 1.x. You can prefix a type name with global:: to indicate that the type belongs to the global namespace, thus avoiding problems where the compiler infers the namespace and gets it wrong.

Finally C# 2.0 includes some syntactic sugar for the new System.Nullable type. You can use T? as a synonym for System.Nullable<T>, where T is a value type. As suggested by the name, this allows values of the type to be 'null', or 'undefined'.

7.2 Are C# generics the same as C++ templates?

No, not really. There are some similarities, but there are also fundamental differences. See the .NET FAQ for more details.

8. Resources

 

8.1 Books

I recommend the following books, either because I personally like them, or because I think they are well regarded by other C# developers. (Note that I get a commission from Amazon if you buy a book after following one of these links.)

See also my .NET recommended books.

 



mhn002

Re: C# Questions

Hey Mai,

I think these should go with C# above

1.1 What is .NET?

.NET is a general-purpose software development platform, similar to Java. At its core is a virtual machine that turns intermediate language (IL) into machine code. High-level language compilers for C#, VB.NET and C++ are provided to turn source code into IL. C# is a new programming language, very similar to Java. An extensive class library is included, featuring all the functionality one might expect from a contempory development platform - windows GUI development (Windows Forms), database access (ADO.NET), web development (ASP.NET), web services, XML etc.

See also Microsoft's definition.

 

1.2 When was .NET announced?

Bill Gates delivered a keynote at Forum 2000, held June 22, 2000, outlining the .NET 'vision'. The July 2000 PDC had a number of sessions on .NET technology, and delegates were given CDs containing a pre-release version of the .NET framework/SDK and Visual Studio.NET.

1.3 What versions of .NET are there?

The final versions of the 1.0 SDK and runtime were made publicly available around 6pm PST on 15-Jan-2002. At the same time, the final version of Visual Studio.NET was made available to MSDN subscribers.

.NET 1.1 was released in April 2003, and was mostly bug fixes for 1.0.

.NET 2.0 was released to MSDN subscribers in late October 2005, and was officially launched in early November.

1.4 What operating systems does the .NET Framework run on?

The runtime supports Windows Server 2003, Windows XP, Windows 2000, NT4 SP6a and Windows ME/98. Windows 95 is not supported. Some parts of the framework do not work on all platforms - for example, ASP.NET is only supported on XP and Windows 2000/2003. Windows 98/ME cannot be used for development.

IIS is not supported on Windows XP Home Edition, and so cannot be used to host ASP.NET. However, the ASP.NET Web Matrix web server does run on XP Home.

The .NET Compact Framework is a version of the .NET Framework for mobile devices, running Windows CE or Windows Mobile.

The Mono project has a version of the .NET Framework that runs on Linux.

1.5 What tools can I use to develop .NET applications?

There are a number of tools, described here in ascending order of cost:

You can see the differences between the various Visual Studio versions here.

1.6 Why did they call it .NET?

I don't know what they were thinking. They certainly weren't thinking of people using search tools. It's meaningless marketing nonsense.

2. Terminology

 

2.1 What is the CLI? Is it the same as the CLR?

The CLI (Common Language Infrastructure) is the definiton of the fundamentals of the .NET framework - the Common Type System (CTS), metadata, the Virtual Execution Environment (VES) and its use of intermediate language (IL), and the support of multiple programming languages via the Common Language Specification (CLS). The CLI is documented through ECMA - see http://msdn.microsoft.com/net/ecma/ for more details.

The CLR (Common Language Runtime) is Microsoft's primary implementation of the CLI. Microsoft also have a shared source implementation known as ROTOR, for educational purposes, as well as the .NET Compact Framework for mobile devices. Non-Microsoft CLI implementations include Mono and DotGNU Portable.NET.

2.2 What is IL?

IL = Intermediate Language. Also known as MSIL (Microsoft Intermediate Language) or CIL (Common Intermediate Language). All .NET source code (of any language) is compiled to IL during development. The IL is then converted to machine code at the point where the software is installed, or (more commonly) at run-time by a Just-In-Time (JIT) compiler.

2.3 What is C#?

C# is a new language designed by Microsoft to work with the .NET framework. In their "Introduction to C#" whitepaper, Microsoft describe C# as follows:

"C# is a simple, modern, object oriented, and type-safe programming language derived from C and C++. C# (pronounced “C sharp”) is firmly planted in the C and C++ family tree of languages, and will immediately be familiar to C and C++ programmers. C# aims to combine the high productivity of Visual Basic and the raw power of C++."

Substitute 'Java' for 'C#' in the quote above, and you'll see that the statement still works pretty well :-).

If you are a C++ programmer, you might like to check out my C# FAQ.

2.4 What does 'managed' mean in the .NET context?

The term 'managed' is the cause of much confusion. It is used in various places within .NET, meaning slightly different things.

Managed code: The .NET framework provides several core run-time services to the programs that run within it - for example exception handling and security. For these services to work, the code must provide a minimum level of information to the runtime. Such code is called managed code.

Managed data: This is data that is allocated and freed by the .NET runtime's garbage collector.

Managed classes: This is usually referred to in the context of Managed Extensions (ME) for C++. When using ME C++, a class can be marked with the __gc keyword. As the name suggests, this means that the memory for instances of the class is managed by the garbage collector, but it also means more than that. The class becomes a fully paid-up member of the .NET community with the benefits and restrictions that brings. An example of a benefit is proper interop with classes written in other languages - for example, a managed C++ class can inherit from a VB class. An example of a restriction is that a managed class can only inherit from one base class.

3. Assemblies

 

3.1 What is an assembly?

An assembly is sometimes described as a logical .EXE or .DLL, and can be an application (with a main entry point) or a library. An assembly consists of one or more files (dlls, exes, html files etc), and represents a group of resources, type definitions, and implementations of those types. An assembly may also contain references to other assemblies. These resources, types and references are described in a block of data called a manifest. The manifest is part of the assembly, thus making the assembly self-describing.

An important aspect of assemblies is that they are part of the identity of a type. The identity of a type is the assembly that houses it combined with the type name. This means, for example, that if assembly A exports a type called T, and assembly B exports a type called T, the .NET runtime sees these as two completely different types. Furthermore, don't get confused between assemblies and namespaces - namespaces are merely a hierarchical way of organising type names. To the runtime, type names are type names, regardless of whether namespaces are used to organise the names. It's the assembly plus the typename (regardless of whether the type name belongs to a namespace) that uniquely indentifies a type to the runtime.

Assemblies are also important in .NET with respect to security - many of the security restrictions are enforced at the assembly boundary.

Finally, assemblies are the unit of versioning in .NET - more on this below.

3.2 How can I produce an assembly?

The simplest way to produce an assembly is directly from a .NET compiler. For example, the following C# program:

    public class CTest
{
public CTest() { System.Console.WriteLine( "Hello from CTest" ); }
}

can be compiled into a library assembly (dll) like this:

    csc /t:library ctest.cs

You can then view the contents of the assembly by running the "IL Disassembler" tool that comes with the .NET SDK.

Alternatively you can compile your source into modules, and then combine the modules into an assembly using the assembly linker (al.exe). For the C# compiler, the /target:module switch is used to generate a module instead of an assembly.

3.3 What is the difference between a private assembly and a shared assembly?

The terms 'private' and 'shared' refer to how an assembly is deployed, not any intrinsic attributes of the assembly.

A private assembly is normally used by a single application, and is stored in the application's directory, or a sub-directory beneath. A shared assembly is intended to be used by multiple applications, and is normally stored in the global assembly cache (GAC), which is a central repository for assemblies. (A shared assembly can also be stored outside the GAC, in which case each application must be pointed to its location via a codebase entry in the application's configuration file.) The main advantage of deploying assemblies to the GAC is that the GAC can support multiple versions of the same assembly side-by-side.

Assemblies deployed to the GAC must be strong-named. Outside the GAC, strong-naming is optional.

 

 

 

 

3.4 How do assemblies find each other?

By searching directory paths. There are several factors that can affect the path (such as the AppDomain host, and application configuration files), but for weakly named assemblies the search path is normally the application's directory and its sub-directories. For strongly named assemblies, the search path is the GAC followed by the private assembly path.

3.5 How does assembly versioning work?

An assembly has a version number consisting of four parts, e.g. 1.0.350.1. These are typically interpreted as Major.Minor.Build.Revision, but this is just a convention.

The CLR applies no version constraints on weakly named assemblies, so the assembly version has no real significance.

For strongly named assemblies, the version of a referenced assembly is stored in the referring assembly, and by default only this exact version will be loaded at run-time. If the exact version is not available, the referring assembly will fail to load. It is possible to override this behaviour in the config file for the referring assembly - references to a single version or a range of versions of the referenced assembly can be redirected to a specific version. For example, versions 1.0.0.0 to 2.0.0.0 can be redirected to version 3.0.125.3. However note that there is no way to specify a range of versions to be redirected to. Publisher policy files offer an alternative mechanism for redirecting to a different version for assemblies deployed to the GAC - a publisher policy file allows the publisher of the assembly to redirect all applications to a new version of an assembly in one operation, rather than having to modify all of the application configuration files.

The restrictions on version policy for strongly named assemblies can cause problems when providing patches or 'hot fixes' for individual assemblies within an application. To avoid having to deploy config file changes or publisher policy files along with the hot fix, it makes sense to reuse the same assembly version for the hot fix. If desired, the assemblies can be distinguised by altering the assembly file version, which is not used at all by the CLR for applying version policy. For more discussion, see Suzanne Cook's When to Change File/Assembly Versions blog entry.

Note that the versioning of strongly named assemblies applies whether the assemblies are deployed privately or to the GAC.

3.6 How can I develop an application that automatically updates itself from the web?

For .NET 1.x, use the Updater Application Block. For .NET 2.x, use ClickOnce.

4. Application Domains

 

4.1 What is an application domain?

An AppDomain can be thought of as a lightweight process. Multiple AppDomains can exist inside a Win32 process. The primary purpose of the AppDomain is to isolate applications from each other, and so it is particularly useful in hosting scenarios such as ASP.NET. An AppDomain can be destroyed by the host without affecting other AppDomains in the process.

Win32 processes provide isolation by having distinct memory address spaces. This is effective, but expensive. The .NET runtime enforces AppDomain isolation by keeping control over the use of memory - all memory in the AppDomain is managed by the .NET runtime, so the runtime can ensure that AppDomains do not access each other's memory.

One non-obvious use of AppDomains is for unloading types. Currently the only way to unload a .NET type is to destroy the AppDomain it is loaded into. This is particularly useful if you create and destroy types on-the-fly via reflection.

Microsoft have an AppDomain FAQ.

4.2 How does an AppDomain get created?

AppDomains are usually created by hosts. Examples of hosts are the Windows Shell, ASP.NET and IE. When you run a .NET application from the command-line, the host is the Shell. The Shell creates a new AppDomain for every application.

AppDomains can also be explicitly created by .NET applications. Here is a C# sample which creates an AppDomain, creates an instance of an object inside it, and then executes one of the object's methods:

    using System;
using System.Runtime.Remoting;
using System.Reflection;
public class CAppDomainInfo : MarshalByRefObject
{
public string GetName() { return AppDomain.CurrentDomain.FriendlyName; }
}
public class App
{
public static int Main()
{
AppDomain ad = AppDomain.CreateDomain( "Andy's new domain" );
CAppDomainInfo adInfo = (CAppDomainInfo)ad.CreateInstanceAndUnwrap(
Assembly.GetCallingAssembly().GetName().Name, "CAppDomainInfo" );
Console.WriteLine( "Created AppDomain name = " + adInfo.GetName() );
return 0;
}
}

 

4.3 Can I write my own .NET host?

Yes. For an example of how to do this, take a look at the source for the dm.net moniker developed by Jason Whittington and Don Box. There is also a code sample in the .NET SDK called CorHost.

5. Garbage Collection

 

5.1 What is garbage collection?

Garbage collection is a heap-management strategy where a run-time component takes responsibility for managing the lifetime of the memory used by objects. This concept is not new to .NET - Java and many other languages/runtimes have used garbage collection for some time.

5.2 Is it true that objects don't always get destroyed immediately when the last reference goes away?

Yes. The garbage collector offers no guarantees about the time when an object will be destroyed and its memory reclaimed.

There was an interesting thread on the DOTNET list, started by Chris Sells, about the implications of non-deterministic destruction of objects in C#. In October 2000, Microsoft's Brian Harry posted a lengthy analysis of the problem. Chris Sells' response to Brian's posting is here.

5.3 Why doesn't the .NET runtime offer deterministic destruction?

Because of the garbage collection algorithm. The .NET garbage collector works by periodically running through a list of all the objects that are currently being referenced by an application. All the objects that it doesn't find during this search are ready to be destroyed and the memory reclaimed. The implication of this algorithm is that the runtime doesn't get notified immediately when the final reference on an object goes away - it only finds out during the next 'sweep' of the heap.

Futhermore, this type of algorithm works best by performing the garbage collection sweep as rarely as possible. Normally heap exhaustion is the trigger for a collection sweep.

5.4 Is the lack of deterministic destruction in .NET a problem?

It's certainly an issue that affects component design. If you have objects that maintain expensive or scarce resources (e.g. database locks), you need to provide some way to tell the object to release the resource when it is done. Microsoft recommend that you provide a method called Dispose() for this purpose. However, this causes problems for distributed objects - in a distributed system who calls the Dispose() method? Some form of reference-counting or ownership-management mechanism is needed to handle distributed objects - unfortunately the runtime offers no help with this.

5.5 Should I implement Finalize on my class? Should I implement IDisposable?

This issue is a little more complex than it first appears. There are really two categories of class that require deterministic destruction - the first category manipulate unmanaged types directly, whereas the second category manipulate managed types that require deterministic destruction. An example of the first category is a class with an IntPtr member representing an OS file handle. An example of the second category is a class with a System.IO.FileStream member.

For the first category, it makes sense to implement IDisposable and override Finalize. This allows the object user to 'do the right thing' by calling Dispose, but also provides a fallback of freeing the unmanaged resource in the Finalizer, should the calling code fail in its duty. However this logic does not apply to the second category of class, with only managed resources. In this case implementing Finalize is pointless, as managed member objects cannot be accessed in the Finalizer. This is because there is no guarantee about the ordering of Finalizer execution. So only the Dispose method should be implemented. (If you think about it, it doesn't really make sense to call Dispose on member objects from a Finalizer anyway, as the member object's Finalizer will do the required cleanup.)

For classes that need to implement IDisposable and override Finalize, see Microsoft's documented pattern.

Note that some developers argue that implementing a Finalizer is always a bad idea, as it hides a bug in your code (i.e. the lack of a Dispose call). A less radical approach is to implement Finalize but include a Debug.Assert at the start, thus signalling the problem in developer builds but allowing the cleanup to occur in release builds.

5.6 Do I have any control over the garbage collection algorithm?

A little. For example the System.GC class exposes a Collect method, which forces the garbage collector to collect all unreferenced objects immediately.

Also there is a gcConcurrent setting that can be specified via the application configuration file. This specifies whether or not the garbage collector performs some of its collection activities on a separate thread. The setting only applies on multi-processor machines, and defaults to true.

5.7 How can I find out what the garbage collector is doing?

Lots of interesting statistics are exported from the .NET runtime via the '.NET CLR xxx' performance counters. Use Performance Monitor to view them.

5.8 What is the lapsed listener problem?

The lapsed listener problem is one of the primary causes of leaks in .NET applications. It occurs when a subscriber (or 'listener') signs up for a publisher's event, but fails to unsubscribe. The failure to unsubscribe means that the publisher maintains a reference to the subscriber as long as the publisher is alive. For some publishers, this may be the duration of the application.

This situation causes two problems. The obvious problem is the leakage of the subscriber object. The other problem is the performance degredation due to the publisher sending redundant notifications to 'zombie' subscribers.

There are at least a couple of solutions to the problem. The simplest is to make sure the subscriber is unsubscribed from the publisher, typically by adding an Unsubscribe() method to the subscriber. Another solution, documented here by Shawn Van Ness, is to change the publisher to use weak references in its subscriber list.

5.9 When do I need to use GC.KeepAlive?

It's very unintuitive, but the runtime can decide that an object is garbage much sooner than you expect. More specifically, an object can become garbage while a method is executing on the object, which is contrary to most developers' expectations. Chris Brumme explains the issue on his blog. I've taken Chris's code and expanded it into a full app that you can play with if you want to prove to yourself that this is a real problem:

    using System;
using System.Runtime.InteropServices;
class Win32
{
[DllImport("kernel32.dll")]
public static extern IntPtr CreateEvent( IntPtr lpEventAttributes,
bool bManualReset,bool bInitialState, string lpName);
[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll")]
public static extern bool SetEvent(IntPtr hEvent);
}
class EventUser
{
public EventUser()
{
hEvent = Win32.CreateEvent( IntPtr.Zero, false, false, null );
}
~EventUser()
{
Win32.CloseHandle( hEvent );
Console.WriteLine("EventUser finalized");
}
public void UseEvent()
{
UseEventInStatic( this.hEvent );
}
static void UseEventInStatic( IntPtr hEvent )
{
//GC.Collect();
bool bSuccess = Win32.SetEvent( hEvent );
Console.WriteLine( "SetEvent " + (bSuccess ? "succeeded" : "FAILED!") );
}
IntPtr hEvent;
}
class App
{
static void Main(string[] args)
{
EventUser eventUser = new EventUser();
eventUser.UseEvent();
}
}

If you run this code, it'll probably work fine, and you'll get the following output:

    SetEvent succeeded
EventDemo finalized

However, if you uncomment the GC.Collect() call in the UseEventInStatic() method, you'll get this output:

    EventDemo finalized
SetEvent FAILED!

(Note that you need to use a release build to reproduce this problem.)

So what's happening here? Well, at the point where UseEvent() calls UseEventInStatic(), a copy is taken of the hEvent field, and there are no further references to the EventUser object anywhere in the code. So as far as the runtime is concerned, the EventUser object is garbage and can be collected. Normally of course the collection won't happen immediately, so you'll get away with it, but sooner or later a collection will occur at the wrong time, and your app will fail.

A solution to this problem is to add a call to GC.KeepAlive(this) to the end of the UseEvent method, as Chris explains.

6. Serialization

 

6.1 What is serialization?

Serialization is the process of converting an object into a stream of bytes. Deserialization is the opposite process, i.e. creating an object from a stream of bytes. Serialization/Deserialization is mostly used to transport objects (e.g. during remoting), or to persist objects (e.g. to a file or database).

6.2 Does the .NET Framework have in-built support for serialization?

There are two separate mechanisms provided by the .NET class library - XmlSerializer and SoapFormatter/BinaryFormatter. Microsoft uses XmlSerializer for Web Services, and SoapFormatter/BinaryFormatter for remoting. Both are available for use in your own code.

6.3 I want to serialize instances of my class. Should I use XmlSerializer, SoapFormatter or BinaryFormatter?

It depends. XmlSerializer has severe limitations such as the requirement that the target class has a parameterless constructor, and only public read/write properties and fields can be serialized. However, on the plus side, XmlSerializer has good support for customising the XML document that is produced or consumed. XmlSerializer's features mean that it is most suitable for cross-platform work, or for constructing objects from existing XML documents.

SoapFormatter and BinaryFormatter have fewer limitations than XmlSerializer. They can serialize private fields, for example. However they both require that the target class be marked with the [Serializable] attribute, so like XmlSerializer the class needs to be written with serialization in mind. Also there are some quirks to watch out for - for example on deserialization the constructor of the new object is not invoked.

The choice between SoapFormatter and BinaryFormatter depends on the application. BinaryFormatter makes sense where both serialization and deserialization will be performed on the .NET platform and where performance is important. SoapFormatter generally makes more sense in all other cases, for ease of debugging if nothing else.

6.4 Can I customise the serialization process?

Yes. XmlSerializer supports a range of attributes that can be used to configure serialization for a particular class. For example, a field or property can be marked with the [XmlIgnore] attribute to exclude it from serialization. Another example is the [XmlElement] attribute, which can be used to specify the XML element name to be used for a particular property or field.

Serialization via SoapFormatter/BinaryFormatter can also be controlled to some extent by attributes. For example, the [NonSerialized] attribute is the equivalent of XmlSerializer's [XmlIgnore] attribute. Ultimate control of the serialization process can be acheived by implementing the the ISerializable interface on the class whose instances are to be serialized.

6.5 Why is XmlSerializer so slow?

There is a once-per-process-per-type overhead with XmlSerializer. So the first time you serialize or deserialize an object of a given type in an application, there is a significant delay. This normally doesn't matter, but it may mean, for example, that XmlSerializer is a poor choice for loading configuration settings during startup of a GUI application.

6.6 Why do I get errors when I try to serialize a Hashtable?

XmlSerializer will refuse to serialize instances of any class that implements IDictionary, e.g. Hashtable. SoapFormatter and BinaryFormatter do not have this restriction.

6.7 XmlSerializer is throwing a generic "There was an error reflecting MyClass" error. How do I find out what the problem is?

Look at the InnerException property of the exception that is thrown to get a more specific error message.

6.8 Why am I getting an InvalidOperationException when I serialize an ArrayList?

XmlSerializer needs to know in advance what type of objects it will find in an ArrayList. To specify the type, use the XmlArrayItem attibute like this:

    public class Person
{
public string Name;
public int Age;
}
public class Population
{
[XmlArrayItem(typeof(Person))] public ArrayList People;
}

 

7. Attributes

 

7.1 What are attributes?

There are at least two types of .NET attribute. The first type I will refer to as a metadata attribute - it allows some data to be attached to a class or method. This data becomes part of the metadata for the class, and (like other class metadata) can be accessed via reflection. An example of a metadata attribute is [serializable], which can be attached to a class and means that instances of the class can be serialized.

    [serializable] public class CTest {}

The other type of attribute is a context attribute. Context attributes use a similar syntax to metadata attributes but they are fundamentally different. Context attributes provide an interception mechanism whereby instance activation and method calls can be pre- and/or post-processed. If you have encountered Keith Brown's universal delegator you'll be familiar with this idea.

7.2 Can I create my own metadata attributes?

Yes. Simply derive a class from System.Attribute and mark it with the AttributeUsage attribute. For example:

    [AttributeUsage(AttributeTargets.Class)]
public class InspiredByAttribute : System.Attribute
{
public string InspiredBy;
public InspiredByAttribute( string inspiredBy )
{
InspiredBy = inspiredBy;
}
}
[InspiredBy("Andy Mc's brilliant .NET FAQ")]
class CTest
{
}
class CApp
{
public static void Main()
{
object[] atts = typeof(CTest).GetCustomAttributes(true);
foreach( object att in atts )
if( att is InspiredByAttribute )
Console.WriteLine( "Class CTest was inspired by {0}", ((InspiredByAttribute)att).InspiredBy  );
}
}

 

7.3 Can I create my own context attibutes?

Yes. Take a look at Peter Drayton's Tracehook.NET.

8. Code Access Security

 

8.1 What is Code Access Security (CAS)?

CAS is the part of the .NET security model that determines whether or not code is allowed to run, and what resources it can use when it is running. For example, it is CAS that will prevent a .NET web applet from formatting your hard disk.

8.2 How does CAS work?

The CAS security policy revolves around two key concepts - code groups and permissions. Each .NET assembly is a member of a particular code group, and each code group is granted the permissions specified in a named permission set.

For example, using the default security policy, a control downloaded from a web site belongs to the 'Zone - Internet' code group, which adheres to the permissions defined by the 'Internet' named permission set. (Naturally the 'Internet' named permission set represents a very restrictive range of permissions.)

8.3 Who defines the CAS code groups?

Microsoft defines some default ones, but you can modify these and even create your own. To see the code groups defined on your system, run 'caspol -lg' from the command-line. On my system it looks like this:

   Level = Machine
Code Groups:
1.  All code: Nothing
1.1.  Zone - MyComputer: FullTrust
1.1.1.  Honor SkipVerification requests: SkipVerification
1.2.  Zone - Intranet: LocalIntranet
1.3.  Zone - Internet: Internet
1.4.  Zone - Untrusted: Nothing
1.5.  Zone - Trusted: Internet
1.6.  StrongName -
0024000004800000940000000602000000240000525341310004000003
000000CFCB3291AA715FE99D40D49040336F9056D7886FED46775BC7BB5430BA4444FEF8348EBD06
F962F39776AE4DC3B7B04A7FE6F49F25F740423EBF2C0B89698D8D08AC48D69CED0FC8F83B465E08
07AC11EC1DCC7D054E807A43336DDE408A5393A48556123272CEEEE72F1660B71927D38561AABF5C
AC1DF1734633C602F8F2D5: Everything

Note the hierarchy of code groups - the top of the hierarchy is the most general ('All code'), which is then sub-divided into several groups, each of which in turn can be sub-divided. Also note that (somewhat counter-intuitively) a sub-group can be associated with a more permissive permission set than its parent.

8.4 How do I define my own code group?

Use caspol. For example, suppose you trust code from www.mydomain.com and you want it have full access to your system, but you want to keep the default restrictions for all other internet sites. To achieve this, you would add a new code group as a sub-group of the 'Zone - Internet' group, like this:

 caspol -ag 1.3 -site www.mydomain.com FullTrust 

Now if you run caspol -lg you will see that the new group has been added as group 1.3.1:

 ...
1.3.  Zone - Internet: Internet
1.3.1.  Site - www.mydomain.com: FullTrust
...

Note that the numeric label (1.3.1) is just a caspol invention to make the code groups easy to manipulate from the command-line. The underlying runtime never sees it.

8.5 How do I change the permission set for a code group?

Use caspol. If you are the machine administrator, you can operate at the 'machine' level - which means not only that the changes you make become the default for the machine, but also that users cannot change the permissions to be more permissive. If you are a normal (non-admin) user you can still modify the permissions, but only to make them more restrictive. For example, to allow intranet code to do what it likes you might do this:

 caspol -cg 1.2 FullTrust

Note that because this is more permissive than the default policy (on a standard system), you should only do this at the machine level - doing it at the user level will have no effect.

8.6 Can I create my own permission set?

Yes. Use caspol -ap, specifying an XML file containing the permissions in the permission set. To save you some time, here is a sample file corresponding to the 'Everything' permission set - just edit to suit your needs. When you have edited the sample, add it to the range of available permission sets like this:

 caspol -ap samplepermset.xml

Then, to apply the permission set to a code group, do something like this:

 caspol -cg 1.3 SamplePermSet

(By default, 1.3 is the 'Internet' code group)

8.7 I'm having some trouble with CAS. How can I troubleshoot the problem?

Caspol has a couple of options that might help. First, you can ask caspol to tell you what code group an assembly belongs to, using caspol -rsg. Similarly, you can ask what permissions are being applied to a particular assembly using caspol -rsp.

8.8 I can't be bothered with CAS. Can I turn it off?

Yes, as long as you are an administrator. Just run:

 caspol -s off

 

9. Intermediate Language (IL)

 

9.1 Can I look at the IL for an assembly?

Yes. MS supply a tool called Ildasm that can be used to view the metadata and IL for an assembly.

9.2 Can source code be reverse-engineered from IL?

Yes, it is often relatively straightforward to regenerate high-level source from IL. Lutz Roeder's Reflector does a very good job of turning IL into C# or VB.NET.

9.3 How can I stop my code being reverse-engineered from IL?

You can buy an IL obfuscation tool. These tools work by 'optimising' the IL in such a way that reverse-engineering becomes much more difficult.

Of course if you are writing web services then reverse-engineering is not a problem as clients do not have access to your IL.

9.4 Can I write IL programs directly?

Yes. Peter Drayton posted this simple example to the DOTNET mailing list:

    .assembly MyAssembly {}
.class MyApp {
.method static void Main() {
.entrypoint
ldstr      "Hello, IL!"
call       void System.Console::WriteLine(class System.Object)
ret
}
}

Just put this into a file called hello.il, and then run ilasm hello.il. An exe assembly will be generated.

9.5 Can I do things in IL that I can't do in C#?

Yes. A couple of simple examples are that you can throw exceptions that are not derived from System.Exception, and you can have non-zero-based arrays.

10. Implications for COM

 

10.1 Does .NET replace COM?

This subject causes a lot of controversy, as you'll see if you read the mailing list archives. Take a look at the following two threads:

http://discuss.develop.com/archives/wa.exe?A2=ind0007&L=DOTNET&D=0&P=68241
http://discuss.develop.com/archives/wa.exe?A2=ind0007&L=DOTNET&P=R60761

The bottom line is that .NET has its own mechanisms for type interaction, and they don't use COM. No IUnknown, no IDL, no typelibs, no registry-based activation. This is mostly good, as a lot of COM was ugly. Generally speaking, .NET allows you to package and use components in a similar way to COM, but makes the whole thing a bit easier.

10.2 Is DCOM dead?

Pretty much, for .NET developers. The .NET Framework has a new remoting model which is not based on DCOM. DCOM was pretty much dead anyway, once firewalls became widespread and Microsoft got SOAP fever. Of course DCOM will still be used in interop scenarios.

10.3 Is COM+ dead?

Not immediately. The approach for .NET 1.0 was to provide access to the existing COM+ services (through an interop layer) rather than replace the services with native .NET ones. Various tools and attributes were provided to make this as painless as possible. Over time it is expected that interop will become more seamless - this may mean that some services become a core part of the CLR, and/or it may mean that some services will be rewritten as managed code which runs on top of the CLR.

For more on this topic, search for postings by Joe Long in the archives - Joe is the MS group manager for COM+. Start with this message:

http://discuss.develop.com/archives/wa.exe?A2=ind0007&L=DOTNET&P=R68370

10.4 Can I use COM components from .NET programs?

Yes. COM components are accessed from the .NET runtime via a Runtime Callable Wrapper (RCW). This wrapper turns the COM interfaces exposed by the COM component into .NET-compatible interfaces. For oleautomation interfaces, the RCW can be generated automatically from a type library. For non-oleautomation interfaces, it may be necessary to develop a custom RCW which manually maps the types exposed by the COM interface to .NET-compatible types.

Here's a simple example for those familiar with ATL. First, create an ATL component which implements the following IDL:

    import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(EA013F93-487A-4403-86EC-FD9FEE5E6206),
helpstring("ICppName Interface"),
pointer_default(unique),
oleautomation
]
interface ICppName : IUnknown
{
[helpstring("method SetName")] HRESULT SetName([in] BSTR name);
[helpstring("method GetName")] HRESULT GetName([out,retval] BSTR *pName );
};
[
uuid(F5E4C61D-D93A-4295-A4B4-2453D4A4484D),
version(1.0),
helpstring("cppcomserver 1.0 Type Library")
]
library CPPCOMSERVERLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");
[
uuid(600CE6D9-5ED7-4B4D-BB49-E8D5D5096F70),
helpstring("CppName Class")
]
coclass CppName
{
[default] interface ICppName;
};
};

When you've built the component, you should get a typelibrary. Run the TLBIMP utility on the typelibary, like this:

    tlbimp cppcomserver.tlb

If successful, you will get a message like this:

    Typelib imported successfully to CPPCOMSERVERLib.dll

You now need a .NET client - let's use C#. Create a .cs file containing the following code:

    using System;
using CPPCOMSERVERLib;
public class MainApp
{
static public void Main()
{
CppName cppname = new CppName();
cppname.SetName( "bob" );
Console.WriteLine( "Name is " + cppname.GetName() );
}
}

Compile the C# code like this:

    csc /r:cppcomserverlib.dll csharpcomclient.cs

Note that the compiler is being told to reference the DLL we previously generated from the typelibrary using TLBIMP. You should now be able to run csharpcomclient.exe, and get the following output on the console:

    Name is bob

 

10.5 Can I use .NET components from COM programs?

Yes. .NET components are accessed from COM via a COM Callable Wrapper (CCW). This is similar to a RCW (see previous question), but works in the opposite direction. Again, if the wrapper cannot be automatically generated by the .NET development tools, or if the automatic behaviour is not desirable, a custom CCW can be developed. Also, for COM to 'see' the .NET component, the .NET component must be registered in the registry.

Here's a simple example. Create a C# file called testcomserver.cs and put the following in it:

    using System;
using System.Runtime.InteropServices;
namespace AndyMc
{
[ClassInterface(ClassInterfaceType.AutoDual)]
public class CSharpCOMServer
{
public CSharpCOMServer() {}
public void SetName( string name ) { m_name = name; }
public string GetName() { return m_name; }
private string m_name;
}
}

Then compile the .cs file as follows:

    csc /target:library testcomserver.cs

You should get a dll, which you register like this:

    regasm testcomserver.dll /tlb:testcomserver.tlb /codebase

Now you need to create a client to test your .NET COM component. VBScript will do - put the following in a file called comclient.vbs:

    Dim dotNetObj
Set dotNetObj = CreateObject("AndyMc.CSharpCOMServer")
dotNetObj.SetName ("bob")
MsgBox "Name is " & dotNetObj.GetName()

and run the script like this:

    wscript comclient.vbs

And hey presto you should get a message box displayed with the text "Name is bob".

An alternative to the approach above it to use the dm.net moniker developed by Jason Whittington and Don Box.

10.6 Is ATL redundant in the .NET world?

Yes. ATL will continue to be valuable for writing COM components for some time, but it has no place in the .NET world.

11. Threads

 

11.1 How do I spawn a thread?

Create an instance of a System.Threading.Thread object, passing it an instance of a ThreadStart delegate that will be executed on the new thread. For example:

    class MyThread
{
public MyThread( string initData )
{
m_data = initData;
m_thread = new Thread( new ThreadStart(ThreadMain) );
m_thread.Start();
}
// ThreadMain() is executed on the new thread.
private void ThreadMain()
{
Console.WriteLine( m_data );
}
public void WaitUntilFinished()
{
m_thread.Join();
}
private Thread m_thread;
private string m_data;
}

In this case creating an instance of the MyThread class is sufficient to spawn the thread and execute the MyThread.ThreadMain() method:

    MyThread t = new MyThread( "Hello, world." );
t.WaitUntilFinished();

 

11.2 How do I stop a thread?

There are several options. First, you can use your own communication mechanism to tell the ThreadStart method to finish. Alternatively the Thread class has in-built support for instructing the thread to stop. The two principle methods are Thread.Interrupt() and Thread.Abort(). The former will cause a ThreadInterruptedException to be thrown on the thread when it next goes into a WaitJoinSleep state. In other words, Thread.Interrupt is a polite way of asking the thread to stop when it is no longer doing any useful work. In contrast, Thread.Abort() throws a ThreadAbortException regardless of what the thread is doing. Furthermore, the ThreadAbortException cannot normally be caught (though the ThreadStart's finally method will be executed). Thread.Abort() is a heavy-handed mechanism which should not normally be required.

11.3 How do I use the thread pool?

By passing an instance of a WaitCallback delegate to the ThreadPool.QueueUserWorkItem() method

    class CApp
{
static void Main()
{
string s = "Hello, World";
ThreadPool.QueueUserWorkItem( new WaitCallback( DoWork ), s );
Thread.Sleep( 1000 );    // Give time for work item to be executed
}
// DoWork is executed on a thread from the thread pool.
static void DoWork( object state )
{
Console.WriteLine( state );
}
}

 

11.4 How do I know when my thread pool work item has completed?

There is no way to query the thread pool for this information. You must put code into the WaitCallback method to signal that it has completed. Events are useful for this.

11.5 How do I prevent concurrent access to my data?

Each object has a concurrency lock (critical section) associated with it. The System.Threading.Monitor.Enter/Exit methods are used to acquire and release this lock. For example, instances of the following class only allow one thread at a time to enter method f():

    class C
{
public void f()
{
try
{
Monitor.Enter(this);
...
}
finally
{
Monitor.Exit(this);
}
}
}

C# has a 'lock' keyword which provides a convenient shorthand for the code above:

    class C
{
public void f()
{
lock(this)
{
...
}
}
}

Note that calling Monitor.Enter(myObject) does NOT mean that all access to myObject is serialized. It means that the synchronisation lock associated with myObject has been acquired, and no other thread can acquire that lock until Monitor.Exit(o) is called. In other words, this class is functionally equivalent to the classes above:

    class C
{
public void f()
{
lock( m_object )
{
...
}
}
private m_object = new object();
}

Actually, it could be argued that this version of the code is superior, as the lock is totally encapsulated within the class, and not accessible to the user of the object.

11.6 Should I use ReaderWriterLock instead of Monitor.Enter/Exit?

Maybe, but be careful. ReaderWriterLock is used to allow multiple threads to read from a data source, while still granting exclusive access to a single writer thread. This makes sense for data access that is mostly read-only, but there are some caveats. First, ReaderWriterLock is relatively poor performing compared to Monitor.Enter/Exit, which offsets some of the benefits. Second, you need to be very sure that the data structures you are accessing fully support multithreaded read access. Finally, there is apparently a bug in the v1.1 ReaderWriterLock that can cause starvation for writers when there are a large number of readers.

Ian Griffiths has some interesting discussion on ReaderWriterLock here and here.

 

 

12. Tracing

 

12.1 Is there built-in support for tracing/logging?

Yes, in the System.Diagnostics namespace. There are two main classes that deal with tracing - Debug and Trace. They both work in a similar way - the difference is that tracing from the Debug class only works in builds that have the DEBUG symbol defined, whereas tracing from the Trace class only works in builds that have the TRACE symbol defined. Typically this means that you should use System.Diagnostics.Trace.WriteLine for tracing that you want to work in debug and release builds, and System.Diagnostics.Debug.WriteLine for tracing that you want to work only in debug builds.

12.2 Can I redirect tracing to a file?

Yes. The Debug and Trace classes both have a Listeners property, which is a collection of sinks that receive the tracing that you send via Debug.WriteLine and Trace.WriteLine respectively. By default the Listeners collection contains a single sink, which is an instance of the DefaultTraceListener class. This sends output to the Win32 OutputDebugString() function and also the System.Diagnostics.Debugger.Log() method. This is useful when debugging, but if you're trying to trace a problem at a customer site, redirecting the output to a file is more appropriate. Fortunately, the TextWriterTraceListener class is provided for this purpose.

Here's how to use the TextWriterTraceListener class to redirect Trace output to a file:

    Trace.Listeners.Clear();
FileStream fs = new FileStream( @"c:\log.txt", FileMode.Create, FileAccess.Write );
Trace.Listeners.Add( new TextWriterTraceListener( fs ) );
Trace.WriteLine( @"This will be writen to c:\log.txt!" );
Trace.Flush();

Note the use of Trace.Listeners.Clear() to remove the default listener. If you don't do this, the output will go to the file and OutputDebugString(). Typically this is not what you want, because OutputDebugString() imposes a big performance hit.

12.3 Can I customise the trace output?

Yes. You can write your own TraceListener-derived class, and direct all output through it. Here's a simple example, which derives from TextWriterTraceListener (and therefore has in-built support for writing to files, as shown above) and adds timing information and the thread ID for each trace line:

    class MyListener : TextWriterTraceListener
{
public MyListener( Stream s ) : base(s)
{
}
public override void WriteLine( string s )
{
Writer.WriteLine( "{0:D8} [{1:D4}] {2}",
Environment.TickCount - m_startTickCount,
AppDomain.GetCurrentThreadId(),
s );
}
protected int m_startTickCount = Environment.TickCount;
}

(Note that this implementation is not complete - the TraceListener.Write method is not overridden for example.)

The beauty of this approach is that when an instance of MyListener is added to the Trace.Listeners collection, all calls to Trace.WriteLine() go through MyListener, including calls made by referenced assemblies that know nothing about the MyListener class.

12.4 Are there any third party logging components available?

Log4net is a port of the established log4j Java logging component.

13. Miscellaneous

 

13.1 How does .NET remoting work?

.NET remoting involves sending messages along channels. Two of the standard channels are HTTP and TCP. TCP is intended for LANs only - HTTP can be used for LANs or WANs (internet).

Support is provided for multiple message serializarion formats. Examples are SOAP (XML-based) and binary. By default, the HTTP channel uses SOAP (via the .NET runtime Serialization SOAP Formatter), and the TCP channel uses binary (via the .NET runtime Serialization Binary Formatter). But either channel can use either serialization format.

There are a number of styles of remote access:

  • SingleCall. Each incoming request from a client is serviced by a new object. The object is thrown away when the request has finished.
  • Singleton. All incoming requests from clients are processed by a single server object.
  • Client-activated object. This is the old stateful (D)COM model whereby the client receives a reference to the remote object and holds that reference (thus keeping the remote object alive) until it is finished with it.

Distributed garbage collection of objects is managed by a system called 'leased based lifetime'. Each object has a lease time, and when that time expires the object is disconnected from the .NET runtime remoting infrastructure. Objects have a default renew time - the lease is renewed when a successful call is made from the client to the object. The client can also explicitly renew the lease.

If you're interested in using XML-RPC as an alternative to SOAP, take a look at Charles Cook's XML-RPC.Net.

13.2 How can I get at the Win32 API from a .NET program?

Use P/Invoke. This uses similar technology to COM Interop, but is used to access static DLL entry points instead of COM objects. Here is an example of C# calling the Win32 MessageBox function:

    using System;
using System.Runtime.InteropServices;
class MainApp
{
[DllImport("user32.dll", EntryPoint="MessageBox", SetLastError=true, CharSet=CharSet.Auto)]
public static extern int MessageBox(int hWnd, String strMessage, String strCaption, uint uiType);
public static void Main()
{
MessageBox( 0, "Hello, this is PInvoke in operation!", ".NET", 0 );
}
}        

Pinvoke.net is a great resource for off-the-shelf P/Invoke signatures.

13.3 How do I write to the application configuration file at runtime?

You don't. See http://www.interact-sw.co.uk/iangblog/2004/11/25/savingconfig.

13.4 What is the difference between an event and a delegate?

An event is just a wrapper for a multicast delegate. Adding a public event to a class is almost the same as adding a public multicast delegate field. In both cases, subscriber objects can register for notifications, and in both cases the publisher object can send notifications to the subscribers. However, a public multicast delegate has the undesirable property that external objects can invoke the delegate, something we'd normally want to restrict to the publisher. Hence events - an event adds public methods to the containing class to add and remove receivers, but does not make the invocation mechanism public.

See this post by Julien Couvreur for more discussion.

13.5 What size is a .NET object?

Each instance of a reference type has two fields maintained by the runtime - a method table pointer and a sync block. These are 4 bytes each on a 32-bit system, making a total of 8 bytes per object overhead. Obviously the instance data for the type must be added to this to get the overall size of the object. So, for example, instances of the following class are 12 bytes each:

    class MyInt
{
...
private int x;
}

However, note that with the current implementation of the CLR there seems to be a minimum object size of 12 bytes, even for classes with no data (e.g. System.Object).

Values types have no equivalent overhead.

13.6 Will my .NET app run on 64-bit Windows?

64-bit (x64) versions of Windows support both 32-bit and 64-bit processes, and corresponding 32-bit and 64-bit versions of .NET 2.0. (.NET 1.1 is 32-bit only).

.NET 1.x apps automatically run as 32-bit processes on 64-bit Windows.

.NET 2.0 apps can either run as 32-bit processes or as 64-bit processes. The OS decides which to use based on the PE header of the executable. The flags in the PE header are controlled via the compiler /platform switch, which allows the target of the app to be specified as 'x86', 'x64' or 'any cpu'. Normally you specify 'any cpu', and your app will run as 32-bit on 32-bit Windows and 64-bit on 64-bit Windows. However if you have some 32-bit native code in your app (loaded via COM interop, for example), you will need to specify 'x86', which will force 64-bit Windows to load your app in a 32-bit process. You can also tweak the 32-bit flag in the PE header using the SDK corflags utility.

Some more explanation here:

http://blogs.msdn.com/gauravseth/archive/2006/03/07/545104.aspx
http://blogs.msdn.com/joshwil/archive/2005/04/08/406567.aspx
http://msdn.microsoft.com/netframework/programming/64bit/gettingstarted/

13.7 What is reflection?

All .NET compilers produce metadata about the types defined in the modules they produce. This metadata is packaged along with the module (modules in turn are packaged together in assemblies), and can be accessed by a mechanism called reflection. The System.Reflection namespace contains classes that can be used to interrogate the types for a module/assembly.

Using reflection to access .NET metadata is very similar to using ITypeLib/ITypeInfo to access type library data in COM, and it is used for similar purposes - e.g. determining data type sizes for marshaling data across context/process/machine boundaries.

Reflection can also be used to dynamically invoke methods (see System.Type.InvokeMember), or even create types dynamically at run-time (see System.Reflection.Emit.TypeBuilder).

14. .NET 2.0

 

14.1 What are the new features of .NET 2.0?

Generics, anonymous methods, partial classes, iterators, property visibility (separate visibility for get and set) and static classes. See http://msdn.microsoft.com/msdnmag/issues/04/05/C20/default.aspx for more information about these features.

14.2 What are the new 2.0 features useful for?

Generics are useful for writing efficient type-independent code, particularly where the types might include value types. The obvious application is container classes, and the .NET 2.0 class library includes a suite of generic container classes in the System.Collections.Generic namespace. Here's a simple example of a generic container class being used:

    List<int> myList = new List<int>();
myList.Add( 10 );

Anonymous methods reduce the amount of code you have to write when using delegates, and are therefore especially useful for GUI programming. Here's an example

    AppDomain.CurrentDomain.ProcessExit += delegate { Console.WriteLine("Process ending ..."); };

Partial classes is a useful feature for separating machine-generated code from hand-written code in the same class, and will therefore be heavily used by development tools such as Visual Studio.

Iterators reduce the amount of code you need to write to implement IEnumerable/IEnumerator. Here's some sample code:

    static void Main()
{
RandomEnumerator re = new RandomEnumerator( 5 );
foreach( double r in re )
Console.WriteLine( r );
Console.Read();
}
class RandomEnumerator : IEnumerable<double>
{
public RandomEnumerator(int size) { m_size = size; }
public IEnumerator<double> GetEnumerator()
{
Random rand = new Random();
for( int i=0; i < m_size; i++ )
yield return rand.NextDouble();
}
int m_size = 0;
}

The use of 'yield return' is rather strange at first sight. It effectively synthethises an implementation of IEnumerator, something we had to do manually in .NET 1.x.

14.3 What's the problem with .NET generics?

.NET generics work great for container classes. But what about other uses? Well, it turns out that .NET generics have a major limitation - they require the type parameter to be constrained. For example, you cannot do this:

    static class Disposer<T>
{
public static void Dispose(T obj) { obj.Dispose(); }
}

The C# compiler will refuse to compile this code, as the type T has not been constrained, and therefore only supports the methods of System.Object. Dispose is not a method on System.Object, so the compilation fails. To fix this code, we need to add a where clause, to reassure the compiler that our type T does indeed have a Dispose method

    static class Disposer<T> where T : IDisposable
{
public static void Dispose(T obj) { obj.Dispose(); }
}

The problem is that the requirement for explicit contraints is very limiting. We can use constraints to say that T implements a particular interface, but we can't dilute that to simply say that T implements a particular method. Contrast this with C++ templates (for example), where no constraint at all is required - it is assumed (and verified at compile time) that if the code invokes the Dispose() method on a type, then the type will support the method.

In fact, after writing generic code with interface constraints, we quickly see that we haven't gained much over non-generic interface-based programming. For example, we can easily rewrite the Disposer class without generics:

    static class Disposer
{
public static void Dispose( IDisposable obj ) { obj.Dispose(); }
}

For more on this topic, start by reading the following articles:

Bruce Eckel: http://www.mindview.net/WebLog/log-0050
Ian Griffiths: http://www.interact-sw.co.uk/iangblog/2004/03/14/generics
Charles Cook: http://www.cookcomputing.com/blog/archives/000425.html

14.4 What's new in the .NET 2.0 class library?

Here is a selection of new features in the .NET 2.0 class library:

  • Generic collections in the System.Collections.Generic namespace.
  • The System.Nullable<T> type. (Note that C# has special syntax for this type, e.g. int? is equivalent to Nullable<int>)
  • The GZipStream and DeflateStream classes in the System.IO.Compression namespace.
  • The Semaphore class in the System.Threading namespace.
  • Wrappers for DPAPI in the form of the ProtectedData and ProtectedMemory classes in the System.Security.Cryptography namespace.
  • The IPC remoting channel in the System.Runtime.Remoting.Channels.Ipc namespace, for optimised intra-machine communication.

and many, many more. See http://msdn2.microsoft.com/en-us/library/t357fb32(en-US,VS.80).aspx for a comprehensive list of changes.

15. Resources

 

15.1 Recommended books

I recommend the following books, either because I personally like them, or because I think they are well regarded by other .NET developers. (Note that I get a commission from Amazon if you buy a book after following one of these links.)

 

15.2 Web sites

 

 

15.3 Blogs

The following Weblogs ('blogs') have regular .NET content:

 

15.4 Free tools

 

 

 



DV

 

VBA FAQ

1. What does a "type mismatch" error mean?

It means that you are trying to compare apples with oranges!

You may have a statement such as:

If member-ID = user-request-ID THEN...

where you are trying to compare two data items.

The error means, quite simply, that the two values are not the same data type. Possibly one is text and the other is numeric.

The same error will occur for assignment statements where you are assigning a value to an item of a different data type, or if you are calling a procedure which is expecting a certain datatype but the value passed is not of the expected type (e.g. a form textbox value that is null or not date/numeric, and which has not been validated first). There will be many other situations when this can occur! Use the debugger to check the values in the variable(s) concerned.

2. Why do I get a "Type mismatch" error trying to use Recordsets in Access 2000?

The "Further VBA" Trainer was developed using Access 97, which used Data Access Objects when accessing tables in VBA code. In section 6, for example, the code in Figure 6.1 contains the line

Dim BookingTableRecords as Recordset.

This is fine in Access 97 but will cause the "Type Mismatch" error in Access 2000, as Access 2000 uses ActiveX Data Objects (with, confusingly, very similar initials).

To adjust code so that it works in Access 2000, do:

·        Add the letters DAO so that the code reads like:
Dim <Recordset name> as DAO.Recordset
(replacing <Recordset name> with the correct name)

·        Still in the code window, choose Tools-->References then ensure that the item 'MS DAO 3.6 Object library' is selected in the list presented; this is not referenced by default.

The code shown in the "Further VBA" Trainer should now work in Access 2000.

3. Why does my list box click event code no longer work?

If you have created a form object such as a command button, then delete the button, the code is left in the form module. If you create another button with the same name, the new button click event will be linked to the code, as the names are the same. This can be handy if you want to redo a form object, as you do not have to redo code.

However, if you delete and recreate a list box, the old code may not always be picked up. So, to re-link it to the list box, go to the object property box and simply recreate the relevant event; this should then link to the code.

Back to Top

 

4. How can I create a multi-purpose query/report which allows the user to enter a variable number of values each time?

Note - the suggestions below mainly use Query Design and the Forms Collection - VBA only comes into play for checking date ranges or for setting the sort order at run-time for a report.

Click here for an Example database - this is the result of following the instructions below, plus some extra bells and whistles.

Click here for explanatory notes - this explains about the extra bells and whistles.

The Query

When a system is up and running, the user may want to do various one-off queries. The information from these queries may be wanted quickly, and the user will not want to go through negotiations with the system developer to produce the report and arrange a price. It is useful to provide a general-purpose query if possible. For example, the Chelmer Leisure Management (see scenario in Access Further Skills textbook) may want queries such as:

Details of all members with a particular category, with certain dates of birth, with certain sporting interests, by surname, by gender, etc.

Rather than having lots of separate queries, it is possible to create a query that picks up the required values from a form via parameter values in text and combo boxes, etc.

30/3/2006. See the Getting Started VBA Trainer section 7.3.5 for how to save this query dynaset to an Excel spreadsheet.

Try the following:

·        Create a form with unbound text boxes that are to contain the user's criteria for a selection of fields from the membership table. Make sure that the text boxes are the same data type as the corresponding field on the membership table.

·        Create a query to select all the fields from the membership table.

·        Add a command button to the form to run the query. Clicking on the button will bring up a list of all the members. The user can then simply look at this, or copy/paste it into a spreadsheet for further analysis. You could also use VBA code to export the data to an Excel spreadsheet; look up DoCmd.TransferSpreadsheet - it's very easy to use.

·        Querying on a numeric field:
Enter [forms]![Memb query]![category no] as the criterion for the category number field, enter the number 2 in the category field on the form and run the query again. Only those records for members with category number = 2 will now be listed. But if you delete the category value and run the query, nothing at all is listed! (as there are no members with a category number field that is null). To correct this, change the query criterion to:
Like [forms]![Memb query]![category no] & * (* is the wildcard character)
Now, leaving the field blank will list all categories and entering a number will list just the required category. Have a look at the SQL to see how the query works.
Note that this uses the 'Forms Collection'. This is how Access allows you to reference fields on an open form. If you try running the query when the form is closed, little parameter boxes will pop up for you to enter the values in.

·        The example database shows a method using a drop-down box based on a Union query.

·        Querying on the starting characters of a text field:
Enter the following for the criterion for the last name:
Like [forms]![Memb query]![Lastname] & "*"
Now, entering a category of 1 and a lastname of J will list all members with a category of 1 and lastname beginning with J.
This is also useful for postcode analysis, e.g. listing all members in the CH1 area of Chelmer.

·        Note that if the user leaves the parameter box blank then this criterion will not select rows where this field is Null as "LIKE '*' " does not select Null values. There must be a non-Null entry in every row for this field. It would therefore be useful to set a default value for the field and to set the Required property to Yes. This comment amended 30/3/2006.

·        Querying on characters within a text field:
Enter the following for the criterion for the sporting interests field:
Like "*" & [forms]![memb query]![sporting interests] & "*"
and try this out with, say, aerobics (or even 'aer').

·        Note that if the user leaves the parameter box blank then this criterion will not select rows where this field is Null as "LIKE '*' " does not select Null values. There must be a non-Null entry in every row for this field. It would therefore be useful to set a default value for the field and to set the Required property to Yes. This comment amended 30/3/2006.

·        Querying on a yes/no field:
Enter the following for the criterion for the sex field:

Like [forms]![memb query]![sex] & "*"

This field is (confusingly!) specified as a yes/no field in the book, so the entry in the form field has also to be yes/no here. It would be better if this was simply M or F in a single-character field, and the user would then select via a combo box.

·        The example database shows a method using a drop-down box based on a table.

·        Querying on a specific value in a date field:
Enter the following for the criterion for the date of birth field:
Like [forms]![memb query]![date of birth] & "*"
Now you can specify a particular date or all dates.


·        Querying on a range of dates.
In some cases, the user may want a range of dates, for example:
Between [forms]![memb query]![date of birth1] And [forms]![memb query]![date of birth2]
but in this case the user must enter values in both dates. It is not possible to use the wildcard * with 'between' or <, =, > for a date field.
Access Help states: "Wildcard characters are meant to be used with text data types, although you can sometimes use them successfully with other data types such as dates, if you don't change the regional settings for these data types".

However, the following is a way round this, by using hidden fields on the form, in addition to the ones above, and VBA code:
(i) create two new text fields on the form, dob1 and dob2 and delete the labels. Format as date fields.

(ii) change the query criterion for the date of birth field to
Between [forms]![memb query]![dob1] And [forms]![memb query]![dob2]
(iii) code the following in the command button click event code, before the command to run the query:

If Not [Date of Birth1] Then ' put the user's value in the

dob1 = [Date of Birth1] ' query field

Else ' or set a default early date

dob1 = #1/1/1001#

End If

If Not [date of birth2] Then ' as above, but set a

dob2 = [date of birth2] ' default late date

Else

dob2 = #12/31/9999#

End If

Now run the query and experiment - watch the values that get set in the dob1 and dob2 fields.

Finally - change the 'visible' property for dob1 and dob2 to 'No', so that the fields are now 'hidden'.

·        Querying on a mixture of parameters or none.
Now try entering a mixture of values in the fields on the form, and see how the query picks up the values and selects appropriate records. Leaving all fields blank will select all records.

All the above has been based on just one table, but it is also applicable to a form based on a query joining two or more tables.

The Report

As a report can be based on a query, then, instead of having the form command button run a variable parameter query, as done above, choose to run a report instead.

The report header should list the parameter values that the user entered in the form fields. This is very straightforward. Create unbound text box field controls in the report header and enter Forms![form name]![field name] in the control to pick up the value from the form (with the appropriate form and field names, of course!). If there is no value in the form, then this will show as blank.

The example database demonstrates all this, with the parameters shown in the report header and with an option for the user to specify the sort order at run time.

Back to Top

 

5. How do I update a table value via a form? For example, subtracting quantity sold from the stock total.

The form shown above is a very simple form based on a table and using three fields, StockNo, Description and QtyInStock. The form is to be used to record sales data.

The form fields for the table fields are locked (by setting the appropriate field property to No) to prevent the user changing them. As these form fields are bound to the table, any change to the value on the form will also change the underlying table row.

The AmtSold field is an unbound text box - i.e. it is not bound to any table.

The ConfirmSale button is a non-Wizard command button (use non-Wizard buttons for purposes that the Wizards do not cover).

The VBA code is as follows:

·        Form Load event (for when the form is opened):
[AmtSold].SetFocus 'move cursor to total sold field
[ConfirmSale].Enabled = False 'disable sale button until sale entered

·        Before Update event for AmtSold field (for when user enters a value in the amount sold field):
[ConfirmSale].Enabled = True 'enable sale button

·        Click event for the ConfirmSale button: (for when use clicks on the button)
[QtyInStock] = [QtyInStock] - [AmtSold] 'adjust stock total
[AmtSold].SetFocus 'move cursor from button to
[ConfirmSale].Enabled = False 'stop user hitting button twice

·So, when the form is opened the sale button is disabled. When a sale is recorded, the button is enabled so that the user can click on it to confirm the sale. This activates code that updates the quantity in stock; as the form field is bound to the table row, this will automatically update the table. The button is then disabled to stop the user from clicking on it again; alternatively, the form could be closed.

·It is also normal to have the unit price on the form plus a calculated field to show the value of the sale (quantity x unit price). See Access FAQ 10 How do I make a calculated field in a query, form or report?

In a properly designed system, the stock code number would be typed in (simulating a scanner) and the form opened showing just that record. There would also be checks (in the ConfirmSale click event) such as:

·        AmtSold > 0

·        AmtSold not null

·        AmtSold <= QtyInStock

The 'Getting Started' VBA Trainer covers validation of data entered into fields on a form.

Back to Top

 

6. How do I create my own keys? For example, membership key = M123456.

See the creating custom primary keys example database.

Back to Top

 

7. How can I change the size of a form at run-time?

Example - suppose you want a form to open so that it is always maximised:

·        In the Form_Load event, code: DoCmd.Maximise
This will maximise the size of the form - and of all other windows (apart from pop-up windows).

·        In the Form_Close event, code: DoCmd.Restore
This will restore the form (and all other windows) to their previous size.

Look up Maximise and Restore in Help.

Also look up Minimise and SelectObject.

Back to Top

 

8. How can I cater for production of (mailing) letters? changed 15/12/2003

Using a fixed letter set up as a report is a method often used by students. The report is designed with the letter text in a text box on the report, and with the variable data picked up from a bound table or query. This is very inflexible, as the user will have to go back to the developer to get the letter changed. This method may or not require VBA, depending on the complexity of the report.

The Mailing Letter example database demonstrates how to:

·        create individual and bulk letters by typing the text in a form, picking items from drop-down boxes, etc.

·        use Mail Merge (with a simple query and one that picks up a parameter value from a form)

All except the simple query Mail Merge use VBA.

Back to Top

 

9. I'm using the debugger for VBA code and get the message "The value in the field or record violates the validation rule for the record or field". What does this mean?

This has happened when one decides to stop the debugger, after replying 'Yes' to 'This command will stop the debugger'.

If you then run the code without the debugger everything is OK.

So it would appear to be caused by the stopping of the debugger, not an error in your code (I think...).

Back to Top

 

10. How do I pass an array as a parameter?

The two methods below show how to specify and use an array as a parameter in a private or public sub procedure or function. The example given is of a Public procedure, but this method also applies to Private procedures and/or Functions.

Method 1:

Public Sub myProc(prmArray() As <datatype>)

·        Where <datatype> = the datatype as in the declaration of the array, or Variant.

·        Note that the actual bounds of the array are not specified; the open and close brackets () indicate that this is an array.

·        The parameter as passed to the procedure must be an array or else the calling code will not compile: "Type mismatch: array or user-defined type expected".

Method 2:

Public Sub myProc(prmArray as Variant)

·        A Variant variable can cope with any datatype, including an array. Note that there are no () after the array name.

·        The calling code will compile if the parameter passed to the procedure is not an array (compare with Method 1).

·        The procedure must therefore check at run-time that the parameter is an array:
If [Not] IsArray(prmArray) then ...

Both Methods:

The array definition in the procedure header does not specify the bounds of the array, but Access provides two useful functions LBound and UBound which determine these at run-time:

For IntCount = LBound(prmArray) to UBound(prmArray) ...

Back to Top

 

11. What does the message "Compile Error: Expected variable or procedure, not module" mean?

This can occur when you have a module name the same name as a function (so Access gets confused, understandably!). Change the module name.

See also Access FAQ 26 What does the message Undefined function '<name>' in expression" mean? .

Back to Top

 

12. What does the message "Run Time Error 2001 - You cancelled the previous operation" mean?

This message is far from helpful, as it bears no obvious relation to the problem!

It can occur in VBA when Access cannot make sense of part of an embedded SQL statement, or when elements within a Domain Aggregate Function are incorrectly specified.

Example:

Look at the Staff Holiday Booking example database. The Holiday form uses a DSum statement to add up the total holiday days booked:

Forms![Staff]!txtSumOfDays = DSum("days", "qryHolDates", "[staffId] = " & Me!staffId)

If you change the "days" to "day" for the first parameter to the DSum statement then you will duly get the error message, as there is no field called "day" on qryHolDates.

The solution is to look carefully at each element of the SQL or Function, and check that they are entirely correct. Put all SQL in string variables, then you can check the contents at run (i.e. failure) time in the Debugger. It could also be useful to put the 3rd parameter (the SQL criterion) for a Domain Aggregate Function in a variable for checking at run/failure time.

Tip. it can be useful to try the function without the optional 3rd parameter (the SQL criterion). If it does not fail, then the fault is in the criterion, so check that carefully. If it still fails, then the fault lies with one (or both!) of the first two parameters.

See the end of section 6.6 of the 'Getting Started' VBA Trainer for a list of things that cause errors in embedded SQL - many of these also apply to Domain Aggregate Function code.

You may also get this error message when you hit OK on an unexpected parameter box, without actually entering a value. The parameter boxes (as you should know) pop up when Access cannot find the item specified - the usual cause is a misspelled name, or an incorrect name for a form or field.

Back to Top

 

13. Why doesn't the Round function round up numbers all ending in 5? Sometimes it rounds up and sometimes it rounds down.

Examples: Round(4.5,0) returns 4

Round(3.5,0) returns 4

The answer can be found at http://www.tek-tips.com/faqs.cfm?fid=3734 . The text at this site as at 11:13 6/12/2004 is copied below (as links can sometimes be removed).

(My thanks to Matthew Dean for pointing this site out to me, when I asked him why Round was apparently inconsistent/wrong).

http://www.techonthenet.com/access/functions/numeric/round.htm has a really neat solution! 8/3/2005.

"If you wanted to round 12.65 to 1 decimal place in the traditional sense (where 12.65 rounded to 1 decimal place is 12.7, instead of 12.6), try adding 0.000001 to your number before applying the Round function:

Round(12.45+0.000001,1)

By adding the 0.000001, the expression that you are rounding will end in 1, instead of 5...causing the Round function to round in the traditional way.

And the 0.000001 does not significantly affect the value of your expression so you shouldn't introduce any calculation errors."

Start of text from tek-tips site....................................

The Round() function rounds 2.45 to 2.4 and is correct. Why?

faq705-3734


Before you use the Round() function in Access, please read this FAQ or you may introduce calculation errors into your application:

Syntax: Round(expression[, numdecimalplaces])

The Access Round() function appears to be a handy way of rounding decimal values to a given number of decimal places. For example, Round(2.341, 2) returns 2.34.

However, it does not perform rounding the way you might expect. Take this example:

Round(2.385,2)

You would expect this to return 2.39, but if you open an immediate window and type ?Round(2.385,2) you will get the following output:

2.38 (not 2.39)

Even though the last digit was 5, it rounded DOWN.


If this is not the behaviour you expected, then here’s the explanation:

The Round() function does a round-to-even, not the more well-known round-to-larger. If the argument ends in 5, the number may be rounded up or down to achieve an even rightmost figure.

So in our example above:

2.385 could be rounded to either 2.38 or 2.39. The former has an even rightmost figure, so this is the result.

The reasoning behind this is to eliminate cumulative errors over a series of calculations. Put another way, 100 half pennies should round to be equal 50 cents, not 100 cents.


A RoundToLarger() function:

Public Function RoundToLarger(dblInput As Double, intDecimals As Integer) As Double

'Implements a variant of the Round() function, that rounds-to-larger
'rather than rounds-to-even, like in Excel:

Dim strFormatString As String 'Format string
Dim strResult As String

'If input is zero, just return zero. Else format as appropriate:
If dblInput <> 0 Then
strFormatString = "#." & String(intDecimals, "#")
If Right(strFormatString, 1) = "." Then
strResult = Format(dblInput, "#")
Else
strResult = Format(dblInput, strFormatString)
End If

Else
strResult = "0"
End If

'If the result is zero, Format() will return "." - change this to "0":
If strResult = "." Then
strResult = "0"
End If

RoundToLarger = CDbl(strResult)

End Function


Acknowledgement: Thanks to “hnawebmaster” for help in compiling this FAQ.


Dave Mc Donald
www.mcdonaldit.com
End of text from site....................................

31/1/2005. I think there is an error in this function if the result is zero. The lines:

'If the result is zero, Format() will return "." - change this to "0":
If strResult = "." Or strResult = "" Then

need changing as shown above in red, or the run time error 13 'Type Mismatch' may occur.

I have also had a go at my own function, but it's dependant on the maximum values allowed by the data types, so will not work for numbers with more than 15 digits in total. The code is shown below, for those who are interested.

Public Function myRound(prmNumber As Double, prmPlaces As Integer) As Double
'own function to round 5-9 up and 0-4 down
'VBA Round function rounds-to-even for .5.
'IMPORTANT - if prmNumber has more than 15 digits (regardless of the where the dec. pt is)
' then the Double datatype would appear to truncate the least significant digits
'try testing with ?myround(12345678901.12345,2) and look at the value passed in prmNumber

Dim dblIntegerPart As Double 'to store the required digits
Dim dblDecimalPart As Double 'to store the surplus decimal places
Dim dblTemp As Double 'for intermediate calc only

'move the decimal point by the required number of digits
'example, 4.45 required to 1 decimal place gives 45.5
dblTemp = prmNumber * (10 ^ prmPlaces) 'note: 10^0 = 1

'truncate the value to get rid of the unwanted decimal places
dblIntegerPart = Fix(dblTemp)
'subtract truncated value from temporary number to find out what the unwanted digits are
dblDecimalPart = dblTemp - dblIntegerPart

'are the unwanted digits +0.5 to +0.99999etc or -0.5 to -0.99999?
If Abs(dblDecimalPart) >= 0.5 Then
If prmNumber < 0 Then
dblIntegerPart = dblIntegerPart - 1 'subtract 1 for -ve number
Else
dblIntegerPart = dblIntegerPart + 1 'add 1 for +ve number
End If
End If

'put decimal point back in original position and return the rounded value
myRound = dblIntegerPart / (10 ^ prmPlaces)

End Function

Back to Top

 

14. How can I add a criterion to a Crosstab query?

See also Access FAQ 17 What is a CROSSTAB query?.

NOTE added 12/4/2005 - this item also applies to 3-D charts. If you look at the SQL generated by the Chart Wizard you will see that it is a TRANSFORM query. If you wanted to add a criterion, then see the information below.

Simple literal criterion.

To add a criterion with a literal value specified in the query is fairly simple. Add the column to the query, set the grouping to ‘Where’ and add the value in the criteria cell. The SQL shown in Access FAQ 17 with a criterion of [Category No] = 1 will then be (Where clause shown in bold):

TRANSFORM Count(Membership.[Category No]) AS [CountOfCategory No]

SELECT Year([Date of Birth]) AS [Year of Birth]

FROM [Membership Category] INNER JOIN Membership ON [Membership Category].[Category No] = Membership.[Category No]

WHERE (((Membership.[Category No])=1))

GROUP BY Year([Date of Birth])

PIVOT [Membership Category].[Category Type];

Parameter criterion.

However, if you used a criterion that used a parameter such as [Please enter Category No] or a Forms Collection reference such as forms!frmcategory![Category No], your query would fail with a message similar to:

The Microsoft Jet database engine does not recognise “[Please enter Category No]” as a valid field name or expression.

or

The Microsoft Jet database engine does not recognise “forms!frmcategory![Category No]” as a valid field name or expression.

A way round this is to put the criteria value(s) in a table and use IN for the criterion expression.

Example (using the query from Access FAQ 17 as above):

1. Create a new table called TempCat, with a numeric field called Category No.

2. Create a form with an unbound textbox called txtCategoryNo.

2.1 In the txtCategoryNo_AfterUpdate event code:
DoCmd.RunSQL "DELETE * FROM TempCat"
DoCmd.RunSQL "INSERT INTO TempCat VALUES (" & txtCategoryNo & ")"
This will delete any previous rows from the table, and add the new value from the textbox to the table. See Part 6 of the
Getting Started VBA Trainer for information about the DoCmd.RunSQL statement (which includes information about suppressing the various information messages that you will get when deleting/inserting table rows)..

3. Alter the Crosstab query criterion to IN (SELECT [Category No] from TempCat), so that the SQL now looks like:

TRANSFORM Count(Membership.[Category No]) AS [CountOfCategory No]

SELECT Year([Date of Birth]) AS [Year of Birth]

FROM [Membership Category] INNER JOIN Membership ON [Membership Category].[Category No] = Membership.[Category No]

WHERE (((Membership.[Category No]) IN (SELECT [Category No] from TempCat)))

GROUP BY Year([Date of Birth]), Membership.[Category No]

PIVOT [Membership Category].[Category Type];

The query will now pick up the required parameter value, which can be varied by changing the value in the textbox.

Tables used to store parameter values do not have to have just one row (though the SELECT SQL in the IN clause must select only one column). The SQL statements can generate several rows in a table. For example, if you wish to select values between a pair of dates, and if the date parameters on a form are called txtStartDate and txtEndDate, then the code to generate rows in a table called TempDate which has just one (date datatype) column could be as shown below. Note that dates in SQL statements must be in USA format!

Dim dtDate As Date

Dim strDate As String

'delete previous rows from table

DoCmd.RunSQL "DELETE * FROM TempDate"

dtDate = CDate(txtStartDate) 'set to start date on form

Do Until dtDate > CDate(txtEndDate) 'loop until end date has been passed

'dates in SQL statements must be in USA format

'so must reverse day and month before putting in SQL statement

strDate = Month(dtDate) & "/" & Day(dtDate) & "/" & Year(dtDate)

'write date to temp table

DoCmd.RunSQL "INSERT INTO TempDate VALUES (#" & strDate & "#)"

dtDate = DateAdd("d", 1, dtDate) 'add one day

Loop

Access Help.

Look up “Create a parameter query” in Access Help.

Additional note - 1st March 2005

A possible public function to do this date-reversal (and to be used wherever needed) is:

Public Function myUSADate(prmUKDate As Date) As Date

'Convert date from dd/mm/yyyy (UK) format to mm/dd/yyyy (USA) format

Dim strDate As String

strDate = Month(prmUKDate) & "/" & Day(prmUKDate) & "/" & Year(prmUKDate)

myUSADate = CDate(strDate)

End Function

And it would be used in an embedded SQL statement as follows:

DoCmd.RunSQL "INSERT INTO TempDate VALUES (#" & myUSADate(dtDate) & "#)"

Use the Debugger Immediate Window to test the function, and specify dates as dd mmm yyyy to avoid confusion, as Access also dates dates specified as dd/mm/yy in this window to be USA format. For example:

?myUSADate(#1 mar 2005#)

Further note - 6th April 2006

If you create a wizard combo box to find records using a date field, then Access generates the following code to convert the dates to USA format:

"#" & Format(Me![cboFindDate], "mm\/dd\/yyyy") & "#"

(where cboFindDate is the name of the combo box)

Back to Top

 

15. Why does a calculated date give the wrong result in an SQL statement?

Access 2002 Help says:

“You must use English (United States) date formats in SQL statements in Visual Basic. However, you can use international date formats in the query design grid.”

(Search with ‘dates in SQL’)

If you have a form with a date field called txtStartdate, a variable called dtDate of Date datatype, and a table called TempDate with just one column for a date, then the following statements will all insert the correct date in the destination table:

dtDate = txtStartDate

DoCmd.RunSQL "INSERT INTO TempDate VALUES (#" & dtDate & "#)" ‘from variable

DoCmd.RunSQL "INSERT INTO TempDate VALUES (#" & txtStartDate & "#)" ‘from form

DoCmd.RunSQL "INSERT INTO TempDate VALUES (#" & Date & "#)" ‘today’s date

However, if you want to do a calculation on a date (calculate a date a library book is due back, for example) statements such as

dtDate = DateAdd("ww", 2, Date) ‘add 2 weeks to today

'dtDate = DateAdd("d", 14, Date) ‘alternative method

DoCmd.RunSQL "INSERT INTO TempDate VALUES (#" & dtDate & "#)"

will treat the date returned from the DateAdd statement as being in mm/dd/yyyy format (I.e. USA format). So you will need to reverse the day and month elements of the date, as demonstrated in VBA FAQ 14 above. Putting the date in a textbox on a form and then using the value from that textbox does not work, even though the textbox displays in correct UK format!

If the day number value of the date is greater than 12 (20/2/2005 for example), Access seems to realise that the date is in UK format and will make adjustments itself (20th February 2005). But if not (3/5/2005 for example), it will assume USA format (5th March 2005 rather than 3rd May 2005).

Back to Top

 

16. Why don't the Date and Time functions work in my module?

Some students have experienced problems where a call in VBA code to the Date() or Time() function returns Null or an invalid/incorrect value (and this can cause a run-time failure 'invalid use of Null' in some cases).

The problem seems to occur in a form or report module where there is a table, report or form field called Date or Time. If you code something like:

Dim dtDate as Date

dtDate = Date() 'get system date

then Access appears to refer to the field called Date, not to its own function. (It would be handy if this situation gave rise to a compilation error or a specific run-time error, then at least you know what was going on).

Solution 1.

Don't use the names Date and Time for table, form or report fields.

(The Chelmer Leisure database from McBride does use these names, unfortunately).

Solution 2.

Create your own functions in a separate Access module. As the module is not bound to a table or query, there is no conflict between names, and Access will use its own functions correctly. Code the following two procedures:

Public Function myDate() As Date
myDate = Date
End Function
'-----------------------------------------------------------
Public Function myTime() As Date
myTime = Time()
End Function

Then code as follows to get the system date and time:

Dim dtDate as Date

Dim dtTime as Date

dtDate = myDate() 'get system date

dtTime = myTime() 'get system time

Back to Top

 

17. Why doesn't my 'On Error' code trap the error?

Check that you are using the same version of MS Access as MS Office.

For example, if you are using MS Access 2003 with MS Office 2002, then the 'On Error' code does not appear to be invoked; any error condition is intercepted by Access with the usual Access error message and the code will fail.

However, if you use an Access 2002 MDE file created with Access 2002, and then run it on a machine that has Access 2003 with Office 2002, all appears to work OK.

Back to Top

 

18. How can I get totals on a form?

You may have something like an Order form with a separate (or sub) form for each Order Line, and want to show the order totals (from counting/adding information from the order lines) on the Order form.

Look at the 'Crash Course' in Access Basics document.

The relevant bit is section 5.5, Exercise Frm4, 'put totals on a form', part (b). The example here counts up a total number of attendees at a class (using the Chelmer Leisure scenario), using DCount. If you want to add up order totals (i.e. total cost of the order) you will need to use DSum to sum up the appropriate value.

Look also at the VBA Trainers and the Example Databases for examples of use of these functions.

Back to Top

 

19. How can I copy details from one form to another?

This is very easily done using the Forms Collection.

There are examples of use in sections 4.3.8 and 8.4.6.2 of the 'Getting Started' VBA Trainer (available from the Student Advice Centre) and Appendix I discusses this Collection briefly.

Suppose (for example) you have a customer form which opens an Order form, and you want to copy customer details to the Order form automatically. You would code the assignment statements in the Form_Load event for the Order form.

Look also at the Example Databases for examples of use of the Forms Collection.

 

20. What does “Run-time error ‘3061’: Too few parameters. Expected 1” mean?

I have had this error when using the OpenRecordset method (DAO code) when I have misspelled a table field name in the SQL. After experimenting a bit, I found that it also occurs with some SQL syntax errors.

If you misspell the table name with SQL in DAO code then you may get run-time error 3078 instead; this has a very clear error message explaining the likely cause of the error.

 

 

 

Creating a Windows Forms Application

Developers will create Windows Forms applications much like they do Visual Basic-based forms today (although with Windows Forms, they get the same level of productivity in all of the languages in Visual Studio, not just Visual Basic). In the following image, you can see that a design surface is used to visually lay out the form with rich controls. To edit source code, developers simply double-click a control and the source editor will appear, enabling quick access to the full event model for the control and form.

Building Windows Forms

Figure 1. Building Windows Forms

Visual Inheritance

Visual inheritance is one of the key new features available in Windows Forms that will enhance developer productivity and facilitate code reuse. For example, an organization could define a standard base form that contains items such as the corporate logo and perhaps a common toolbar. This form can be used by developers through inheritance and extended to meet the requirements of specific applications while promoting a common user interface across the organization. The creator of the base form can specify which elements can be extended and which must be used as is, ensuring that the form is reused appropriately

Precision Form Design

Developers will have an unprecedented level of control and productivity when designing the look and feel of their Windows Forms applications. Features such as the In-Place Menu Editor, Control Anchoring, Control Docking, and many new controls enable a higher level of power and precision for developers building rich Windows-based user interfaces.

With the In-Place Menu Editor, developers can quickly and easily add menus to a form, modify them, and view how they look without having to run the application. Controls on the form are more effective with Control Anchoring, enabling a form to automatically resize controls as a user resizes the form. With Control Docking, controls can be docked on any side of a form, providing greater flexibility in layout.

In-place Menu Editor

Figure 2. In-place Menu Editor

Existing ActiveX® controls can be leveraged and run on any form as well, preserving investments in existing technologies.

New controls-including Link Label, Tray Icon, and Print Preview-provide additional common functionality for developers. Link Label provides HTML-like linking to a specified URL. Text displayed using this control will appear underlined and the cursor will change to a hand as the mouse moves over it, firing an actionable event when clicked. Tray Icon enables developers to create applications that run in the Windows tray similar to the Microsoft SQL Server™ Service Manager. Windows Forms offers a printing framework that makes printing simple, including a Print Preview window with the Print Preview control.

Developers can build applications that support the broadest audience of users with Windows Forms. Windows Forms controls implement Microsoft Active Accessibility® programming interfaces, making it straightforward to build applications that support accessibility aids such as screen readers.

VB6/VB.NET Resource files III

Custom Resources

The "Big Deal" with custom resources is that you normally have to provide a way to process them in your program code. As Microsoft states it, "This usually requires the use of Windows API calls." That's what we'll do.

The example we'll use is a fast way to load an array with a series of constant values. Remember that the resource file is included into your project, so if the values that you need to load change, you'll have to use a more traditional approach such as a sequential file that you open and read. The Windows API we'll use is the CopyMemory API. CopyMemory copies block of memory to a different block of memory without regard to the data type that is stored there. This technique is well known to VB 6'ers as an ultra fast way to copy data inside a program.

This program is a bit more involved because first we have to create the a resource file containing a series of long values.

I simply assigned values to an array:

Dim longs(10) As Long
longs(1) = 123456
longs(2) = 654321

... and so forth.

Then the values can be written to a file called MyLongs.longs using the VB 6 "Put" statement.

Dim hFile As Long
hFile = FreeFile()
Open _
   "C:\your file path\MyLongs.longs" _
   For Binary As #hFile
Put #hFile, , longs
Close #hFile

It's a good idea to remember that the resource file doesn't change unless you delete the old one and add a new one. So, using this technique, you would have to update the program to change the values. To include the file MyLongs.longs into your program as a resource, add it to a resource file using the same steps described above, but click the Add Custom Resource ... instead of Add Icon ... Then select the MyLongs.longs file as the file to add. You also have to change the "Type" of the resource by right clicking that resource, selecting "Properties", and changing the Type to "longs". Note that this is the file type of your MyLongs.longs file.

To use the resource file you have created to create a new array, first declare the Win32 CopyMemory API call:

Private Declare Sub CopyMemory _
   Lib "kernel32" Alias _
   "RtlMoveMemory" (Destination As Any, _
   Source As Any, ByVal Length As Long)

Then read the resource file:

Dim bytes() As Byte
bytes = LoadResData(101, "longs")

Next, move the data from the bytes array to an array of long values. Allocate an array for the longs values using the integer value of the length of the string of bytes divided by 4 (that is, 4 bytes per long):

ReDim longs(1 To (UBound(bytes)) \ 4) As Long
CopyMemory longs(1), bytes(0), UBound(bytes) - 1

Now ... this may seem like a whole lot of trouble when you could just initialize the array in the Form Load event, but it does demonstrate how to use a custom resource. If you had a large set of constants that you needed to initialize the array with, it would run faster than any other method I can think of and you wouldn't have to have a separate file included with your application to do it.

VB6/VB.NET Resource files II

VB6 supports

  • A string table editor
    ("Edit String Tables...")
  • Custom cursors - "CUR" files
    ("Add Cursor...")
  • Custom icons - "ICO" files
    ("Add Icon...")
  • Custom bitmaps - "BMP" files
    ("Add Bitmap...")
  • Programmer defined resources
    ("Add Custom Resource...")

VB 6 provides a simple editor for strings but you have to have a file created in another tool for all of the other choices. For example, you could create a BMP file using the simple Windows Paint program.

Each resource in the resource file is identified to VB 6 by an Id and a name in the Resource Editor. To make a resource available to your program, you add them in the Resource Editor and then use the Id and the resource "Type" to point to them in your program.

Let's add four icons to the resource file and use them in the program.

When you add a resource, the actual file itself is copied into your project. Visual Studio 6 provides a whole collection of icons in the folder ...

C:\Program Files\Microsoft Visual Studio\Common\Graphics\Icons

To go with tradition, we'll select the Greek philosopher Aristotle's four "elements" - Earth, Water, Air, and Fire - from the Elements subdirectory. When you add them, the Id is assigned by Visual Studio (101, 102, 103, and 104) automatically.

To use the icons in a program, we use a VB 6 "Load Resource" function. There are several of these functions to choose from:

  • LoadResPicture(index, format) for bitmaps, icons, and cursors

Use the VB predefined constants vbResBitmap for bitmaps, vbResIcon for icons, and vbResCursor for cursors for the "format" parameter. This function returns a picture that you can use directly. LoadResData (explained below) returns a string containing the actual bits in the file. We'll see how to use that after we demonstrate icons.

  • LoadResString(index) for strings
  • LoadResData(index, format) for anything up to 64K

As noted earlier, this function returns a string with the actual bits in the resource. These are the values that can be used for format parameter here:

    1 Cursor resource
    2 Bitmap resource
    3 Icon resource
    4 Menu resource
    5 Dialog box
    6 String resource
    7 Font directory resource
    8 Font resource
    9 Accelerator table
    10 User-defined resource
    12 Group cursor
    14 Group icon

Since we have four icons in our AboutVB.RES resource file, let's use LoadResPicture(index, format) to assign these to the Picture property of a CommandButton in VB 6.

I created an application with four OptionButton components labeled Earth, Water, Air and Fire and four Click events - one for each option. Then I added a CommandButton and changed the Style property to "1 – Graphical". This is necessary to be able to add a custom icon to the CommandButton. The code for each OptionButton (and the Form Load event -- to initialize it) looks like this (with the Id and Caption changed accordingly for the other OptionButton Click events):

Private Sub Option1_Click()
   Command1.Picture = _
      LoadResPicture(101, vbResIcon)
   Command1.Caption = _
      "Earth"
End Sub

 

VB6/VB.NET Resource files I

After Visual Basic students learn all about loops and conditional statements and subroutines and so forth, one of the next things that they often ask about is, "How do I add a bitmap, a wav file, a custom cursor or some other special effect?" One answer is resource files. When you add a file using Visual Studio resource files, they're integrated directly into your Visual Basic project for maximum execution speed and minimum hassle packaging and deploying your application.

Resource files are available in both VB 6 and VB.NET, but the way they're used, like everything else, is quite a bit different between the two systems. This article is part one of a two part series about resource files. This first article is about how to use them in VB 6 and the next article covers VB.NET.

Keep in mind that this isn't the only way to use files in a VB project, but it has real advantages.

For example, you could include a bitmap in a PictureBox control or use the mciSendString Win32 API. "MCI" is a prefix that usually indicates a Multimedia Command String. (For more information about MCI API calls, consult this page at Microsoft.)

Creating a Resource File in VB 6

You can see the resources in a project in both VB 6 and VB.NET in the Project Explorer window (Solution Explorer in VB.NET - they had to make it just a little bit different). A new project won't have any since resources aren't a default tool in VB 6. So let's add a simple resource to a project and see how that is done.

Step one is to start VB 6 by selecting a Standard EXE project on the New tab in the startup dialog. Now select the Add-Ins option on the menu bar, and then the Add-In Manager.... This will open the Add-In Manager dialog window.

Scroll down the list and find VB 6 Resource Editor. You can just double-click it or you can put a check mark in the Loaded/Unloaded box to add this tool to your VB 6 environment. If you think you're going to use the Resource Editor a lot, then you can also place a check mark in the box Load on Startup and you won't have to go through this step again in the future. Click "OK" and the Resources Editor pops open. You're ready to start adding resources to your project!

Go to the menu bar and select Project then Add New Resource File or just right-click in the Resource Editor and select "Open" from the context menu that pops up. A window will open, prompting you for the name and location of a resource file. The default location will probably not be what you want, so navigate to your project folder and enter the name of your new resource file into the File name box. In this article, I'll use the name "AboutVB.RES" for this file. You'll have to confirm the creation of the file in a verification window, and the a "AboutVB.RES" file will be created and filled into the Resource Editor.

VB.NET Resources - Faster, Smarter, Stronger
Demonstrated how to use resource files in VB 6. Resource files let you add a bitmap, a wav file, a custom cursor or some other special effect to your Visual Basic project.

Microsoft defines them this way,

"A resource is any nonexecutable data that is logically deployed with an application."

The main advantage of resource files is that they're integrated directly into your Visual Basic project for maximum execution speed and minimum hassle packaging and deploying your application.

Although VB 6 has a complete set of tools to add resources, only the main resources: strings, icons, bitmaps, and cursors, have built in processing in Visual Basic 6 and only strings can be created with a built in editor in VB 6.

You can include any digital resource in your VB 6 project, but you have to provide your own processing code - typically with a Win32 API call of some sort.

With VB.NET, your options are expanded! The resource types that are supported in the Resource Editor are:

  • Strings
  • Images (PNG, BMP, GIF, JPEG, and TIFF are supported!)
  • Icons
  • Audio
  • Files
  • Other

Another area where .NET provides better support for resources is in globalization. This was always one reason for using resources, but Microsoft has heard clearly heard the jingling clink of Rupees, Euros, Yen, and Kroner in .NET.

In addition to giving you the ability to include resources into your main assembly, .NET also lets you package resources into satellite assemblies for even more flexibility. This is the key for effective globalization because you can include just those satellite assemblies that a particular market somewhere in the world needs.

You don't have to "add-in" a special tool to use resources in VB.NET. Resources are one of the properties of the project so all you have to do is display the Properties tabbed dialog for a solution and then select the Resources tab.

Since resources are a property of the solution in VB.NET, you access then just like other properties. By name, using the My.Resources object. To make this more clear, let's build the same application we used for VB.6 and display icons for Aristotle's "four elements".

To add the elements icons, select Properties for a Solution in Solution Explorer, or select the SolutionName Properties from the Project Menu. Then select the Resources tab. You can add icons to this file by either choosing Add Existing File ... from the Add Resources drop down menu, or just drag and drop from a Windows Explorer window.

The code to use the icon resources is very similar to the VB 6 code, but with ALL of the names changed, of course. Here's what the new code looks like:

Private Sub RadioButton1_CheckedChanged( ...
   Handles MyBase.Load
   Button1.Image = My.Resources.EARTH.ToBitmap
   Button1.Text = "Earth"
End Sub

To download the complete sample application in VB.NET,Click Here. Note that this application was created using VB.NET 2005 and Framework 2.0.

This works great when you're using resource files in about the same way that they were used in VB 6, but if you want to take advantage of some of the advanced features, then the resource files have to be converted from .resx files, where they're stored in XML format. For example, here's a snippet from the .resx file we just created:


      type="System.Resources.ResXFileRef,
   System.Windows.Forms">
      ..\Resources\CLOUD.ICO;System.Drawing.Icon,
      System.Drawing, Version=2.0.0.0,
      Culture=neutral,
      PublicKeyToken=b03f5f7f11d50a3a

   

Text based XML doesn't work too well in binary files. To convert .resx files (and also .txt files) into common language runtine binary .resources files, use the Resource File Generator (resgen.exe) to convert .resx (or .txt files) into a format that can be embedded in a runtime binary executable or compiled into satellite assemblies.

And DOS will never die! You have to run resgen.exe from a Command Prompt line.

Excel Automation Samples

Automate Excel Report generation, populate DataGrids and perform high performance Excel

compatible calculations without Excel.


Excel Reporting
Easily create richly formatted Excel reports without Excel
Excel Reporting

With the wide adoption of Microsoft Excel in so many business settings, there is no

substitute for taking advantage of what spreadsheets have to offer.

With SpreadsheetGear for .NET, your Windows and ASP.NET applications instantly

 gain the ability to create, read, modify, calculate and write Excel workbooks.

With our extensive API, you can quickly deliver richly formatted interactive Excel reports

 which your users already know how to use.


Excel to DataGrid
Quickly populate DataGrids from Excel workbooks without Excel
Excel to DataGrid

Excel is an excellent source of data for the Windows Forms DataGrid

 and ASP.NET DataGrid controls included in Microsoft .NET, as well as the

many third party controls which accept data from DataSets and DataTables.

With our spreadsheet component and one line of Microsoft Visual C# or

Microsoft Visual Basic .NET code, you can quickly read an Excel workbook

and return any range or set of ranges as a DataTable or DataSet.


Excel Calculations
Fastest and most complete Excel compatible calculations without Excel
Excel Calculation

The true power of SpreadsheetGear for .NET is realized when using its superior

calculation engine. Unmatched performance and accuracy deliver a compelling

advantage to data critical client and web applications. There is no need to

rely on Excel to calculate spreadsheet data. Simply load or create a workbook, change

 and add values and formulas, and calculate results with the fastest and most

complete calculation engine available in a spreadsheet component.


Excel Charting
Enable users to visualize data by adding Excel charts to reports
Excel Charting

Richly formatted workbooks with fast and complete calculations are the heart and soul

 of a spreadsheet, but the ability to make good decisions is greatly enhanced by

the ability to visualize data. Enhance your users understanding of their data by

taking advantage of SpreadsheetGear for .NET's ability to read and write Excel charts

and modify the data which these charts refer to.

When a new version of your application is available

Users get notified as they run the older version. 

Now, when you investigate the files in the bin\release folder, you should find a copy of VBDemo.dll.

 It's this copy that your application will use, rather than the globally registered copy. At this point,

remove the net—that is, unregister VBDemo again. Run the application, and amazingly, it continues

 to work. This time, the application displays the current path, because the component is running

from within the current path. Because Visual Studio has copied the DLL locally, and because

of Reg-Free COM support, your application can use a local copy instead of the globally

registered copy.

Obviously, getting Reg-Free COM to work requires more effort than simply copying the COM

component locally. If you examine the files within the output folder, you'll also find

RegFreeCOMDemo.exe.manifest. If you open this file with a text editor, you'll see that the manifest

 file contains information about the COM component, and it's this file that makes Reg-Free COM work.

If you copy RegFreeCOMDemo.exe along the manifest file and the COM component to a new

computer running Windows XP, the application would continue to run. Combined with ClickOnce

deployment, it's easy to see how Reg-Free COM makes developers lives simpler—no more hassles

with registering COM components and no more DLL hell.

Reg-Free COM does have some limitations, as you might imagine. It requires Windows XP or later

 in order to run—if you attempt to deploy your application to an earlier version of Windows, you'll

see an error message indicating that you need a later version of Windows. Reg-Free COM is perfect

for deploying ActiveX controls, ActiveX DLLs, and other business objects, but it won't really work

 for all components. It only works for in-process components, so ActiveX EXEs are out.

It won't work for COM components that are part of the operating system. Reg-Free COM

also won't work for COM components that require a larger application—attempting to use

Reg-Free COM with the Microsoft Word or Microsoft Excel type libraries, for example,

wont' work, because these type libraries require a full copy of Word or Excel to be installed

on the target machine. For a large percentage of COM components that you'll want to deploy

with your applications, however, Reg-Free COM can make your life a lot easier. For more

information on Reg-Free COM, read

Simplify App Deployment with ClickOnce and Registration-Free COM.

Language Innovations

Continuing a tradition of balancing power and flexibility with ease of use, Visual Basic 2005 adds

a number of new features that keep Visual Basic in sync with the .NET Framework and make it the

premier language for business application development. The following sections outline some

of the new language features in Visual Basic 2005.

Operator Overloading

The .NET Framework has historically supported overloaded operators, and Visual Basic adds

 support for this feature in the 2005 release. This feature allows developers to consume operators

whose functionality has been overloaded by a particular class (a feature that wasn't available in previous

releases) and it also allows developers to overload the functionality for the standard language operators,

such as +, -, *, /, and so on.

You have most likely taken overloaded operator behavior for granted in classes where it's

already supported. Although it may not be common practice to write code like this, the code works

because of the overloaded + operator:

Dim inputString as String = "Hello, "
Dim outputString As String = inputString + " World!"

The + operator is generally only defined for numeric values, but the String class overloaded

the behavior of the operator so that it concatenates its two operands.

The operator overloading feature in Visual Basic 2005 consists of two distinct parts: consumption

 and declaration. Consumption is easy to demonstrate; take the Point structure, for example. You often

need to take an existing Point instance and add a Size structure to it, effectively offsetting the

Point by the values stored in the Size structure. The Point class includes code to handle addition

 and subtraction operations, using the standard + and – operators. That is, the following code

does just what you might expect:

Dim p1 As New Point(100, 100)
Dim s1 As New Size(20, 30)

Dim p2 As Point = p1 + s1
Dim p3 As Point = p1 – s1

After this code executes, p2 contains {120, 130} and p3 contains {80, 70}, as you might expect.

 At compile time, the compiler locates the correct special function, defined to handle addition or

subtraction of Point and Size structures, and turns the addition and subtraction operators into function

 calls to the special functions. The following code, required in previous versions of Visual Basic,

demonstrates how you might add Point and Size values without support for operator overloading:

Dim p2 As point = Point.op_Addition(p1, s1)

The developers who created the code for the Point structure provided the op_Addition function

(and the corresponding op_Subtraction, op_Equality, and so on). If these functions didn't exist,

the code wouldn't have compiled because the compiler must convert from the overloaded

operators into the appropriate function call at compile time. An expression like "p1 + s1" is,

in reality, simply a function call to the appropriate addition function, with two arguments, like this:

Point.op_Addition(p1, s1)

Because the addition function is simply a procedure, the compiler can determine which overloaded

version to use, based on the parameters. Because of the new operator overloading functionality

built into Visual Basic 2005, you can perform operations such as those shown here using standard

 mathematical operators on the Point and Size structures. (In previous versions of Visual Basic,

you were required to call the op_Addition method explicitly, for example, to add a Point and

 a Size structure.)

Although it's not likely a feature you'll need often, Visual Basic 2005 also adds support for

creating your own overloaded operators. The Visual Basic compiler allows you to overload

many operators, as shown in the following table:

+ + (unary)
- - (unary)
* \
/ ^
& Like
Mode And
Or Xor
Not <<
>> = (comparison)
<> <
<= >
>= CType
IsTrue IsFalse

Generics

Before looking at the concept of generics and how they can help you write better, more efficient,

and maintainable Visual Basic code, examine a common scenario. Let's say that you've created

a class as part of an application, and you'd like to create a collection containing instances of that class.

 For this example, you have created a simple class, named Customer, containing the following code:

Class Customer
    Public Name As String
    Public Age As Integer

    Public Sub New(ByVal Name As String, ByVal Age As String)
        Me.Name = Name
        Me.Age = Age
    End Sub
End Class

If you want to create a data structure that contains a collection of Customer objects, you could use the System.Collections.ArrayList class and could write code like this:

Dim customerList As New System.Collections.ArrayList
customerList.Add(New Customer("Tom", 48))
customerList.Add(New Customer("Peter", 45))
customerList.Add(New Customer("Jerry", 73))
customerList.Add("Hello, this won't work!")

For Each cust As Customer In customerList
    Console.WriteLine(cust.Name)
Next

Note the inconsistency in the data. Although the ArrayList class allows you to insert Customer

objects, it also is just as obliging when it comes to String, Integer, or any other object type.

The simplicity of the ArrayList class leads to its downfall—there's no way to easily restrict the type

 of data you add to it. Exposing a public ArrayList as a property of a class is a recipe for disaster,

 if you care about data integrity.

Creating your own collection class that derives from the System.Collections.CollectionBase

class provides a type-safe alternative. For example, the following class allows only Customer

objects to be added to its collection:

Class CustomerList
    Inherits CollectionBase

    Public Sub Add(ByVal cust As Customer)
        Me.List.Add(cust)
    End Sub
End Class

Given the CustomerList class, it would be impossible to add any object to your collection that

wasn't a Customer object. This solution is fine, as long as you only work with Customer objects.

 What happens when you also need a strongly typed collection class for working with Invoices?

 Or later, when you need a collection for Products, Orders, and so on? Creating and maintaining individual collection classes for each type could become a maintenance nightmare. Therein lies the motivation for the new language feature: generics.

Generics allow classes, structs, interfaces, and methods to be parameterized by the types of data

they store and manipulate. Generics are useful because many common classes and structures can be

parameterized in this way. These are called generic declarations. The .NET Framework provides

a group of generic collection classes as part of its System.Collections.Generic namespace.

For example, the System.Collections.Generic namespace provides a generic List class. To consume

the List class, you must supply the type for the items within the collection at the time you declare the

 instance of the list, like this (assuming that you've added an Imports statement for the System.Collections.Generic namespace to your code file):

Dim customerList As New List(Of Customer)

As you can probably guess, the "Of" keyword allows you to specify the type associated with

elements of the generic collection. To use the generic List class, you could write code like the following:

Dim customerList As New List(Of Customer)
customerList.Add(New Customer("Tom", 48))
customerList.Add(New Customer("Ken", 47))
customerList.Add(New Customer("Peter", 45))
customerList.Add(New Customer("Jerry", 73))

For Each cust As Customer In customerList
    Console.WriteLine(cust.Name)
Next

Of course, once you've created an instance of the List class with a Customer type as its type

argument (or any other type, for that matter), you are limited to storing only Customer objects

(or objects of a class derived from Customer). Generics provide strong typing, meaning you can no longer improperly store any other type into the list. Take, for example, this code:

Dim customerList As New List(Of Customer)
customerList.Add(New Customer("Tom", 48))
customerList.Add("Hello there!")
Dim c As Customer = customerList(0)

Notice two important things:

  • Unless you remove the third line, the code won't compile. The compiler checks to verify
  •  that you're using the object created by the generic class definition correctly.
  • In the final line, there's no need to cast the value returned from the List object. Each element
  •  contained within the List instance is always of the type specified when you declare the instance of the List class.

Besides the List class, the System.Collections.Generic namespace also provides generic versions of the Dictionary, SortedDictionary, LinkedList, Queue, and Stack classes. Because the .NET Framework

 includes so many generic collection classes, it's unlikely that you'll need to create your own generic class.

 If you find a need to create your own, however, Visual Basic 2005 makes it possible.

For example, if you needed to create a class that contained a one-dimensional array of a single

data type (often called a vector), you might create a class like the following:

Public Class TypedVector(Of T)
    ' Create a one-dimensional array of 
    ' the specified type.
    Private arrayValue() As T

    Public Sub New(ByVal Size As Integer)
        ReDim arrayValue(Size - 1)
    End Sub

    Default Public Property Item(ByVal Index As Integer) As T
        Get
            Return arrayValue(Index)
        End Get
        Set(ByVal Value As T)
            arrayValue(Index) = Value
        End Set
    End Property
End Class

When you use the generic class declaration TypedVector, as in the example that follows, you can

specify the actual type to be used by the generic class. In this case, the code instructs the TypedVector

class to use the Integer type by specifying it as a type argument with the "(Of Integer)" syntax after

the class name. This example creates an array containing five integers:

Dim myVector As New TypedVector(Of Integer)(4)
myVector(0) = 73
myVector(1) = 27
' Fill the rest of the items...

To create a TypedVector instance that could contain only instances of some specific business type,

 perhaps Customer objects, you could use code like the following:

Dim myVector As New TypedVector(Of Customer)(11)
myVector(0) = someCustomerObject

It's clear that you won't need generics for every application you write. When you do need to create a

strongly typed collection, there's no better solution than to take advantage of either the built-in classes from the System.Collections.Generic namespace or to create your own generic collection class.

IsNot Keyword

Although it hasn't been a terrible burden in the past, determining whether an object reference is not the

same as another object reference has historically involved a somewhat clumsy construct:

If Not (obj Is Nothing) Then

Visual Basic 2005 adds the IsNot keyword, making it possible to rewrite the previous example:

If obj IsNot Nothing Then

Sometimes, it's the little things that make a big difference.

Using Keyword

Many classes in the .NET Framework provide a Dispose method as a standard mechanism for

releasing resources used by the object. In general, it's a good rule of thumb to call the Dispose method

of an object you're using when you're done with the object. When you're working with managed

wrappers around unmanaged or limited resources, as is the case when you work with a database

connection or a Windows font or brush, you should always explicitly call the Dispose method if the

object provides it, in order to release the unmanaged resource. Imagine a form that draws text in

a specific font and brush in its Paint event handler. In previous versions of Visual Basic, you were required

to write code something like the following:

Private Sub Form1_Paint( _
 ByVal sender As Object, ByVal e As PaintEventArgs) _
 Handles MyBase.Paint

    Dim myFont As Font
    Dim myBrush As Brush
    Try
        myFont = New Font("Verdana", 12)
        myBrush = New SolidBrush(Color.FromArgb(123, 0, 123))
        e.Graphics.DrawString( _
         "Hello, World!", myFont, myBrush, 0, 0)

    Finally
        If myFont IsNot Nothing Then
            myFont.Dispose()
        End If

        If myBrush IsNot Nothing Then
            myBrush.Dispose()
        End If
    End Try
End Sub

Visual Basic 2005 adds the Using keyword, which creates a block of code that disposes of an object

for you at the end of the block, no matter how the block is exited. The previous code example can be

rewritten more efficiently, as in the following example:

Private Sub Form1_Paint( _
 ByVal sender As Object, ByVal e As PaintEventArgs) _
 Handles MyBase.Paint
    ' Create a new font.
    Using myFont As New Font("Verdana", 12)
        ' Create a new solid brush.
        Using myBrush As New SolidBrush( _
         Color.FromArgb(123, 0, 123))
            e.Graphics.DrawString( _
             "Hello, World!", myFont, myBrush, 0, 0)
        End Using
    End Using
End Sub

Unsigned Integer Types

Historically, Visual Basic has supported unsigned byte and signed integer types, but it hasn't supported

signed bytes or unsigned integers. In Visual Basic .NET 2002 and 2003, developers could create and

 use the .NET Framework's unsigned types, but they couldn't perform mathematical operations on

 these values. Visual Basic 2005 adds full support for these types, as well as the necessary

corresponding conversion methods. The following table lists the four new intrinsic types

(SByte has no type character because Byte has no type character):

VB Keyword Conversion Operator

Type Character

 

Range CLR Name
SByte (1 byte) CSByte(o) (None) -128 through 127 System.SByte
UShort (2 bytes) CUShort(o) US 0 through 65,535 System.UInt16
UInteger (4 bytes) CUInt(o) UI 0 through 4,294,967,295 System.UInt32
ULong (8 bytes) CULng(o) UL 0 through 18,446,744,073,709,551,615 (1.8...E+19) System.UInt64

Partial Classes

Partial classes allow a single class to be split into multiple files. Although this may seem like a feature that

won't affect your day-to-day development, it's a feature that will play a part in just about every

application you write. The most useful aspect of this feature is for code generators like Visual Studio

that can generate code into a different file than user-written code, but still provide functionality

within the same class as the user's code. In this manner, the visual designer can more easily parse

and regenerate its code without affecting the code the user has written.

The primary class is created just like a normal class, but the additional portions that the class

contains are declared using the Partial keyword. For example, you might see code like the following:

Public Class Form1
  Inherits Windows.Forms.Form
  ' Your Code
End Class

Partial Public Class Form1
  ' Designer code
  Sub InitializeComponent()
    ' Information about your controls goes here...
  End Sub
End Class

A single class can be broken up into multiple partial classes and all partial classes of the same name,

within the same namespace, are compiled into a single entity at compile time. This feature figures

prominently in the way Visual Studio manages your code, making it easy for tools provided by

the environment to update their code but leave yours intact. This is a feature you'll want to consider

when working on projects with multiple developers. Having classes split across multiple files makes it

far easier to manage source control with concurrent modification of the same class.

Background Worker Object

Handling asynchronous processing has always been a difficult area for many Visual Basic developers,

and ensuring that the callback procedure ran on the correct thread caused many headaches in previous

versions. Visual Basic 2005 developers can take advantage of the BackgroundWorker component

to handle these tasks automatically. By dragging this component onto any design surface, developers

 can easily run slow-running processes as background tasks.

By calling the RunWorkerAsync method of the BackgroundWorker component, developers can

execute code in the DoWork event handler of the component. This code runs in a separate thread

from the main user-interface thread, allowing the user interface to continue while the slower running

process executes without blocking the main thread. When the background task is complete,

the BackgroundWorker component raises its RunWorkerComplete event, allowing the caller

 to react to the fact that the task is completed. The RunWorkerComplete event handler also

 runs in the main thread, making it possible to update controls on the form containing the component.

If you've thought asynchronous processing was too difficult, or if you've fought with and never mastered

 the various intricacies of multi-threaded user interfaces, the BackgroundWorker component can make

 your life easier. You won't need to worry about the thread safety of your forms, and you'll be able

to simply add background processing to applications using this powerful new component.

Summary

As you can see from the few additions listed here, Visual Basic 2005 continues to build on the

success of Visual Basic .NET 2003, with its goal of providing the best environment and language

for creating applications quickly. With the new language features, IDE features, and productivity tools,

Visual Basic 2005 allows you to work more productively, using all the language features provided

 by the .NET Framework.

Windows Forms Enhancements
Runtime errors are easy to diagnose using the new Exception Assistant.

Visual Studio 2005 adds many new features in the area of Windows Forms, both at design and run time; there are simply too many to mention them all here. Two of these features make it far easier to lay out forms just the way you want them—snap lines and in-place property editing.

Snap lines make it easy to align controls with other controls as you layout forms. You might want to align a new control with the top of one control and the right side of another. Figure 12 shows such an arrangement. As you move Button3 so that it's near the edges of Button1 and Button2, the Windows Forms designer displays blue snap lines, as shown in Figure 12. You use these snap lines to align Button3 with the right edge of Button1 and the top edge of Button2.

Figure 12. Snap lines can align controls with the edges of other controls.

As you move Button3 along the height of Button2, the text in Button3 aligns with the text in Button2, and red snap lines indicate this, as shown in Figure 13. If you continue to drag Button3 down, the snap lines shift, as shown in Figure 14. Now, it's simple to align Button3 with the bottom of Button2 and the right of Button1.

Figure 13. Snap lines make it easy to align text in controls.

Figure 14. You can use snap lines to align along any edge.

The Windows Forms designer adds a large number of new controls to its stable of existing controls in the 2005 release. You'll find a new ToolStrip control that augments the existing Toolbar control, adding functionality and behavior that makes it easier to create applications emulating the rich Microsoft Office 2003-style user interface. In addition, there are a number of other useful new controls, including the FlowLayoutPanel, TableLayoutPanel, and managed WebBrowser controls. The SplitContainer control is a huge improvement over laying out Panel and Splitter controls as you might have in previous releases. The DataGridView control is a major improvement over the existing DataGrid control, making it far easier to build the exact user experience that you need.

Working with Data

Although the 2005 release of Visual Studio adds too many data-related features to show them all here, two features really stand out in this new version—the DataSources window and drag-and-drop data binding. Visual Studio .NET 2002 and 2003 did a great job making it easy to bind user interface features to data sources, providing the various data adapter components that you could drag and drop onto a particular form. This technique was certainly easier than writing code, but it did affect maintainability; an individual connection object per form made it tricky to modify the location of your data! In Visual Studio 2005, the project-wide DataSources window provides a holistic view of your application's data.

The DataSources window, as shown in Figure 15, allows you to set up project-wide data sources, selecting items to be used from within the application. When you need to work with a specific data item, you can drag a table or group of fields onto a form, and Visual Studio 2005 can create bound controls for you.

Figure 15. The DataSources window allows you to create project-wide data sources that you can drag and drop onto any design surface. You can also refer to these items programmatically.

Binding data to existing controls couldn't be easier, either. Drag a field from the Data Sources window onto an existing control, and Visual Studio 2005 creates the type of control selected in the Data Sources window, and sets the appropriate binding properties so that you don't need to dig into them yourself. If you have existing controls, you can still drag from the Data Sources window. Drag and drop onto an existing control, and the bindings are set up for you, as well. This type of binding (often called "connect the dots" data binding, simply sets the appropriate data binding properties for existing controls. Whether you're creating bound forms by dragging from the DataSources window, or binding existing controls, you no longer need to maintain your data sources in code, nor in individual components on each form. The DataSources window manages all your data sources for you in one convenient location.

Just My Code

When you create a new project in Visual Basic, the project generally includes a lot of code that you didn't write. Visual Basic 2005 adds the option to always skip over any code that you didn't write as you're single-step debugging through your code. The Enable Just My Code Stepping option (see Figure 16) allows you to control this behavior and is enabled by default.

Figure 16. Just My Code stepping allows you to step over code you didn't write, saving you time and effort.

XML code comments

Documenting your code is a crucial part of an application, and Visual Basic 2005 adds support for creating XML-based comments in your code that can easily be extracted, parsed, and turned into documentation. Outside a procedure, enter ''' (a comment that begins with ''') and then press Enter. Visual Studio 2005 creates the structure of the XML-based comments for you, as shown in Figure 17.

Figure 17. Typing ''' inserts XML-based code comments. Fill in the details, and you can easily create code documentation.

If you supply a description for a parameter in the XML comments as the content of the XML element, developers consuming your method will see IntelliSense help, including your description, as they write their code. You can also select a menu item within Visual Studio that creates Web-based documentation from the XML documentation within your project

ClickOnce Installation and Reg-Free COM

Deploying applications built on the .NET Framework has traditionally been a much simpler process than deploying COM-based applications, but the 2005 release of Visual Studio makes deploying Windows applications even easier. With the addition of ClickOnce functionality, you can create and deploy desktop applications using a safe and simple system-controlled installation and update mechanism, allowing for updates, as needed, from a central location.

Starting with the 1.0 release of .NET, most applications written using the .NET Framework (managed applications) were isolated—all their components were installed local to the application, instead of requiring registration and shared components—and allowed for zero-impact deployment. This solved the DLL problems suffered by COM developers, as they attempted to manage the versions of installed components.

In addition, managed Windows applications have had the capability of running from a Web site, ensuring that their content was always current. Using this approach adds many technological limitations because of security implications. In addition, Windows applications generally run slower over the Web, because parts of the application get downloaded as they're needed, and there's no built-in support for offline use.

ClickOnce technology allows for simple deployment, secure and reliable updates, and it's integrated into Visual Studio. You get the best of both worlds; applications run locally but can be deployed and updated over the Web. ClickOnce makes it easy to install your applications and to provide ongoing updates. Rather than forcing you to distribute new versions of your application, you can deploy updates from a centralized server, updating just the portions of the application that have changed.

ClickOnce can deploy your applications to a client machine from a Web location, from a UNC share, or from a file location (such as a CD). When you deploy your applications using a Web link, users click on a link on a Web page, causing the application to download and install on the client computer. This process creates the necessary Start Menu shortcuts and sets up the item in the Add/Remove Programs dialog box. You can also deploy ClickOnce applications so that they run remotely, and don't install anything locally. (You still have the option of creating a standard MSI file, installing applications as you did in previous versions of Visual Studio, of course.)

Although you can accept all the default options for deploying using ClickOnce, it's instructive to see all the available settings. The Properties dialog box for your project includes a Publish option, as shown in Figure 18. You can specify the location from which users download your application, along with several other options.

Figure 18. Select Publish from within the project's Property pages to configure publishing settings.

Clicking the Updates button displays the dialog box shown in Figure 19. Here you can configure whether your application should check for updates automatically, and if so, how often to perform the updates. ClickOnce-installed applications are smart about updates. If you select to have the updates occur in the background as the application is running, the installer actually tears down and rebuilds the application with the new version while the application is running. You can specify the interval at which you'd like the updates to occur, as well.

Figure 19. Select Updates to configure when and how your application downloads its updates.

Once you've created a project you'd like to deploy using ClickOnce, you can right-click on the project in the Solution Explorer window and select Publish from the context menu. You'll see the dialog box shown in Figure 20. Enter a Web site from which users can download your application.

Figure 20. Publish your project from within Visual Studio.

Next, you can select whether the application will be available both online and offline, or only online. Figure 21 shows the page that allows you to select these settings.

Figure 21. As you post the application, you can determine whether it will be available offline.

Once you've approved the settings (see Figure 22), Visual Studio stores the application at the site you've selected, and allows you to test the deployment by browsing to the site, as shown in Figure 23.

Figure 22. Click Finish to complete the publishing of your application.

Figure 23. Browsing to the download site displays this page.

When you click the deployment link on the installation page, ClickOnce first verifies that all the prerequisites have been installed, and then verifies the installation, as shown in Figure 24. Finally, ClickOnce runs the application, and you can run it later, as well, using the shortcut ClickOnce places on your Start menu.

Figure 24. Users must confirm the installation before installing it on their local hard drive.

Updating the application is easy, as well. Once you make changes to the application and modify the version number within the AssemblyInfo.vb file, redeploy the application using the same settings as before. This creates a new folder on the Web server corresponding to your new version number. Then, when users attempt to run the application, they'll see a dialog box like the one shown in Figure 25, and they'll have the opportunity to download and run the updated version of your applications.

Figure 25. When a new version of your application is available, users get notified as they run the older version.

There's more to ClickOnce than you've seen here, but it's certainly easy to get started deploying your applications using this new, exciting technology.

Developers attempting to deploy applications that take advantage of COM components have always faced a daunting challenge because of the very nature of COM components: they must be globally registered on the target machine. This global registration requirement can cause trouble, especially in terms of versioning and application collision—the feature developers love to hate known as "DLL hell."

Deploying .NET components has always been easier: developers can simply isolate a component with the deployed application, or they can install a shared component in the Global Assembly Cache (GAC), where multiple versions of a component can co-exist side by side. Finally, with the addition of Reg-Free COM, you can have the best of both worlds. You can continue to take advantage of existing COM components, but you won't have to worry about registering the components on the target machine.

To test Reg-Free COM, try this experiment. In Visual Basic 6.0, create a simple ActiveX DLL named VBDemo and include the following method within Class1, the class created by the project template:

Public Function GetPath() As String
    GetPath = App.Path
End Function

Build the DLL, saving it in a convenient location. (Remember that when Visual Basic 6.0 builds an ActiveX DLL, it also registers it on the development machine.)

Within Visual Studio 2005, create a new Windows Application project named RegFreeCOMDemo, and set a COM reference to the existing VBDemo DLL. Add a button to the form, and modify the button's Click event handler to call the COM component:

Private Sub Button1_Click(ByVal sender As System.Object, _
 ByVal e As System.EventArgs) Handles Button1.Click
  Dim test As New VBDemo.Class1
  MsgBox(test.GetPath)
End Sub 

Build the Visual Basic 2005 application, and verify that running the application and clicking the button displays the path from which the COM component runs.

Navigate to the bin\release folder for the RegFreeCOMDemo project. Within the folder, you'll find RegFreeCOMDemo.exe, the main project executable. You'll find RegFreeCOMDemo.exe.config, the configuration file. You'll also find Interop.VBDemo.dll, the interop assembly that makes it possible for your application to communicate with the COM component. What you won't find, however, is VBDemo.dll, the COM component itself.

To prove a point, try unregistering VBDemo.dll. (From the Start | Run menu, type regsvr32 /u \VBDemo.dll, specifying the full path for the COM component.) Run the RegFreeCOMDemo.exe application again, and this time, of course, it fails with an exception when you click the button. Because you unregistered the COM component, the code simply can't find it when it needs it.

To demonstrate Reg-Free COM, you'll need to be able to build your project again, so you must now re-register the COM component. (Use regsvr32 from the Start | Run menu again.) Switch back to Visual Studio, and select the Project | View All Files menu item. Within the Solution Explorer window, expand the References node, and select VBDemo in the list of references. In the Properties window, set the Isolated property to True. (See Figure 26.) Rebuild the project, and switch back to the bin\release folder. (You need to run the project outside of Visual Studio to demonstrate Reg-Free COM.)

IntelliSense code snippets

The new code snippet feature, IntelliSense code snippets, allows you to right-click within the Visual Basic code editor, and select from a hierarchical list of tasks you're most likely to want to accomplish. Selecting a task inserts the prewritten code into your source file for you. Figure 2 shows this feature in action.

Figure 2. Use IntelliSense code snippets within Visual Studio to simplify your coding process.

Once you've inserted the code snippet, you have the option to fix up items such as hard-coded paths and control references, as shown in Figure 3. With your adjustments, the inserted code can work correctly within your environment. The code snippet can include a reference to the appropriate online help topic, and provides tool tips reminding you of changes you need to make. The Insert Snippets option can determine whether you're inside a procedure or not, and can make the appropriate list of snippets available depending on the current context.

Figure 3. Replace values within the code snippet so that the code works correctly for you. Use the Tab key to navigate among the placeholders in the snippet.

Of course, you can create your own code snippets as well. These IntelliSense items are stored as XML files within a folder hierarchy (it's the folder hierarchy that determines the fly-out menus appearing within the code editor), and you have the option of saving your own code snippets, decorating the XML with your own tool tips and help references.

Edit and Continue

Quick development turnaround time has always been the hallmark of developing in Visual Basic, and programmers who miss the edit and continue feature in Visual Basic 6 can rejoice—it's back! In Visual Basic 2005, you can make changes to your code during debugging, back up the code instruction pointer if you want, and re-execute lines of code with the modified content. While in Break mode, you can modify code or fix bugs; almost any code modification will work. (Of course, some modifications force you to switch back to design mode and rebuild the project and there's no way around it.) This change for Visual Studio 2005 has huge numbers of Visual Basic developers cheering!

Debugger DataTips

While in Debug mode using Visual Studio .NET 2003, you could place the cursor over a simple variable, such as a string, to inspect its value. In Visual Studio 2005, this feature has been greatly enhanced; data tips now support complex types, as well. Figure 4 shows a simple example in which the data tip displays information about a complex type, and shows off the ability to drill into the hierarchy for the type. In addition to displaying values, you can also edit those values from within the data tips.

Figure 4. Data tips have been greatly enhanced for Visual Studio 2005. You can investigate complex data structures while debugging, without needing to load a separate window.

AutoCorrect

Visual Basic 2005 adds an AutoCorrect feature, making it simple to determine why invalid code won't compile, and to choose from multiple options in order to fix the errant code. If you make a simple typing error, as shown in Figure 5, clicking the Smart Task produces a list of suggestions from which you can select the correct code.

Figure 5. AutoCorrect can fix typing errors, supplying suggestions based on the text you've entered.

If you attempt to use a class, but neglect to import its namespace or provide the full class name, the AutoCorrect Smart Task offers suggestions, as shown in Figure 6.

Figure 6. If you neglect to import a namespace, you can count on AutoCorrect to supply the full name.

Imagine that you've created a read/write property and then decide to make it read-only. You add the ReadOnly keyword, but of course, now you see a blue "squiggle" indicating that something's wrong with the Set block (see Figure 7).

Figure 7. It's easy to see when code has an error. It's harder to figure out what to do about it. Visual Basic's AutoCorrect makes it easier.

Expanding the Smart Task displayed near the error displays a dialog box, as shown in Figure 8. On this dialog box, you can select either of the possible remedies, and then click the associated link to make the change.

Figure 8. Visual Basic Error Correction dialog box makes it easy to select and apply fixes.

Design-Time Expression Evaluation

Visual Studio 2005 adds back familiar functionality from Visual Basic 6—the ability to evaluate expressions at design time, in the Immediate window. It's useful to be able to call Framework methods and user-defined methods from within the Immediate window in order to test methods, debug code, and more. Figure 9 shows an example of evaluating expressions from within the Immediate window at design time.

Figure 9. Like Visual Basic 6, Visual Basic 2005 allows you to perform calculations in the Immediate window at design time.

You can also call code you've written. For example, Figure 10 shows an example calling code within a form's class. Of course, as you type into the Immediate window in Design mode, you get IntelliSense help just as you do in Break mode.

Figure 10. You can call code you've written yourself while in Design mode, even if the code is inside a form's class.

Exception Assistant

Visual Studio 2005 provides assistance when an unhandled run-time exception occurs. The exception handler bubble provides standard information about the exception, pointing to the exact location within the line of code if it can, and also gives helpful information indicating what to do about the exception, and how to prevent the exception from occurring again. Figure 11 shows the exception assistance bubble in action.

Visual Basic 2005

Visual Studio 2005 and Visual Basic 2005 add many features and tools that make your development experience more productive. The following sections outline just a few.

The My Namespace

Imagine being able to find the functionality you need within the huge set of classes available as part of the .NET Framework immediately. Imagine using a single reference to accomplish goals that would otherwise require many lines of code. Imagine being more productive than you could ever have been programming in previous versions of Visual Basic or Visual Basic 6. These goals, and more, have been met with the addition of the My namespace to Visual Basic 2005.

Like shortcuts on your Windows desktop make it easier for you to find the applications and files you need in Windows, the My namespace classes provide shortcuts to commonly used functionality within the .NET Framework. In addition, the My namespace adds functionality that was previously difficult to find, difficult to use, or was simply not possible without unmanaged API calls.

The My namespace includes the following classes, each of which includes a number of useful members:

  • Application
  • Computer
  • Forms
  • Resources
  • Settings
  • User

For example, to play an audio file in Visual Basic 2005, rather than using DirectX or Win32 API calls, you could write this simple single line of code:

My.Computer.Audio.Play("C:\Beep.wav")

Or, to play a system sound, you might write code like this:

My.Computer.Audio.PlaySystemSound(SystemSounds.Asterisk)

In addition, My namespace supplies functionality that otherwise requires substantial code. For example, the following code demonstrates how you can use simple code to ping a computer:

If My.Computer.Network.IsAvailable Then
    If My.Computer.Network.Ping("http://www.microsoft.com") Then
        MsgBox("Microsoft's site is available.")
    End If
End If

Working with the file system has never been easier. My.Computer.FileSystem provides a flat, stateless, discoverable, and easy-to-use way to perform common file system operations. File system operations such as copying, deleting, moving, and renaming files and folders are at the core of My.Computer.FileSystem. For example, developers frequently require file, folder, and drive properties (such as a file's size, encoding, and so on). Using My.Computer.FileSystem, commonly referenced file, folder, and drive properties can be obtained easily. For example, to display the size of each drive in your system, you might write code like the following:

For Each drv As DriveInfo In My.Computer.FileSystem.Drives
    If drv.IsReady Then
        Debug.WriteLine(String.Format( _
         "{0}:\ {1:N0}", drv.Name, drv.TotalSize))
    End If
Next

The My.User class provides information about the current user, including Name and IsInRole properties. You could use code like the following to display the current user's information and whether the user is an administrator:

MsgBox(My.User.Identity.Name & ":" & _
    My.User.IsInRole("Administrators"))

If you wanted to determine the current user's application data folder, you could use simple code like the following:

MsgBox( _
My.Computer.FileSystem.SpecialDirectories.CurrentUserApplicationData)

The My namespace also adds functionality that returns many of the RAD features of Visual Basic 6 to the .NET development platform. For example, Visual Basic developers historically accessed forms by name, relying on the runtime engine to maintain a collection of all the available form classes. Using the new My.Forms collection, developers can write code to open and interact with an instance of a form class created as part of a solution, like the following:

My.Forms.HelpForm.Show() 

In addition, the My namespace includes a number of other dynamically created collections, including Forms, WebServices, Resources, and Settings. The My.WebServices collection allows developers to easily call Web Service methods taking advantage of IntelliSense and strong typing, and makes the call simpler as well. For example, imagine a Web Service named Numbers that has already been declared in a project. Calling the NumToStr method of the Web Service couldn't get much easier than this:

MsgBox(My.WebServices.Numbers.NumToStr(123.45))

The My namespace has been devised to make it simpler for Visual Basic developers to accomplish their goals with less work, and with less searching through multiple namespaces. You'll find that many arduous tasks in previous releases are now only a single reference away.

IntelliSense and Code Snippets

Visual Basic 2005 is more aggressive than previous versions when it comes to warning you of coding errors. For example, attempting to use a variable before it has been assigned a value triggers a warning, as shown in Figure 1.

Taking advance of the form event model in VB.NET 2005

maisicon_1.GIF

 

Normally when you write a code, you create procedures that you own code calls, as necessary.  You create parameters and their types, and you determine the return value, if any.  In an Event driven Environment, like windows, your application must be able to react to things that happens while the application runs.   For example when a user clicks a button, or types of a key, your application may have to take a particular action.   Think of these occurrences as “interesting things” happening in your application, that is,  they are interesting in an application, at least:

 

Try this out:

 

Namespace My

 

  ' The following events are available for My Application Eventhandles:

  '

  ' Startup: Raised when the application starts, before the startup form is created.

  ' Shutdown: Raised after all application forms are closed.  This event is not raised if the application terminates abnormally.

  ' UnhandledException: Raised if the application encounters an unhandled exception.

  ' StartupNextInstance: Raised when launching a single-instance application and the application is already active.

  ' NetworkAvailabilityChanged: Raised when the network connection is connected or disconnected.

  Partial Friend Class MyApplication

 

    Protected Overrides Function OnInitialize(ByVal commandLineArgs As System.Collections.ObjectModel.ReadOnlyCollection(Of String)) As Boolean

      My.Application.MinimumSplashScreenDisplayTime = 3000

#If Beta Then

      My.Application.SplashScreen = My.Forms.SplashScreen2

#End If

 

      Return MyBase.OnInitialize(commandLineArgs)

    End Function

 

    Private Sub MyApplication_Shutdown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shutdown

      My.Application.Log.WriteEntry( _

        String.Format("Application shut down by {0} at {1:t}", _

        My.User.Name, My.Computer.Clock.LocalTime), _

          TraceEventType.Information)

    End Sub

 

    Private Sub MyApplication_Startup(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.ApplicationServices.StartupEventArgs) Handles Me.Startup

      My.Application.Log.WriteEntry( _

        String.Format("Application started by {0} at {1:t}", _

        My.User.Name, My.Computer.Clock.LocalTime), _

          TraceEventType.Information)

    End Sub

 

    Private Sub MyApplication_UnhandledException(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.ApplicationServices.UnhandledExceptionEventArgs) Handles Me.UnhandledException

      My.Application.Log.WriteException(e.Exception, _

        TraceEventType.Critical, "Unhandled exception")

    End Sub

  End Class

 

End Namespace

 

Module Globals

  Public Sub DisplayAlert(ByVal Text As String)

    MessageBox.Show(Text, Application.ProductName, MessageBoxButtons.OK)

  End Sub

End Module

 

Public NotInheritable Class SplashScreen2

 

  'TODO: This form can easily be set as the splash screen for the application by going to the "Application" tab

  '  of the Project Designer ("Properties" under the "Project" menu).

 

 

  Private Sub SplashScreen2_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

    'Set up the dialog text at runtime according to the application's assembly information. 

 

    'TODO: Customize the application's assembly information in the "Application" pane of the project

    '  properties dialog (under the "Project" menu).

 

    'Application title

    If My.Application.Info.Title <> "" Then

      ApplicationTitle.Text = My.Application.Info.Title & " BETA"

    Else

      'If the application title is missing, use the application name, without the extension

      ApplicationTitle.Text = _

        System.IO.Path.GetFileNameWithoutExtension( _

      My.Application.Info.AssemblyName)

    End If

 

    'Format the version information using the text set into the Version control at design time as the

    '  formatting string.  This allows for effective localization if desired.

    '  Build and revision information could be included by using the following code and changing the

    '  Version control's designtime text to "Version {0}.{1:00}.{2}.{3}" or something similar.  See

    '  String.Format() in Help for more information.

    '

    '    Version.Text = System.String.Format(Version.Text, My.Application.Info.Version.Major, My.Application.Info.Version.Minor, My.Application.Info.Version.Build, My.Application.Info.Version.Revision)

 

    Version.Text = System.String.Format(Version.Text, _

      My.Application.Info.Version.Major, _

      My.Application.Info.Version.Minor)

 

    'Copyright info

    Copyright.Text = My.Application.Info.Copyright

  End Sub

 

End Class

 

Partial Public Class Switchboard

  Inherits System.Windows.Forms.Form

 

  _

  Public Sub New()

    MyBase.New()

 

    'This call is required by the Windows Form Designer.

    InitializeComponent()

 

  End Sub

 

  'Form overrides dispose to clean up the component list.

  _

  Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)

    If disposing AndAlso components IsNot Nothing Then

      components.Dispose()

    End If

    MyBase.Dispose(disposing)

  End Sub

 

  'Required by the Windows Form Designer

  Private components As System.ComponentModel.IContainer

 

  'NOTE: The following procedure is required by the Windows Form Designer

  'It can be modified using the Windows Form Designer. 

  'Do not modify it using the code editor.

  _

  Private Sub InitializeComponent()

    Me.MyApplicationButton = New System.Windows.Forms.Button

    Me.MyComputerButton = New System.Windows.Forms.Button

    Me.MyFormsButton = New System.Windows.Forms.Button

    Me.MyResourcesButton = New System.Windows.Forms.Button

    Me.MySettingsButton = New System.Windows.Forms.Button

    Me.MyUserButton = New System.Windows.Forms.Button

    Me.MyWebServicesButton = New System.Windows.Forms.Button

    Me.SuspendLayout()

    '

    'MyApplicationButton

    '

    Me.MyApplicationButton.Location = New System.Drawing.Point(12, 12)

    Me.MyApplicationButton.Name = "MyApplicationButton"

    Me.MyApplicationButton.Size = New System.Drawing.Size(165, 33)

    Me.MyApplicationButton.TabIndex = 0

    Me.MyApplicationButton.Text = "My.Application"

    '

    'MyComputerButton

    '

    Me.MyComputerButton.Location = New System.Drawing.Point(12, 50)

    Me.MyComputerButton.Name = "MyComputerButton"

    Me.MyComputerButton.Size = New System.Drawing.Size(165, 33)

    Me.MyComputerButton.TabIndex = 1

    Me.MyComputerButton.Text = "My.Computer"

    '

    'MyFormsButton

    '

    Me.MyFormsButton.Location = New System.Drawing.Point(12, 126)

    Me.MyFormsButton.Name = "MyFormsButton"

    Me.MyFormsButton.Size = New System.Drawing.Size(165, 33)

    Me.MyFormsButton.TabIndex = 2

    Me.MyFormsButton.Text = "My.Forms"

    '

    'MyResourcesButton

    '

    Me.MyResourcesButton.Location = New System.Drawing.Point(9, 164)

    Me.MyResourcesButton.Name = "MyResourcesButton"

    Me.MyResourcesButton.Size = New System.Drawing.Size(165, 33)

    Me.MyResourcesButton.TabIndex = 3

    Me.MyResourcesButton.Text = "My.Resources"

    '

    'MySettingsButton

    '

    Me.MySettingsButton.Location = New System.Drawing.Point(9, 202)

    Me.MySettingsButton.Name = "MySettingsButton"

    Me.MySettingsButton.Size = New System.Drawing.Size(165, 33)

    Me.MySettingsButton.TabIndex = 4

    Me.MySettingsButton.Text = "My.Settings"

    '

    'MyUserButton

    '

    Me.MyUserButton.Location = New System.Drawing.Point(12, 88)

    Me.MyUserButton.Name = "MyUserButton"

    Me.MyUserButton.Size = New System.Drawing.Size(165, 33)

    Me.MyUserButton.TabIndex = 5

    Me.MyUserButton.Text = "My.User"

    '

    'MyWebServicesButton

    '

    Me.MyWebServicesButton.Location = New System.Drawing.Point(12, 240)

    Me.MyWebServicesButton.Name = "MyWebServicesButton"

    Me.MyWebServicesButton.Size = New System.Drawing.Size(165, 33)

    Me.MyWebServicesButton.TabIndex = 6

    Me.MyWebServicesButton.Text = "My.WebServices"

    '

    'Switchboard

    '

    Me.ClientSize = New System.Drawing.Size(186, 284)

    Me.Controls.Add(Me.MyWebServicesButton)

    Me.Controls.Add(Me.MyUserButton)

    Me.Controls.Add(Me.MySettingsButton)

    Me.Controls.Add(Me.MyResourcesButton)

    Me.Controls.Add(Me.MyFormsButton)

    Me.Controls.Add(Me.MyComputerButton)

    Me.Controls.Add(Me.MyApplicationButton)

    Me.Font = New System.Drawing.Font("Microsoft Sans Serif", 9.75!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))

    Me.Name = "Switchboard"

    Me.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen

    Me.Text = "Switchboard"

    Me.ResumeLayout(False)

 

  End Sub

  Friend WithEvents MyApplicationButton As System.Windows.Forms.Button

  Friend WithEvents MyComputerButton As System.Windows.Forms.Button

  Friend WithEvents MyFormsButton As System.Windows.Forms.Button

  Friend WithEvents MyResourcesButton As System.Windows.Forms.Button

  Friend WithEvents MySettingsButton As System.Windows.Forms.Button

  Friend WithEvents MyUserButton As System.Windows.Forms.Button

  Friend WithEvents MyWebServicesButton As System.Windows.Forms.Button

 

End Class

 

Public Partial Class MyWebServicesForm

    Inherits System.Windows.Forms.Form

 

    _

    Public Sub New()

        MyBase.New()

 

        'This call is required by the Windows Form Designer.

        InitializeComponent()

 

    End Sub

 

    'Form overrides dispose to clean up the component list.

    _

    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)

        If disposing AndAlso components IsNot Nothing Then

            components.Dispose()

        End If

        MyBase.Dispose(disposing)

    End Sub

 

    'Required by the Windows Form Designer

    Private components As System.ComponentModel.IContainer

 

    'NOTE: The following procedure is required by the Windows Form Designer

    'It can be modified using the Windows Form Designer. 

    'Do not modify it using the code editor.

    _

    Private Sub InitializeComponent()

    Me.NumbersButton = New System.Windows.Forms.Button

    Me.NumberTextBox = New System.Windows.Forms.TextBox

    Me.ResultsLabel = New System.Windows.Forms.Label

    Me.SuspendLayout()

    '

    'NumbersButton

    '

    Me.NumbersButton.Location = New System.Drawing.Point(12, 49)

    Me.NumbersButton.Name = "NumbersButton"

    Me.NumbersButton.Size = New System.Drawing.Size(324, 28)

    Me.NumbersButton.TabIndex = 0

    Me.NumbersButton.Text = "Convert number to text"

    '

    'NumberTextBox

    '

    Me.NumberTextBox.Location = New System.Drawing.Point(12, 12)

    Me.NumberTextBox.Name = "NumberTextBox"

    Me.NumberTextBox.Size = New System.Drawing.Size(85, 22)

    Me.NumberTextBox.TabIndex = 1

    Me.NumberTextBox.Text = "123.45"

    '

    'ResultsLabel

    '

    Me.ResultsLabel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle

    Me.ResultsLabel.Location = New System.Drawing.Point(12, 87)

    Me.ResultsLabel.Name = "ResultsLabel"

    Me.ResultsLabel.Size = New System.Drawing.Size(413, 24)

    Me.ResultsLabel.TabIndex = 2

    Me.ResultsLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft

    '

    'MyWebServicesForm

    '

    Me.ClientSize = New System.Drawing.Size(440, 136)

    Me.Controls.Add(Me.ResultsLabel)

    Me.Controls.Add(Me.NumberTextBox)

    Me.Controls.Add(Me.NumbersButton)

    Me.Font = New System.Drawing.Font("Microsoft Sans Serif", 9.75!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))

    Me.Name = "MyWebServicesForm"

    Me.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen

    Me.Text = "My.WebServices"

    Me.ResumeLayout(False)

    Me.PerformLayout()

 

  End Sub

  Friend WithEvents NumbersButton As System.Windows.Forms.Button

  Friend WithEvents NumberTextBox As System.Windows.Forms.TextBox

  Friend WithEvents ResultsLabel As System.Windows.Forms.Label

End Class

 

You must know we need all these included in the project:

 

 

obj\Debug\ResolveAssemblyReference.cache

obj\Debug\MyNamespace.Switchboard.resources

obj\Debug\MyNamespace.Resources.resources

obj\Debug\MyNamespace.vbproj.GenerateResource.Cache

obj\Debug\MyNamespace.LoginForm.resources

obj\Debug\MyNamespace.MyApplicationForm.resources

obj\Debug\MyNamespace.MyComputerClipboardForm.resources

obj\Debug\MyNamespace.MyForms1.resources

obj\Debug\MyNamespace.MyForms2.resources

obj\Debug\MyNamespace.MyForms3.resources

obj\Debug\MyNamespace.MyResourcesForm.resources

obj\Debug\MyNamespace.MySettingsForm.resources

obj\Debug\MyNamespace.MyUserForm.resources

 ***More to come...

VB Sources

Click on  “VB Sources”.  line above.  Check it out another favor VB sources links from me

 

Visual Basic and gained a lot of expertise thereafter

Visual Basic and gained a lot of expertise thereafter

Since 1999 I programmed in Basic which was quite different. It used to be procedural and linear, which means the logic flow was controlled with GOTO commands and each line of code was numbered. A lot later in 2000 I started working with Visual Basic and gained a lot of expertise thereafter.

My Visual Basic (VB) projects were primarily related to data management systems, which I built with MS Access database back end. Some of them were made in MS VB 6.0 but most used MS Access VBA module which allows for building full-scale user interfaces with any logic behind the scene. In fact, MS Access 2000 has full featured VB 6.0 built in, which gave me all necessary tools to build advanced interfaces for handling data and running reports.

Total number of my VB projects is 5, but I'd like to mention just a few of them on these pages. One, for example, is an application for Cordis Corporation, FL. They needed an application to store data for all their manufacturing clients,  5 months of my work time resulted in excellent stand-alone database application with neat interfaces and accurate understandable reports. Every medical products  can be tracked, updated and current  raw products status can be printed. In my code I gave the client ability to enter detailed medical devices amount breakdown as well as every business expense related to each order, report  total profit forthis project individually as well as all projects together for any selected date range of interest. The application exceeded my customer's expectation and serves the company's needs since June 2000.

While working with VB applications at WildcardSystems I pay special attention to re-using existing code in visual source safe, which makes my code efficient, look elegant and compact. To do that I think thoroughly how to layout the logic flow in advance. Spending extra few minutes at this stage saves hours later during actual coding. I rewrite custom functions and subs wherever possible and pass necessary parameters. This approach works out well and brings extra convenience.  I resused my custom functions and procedures can be used later in other projects too.

In early 2005 I developed an application for Validation engineering at Cordis Corporation. The project was based on MS Access's VBA module as well and included dozens of custom made user forms which allowed for searching Employeed ID, Documentation reviewed, how many times document was review before approved by Validation Engineer team.  Documentation of store procedural was created for new systems application for new employee walk in without much knowledge of  the sysrems can follow through and repeat step by step, read comments and reserve items for a desired date. The Engineers had secure access to the list of their document, their activity history, some personal data such as full name, business phone number, address, ID number and a lot more. When the clients placed new training for new products for medical devices engineering, lpeople could override them, if need arises. Overdue items were flagged automatically and a special warning message popped up onValidation engineer screens.

To conclude, I have 3 years of practical experience with Visual Basic and have accrued a lot of knowledge and skills in this area. Suffice it to say that I spent more time working with VB.NET cumulatively than with any other programming language over the last 3 years. The total number of projects that I completed in VB is small but my code efficiency and elegance brought me a recognition. And now I am delighted to use the new Microsoft's VB.NET technology, which brought class inheritance, polymorphism and many other new features, allowing for easier programming techniques.

Being proficient with Visual Basic and other development tools
One of my most recent accomplishments include ASP.NET, ADO.NET and VB.NET

contribute.jpg

One of my most recent accomplishments include ASP.NET, ADO.NET and VB.NET which I switched my interest to in 2005. I took formal Microsoft certified classes and although I didn't have as many years of experience with these technologies as with other development tools like ColdFusion, MS SQL server or Visual Basic, for example, I was able to grasp the idea and start building applications very soon. Many years of experience with other languages, familiarity with VB 6.0 and MS Visual Studio as well as other factors played their role for sure. If you check out my most recent projects you notice how well I adapted to this new technology.

Thus one of my most recent projects includes designing viewer application for several different file formats, such as image files of all modern formats, Rich Text Format viewer and editor,  I have fulfilled this job quite easily and my application turned out to work well. Here is the list of technologies, used in the project:

ADO.NET for connecting to SQL 2000 and MS Access databases: .NET Data Connection, Data Adapter, Data Set, Data Reader, their methods and properties.

Windows Forms designer, MS Visual Studio .NET.

 Main Menu, Image List, Tool bar, Status Bar.

Context menu, Menu Cloning.

RichTextBox class, majority of its methods and properties.

IO (InputOutput) class, methods and properties, reading and writing files from/to hard disk.

Object serializing.

Open File and Save File dialog classes, their methods and properties.

Exceptions handling, Try, Catch, Finally blocks, methods and properties.

Bitmap class, majority of its methods and properties.

File class and its methods.

HelpProvider class and its methods.

A lot more which doesn't make sense to name individually.

Working with .NET technology became true delight and impressed me a lot. Although I don't have many years of practical use of this technology yet, I feel very comfortable switching to .NET at this point and certainly plan on doing so in the nearest future