Using a COM Component from Unmanaged C

Using a COM Component from Unmanaged C++

COM objects such as the phone number validator don't have a user interface. Putting together a quick application with a very simple user interface serves as a good way to test it. For this component, a dialog-based application with a text box for entering phone numbers will work well. The application creates an instance of the COM component you built with ATL, and uses it to validate the number that is entered. There are just a few steps to building the test project:

  • Create an empty project

  • Create a dialog box

  • Connect the fields on the dialog box to variables

  • Connect the button on the dialog box to a function

  • Code the function

  • Adjust the structure of the application and add COM support

Creating an Empty Project

Close the PhoneFormat solution and create a new MFC Application called PhoneTest . On the Application Type tab of the MFC Application Wizard, select Dialog Based. The other defaults are all okay for this application, so click Finish. This will create a project, and a starter dialog box with the ID IDD_PHONETEST_DIALOG .

Creating a Dialog Box

In the Resource View, double-click IDD_PHONETEST_DIALOG to edit the main dialog box of the application. Delete the static label with the TODO instruction. Click the OK button and use the Properties window to change the ID of the button to IDC_VALIDATE and the caption to Validate . Change the caption on the Cancel button to Close, but leave its ID unchanged.

Add an Edit Control and change its ID to IDC_NUMBER . Put a static label before it, captioned Number to Validate: .

Add two more static controls, one with the ID IDC_ERROR and the other with the ID IDC_MESSAGE , between the edit box and the two buttons . Change the caption of each of these to an empty string, and lengthen IDC_MESSAGE so that it can accommodate an error message.

Click in the background of the dialog box (away from all the controls) to select the dialog box itself, and then drag the bottom of the dialog box upward to make a smaller dialog box. The completed dialog box should resemble Figure 8.2.

Figure 8.2. Building a simple MFC dialog box to exercise the ValidatePhoneNumber() method.


Connecting the Fields on the Dialog Box to Variables

Right-click the edit box, IDC_NUMBER , and choose Add Variable. This brings up the Add Member Variable Wizard. Leave the Control Variable check box selected, and from the drop-down box at the far right, choose Value rather than the default Control. This changes the Variable Type to CString . Enter Number for the Variable Name and click Finish. If you switch to the Class View and expand CPhoneTestDlg , you can see the new member variable.

In the same way, connect the static label called IDC_ERROR to a member variable called Error and connect IDC_MESSAGE to Message . If you have trouble clicking the static controls because they don't have a caption, use the drop-down box at the top of the Properties window to select them. Just drop it down and select the appropriate ID; you'll see the control selected on the dialog box. Then you can right-click it to add the variable.

Connecting the Button on the Dialog Box to a Function

Click the Validate button on the dialog box. At the top of the Properties window is a toolbar. Find the Event button (it looks like a lightning strike) and click it. Click to the right of BN_CLICKED ; a drop-down box appears. Drop it down and choose the only entry, <Add> OnBnClickedValidate . The function name appears in the Class View underneath CPhoneTestDlg and you are switched to editing the function.

Coding the Function

When the button is clicked, the test code calls the ValidatePhoneNumber() method of the IPhoneNumber interface, as implemented in the CPhoneNumber class developed earlier in this chapter. Your coding effort will be substantially less if you use the #import directive. The #import directive enables you to treat a COM component as though it were an ordinary C++ object.


If you get any errors when you compile that refer to the #import line, make sure the path to PhoneFormat.dll is correct for your machine. (The .. in this line ensures that as long as the PhoneFormat and PhoneTest project folders are both under the same folder, this line will work.) Don't move the DLL; change the #import directive if necessary.

Open the source code for CPhoneTestDlg , and scroll to the top of the file. After the #include statements that are already there, add this line:

 #import "..\PhoneFormat\Debug\PhoneFormat.dll"  no_namespace 

Edit OnBnClickedValidate() so that it looks like this:

 void CPhoneTestDlg::OnBnClickedValidate() {     USES_CONVERSION;     UpdateData();     _bstr_t number = Number;     unsigned char errorcode = 0;     BSTR errormessage;     try      {         IPhoneNumberPtr phone("PhoneFormat.PhoneNumber");         phone->ValidatePhoneNumber(number, &errorcode, &errormessage);         Error.Format("%u", errorcode);         Message = W2A(errormessage);     }     catch (_com_error e)     {         Error.Format("%u", 99);         Message = e.ErrorMessage();     }     UpdateData(false); } 

This function, like ValidatePhoneNumber() , uses the USES_CONVERSION macro to bring in some simple conversion macros. The call to UpdateData() moves the value the user typed in the edit box into the member variable, Number . Because ValidatePhoneNumber() takes a BSTR , this code creates a _ bstr_t variable to pass in. The _bstr_t type encapsulates a BSTR with easy-to-use constructors that call SysAllocString for you, if required.

The actual COM call is wrapped in a try block in case anything goes wrong, such as the COM component being unavailable. Thanks to the #import directive, creating an instance of the COM component and calling its methods looks just like creating an ordinary C++ object. If you were wondering how to decide what string to pass into the IPhoneNumberPtr , it's the progid (short for program ID) of the COM componentthe name of the project, a dot, and the name of the class you added into the project PhoneFormat.PhoneNumber , in this case. In cases where you don't write the COM component you are using, expect to be told the progid.


If you haven't seen the Format() method of the CString class before, it's an easy way to convert a number to a string. The %u for the first parameter indicates that you want the number treated as a simple unsigned number.

After the call to ValidatePhoneNumber() , this code sets the member variables associated with the two static controls, and then calls UpdateData(false) to send the new values to the dialog box.

Build the project at this point to make sure you don't have any errors, but it's not quite ready to run.

Adjusting the Structure of the Application and Adding COM Support

In the Class View, expand CPhoneTestApp and double-click OnInitInstance to edit it. Most dialog-based applications respond differently when the dialog box is dismissed with OK than when it is dismissed with Cancel. This one does not need to, so the code can be quite a bit simpler. Find and remove these lines:

 INT_PTR nResponse = dlg.DoModal(); if (nResponse == IDOK) {     // TODO: Place code here to handle when the dialog is     //  dismissed with OK } else if (nResponse == IDCANCEL) {     // TODO: Place code here to handle when the dialog is     //  dismissed with Cancel } 

In their place, add this single line:


(Because you changed the ID of the Validate button from IDOK to IDC_VALIDATE , clicking it doesn't dismiss the dialog box.) To activate COM support for this application, add this line at the very beginning of InitInstance() :


To clean up your COM work before returning from InitInstance() , add this line after the call to DoModal() and before the return statement:


Now, build the project again. It's ready to run. Enter a good phone number, such as (800) 555-1212 and click Validate: You see an error code of 0 and a message of OK. Validate a bad phone number, and you see an error code of 1 and a message of Bad format . Click Close, and the application ends.

Debugging a COM Application

If your COM component doesn't behave as you expect, you need to debug it. There's really nothing special about the process in this case, even though some of the code is not in the solution you have open. Here's how to step from OnBnClickedValidate() into ValidatePhoneNumber() :

  1. Put a breakpoint on the line in OnBnClickedValidate() that calls ValidatePhoneNumber() .

  2. Start the application by choosing Debug, Start Debugging. When the dialog box appears, enter a phone number.

  3. Control stops when it reaches your breakpoint. Click the Step Into button on the Debug toolbar, or press F11.

  4. The debugger first steps into a bstr_t copy constructor that is making a copy of number. Press Shift+F11 or click Step Out to leave this constructor.

  5. Step into the function call once again and notice that control is inside the overload of operator -> for the smart COM pointer that the #import directive created. Step out of this function also.

  6. Step into the call for a third time and notice that the smart COM pointer's code for ValidatePhoneNumber() is actually calling a function called raw_ValidatePhoneNumber() . Step over (F10) once, and then Step into the call to raw_ValidatePhoneNumber() .

  7. You'll find yourself in the code for another operator overload. Step out.

  8. Step in one more time andpresto! You're in the code you wrote for ValidatePhoneNumber() . Now that you're here, try setting a breakpoint. That saves you having to step in and out so much on the way to the relevant code.

After you have set a breakpoint in the COM component from inside your test solution, you should find it pretty simple to watch the component at work and understand any errors. Even if you stop debugging and start up again, your breakpoint will still be in place.

Microsoft Visual C++. NET 2003 Kick Start
Microsoft Visual C++ .NET 2003 Kick Start
ISBN: 0672326000
EAN: 2147483647
Year: 2002
Pages: 141
Authors: Kate Gregory © 2008-2017.
If you may any questions please contact us: