Chapter 6: Migrating From Visual C to Visual C.NET


Visual C++ (VC++) .NET is an environment provided by the .NET Framework that simplifies development of different types of applications, such as Windows applications, Web applications, and Web services for traditional C++ programmers. This is because VC++.NET allows you to continue developing applications using technologies, such as Win32 Application Programming Interface (API), Microsoft Foundation Classes (MFC), and Active Template Library (ATL) or use the new class library provided by .NET.

With VC++.NET, you can develop both managed and unmanaged code as compared to VC++ in which applications are by default unmanaged. VC++.NET also allows you to integrate both managed and unmanaged code in a single application. As a result, you can combine the best of both the technologies to create an application, such as an easy user interface development provided by .NET and reusability of code provided by COM components.

Managed extensions for C++ allow you to write managed code in VC++.NET and platform invoke capabilities allow you to call unmanaged APIs present in native Dynamic Link Libraries (DLLs) from a managed application.

This chapter describes managed extensions for C++ programming and the platform invoke capabilities provided by VC++.NET. It also uses a case study to describe how you can migrate a VC++ application that uses an ActiveX control and a COM component to VC++.NET.

Understanding Managed Extensions for C++

Managed extensions for C++ provide extensions to the syntax of C++ using which you can design and develop managed applications in VC++.NET. These applications target the .NET runtime for their execution. Managed extensions allow you to use the existing C++ components in .NET applications. This is because managed extensions for C++ use the keywords and syntax of C++ language and follow the execution rules of .NET. If you code your VC++.NET application without using managed extensions for C++, the code compiles to the native binary by default.

The advantages of using managed extensions for C++ in VC++.NET applications are:

  • Simplified creation of new projects: Includes templates for creating different types of projects such as class libraries, console applications, or Web services. These templates automatically include the startup code in the applications, which facilitates speedy development of applications.

  • Improved performance: Generates Microsoft Intermediate Language (MSIL) code if you compile the application with the /clr compiler option. The MSIL code is managed and accesses the services of the .NET runtime to improve the runtime performance of applications.

  • Complete control over the .NET environment: Provides applications direct access to intrinsic pointers used while developing systems applications. In addition, managed extensions allow you to explicitly control involuntary system operations, such as garbage collection and boxing. The process of converting a value type such as integer into the type object is called boxing.

  • Integration of managed and unmanaged code: Allows you to include managed and unmanaged code in a single executable file. This integration offers several advantages to your application, such as:

    • Segregation of parts of performance-critical code in the unmanaged portion of the application so that they run directly at the level of the operating system, without the intervention of .NET runtime layer.

    • Direct access to all unmanaged low-level APIs and unmanaged libraries such as DLLs.

    • Portability of unmanaged code to the managed platform provided by .NET, which reuses the existing C++ code.

    • Forward compatibility by allowing use of managed features in existing C++ applications.


  • Support for generic programming: Allows you to create templates that reduce effort required to code different methods which have the same logic but differ only in the type of data they operate upon.

Listing 6-1 shows a simple console application in VC++.NET developed using managed extensions for C++. This application shows a welcome message to end users:

Listing 6-1: Creating a Simple Console Application in VC++.NET

start example
 #include "stdafx.h" #using <mscorlib.dll> using namespace System; // This is the entry point for this application int main(void) {    Console::WriteLine(S"Welcome to Managed Extensions For C++!");    return 0; } 
end example

In the above listing, the main() method shows a welcome message to an end user on the command prompt. The listing uses the managed library, mscorlib.dll that provides the managed runtime environment for the code.

Managed extensions for C++ provide several features to applications, such as managed types, events and delegates, string literals, properties, managed operators, exception handling and finalization. These features simplify the task of programming. In addition, managed extensions include some new keywords, such as __abstract and __sealed that define abstract and final classes.

Managed Types

Managed types use features provided by the .NET runtime and are subject to the execution rules of the runtime. These types are of the following two kinds:

  • GC types: Are allocated on the runtime heap. These types do no have to be explicitly deallocated. The runtime automatically frees up the memory occupied by GC types when all active references to them become out of scope.

  • Value types: Can be allocated on the runtime stack or the runtime heap. Value types hold the values of local or global variables, which have short lifetime and for which garbage collection would be an expensive process.

Note

To learn more about GC and value types, see the Introduction to Managed Extensions ReferencePoint.

Delegates and Events

Delegates are pointers to methods defined in a __gc class with a definite signature. Delegates can also point to unmanaged C++ functions if these functions are defined in a native DLL and invoked from a managed application using the platform invoke capabilities of managed extensions. Delegate is a class whose constructor consists of two parameters, one is a reference to a managed object and the other is the address of the specific method of the managed object. Delegates execute the referenced method using the Invoke function call, which has the same signature as the referenced method.

Managed extensions for C++ support events and declare them with the __event keyword. The event model followed is the publisher-subscriber model in which the publisher or the source of the event notifies the subscribers that a particular event has occurred. The publisher does not need to know about the number of subscribers or their types. It raises the events and the subscribers define their event handlers that respond to particular events. Events in managed extensions are based on delegates. The publisher raises an event by passing the appropriate parameters to the event call. This call translates to the Invoke method on the delegate. The subscribers can subscribe or unsubscribe to the event using += and -=methods, which internally call the Combine and Remove methods of the underlying delegate. The Combine and Remove methods add and remove methods from the list of methods referenced by a multicast delegate.

Note

To learn more about delegates and events, see the Introduction to Managed Extensions ReferencePoint.

String Literals

Managed extensions for C++ use the System::String class to declare string variables. You can use the ordinary L-prefixed C++ string literals and the variables of the type System::String interchangeably because an implicit conversion exists between the two. In addition, managed extensions define a new string literal prefixed by the letter S.

Note

To learn more about string lierals, see the Introduction to Managed Extensions ReferencePoint.

Properties

Managed extensions for C++ support the definition of properties in a class. These properties behave just like the other data members of the class because you can retrieve and set their values using the same syntax as that of an ordinary member. Managed extensions implement properties by defining two methods prefixed by the __property keyword. One of these methods is get, which retrieves the value of the property and the other is the set method that sets the property value. You can also define your own implementation of the two methods. In addition, the access specifiers for both the methods can be different. For example, you can have a public get method and a private set method. The two types of properties in managed extensions are:

  • Scalar properties: Are properties whose get method does not include any arguments and returns the type T. The set method of scalar properties uses an argument of the type T and returns the void type. You cannot overload scalar properties.

  • Indexed properties: Are properties in which the get method includes a set of arguments and returns a single value of any type. The set method uses the same set of arguments as the get method and in the same order, along with an additional argument of the type that the get method returns. The set method returns the void type. You can access indexed properties in the same way as you access single or multidimensional arrays. Indexed properties allow overloading.

Note

To learn more about scalar and indexed properties, see the Introduction to Managed Extensions ReferencePoint.

Managed Operators

Managed extensions support managed operators, which have different calling mechanisms for the _value and _gc types. _value types call them using the usual infix notation and _gc types use predefined names to call them. Listing 6-2 shows how to use managed operators in VC++.NET:

Listing 6-2: Using Managed Operators

start example
 #include "stdafx.h" #using <mscorlib.dll> using namespace System; __gc class gcClass {    public:    int i;    static int op_Addition(gcClass* gc1, gcClass* gc2)    {        return gc1->i + gc2->i;     } }; __value class valueClass {    public:    int i;    static int op_Subtraction(valueClass vc1, valueClass vc2)    {        return vc1.i - vc2.i;    } }; int main(void) {    gcClass * g1 = new gcClass();    gcClass * g2 = new gcClass();    g1->i = 10;    g2->i = 20;    /*Console::WriteLine(*g1 + g2); Error! Cannot use infix notation for gc types*/    Console::WriteLine(gcClass::op_Addition(g1,g2));    valueClass v1, v2;    v1.i = 20;    v2.i = 15;    Console::WriteLine(v1 - v2);    Console::WriteLine(valueClass::op_Subtraction(v1, v2));    return 0; } 
end example

The above listing defines a _gc class, gcClass and a _value class, valueClass. gcClass defines the op_Addition() addition operator method that adds the values of two gcClass types. valueClass defines the op_Subtraction() minus operator method that subtracts the value of one valueClass object from the value of another valueClass object. In the main() method, the op_Addition() operator method is called directly to add the values of two gc types. The compiler raises an error if you use the infix notation to add values of the two gc types, as shown in the commented outline. Value types can use both the infix notation and the direct function call to access managed operators.

The different types of managed operators are:

  • Arithmetic: Perform simple arithmetic operations, such as add and subtract. For example, op_Addition, op_Subtraction, and op_Multiply.

  • Logical: Perform logical operations on Boolean vales. For example, op_LogicalAnd and op_LogicalOr.

  • Bitwise: Perform manipulations on individual bits of a value. For example, op_BitwiseAnd and op_BitwiseOr.

  • Unary: Perform manipulations on a single operand. For example, op_Decrement and op_Negation.

  • Conversion operators: Perform conversions between two different types. They are of the following two types:

    • Convert-from operators: Convert an object of a class to the object of that class which defines the conversion operator.

    • Convert-to operators: Convert an object of the class that defines the conversion operator to an object of another class.


Exception Handling

Managed extensions extend the concept of structured exception handling in C++ to include managed exceptions that are pointers to managed types. The base class for all managed exceptions is System::Exception that includes methods for processing managed exceptions. Your application can throw a gc type exception directly but has to box a value type object first before throwing it as an exception. Managed extensions use the C++ throw expression to throw exceptions. The try-catch block to handle exceptions is also the same as that in C++.

Using managed extensions, your applications can throw both managed and unmanaged exceptions from the same try block. However, catch blocks are separate for managed and unmanaged exceptions. When your application throws an exception of the unmanaged C++ object type, the runtime builds a wrapper of the exception type, System::Runtime::InteropServices::SEHException, around the unmanaged exception. While looking for the appropriate catch clause, if a catch clause of an unmanaged C++ type is encountered, the unmanaged exception is unwrapped and compared with the unmanaged C++ type. If there is a match, the processing of the exception proceeds the usual way. However, if a catch clause of the type SEHException or its base classes is encountered first, the clause will catch the exception. As a result, you should place the catch clauses intercepting unmanaged exceptions before the clauses intercepting managed exceptions. You can put catch(...) or catch(Object *) in the end because these would catch any exception. When the runtime finds an appropriate handler to intercept the exception, it unwinds the stack until that point and then executes the catch clause. The stack does not unwind for _gc classes because they are allocated on the heap.

Managed extensions also support a _finally clause in which you place the statements that have to be executed irrespective of occurrence of exception. These statements could include freeing up memory resources or file handles.

Listing 6-3shows the implementation of structured exception handling in VC++.NET:

Listing 6-3: Handling Exceptions in Managed Extensions

start example
 #include "stdafx.h" #using <mscorlib.dll> using namespace System; using System::String; //Defining a gc class __gc class gcSample {    private:    String *s;    public:    void setValue(String *str)    {       s=str;    }    String * getValue()    {       return s;    } }; //Defining a value class __value class valueSample {    private:    String *s;    public:    void setValue(String *str)    {       s=str;    }    String * getValue()    {       return s;    } }; //Defining an unmanaged class class unmanagedSample {    private:    char *s;    public:    void setValue(char *str)    {       s=str;    }    char * getValue()    {       return s;    } }; int main(void) {    int i=0;    gcSample * gS = new gcSample();    valueSample vS;    unmanagedSample uS;    gS->setValue(S"Exception raised in gc type of managed code");    vS.setValue(S"Exception raised in value type of managed code");    uS.setValue("Exception raised in unmanaged code");    try {          while (i<3)          {             i++;             try{             if (i==1)              {                /*Throwing a managed exception of the type __gc*/                throw gS;             }             else if (i==2)             {                /*throw vS; Error! value type must be boxed first*/                /*Throwing a managed exception of the type __value*/                throw __box(vS);             }             else             {                /*Throwing an unmanaged exception*/                throw uS;             }          }          //Catching unmanaged exception          catch(unmanagedSample s)          {             Console::WriteLine(s.getValue());          }          //Catching managed exceptions          catch(gcSample *s)          {             Console::WriteLine(s->getValue());          }          catch(__box valueSample* s)          {             Console::WriteLine(s->getValue());          }       };    }    __finally    {       Console::WriteLine(S"In the finally block");    }    return 0; } 
end example

The above listing defines three classes: gcSample, valueSample, and unmanagedSample. gcSample and valueSample are managed and unmanagedSample is unmanaged. Each of these classes contains a string variable and methods to set and retrieve the value of this variable. The main() method instantiates each class and throws the object of each class as an exception, one after another. The appropriate catch clauses intercept these exceptions and show messages on the command prompt. The _finally block executes in the end. In addition, the compiler raises an error when the application throws the value type object directly, as shown in the commented line of code. You need to box the value type first before throwing it as an exception.

New Keywords

Managed extensions include the following new keywords to provide added functionality:

  • _abstract: Defines an abstract class or struct. This means that you cannot directly create an instance of that class or struct. You need to derive a subclass from the abstract class and then access its functionality from the derived class. In addition, the __abstract keyword operates on a __gc class or struct only. Listing 6-4 shows the implementation of an abstract class in VC++.NET:

Listing 6-4: Abstract Class using Managed Extensions

start example
 #include "stdafx.h" #using <mscorlib.dll> using namespace System; __abstract __gc class A {    public:    void fn()    {       Console::WriteLine(S"Base class function");    } }; __gc class B:public A {    public:    void fn()    {       A::fn();       Console::WriteLine(S"Derived class function");    } }; int main(void) {    /*A* a = new A(); Error! Abstract class cannot be instantiated*/    B* b = new B();    b->fn();    return 0; } 
end example

The above listing defines an abstract class, A, along with its member function f(). The class B inherits class A and overrides the function f(). In the main() method, when you create an instance of the class A, the compiler raises an error, as shown in the commented line of code.

  • __identifier: Provides a method by which your application treats a C++ keyword as an identifier or a variable name. This eliminates the problem that arises in language interoperability when one application uses classes created in another language. Listing 6-5 shows the use of __identifier keyword in VC++.NET:

Listing 6-5: Using the __identifier Keyword

start example
 #include "stdafx.h" #using <mscorlib.dll> /*Including the dll that contains the 'template' class*/ #using "template.dll" int main(void) {    __identifier(template) *temp = new __identifier(template);    return 0; } 
end example

The above listing includes the template.dll file that defines a class, template. Although template is a keyword in C++, you can use it as an identifier for a class by passing it as an argument to __identifier, as shown in the main() method.

  • __sealed: Disallows a class or structure from being derived further. The __sealed keyword operates on __gc classes and structs but not on __gc interfaces. __value types are implicitly sealed. When the __sealed keyword prefixes a method, you cannot override that method. The __sealed keyword applies to virtual methods only. In addition, a class cannot be abstract and sealed at the same time. Listing 6-6 shows the implementation of a sealed class and a sealed function in VC++.NET:

Listing 6-6: Using the __sealed Keyword

start example
 #include "stdafx.h" #using <mscorlib.dll> __sealed __gc class A {    public:    int i; }; /*__gc class B:public A Error! Cannot inherit from a sealed class { };*/ __gc class C {    public:    __sealed virtual void f()    {} }; __gc class D:public C {    public:    /*void f()    Error! Derived class cannot override a sealed function     {}*/    /*__sealed int i; Error! Cannot define a sealed data member*/    /*__sealed int g()     Error! Cannot define a sealed non-virtual member function    {}*/    /*__sealed static void h()    Error! Cannot define a sealed static member function    {}*/ }; int main(void) {    return 0; } 
end example

The above listing defines the class A as sealed. When you try to derive a class B from the sealed class A, the compiler raises an error. This is shown in the commented line of code. The listing also defines a class C with a sealed virtual member function, f(). When the class D tries to override the sealed function f(), the compiler raises an error. In addition, as shown in the commented lines of code in class D, you cannot use the __sealed keyword on a data member, an ordinary non-virtual member function, and on a static member function.

  • __typeof: Provides a mechanism to determine the type of an object. __typeof requires a managed type as an argument and returns a pointer to the System::Type. You need not create an object to call __typeof because it can use an abstract-declarator as an argument. Listing 6-7 shows the use of __typeof keyword in VC++.NET:

Listing 6-7: Using the __typeof Keyword

start example
 #include "stdafx.h" #using <mscorlib.dll> using namespace System; __gc class Sample {    public:    int i;    void show()    {} }; int main(void) {    Sample *ps = new Sample();    Type* pType1 = ps->GetType();    Type* pType2 = __typeof(Sample);    Console::WriteLine(pType1);    Console::WriteLine(pType2);    return 0; } 
end example

The above listing defines a managed class, Sample. The main() method instantiates the Sample class and calls the GetType() method on the managed object ps of the Sample class. The GetType() method returns the type of the calling object. Alternatively, you can retrieve the type of a managed object using the __typeof keyword, as shown in the main() method of the above listing. The __typeof keyword does not need an object of the Sample class to retrieve its type.

Integration of Managed and Unmanaged Code

Managed extensions provide three ways to combine managed and unmanaged classes in an application, such as:

  • Embedding unmanaged classes in managed classes

  • Using __nogc pointers in managed classes

  • Using __gc pointers in unmanaged classes

Embedding Unmanaged Classes in Managed Classes

You can either embed or nest an unmanaged class in a managed class. If the unmanaged class is nested in a managed class, you need to use the __nogc keyword before declaring or defining the unmanaged class. In addition, before you call any unmanaged function of the embedded unmanaged object, you need to pin the object of the managed class or the embedded subobject. Pinning ensures that the garbage collector does not move the object in the managed heap at the time of compaction and prevents the object reference from becoming invalid.

Note

Compaction is the process by which the garbage collector consolidates noncontiguous chunks of available memory by internally moving the objects in the runtime heap.

Using __nogc Pointers in Managed Classes

You can use __nogc pointers in managed classes to build a wrapper of a managed class around an unmanaged C++ class.

Note

__nogc pointers cannot point to the runtime heap, unlike __gc pointers whose variables are known to the runtime heap.

This facilitates interoperability between unmanaged and managed code. To wrap the unmanaged class U with the managed class M:

  1. Declare a variable of the type U* in M.

  2. For every constructor in U, define a corresponding constructor in M that instantiates U using the __nogc new operator. This calls the constructor of U.

  3. Define a destructor in M that calls the delete operator on the pointer to U, if M is the only class that holds a reference to U.

  4. For every remaining method in U, define an identical method in M that calls the method of U, using the pointer to U declared in M.

Note

To learn more about _nogc pointers, see the Introduction to Managed Extensions ReferencePoint.

Using __gc Pointers in Unmanaged Classes

You cannot declare a __gc pointer type to a member of an unmanaged class. If you want to refer to objects in the managed heap from the C++ heap, you need to use the type-safe wrapper template gcroot<>, which is declared in the vcclr.h header file. This gcroot template uses handles into the runtime heap provided by the System::Runtime::InteropServices::GCHandle value class. These handles are not automatically garbage collected and need to be freed in the destructor of the gcroot class. As a result, the memory management system should be robust enough to prevent any dangling references in the runtime heap.

Note

To learn more about _gc pointers, see the Introduction to Managed Extensions ReferencePoint.

Finalization

Finalization is the process of calling the Finalize method, which executes on an object before the runtime garbage collector reclaims the space occupied by the object. The Finalize method runs on a separate thread of execution. You cannot define your own implementation of the Finalize method because it is a protected virtual member of the System::Object class. When you define a destructor on a __gc class, the compiler interprets it as the Finalize method. The compiler also inserts code in the destructor to suppress further finalization by including a call to the System::GC::SuppressFinalize method. This ensures that the garbage collector does not perform the finalization process again. In addition, if you define destructors in both the base class and the derived class, the compiler inserts code that calls the base class’s Finalize method from the derived class’s Finalize method. If you omit the destructor from the definition of the __gc class, the garbage collector will automatically release the memory occupied by the object when all active references to the object go out of scope. Listing 6-8 shows the implementation of destructors in VC++.NET:

Listing 6-8: Using Destructors in Managed Classes

start example
 #include "stdafx.h" #using <mscorlib.dll> using namespace System; __gc class Base {    public:    ~Base()    {       Console::WriteLine(S"In ~Base");    } }; __gc class Derived:public Base {    public:    ~Derived()    {       Console::WriteLine(S"In ~Derived");    } }; int main(void) {    Derived *d = new Derived();    return 0; } 
end example

The above listing defines a Base class and a Derived class, along with their destructors.

Listing 6-9 shows the compiler generated equivalent of Listing 6-8:

Listing 6-9: Compiler Generated Finalization Code

start example
 __gc class Base {    public:    Base::Finalize()    {       Console::WriteLine(S"in ~Base");     }    virtual ~Base()    {       System::GC::SuppressFinalize(this);       Base::Finalize();    } }; __gc class Derived : public Base {    public:    Derived::Finalize()     {        Base::Finalize();     }    virtual ~Derived()     {        System::GC:SuppressFinalize(this);       Derived::Finalize();    } }; 
end example

The above listing shows how the compiler adds the Finalize method to each of the managed classes that define a destructor. The Finalize method implements the code contained in the destructor. In addition, the compiler converts destructors in the managed classes to virtual, adds code to suppress the finalize method called by the garbage collector, and then calls the Finalize method it just defined.

Tip

Alternatively, you can derive your managed class from the IDisposable class, override the Dispose method, and call it from the destructor of the managed class. You need to call the GC::SuppressFinalize method from the Dispose method.




Migrating Unmanaged Applications to. NET
Migrating Unmanaged Applications to. NET
ISBN: N/A
EAN: N/A
Year: 2004
Pages: 31

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