Cool Stuff in Visual C++ 5.0

Robert Coleridge
MSDN Content Development Group

Compiler Support for COM

With the release of Microsoft® Visual C++® 5.0, Component Object Model (COM) programming just got easier. The compiler now has built-in support for many COM features. A few of the new COM features are discussed below.

#import Keyword

Imagine being able to invoke methods on a third-party COM object as easily as you would any class you wrote yourself! This is the power that the #import keyword gives you. By specifying the name of a COM object, whether a DLL, .exe, or typelib, the compiler generates a header file and source for inline functions for accessing the COM object directly. The code generated is based on _com_ptr_t and throws _com_error exceptions even if the actual DLL or .exe does not. The days of wading through a type library just to write the interface signature declarations are over!

New COM Classes

Along with some other fancy enhancements to the compiler come four new classes and templates that either enhance the #import keyword or simply make COM programming easier.

_com_ptr_t

The _com_ptr_t class is a very powerful wrapper template for smart interface pointers. If you've done COM programming in the past, then you know how important it is to keep reference counts straight. Keeping reference counts straight is one of the most tedious problems to debug in COM. You no longer have this headache when you use _com_ptr_t. This simple template greatly simplifies all of the reference counting work for you. AddRef and Release are handled safely behind the scenes.

_com_error

There is now a standard package to use when throwing COM exceptions: the _com_error package. This class encapsulates any HRESULT coming back from a COM function and any associated IErrorInfo. The _com_error package provides several COM methods for extracting requested information, such as the HRESULT, a text description of the error, and the fields from the IErrorInfo structure.

_variant_t and _bstr_t

The COM specification defines the various data types that are standard to COM programming. If you need to pass a string, use a BSTR data type, and if you need to pass objects or unknown data use a VARIANT. Although these types are fundamental to COM programming, there has not been much support for them until now. These two data types are now easier to use with the new release of Visual C++, which uses the C++ classes _variant_t and _bstr_t.

New Keywords

bool/true/false

With the introduction of an integral Boolean data type (bool), the application size and, potentially, the size of your data get smaller. Boolean data types of the older type, BOOL, were defined as an unsigned short. BOOL was either 2- or 4-bytes long! What this meant was that any stored data records containing BOOLs took up extra space with leading zeros. If you did not write special code to trim the size of this data type that was stored out to disk, your data files suffered from excessive empty space. Now with the bool data type this empty space is minimized since a bool is only 1-byte long. With the introduction of the bool data type come its associated values: true and false. Note that these values are not the old values of TRUE and FALSE; however, at the present time they equate to the same value.

mutable

The new storage class specifier, mutable, allows you to declare class member data as "modifiable even in a const object." This allows you to distinguish between "abstract const" (const to the user) and "concrete const" (const to the implementation).

This attribute has a number of possible uses. Here is a simple example of one:
class Object
{
public:
	void x();
	void y() const;
private:
	int		m1;
	mutable int	m2;
};
void Object::x()	// Can always modify object.
{
	m1 = 0;
	m2 = 1;
}
void Object::y() const	// Object is const.
{
	// cannot modify m1
	m2 = 2;		// Can modify m2 because it is mutable.
}

MFC and the WinINet API

With this release of Visual C++, the Microsoft Foundation Class (MFC) library encapsulates the WinINet application programming interface (API). What used to take a significant amount of coding and testing time now takes only a few minutes. In addition, you can reduce the number of WinINet API function calls to a single class invocation. There are currently a total of 13 new WinINet API classes in the MFC. Programming for the Web has never been easier.

ATL 2.1 Compiler Optimization

Be aware that ActiveX™ Template Library (ATL) 2.1 has been optimized for the Visual C++ 5.0 compiler. In particular, you should be aware that many ATL classes are declared with the ATL_NO_VTABLE macro. This has the effect of creating objects without vtables. For further information see the article in the ATL documentation titled "Compiler Optimizations." Note that with this new release of ATL there are 39 new classes that facilitate control creation, seven renamed classes, and seven classes that have been dropped entirely.

Tips and Tricks

The New bool Data Type: When to Use It

The introduction of internal support for Boolean data types in the newest release of Visual C++ prompts an interesting question: When should we use it? This arises from the discrepancy in size between the older BOOL type, which is 4 bytes long, and the new bool type, which is 1 byte long. If BOOL data has been stored to disk, then you need to retrieve it as a BOOL or convert the data. If BOOL data is being passed via pointer to another application or DLL, then receive the data as a BOOL.

If the new application has no interaction with older BOOLs, then use the new bool type, since it is now integral to the compiler and can be optimized for.

When is True Not True? When Doing Inter-Language Programming

The difficulty here lies in inter-language support. In both Visual C++ and Visual Basic® the value for false is zero, but the value for true is not the same in both languages! Visual C++ uses a positive one (1) and Visual Basic uses a negative one (–1)!

This discrepancy can cause some conditional statements to fail when they should not. For example, suppose we have a COM object written in Visual C++, called "Foo." It has one interface, called "Bar," which returns a Boolean result. The problem can be easily seen with the following code:
dim bResult as Boolean

// This code will NOT work since Visual C++ and Visual Basic have differing 
// 'true' values.
bResult = Foo.Bar();
if bResult = True then
	DoSomeThing();

This problem is identical when Visual C++ calls Visual Basic. However, there is a simple fix to this problem. Simply change the conditional to look like the following:
dim bResult as Boolean
// This code WILL work since both know that true does not equal 0.
bResult = Foo.Bar();
if bResult then
	DoSomeThing();

By making this small change in coding you can ensure that any code you write that deals with Boolean conditionals will have interlanguage operability.

How to Make True = True

Under Visual C++ version 4.2 and earlier, the data type BOOL was used to define Boolean data types and return codes. If an application that uses this data type is recompiled under Visual C++ 5.0, it will still function as expected but you may receive a warning that you did not receive before. You will definitely get this warning if you begin to mix and match the older BOOL with the newer built-in bool data type. The compiler is simply warning you that the optimization techniques it has for bool data types cannot be used, since the two data types are internally different.

You can disable the warning with the following pragma:
#pragma warning (disable : 4800)	// Turns off bool warning.

You should only disable the warning as a temporary solution, however. It would be better to change your BOOLs to bool, or not to mix and match the two.

How to Use a Structure to Execute Code Before and After Your Main Code

When using smart interface pointers from a template, it is often necessary to have certain code execute before and after the main program executes. For example, let's assume that you needed to have OLE initialized before your program started and have OLE uninitialize after your program is finished. The technique below demonstrates how to do this.

Define a global structure that looks like the following:
struct OleInit
{
	OleInit()	
	{
		 OleInitialize(NULL);
	}
	~OleInit()
	{
		 OleUninitialize();
	}
} Init_Ole;

Place the above code segment in your code outside of any function, for example above your main routine. Since this code is outside of any function, the constructor gets called prior to the main routine. Calling the constructor, in effect, calls the OleInitialize routine. At the end of the program's life the destructor is called, thus calling the OleUninitialize routine. This technique could be applied to any code that must be started prior to the main routine starting and any code that must run after the program is finished.