[Previous] [TOC] [Next]

Connectivity Example

Let's say that we use Visual B++ to create an ActiveX DLL that contains a single class called CTest. Let's also say that CTest has a property (defined both as a procedure and as a public data item), a method (sub), and an event. Create the sub with no parameters, name the sub DoIt, and within the sub invoke Visual B++'s MsgBox routine with App.EXEName as an argument. How can this be consumed by a Visual C++ developer?

First compile the project to create the ActiveX DLL. Let's call the project ObjTest, so now we have OBJTEST.DLL. That does it for the Visual B++ piece. Notice that I haven't elaborated on the process here, as I'm assuming that this is not your first foray into building ActiveX components.

Next start Visual C++ and create a new MFC AppWizard workspace (select New from the File menu, then select MFC AppWizard (exe) from the Projects tab). Name the workspace Test. Using the wizard that starts when you click OK, add whatever options you want. If this is your first experience with MFC, C++, or Visual C++ (or all the above), I'd suggest you select Single Document in Step 1 and check the Automation checkbox in Step 3; leave the default choices for all the other options. When you click the wizard's Finish button, the wizard will build an application for you. The resulting application is Windows API, C++, and MFC code. Don't expect to see anything similar to the Visual B++ interface.

Next use the ClassWizard to add the necessary C++ classes to mirror your Visual B++-generated OBJTEST CTest class.

  1. Select ClassWizard from the View menu.
  2. In the MFC ClassWizard dialog box click the Add Class button and select From A Type Library.
  3. Select OBJTEST.DLL from the File Open dialog box. The next dialog box should show _CTest and __CTest highlighted, since it should be your only class. Click OK. The ClassWizard will now add more C++ source code to your project.

Now that all the automatic code has been built, select Find In Files from the Edit menu, enter AfxOleInit, and then hit Enter. Double-click the line that appears in the Find In Files 1 tab in your Output window to go to the code that the IDE found. The code will look something like this:

// Initialize OLE libraries
  if (!AfxOleInit())
  {
      AfxMessageBox(IDP_OLE_INIT_FAILED);
      return FALSE;
  }

This code was inserted because you checked the Automation checkbox in Step 3 of the AppWizard. Before an application can use any of the OLE system services, it must initialize the OLE system DLLs and verify that the DLLs are the correct version. The AfxOleInit routine does all we want it to. We need to run this routine, of course, because we're about to talk to-automate-our OBJTEST DLL via OLE and COM.

Navigate through the code to someplace suitable where you can add the necessary code to automate the DLL. I suggest you use Find In Files to locate void CTestApp::OnAppAbout(). This is a class function (I can't really explain what this is here, so please just carry on) that's used to create the application's About box. Replace the two lines of code between the C-style braces ({}) with the following:

_CTest * pCTest = new _CTest;

  if(FALSE == pCTest -> CreateDispatch("ObjTest.CTest"))
  {
      AfxMessageBox("Can't create dispatch table.");
  }
  else
  {
      pCTest -> DoIt();

      pCTest -> DetachDispatch();
      pCTest -> ReleaseDispatch();
  }

You also need to add a statement at the top of the file to include the new header file:

#include "ObjTest.h"

Next build and run the application. If you followed all my instructions exactly you shouldn't have any trouble building the application. If you do, go over the instructions once more.

So what just happened, from the top?

Using ClassWizard to add the type library caused Visual C++ to add some C++ classes to your project to mirror those in the DLL. Visual C++ will have named these classes the same as your Visual B++ classes prefixed with an underscore. _CTest is our main class.

The code we added to the About box routine creates a new _CTest C++ class instance and stores its address in memory in the variable pCTest (a pointer to a _CTest object, if you will). The C++ class inherits some routines (think of Implements), one of which we now call: pCTest -> CreateDispatch(). This routine connects C++ functions defined in our C++ class _CTest with interfaces in a CTest object-the Visual B++ object, that is. We then call DoIt, our routine that does something. You should see the message OBJTEST appear in a message box when this is called (when you select About from the Help menu). Since we're basically through with our Visual B++ class now, we disconnect ourselves from it, which is what DetachDispatch and ReleaseDispatch do.

NOTE
The class __CTest (two underscores) contains the event we defined.
Can these routines in the C++ classes be called from routines in other languages, perhaps from C? The answer is "Yes," because you can export the routines (make them available as "ordinary exports") by using a class modifier such as Class _declspec(dllexport) _CTest{};. However, this exposes the C++ class methods and properties via their decorated names; each will also be expecting to be passed an instance of the class type via a pointer parameter called this. All in all, not very easy.
NOTE


[Previous] [TOC] [Next]