SAMPLE: Using VBX Controls in a "Non-MFC" Application

ID: Q118494


The information in this article applies to:


SUMMARY

Occasionally, you may want to have the capability of using VBX controls in an application that has been written in straight C instead of C++ or that was not written to use the entire Microsoft Foundation Classes framework. Although the support for VBX controls is available only to a Microsoft Foundation Classes application, it is possible to create a minimal Foundation Classes application which provides the ability to use VBX controls.

The following sample, VBXC, demonstrates a technique for creating a minimal MFC application which enables VBX support. The "minimal MFC application" here is a C++ based module that uses the MFC CWinApp class and that can be linked with a C based application to provide support for VBX controls.


MORE INFORMATION

The following file is available for download from the Microsoft Software Library:

VBXC.EXE
For more information about downloading files from the Microsoft Software Library, please see the following article in the Microsoft Knowledge Base:
Q119591 How to Obtain Microsoft Support Files from Online Services
Any application which uses VBX controls will have to be a Microsoft Foundation Classes application. This simply means that it must have a CWinApp object. This article shows you how to create this CWinApp object, and provides the code necessary to support use of VBX controls. One of the consequences of including a CWinApp object is that you are automatically using the Foundation-provided WinMain function and the message loop contained within the CWinApp::Run() function. However, you must have a CWinApp object to use VBX controls in your application.

NOTE: Be aware that this is not intended as a replacement for knowledge of the Microsoft Foundation Classes. You will be linking with and calling into the MFC library functions, and you should be aware of how this will affect your program.

The idea is to create a CWinApp object which initializes the VBX support and then calls the underlying code in your application (i.e. it uses your application's WinMain). After creating a module which contains such an object, you can link with this module to obtain rudimentary support for VBX controls. You can then enhance the module as necessary to obtain further VBX capabilities. This article outlines the header and implementation files for a module which will give you this capability.

Following is the general implementation of the module that is referred to in this article as USEVBX:

////////////////////////////////////////////////// 
//   FILE USEVBX.H
// 
#ifdef __cplusplus      // two underscores
extern "C" {
#endif

void InitializeVBX(HWND hDlg,LPCSTR lpszResourceName);

#ifdef __cplusplus
}
#endif

// 
//  EOF - USEVBX.H
/////////////////////////////////////////////////// 

////////////////////////////////////////////////// 
// FILE USEVBX.CPP
// 
#include <afxwin.h>
#include <afxext.h>
#include "usevbx.h"

// Prototype the application's _WinMain
extern "C"
int PASCAL _WinMain(HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine,
                     int nCmdShow);

class CVBXApp : public CWinApp
{
public:
  virtual BOOL InitInstance();
  virtual int Run();
};

BOOL CVBXApp::InitInstance()
{
  EnableVBX();
  return CWinApp::InitInstance();
}

int CVBXApp::Run()
{
  // Call the main application's WinMain.
  return _WinMain(m_hInstance,m_hPrevInstance,
                  m_lpCmdLine,m_nCmdShow);
}

void InitializeVBX(HWND hDlg,LPCSTR lpszResourceName)
{
  CWnd wndDlg;
  wndDlg.Attach(hDlg);
  wndDlg.ExecuteDlgInit(lpszResourceName);
  wndDlg.Detach();
}

CVBXApp theVBXApp;

// 
// EOF - USEVBX.CPP
////////////////////////////////////////////////////////  
Once you have created the USEVBX.CPP source module, integrate it into your project in the following manner:

  1. Select Project/Edit and add the file USEVBX.CPP to your project.


  2. Link in the appropriate MFC libraries. (e.g. MAFXCWD, LAFXCWD). You can do this by just selecting Options/Project, and checking the "Use Microsoft Foundation Classes" option, or you can explicitly put the library file name in your link options under Options/Project/ Linker/Libraries. For more information on library naming conventions, see Appendix B of the "Class Library User's Guide".


  3. Rename your current application's WinMain function to _WinMain.


  4. For any dialog box which contains VBX controls, add a handler for WM_INITDIALOG which first calls InitializeVBX(hDlg,lpszResourceName). This will initialize the VBX controls with the properties you specify in App Studio. You should also include the USEVBX.H header file in any module which calls this function.


The InitializeVBX function first creates a CWnd object and attaches it to your dialog box window so that it can perform the VBX initialization. It then calls the function ExecuteDlgInit which performs ComboBox and VBX initialization. After that it must detach the CWnd object so that the dialog box window is not destroyed when the CWnd object is deleted (the CWnd object is defined as a local so it will be deleted as soon as we exit the function).

This will enable VBX support in your application. You will probably want to be able to use App Studio to put VBXs on your dialog templates. You can't do this with a non-Microsoft Foundation Classes resource file so you must convert your current RC file. There is an article which discusses how to perform this conversion. The article is available in the Microsoft Knowledge Base and can be found by querying on the following words:

converting rc file symbol directives

The article is Q99391, titled: "Converting an RC File to Use Microsoft Foundation Classes".

Once you have a resource script which is compatible with the Microsoft Foundation Classes you can use App Studio to put VBX controls on your dialog templates. You can also use App Studio to set initial properties for the controls, but for the initialization to be performed you must make the call to InitializeVBX in the WM_INITDIALOG processing of your dialog box.

To access the properties of the VBX you will have to create a C interface which implements the functionality contained in the CVBControl class. This can be done as needed. NOTE: The example VBXC implements most of these functions but the sample is for demonstration purposes only, and does not include instructions on how to re-implement the entire CVBControl class. For example, here is how you might add the capability to use the SetNumProperty and GetNumProperty functions:

  1. Insert these prototypes directly after the InitializeVBX function in the header file USEVBX.H:
    
    // The last parameter in CVBControl::GetNumProperty has a
    // default of 0 in C++ but the default of 0 must be supplied
    // by the caller in C because default parameters are not
    // allowed.
    LONG GetNumProperty(HWND hVBX,LPCSTR lpszPropName,int index);
    BOOL SetNumProperty(HWND hVBX,LPCSTR lpszPropName,
                        LONG lValue,int index); 


  2. Now implement the functions in the file USEVBX.CPP:
    
    LONG GetNumProperty(HWND hVBX,LPCSTR lpszPropName,int index)
    {
      CVBControl *pVBCtrl = (CVBControl *)CWnd::FromHandle(hVBX);
      ASSERT(pVBCtrl->IsKindOf(RUNTIME_CLASS(CVBControl)));
      return pVBCtrl->GetNumProperty(lpszPropName,index);
    }
    BOOL SetNumProperty(HWND hVBX,LPCSTR lpszPropName,
                        LONG lValue,int index)
    {
      CVBControl *pVBCtrl = (CVBControl *)CWnd::FromHandle(hVBX);
      ASSERT(pVBCtrl->IsKindOf(RUNTIME_CLASS(CVBControl)));
      return pVBCtrl->SetNumProperty(lpszPropName,lValue,index);
    } 


  3. To call these functions, you can simply use the Windows API function GetDlgItem to get the handle to the VBX window and pass it to the property function. e.g.
    
       // Toggle the TabStop property
       LONG l;
       HWND hwndVBX;
       hwndVBX = GetDlgItem(hDlg,IDC_VBX1);
       l = GetNumProperty(hwndVBX,"TabStop",0);
       SetNumProperty(hwndVBX,"TabStop",!l,0); 


NOTE: There is an assertion which verifies that the CWnd object retrieved is actually a CVBControl object. This might at first appear erroneous because the function call CWnd::FromHandle could return a temporary object which would simply be a CWnd, not a CVBControl. Considering that the calling application is not likely to be creating CVBControl objects itself, it seems that this function call would always return a temporary CWnd object, and the assertion would fail.

However, VBX controls are a special case. MFC implements VBX controls by always creating a permanent CVBControl object for every VBX control created. These objects are attached to the window of the VBX and are automatically deleted when the parent window of the VBX is destroyed.

Since there is a permanent CVBControl object attached to every VBX control which has been successfully created, the call to CWnd::FromHandle will always return the permanent CVBControl object. If it does not return the permanent handle, an assertion should be flagged.

An example of when you would get an assertion failure would be if the IDC_VBX1 identifier used above was actually the ID of a non-VBX control. In this case, there would be no CVBControl object attached and the ASSERT above would flag that.

NOTE: You can use this method to safely add VBX controls to a Windows application written in C. You should not, however, use this method to add other MFC classes to such an application. To do so would circumvent the MFC framework and cause the following problems:

  1. You are not using the framework's message loop. This means that none of the command routing or pre-translation of messages will occur.


  2. You are not calling the framework's idle processing. The biggest problem with this is that if you begin to use functions from the MFC libraries which create temporary objects (either directly or indirectly), those objects will never be deleted and you will have memory leaks.


If you would like to gain more of the functionality that the Microsoft Foundation Classes can provide in your application, then you should begin by doing a full conversion of your application to an MFC application. Refer to the "Class Library User's Guide" and the "Class Library Reference" for more information on how to convert your application to MFC.

Additional query words:


Keywords          : kbcode kb16bitonly kbMFC kbVBX kbVC kbVC100 kbVC150 
Version           : 1.00 1.50
Platform          : WINDOWS 
Issue type        : kbinfo 

Last Reviewed: July 28, 1999