Building an Unmanaged C++ Client
With the final example, things start to get really sticky. Creating the code to enable an unmanaged C++ application to access components created with C# and VB.NET is simple and fairly straightforward. It is with the configuration of the project where things start to get messy.
To create the project, follow these steps:
Select New and then Project from the File menu.
Select Visual C++ Projects on the left side of the New Project dialog, and Win32 Project on the right side. Name the project UnmgdClient. If the previous example project is still open, be sure that the Close Solution radio button is selected. Click OK to create the new project.
In the Win32 Application Wizard, select Application Settings on the left side, and then specify Console application for the application type. Click Finish to create the project.
Building the Managed C++ Interface
Creating the managed interface for the CLR components is similar to creating the managed wrapper for your ATL component. To create the interface, follow these steps:
Add a new generic class to the project, using the same method as in prior days. Name the new class MgdClient.
Add a new function to the MgdClient class. Specify the return value for the new function as double, the name as GetTaxes, the access as public, and add two parameters. Specify the first parameter type as double and the name as dbPurchaseAmt. Specify the second parameter type as wchar_t* and the name as strCategory.
Edit the new function as in Listing 7.
Listing 7The GetTaxes Function
doubleMgdClient::GetTaxes(double dbPurchaseAmt, wchar_t* strCategory) { double dbTaxAmt; // Declare and create an instance of the CLR object CSTaxCalc::CTaxCalculations* pGetTax = new CSTaxCalc::CTaxCalculations(); // Get the amount of taxes to collect dbTaxAmt = pGetTax->CalculateTaxes(dbPurchaseAmt, strCategory); // Return the result return dbTaxAmt; }
If you are using the C# component, add the directives in Listing 8 at the top of the MgdClient.cpp file. If you are using the VB component, substitute the word VBTaxCalc for every occurrence of CSTaxCalc in both Listing 7 and 8.
Listing 8The MgdClient Directives
#include "StdAfx.h" #include "mgdclient.h" #using <mscorlib.dll> #using "CSTaxCalc.dll" using namespace System;
Coding and Configuring the Unmanaged C++ Client
To add the code for the main function, follow these steps:
Open the UnmgdClient.cpp file.
Add the #include directives in Listing 9.
Listing 9The UnmgdClient Directives
// UnmgdClient.cpp : Defines the entry point for the console application. // #include "stdafx.h" [ic:Input]#include <windows.h> #include <stdio.h> #include "atlbase.h" #include "mgdclient.h"
Edit the main function, adding the code in Listing 10.
Listing 10The Main Function
int _tmain(int argc, _TCHAR* argv[]) { MgdClient* pGetTax = new MgdClient(); double dbPurchaseAmt, dbTaxAmt, dbTotalDue; // Create the string for the category being purchased wchar_t pstrCategory[20]; wcscpy(pstrCategory, L"clothing"); // Specify the purchase amount dbPurchaseAmt = 45.0; // Get the amount of taxes to collect dbTaxAmt = pGetTax->GetTaxes(dbPurchaseAmt, pstrCategory); if (dbTaxAmt > 0.0) { // Calculate the total due dbTotalDue = dbPurchaseAmt + dbTaxAmt; // Display the category being purchased USES_CONVERSION; printf("%s\n", W2A(pstrCategory)); // Display the purchase amount printf("Purchase Amount = %9.2f\n", dbPurchaseAmt); // Display the tax due printf(" Tax = %9.2f\n", dbTaxAmt); // Display the total amount printf(" Total Due = %9.2f\n", dbTotalDue); } return 0; }
In the Solution Explorer pane, select and right-click the project node for the MgdClient.cpp file. Select Properties from the context menu.
On the Configuration combo box, select All Configurations.
On the left side of the Properties dialog, select C/C++, General. On the right side, for the Compile As Managed entry, specify Assembly Support (/clr). For the Resolve #using References entry, enter $(outdir).
Click the Apply button to apply the configuration changes without closing the Properties dialog.
In the Solution Explorer pane, select the stdafx.cpp file.
On the left side of the Properties dialog, select C/C++, Precompiled Headers. On the right side, in the Create/Use Precompiled Headers entry, select Not Using Precompiled Headers.
Click the Apply button to apply the configuration changes without closing the Properties dialog.
In the Solution Explorer pane, select the UnmgdClient project node.
On the left side of the Properties dialog, select Build Events, Pre-Build Events. On the right side, in the Command Line entry, if you are using the C# component, enter:
copy "..\CSTaxCalc\bin\$(ConfigurationName) \CSTaxCalc.dll" $(ConfigurationName)
If you are using the VB component, enter:
copy "..\VBTaxCalc\bin\VBTaxCalc.dll" $(ConfigurationName)
On your system, replace the relative path specified with the actual path to the project directories on your system if necessary. Because the solution file isn't located in the project directory for either component in this example, you can't use the $(SolutionDir) label.
On the left side of the Properties dialog, select C/C++, Precompiled Headers. On the right side, in the Create/Use Precompiled Headers section, select Not Using Precompiled Headers.
In the Configuration combo box, select Active(Debug). The settings you change from here on affect only the Debug builds.
On the left side of the Properties dialog, select C/C++, Code Generation. On the right side, for the Basic Runtime Checks entry, select Default. For the Runtime Library entry, select Multi-threaded Debug (/MTd). For the Enable Minimal Rebuild entry, select No.
On the left side of the dialog, select C/C++, General. On the right side, for the Debug Information Format, select Program Database (/Zi).
Click OK to apply the configuration changes and close the Properties dialog.
You should now be able to compile and run the unmanaged client application, accessing the C# or VB.NET component that you created in the previous example. Running the unmanaged client produces the output shown in Figure 3.
Figure 3 The unmanaged C++ client accessing CLR components created using C# or VB.NET.