.NET Framework vs. ActiveX

.NET Framework vs. ActiveX

As a Visual Basic developer, you will normally not be concerned with the run-time systems that underlie your Visual Basic applications. Visual Basic 6, for example, makes the details of how ActiveX works largely transparent. The Visual Basic 6 runtime handles all of the messy details that come with implementing an ActiveX-compliant component or application. Licensing, persistable objects, Microsoft Transaction Server (MTS) transaction awareness, and binary compatibility are exposed as simple settings that you can turn on or off. In the same vein, Visual Basic .NET does a good job of hiding the details of what happens under the hood. For example, you do not need to know that you are creating or using a .NET component. A .NET component is just like any other component. It has properties, methods, and events just as an ActiveX component does. Why should you care about the differences between ActiveX and .NET if everything basically looks the same?

On the surface, it doesn t matter whether you re using ActiveX, .NET, or your best friend s component model they all look about the same. When you dig into the details, however, you need to understand the machine that lies beneath.

If you have ever created an ActiveX control in Visual Basic 6, you may have found that it behaves slightly differently from other ActiveX controls that you bought off the shelf. For example, if you add a BackColor property to your control, you ll notice when you test it that the color picker is not associated with your control. Digging deeper, you ll find that you need to change the type of the property to OLE_COLOR and set the Property ID attribute on the property to BackColor. Only then will the property behave like a BackColor property. In solving this problem, you needed to cross over from pure Visual Basic into the world of ActiveX. Although Visual Basic attaches different terminology to options and language statements, you end up being directly or indirectly exposed to ActiveX concepts such as dispatch IDs (DISPIDs), what Visual Basic refers to as property IDs, and OLE types such as OLE_COLOR. Visual Basic, as much as it tries, cannot hide this from you. The more properties, events, methods, and property pages you add to your Visual Basic 6 ActiveX control, the more problems you encounter that require an ActiveX-related solution.

Visual Basic .NET works in much the same way. Most of the time, you are just dealing with Visual Basic. However, when you need your application or component to behave consistently with other types of applications, whether they be standard Windows applications or Web service server objects, you will need a detailed understanding of the environment in which you want your application to run. In the case of .NET applications, you will need to understand how .NET works. The more you know about the target environment, the better equipped you are to create a component or application that behaves well in that environment. So let s dig a bit and uncover the machine that will run your upgraded Visual Basic .NET application: the .NET Framework.

.NET Framework

The .NET Framework is composed of two general parts: the common language runtime and the Framework class library. The runtime is the foundation upon which the .NET Framework is based. It provides the basic services on which all .NET applications depend: code execution, memory management, thread management, and code security. The Framework class library provides building blocks for creating a variety of .NET applications, components, and services. For example, the Framework class library contains the base classes for creating ASP.NET Web applications, ASP.NET XML Web services, and Windows Forms. It defines all of the value types, known as System types, such as Byte, Integer, Long, and String. It gives you complex structure classes such as Collection and HashTable, as well as the interfaces such as ICollection and IDictionary so you can define your own custom implementation of a standard Collection or HashTable class.

The .NET Framework as a whole, since it works across all .NET languages, can be thought of as an expanded version of the Visual Basic 6 runtime. The common language runtime corresponds to the Visual Basic Language Runtime in Visual Basic 6, which includes the byte code interpreter and memory manager. The counterparts of the .NET Framework class library in Visual Basic 6 include the Visual Basic forms package, the Collection object, and global objects such as App, Screen, Printer, and Clipboard.

The main difference between the two environments is that Visual Basic 6 is a closed environment, meaning that none of the intrinsic Visual Basic types, such as Collection, App, Screen, and so on, can be shared with other language environments, such as C++. Likewise, Microsoft Visual C++ is largely a self-contained language environment that includes its own runtime and class libraries, such as MFC and ATL. The MFC CString class, for example, is contained within the MFC runtime and is not shared with other environments such as Visual Basic.

In closed environments such as these, you can share components between environments only when you create them as ActiveX components, and even then there are a number of limitations. ActiveX components need to be designed and tested to work in each target environment. For example, an ActiveX control hosted on a Visual Basic 6 form may work wonderfully, but the same control may not work at all when hosted on an MFC window. You then need to add or modify the interfaces or implementation of your ActiveX component to make it work with both the Visual Basic 6 and MFC environments. As a result, you end up duplicating your effort by writing specialized routines to make your ActiveX component work in all target environments.

The .NET Framework eliminates this duplication by creating an environment in which all languages have equal access to the same broad set of .NET types, base classes, and services. Each language built on the .NET Framework shares this common base. No matter what your language of choice is Visual Basic .NET , C#, or COBOL (for .NET) the compiler for that language generates exactly the same set of .NET runtime instructions, called Microsoft Intermediate Language (MSIL). With each language distilled down to one base instruction set (MSIL), running against the same runtime (the .NET common language runtime), and using one set of .NET Framework classes, sharing and consistency become the norm. The .NET components you create using any .NET language work together seamlessly without any additional effort on your part.

Now that you have seen some of the differences between the Visual Basic 6 ActiveX-based environment and the Visual Basic .NET environment, let s focus on various elements of the .NET Framework and see how each element manifests itself in Visual Basic .NET. The elements we will be looking at are memory management, type identity, and the threading model. Each of these areas will have a profound impact on the way you both create new Visual Basic .NET applications and revise upgraded Visual Basic 6 applications to work with Visual Basic .NET.

Memory Management

Visual Basic .NET relies on the .NET runtime for memory management. This means that the .NET runtime takes care of reserving memory for all Visual Basic strings, arrays, structures, and objects. Likewise, the .NET runtime decides when to free the memory associated with the objects or variables you have allocated. This is not much different from Visual Basic 6, which was also responsible for managing the memory on your behalf. The most significant difference between Visual Basic 6 and Visual Basic .NET in terms of memory management involves determining when an object or variable is freed.

In Visual Basic 6, the memory associated with a variable or object is freed as soon as you set the variable to Nothing or the variable falls out of scope. This is not true in Visual Basic .NET. When a variable or object is set to Nothing or falls out of scope, Visual Basic .NET tells the .NET runtime that the variable or object is no longer used. The .NET runtime marks the variable or object as needing deletion and relegates the object to the Garbage Collector (GC). The Garbage Collector then deletes the object at some arbitrary time in the future.

Because we can predict when Visual Basic 6 will delete the memory associated with a variable, we refer to the lifespan of a variable in that language as being deterministic. In other words, you know the exact moment that a variable comes into existence and the exact moment that it becomes nonexistent. The lifespan of a Visual Basic .NET variable, on the other hand, is indeterministic, since you cannot predict exactly when it will become nonexistent. You can tell Visual Basic .NET to stop using the variable, but you cannot tell it when to make the variable nonexistent. The variable could be left dangling for a few nanoseconds, or it could take minutes for the .NET Framework to decide to make it nonexistent. In the meantime, an indeterminate amount of your Visual Basic code will execute.

In many cases it does not matter whether or not you can predict when a variable or object is going to be nonexistent. For example, a simple variable such as a string or an array that you are no longer using can be cleaned up at any time. It is when you are dealing with objects that things get interesting.

Take, for example, a File object that opens a file and locks the file when the File object is created. The object closes the file handle and allows the file to be opened by other applications when the object is destroyed. Consider the following Visual Basic .NET code:

Dim f As New File Dim FileContents As String f.Open("MyFile.dat") FileContents = f.Read("MyFile.dat") f = Nothing FileContents = FileContents & " This better be appended to my file! " f.Open("MyFile.dat") f.Write(FileContents) f = Nothing

If you run this application in Visual Basic 6, it will run without error. However, if you run this application in Visual Basic .NET, you will encounter an exception when you attempt to open the file the second time. Why? The file handle associated with MyFile.dat will likely still be open. Setting f to Nothing tells the .NET Framework that the File object needs to be deleted. The runtime relegates the object to the garbage bin, where it will wait until the Garbage Collector comes along to clean it up. The File object in effect remains alive and well in the garbage bin. As a result, the MyFile.dat file handle is still open, and the second attempt to open the locked file will lead to an error.

The only way to prevent this type of problem is to call a method on the object to force its handle to be closed. In this example, if the File object had a Close method, you could use it here before setting the variable to Nothing. For example,

f.Close f = Nothing

Dispose: Determinism in the Face of Chaos

Despite all of the benefits that a garbage-collected model has to offer, it has one haunting side effect: the lack of determinism. Objects can be allocated and deleted by the hundreds, but you never really know when or in what order they will actually terminate. Nor do you know what resources are being consumed or locked at any given moment. It s confusing, even chaotic. To add some semblance of order to this system, the .NET Framework offers a mechanism called Dispose to ensure that an object releases all its resources exactly when you want it to. Any object that locks resources you need or that otherwise needs to be told to let go should implement the IDisposable interface. The IDisposable interface has a single method, Dispose, that takes no parameters. Any client using the object should call the Dispose method when it is finished with the object.

One More Thing to Worry About

If you ve been using Visual Basic 6, you re not accustomed to calling Dispose explicitly on an object when you write code. Unfortunately, when it comes to Visual Basic .NET, you will have to get accustomed to doing so. Get in the habit now of calling Dispose on any object when you are done using it or when the variable referencing it is about to go out of scope. If we change the File object shown earlier to use Dispose, we end up with the following code:

Dim f As New File Dim FileContents As String f.Open("MyFile.dat") FileContents = f.Read("MyFile.dat") f.Dispose f = Nothing FileContents = FileContents & " This better be appended to my file! " f.Open("MyFile.dat") f.Write(FileContents) f.Dispose f = Nothing

note

The Visual Basic Upgrade Wizard does not alert you to cases in which you may need to call Dispose. We advise you to review your code after you upgrade to determine when an object reference is no longer used. Add calls to the object s Dispose method to force the object to release its resources. If the object notably ActiveX objects that do not implement IDisposable does not support the Dispose method, look for another suitable method to call, such as Close. For example, review your code for the use of ActiveX Data Objects (ADO) such as Recordset and Connection. When you are finished with a Recordset object, be sure to call Close.

When You Just Want It All to Go Away

While your application runs, objects that have been created and destroyed may wait for the Garbage Collector to come and take them away. At certain points in your application, you may need to ensure that no objects are hanging around locking or consuming a needed resource. To clean up objects that are pending collection, you can call on the Garbage Collector to collect all of the waiting objects immediately. You can force garbage collection with the following two calls:

GC.Collect GC.WaitForPendingFinalizers

note

The two calls to Collect and to WaitForPendingFinalizers are required in the order shown above. The first call to Collect kicks off the garbage collection process asynchronously and immediately returns. The call to WaitForPendingFinalizers waits for the collection process to complete.

Depending on how many (or few) objects need to be collected, running the Garbage Collector in this manner may not be efficient. Force garbage collection sparingly and only in cases where it s critical that all recently freed objects get collected. Otherwise, opt for using Dispose or Close on individual objects to free up needed resources as you go.

Type Identity

Mike once played on a volleyball team where everyone on his side of the net, including himself, was named Mike. What a disaster. All the other team had to do was hit the ball somewhere in the middle. Someone would yell, Get it, Mike! and they would all go crashing into a big pile. To sort things out, they adopted nicknames, involving some derivation of their full names. After that, the game went much better.

Like names in the real world, types in Visual Basic can have the same name. Instead of giving them nicknames, however, you distinguish them by using their full name. For example, Visual Basic has offered a variety of data access models over the years. Many of these data access models contain objects with the same names. Data Access Objects (DAO) and ActiveX Data Objects (ADO), for instance, both contain types called Connection and Recordset. Suppose that, for whatever reason, you decided to reference both DAO and ADO in your Visual Basic 6 project. If you declared a Recordset variable, the variable would be either a DAO or an ADO Recordset type:

Dim rs As Recordset

How do you know which type of Recordset you are using? One way to tell is to look at the properties, methods, and events that IntelliSense or the event drop-down menu gives you. If the object has an Open method, it is an ADO Recordset. If instead it has an OpenRecordset method, it is a DAO Recordset. In Visual Basic 6, the Recordset you end up with depends on the order of the references. The reference that appears higher in the list wins. In Figure 2-1, for example, the Microsoft ActiveX Data Objects 2.6 Library reference occurs before the reference to the Microsoft DAO 3.6 Object Library, so ADO wins and the Recordset is an ADO Recordset type.

Figure 2-1

ADO 2.6 reference takes precedence over DAO 3.6.

If you change the priority of the ADO reference by selecting it and clicking the down arrow under Priority, the DAO reference will take precedence. Clicking OK to apply the change transforms your Recordset type to a DAO Recordset.

Suppose you want to use both types of Recordset objects in your application. To do so, you need to fully qualify the type name as follows:

Dim rsADO As ADODB.Recordset Dim rsDAO As DAO.Recordset

As you can see, Visual Basic 6 is quite flexible when it comes to using types. Indeed, you could argue that it is too flexible, since you could mistakenly change the type for variables in your code simply by changing the order of a reference.

Visual Basic .NET is stricter about the use of the same types in an application. The general rule is that you need to fully qualify every ambiguous type that you are using. If you are referencing both ADO and DAO, for example, you are forced to fully qualify your use of the types just as you would in Visual Basic 6:

Dim rsADO As ADODB.Recordset Dim rsDAO As DAO.Recordset

Using Imports

To help you cut down on the number of words and dots that you need to type for each reference, Visual Basic .NET allows you to import a namespace. You can think of it as a global With statement that is applied to the namespace. (A namespace is similar to a library or project name in Visual Basic 6.) For example, type references can become quite bothersome when you are dealing with .NET types such as System.Runtime.Interopservices.UnmanagedType. To simplify the qualification of this type, you can add an Imports statement to the beginning of the file in which it is used:

Imports System.Runtime

This statement allows you to reference the type as Interopservices.UnmanagedType. You can also expand the Imports clause to

Imports System.Runtime.Interopservices. 

and then simply refer to Unmanaged Type in your code.

Managing Conflicts

Imports works great until there is a conflict. As we indicated earlier, in Visual Basic 6, the rule is that the type library that is higher in the precedence list takes priority. Visual Basic .NET is different in that all conflicts are irreconcilable. You have to either change your Imports clause to avoid the conflict or fully qualify each type when it is used. Suppose that you add Imports statements for ADO and DAO as follows:

Imports ADO Imports DAO

Now suppose that you want to declare a variable of type Recordset. As in the volleyball game described earlier, it s as if you yelled out, Recordset! Both ADO and DAO jump in. Crash! Big pile. Any attempt to use the unqualified type Recordset will lead to an error that states, The name Recordset is ambiguous, imported from Namespace ADO, DAO. To resolve the problem, you need to either fully qualify the type or remove one of the Imports statements.

No More GUIDs

Each ActiveX type, whether it is a class, an interface, an enumerator, or a structure, generally has a unique identifier associated with it. The identifier is a 128-bit, or 16-byte, numeric value referred to as UUID, GUID, LIBID, CLSID, IID, or <whatever>ID. No matter what you call it, it is a 128-bit number.

Rather than make you think in 128-bit numbers, Visual Basic (and other languages) associates human-readable names with each of these types. For example, if you create a Visual Basic 6 class called Customer, its type identifier will be something like {456EC035-17C9-433c-B5F2-9F22C29D775D}. You can assign Customer to other types, such as LoyalCustomer, if LoyalCustomer implements the Customer type with the same ID value. If the LoyalCustomer type instead implements a Customer type with a different ID value, the assignment would fail with a Type Mismatch error. In ActiveX, at run time, the number is everything; the name means little to nothing.

In .NET, on the other hand, the name is everything. Two types are considered the same if they meet all of the following conditions:

  • The types have the same name.

  • The types are contained in the same namespace.

  • The types are contained in assemblies with the same name.

  • The assemblies containing the types are weak named.

Note that the types can be in assemblies that have the same name but a different version number. For example, two types called Recordset contained in the namespace ADODB are considered the same type if they live in an assembly such as Microsoft.ADODB.dll with the same name. There could be two Microsoft.ADODB.dll assemblies on your machine with different version numbers, but the ADODB.Recordset types would still be considered compatible. If, however, the Recordset types lived in different assemblies, such as Microsoft.ADODB_2_6.dll and Microsoft.ADODB_2_7.dll, the types would be considered different. You cannot assign two variables of type Recordset to each other if each declaration of Recordset comes from an assembly with a different name.

Threading Model

Visual Basic 6 ActiveX DLLs and controls can be either single threaded or apartment threaded; they are apartment threaded by default. Apartment threading means that only one thread can access an instance of your Visual Basic 6 ActiveX component at any given time. In fact, the same thread always accesses your component, so other threads never disturb your data, including global data. Visual Basic .NET components, on the other hand, are multithreaded by default, meaning that two or more threads can be executing code within your component simultaneously. Each thread has access to your shared data, such as class member and global variables, and the threads can change any data that is shared.

Visual Basic .NET multithreaded components are great news if you want to take advantage of MTS pooling, which requires multithreaded components. They are bad news if your component is not multithread safe and you wind up trying to figure out why member variables are being set to unexpected or random values in your upgraded component.

There is certainly more to cover on this topic, but we ll leave that for the discussion of threading in Chapter 11where we discuss how to make your multithreaded Visual Basic .NET components multithread safe.



Upgrading Microsoft Visual Basic 6.0to Microsoft Visual Basic  .NET
Upgrading Microsoft Visual Basic 6.0 to Microsoft Visual Basic .NET w/accompanying CD-ROM
ISBN: 073561587X
EAN: 2147483647
Year: 2001
Pages: 179

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net