C++
Listed below are the questions that we are asked quite often. Before you write us, be sure to check here.

Programming

Programming

How do I insert your control to a MDI project?

The multiple-document interface (MDI) technique uses a single primary window, called a parent window, to visually contain a set of related document or child windows. Each child window is essentially a primary window, but it is constrained to appear only within the parent window instead of on the desktop. 

In Visual Studio 6.0, you have to do the followings ( in order to create a skeleton MDI project ):

  • Select File\New item
  • Select MFC AppWizard (exe)
  • Type the project name in the "Project name" field
  • Select "Multiple documents" when the MFC AppWizard is asking "What type of application would you like to create?"
  • Click Next button, until the AppWizard displays the list of classes that will be created. 
  • Select the C...View class ( where ... is the project name ). By default, it is derived from the CView class
  • Select  the CFormView as base class for the C...View class ( where ... is the project name )
  • Click Finish.

The AppWizard creates the skeleton for your MDI project. Now, follow the steps ( in order to insert an ActiveX control to the dialog ) 

  • Locate the "Resource View" page, Locate the "Dialog" item, and Locate the resource dialog for the view ( IDD_..._FORM, where ... is the project name). ( By default, the AppWizard open automatically the resource dialog for the CFormView class ).
  • Do a right click on the dialog resource, and select "Insert ActiveX Control" item, from the resource dialog's context menu.
  • Look in the "ActiveX control" list for the control that you are going to insert ( let's say exGrid ).
  • click the OK button. 

After all these you have the ActiveX control in any view of your MDI application.

See Also: I have your control on my VC dialog and it flickers when I click the control. Any suggestion?, How do I insert a member variable of ActiveX type to my application, using C++ Visual Studio 6.0? 

How do I insert a member variable of ActiveX type to my application, using C++ Visual Studio 6.0?

The application requires C++ wrapper classes to handle member variables of ActiveX type. 

In Visual Studio 6.0 you can follow the steps:

  • Insert the ActiveX control to your dialog/formview
  • In design mode, select the control, and invoke the class wizard ( CTRL + W )
  • Select "Member Variables" page
  • Select the identifier of the control, and press the "Add Variable ..." button
  • The ClassWizard asks 'The ActiveX Control "..." has not been inserted into the project. Developer Studio will do this now, and generate a C++ wrapper class for it', click OK
  • A confirmation dialog will be shown, click ok 
  • The  "Add Member Variable" dialog shows up, and is asking for the name of the variable that needs to be inserted
  • Type the name of the variable and click ok.
  • Click OK, to close the ClassWizard dialog.

How do I insert a member variable of ActiveX type to my application, using C++ Visual Studio 7.0?

In C++ Visual Studio 7.0, the application requires wrapper classes to handle member variables of ActiveX type, like in the Visual Studio 6.0. In Visual Studio 7.0, the ClassWizard is missing, instead you have Project\Add Class option to add C++ wrapper classes for a type library.

In Visual Studio 7.0 use these steps to add a member variable of ActiveX control:

  • Insert the ActiveX control to your dialog/formview
  • Select the "Class View" page
  • Select the project item
  • Select the "Project\Add Class" menu item
  • Select the "Visual C++\MFC" category, and select the "MFC Class From ActiveX Control"
  • Click Open, and the "Add Class From ActiveX Control Wizard" dialog is shown
  • Select your ActiveX control in the "Available ActiveX Controls" list
  • # Select the default interface of the ActiveX control, and move it to the "Generated classes" list. The wizard will generate a c++ wrapper class for each interface selected. 
  • Click Finish, and the "Add Class" wizard added the wrapper classes for your ActiveX control. 
  • If you need other classes used by the ActiveX component, select again the Add Class menu item
  • In the Templates window, select MFC Class From TypeLib
  • Click Open, and the "Add Class From Typelib Wizard" dialog is shown
  • Select your ActiveX component (dll, ocx, tlb files)
  • From the Interfaces list, select all the objects that you will need 
  • Click on the Add button (>)
  • Click on Finish, and the "Add Class" wizard will add the wrapper classes for all the objects you selected
  • Select the ActiveX control in design mode
  • Select "Project\Add Variable..." menu item, and the Developer Studio will show up the "Add Member Variable Wizard" dialog.
  • Type the "Variable type" ( the name of the class that's generated at point # )
  • Type the "Variable name"
  • Click Finish

After all these steps, the Developer Studio adds a member of ActiveX type to your dialog/formview class. For instance, if you generated c++ wrapper class for exGrid ActiveX control, CGrid class, your member variable will be declared as CGrid grid, where 'CGrid' is the name of the wrapper class, and 'grid' is the name of the member variable. Now, once that you have declared your ActiveX member variable, you can call methods or properties of the control using this wrapper class.

If you need to use a property of the CGrid class that gets an IDispatch* (LPDISPATCH) ( in the CGrid wrapper class ) you can use a wrapper class that handles that specific type. For instance, in the CGrid wrapper class of the exGrid ActiveX control, the Items property looks like following:

LPDISPATCH get_Items()
{
	LPDISPATCH result;
	InvokeHelper(0xc, DISPATCH_PROPERTYGET, VT_DISPATCH, (void*)&result, NULL);
	return result;
}

In this case, we need to add a new wrapper class that handles the IItems interface of the exGrid ActiveX control. Use the following steps to add new wrapper classes from the control's Type Library:

  • Select "Project\Add Class" menu item
  • Select "Visual C++\MFC" category in the "Add Class" wizard
  • Select the "MFC Class From TypeLib" template
  • Click Open, and the Developer Studio runs the "Add Class From Typelib Wizard" dialog
  • Select the ExGrid ActiveX Control in the "Available type libraries" list
  • Select the IItems interface in the "Interfaces" list, and move it to "Generated classes" list
  • Click Finish, and the wizard will generate and include the CItems wrapper class for IItems interface, to your application

Using the same technique, you can include wrapper classes for all types that you need to use like IColumn, IColumns, and so on. The following sample shows how to add a column and an item using the member variable that we have added earlier:

m_grid.BeginUpdate();
	CColumns0 columns = m_grid.get_Columns();
	columns.Add( _T("Column 1") );
	CItems items = m_grid.get_Items();
	items.AddItem( COleVariant( _T("new item") ) );
m_grid.EndUpdate();

Before compiling and running the code, you need to include:

#include "CItems.h"
#include "CColumns0.h"

How can I change the size of the control's font?

The following sample VC++ 6.0 changes the font for the exEdit control, at runtime, using the OleCreateFontIndirect API function:

The following sample VC++ NET changes the font for the exGrid control, at runtime, in a MFC application, where m_grid is a CWnd class that hosts an exGrid component:

The sample uses the #import <exgrid.dll> line to import the control's type library. It defines the EXGRIDLib namespace, where you can locate definitions for all objects, methods, properties and events that belong to the control. The VC++ .NET is not able to generate the control's wrapper, so you can use the #import keyword to import the control's type library to your project.

If your Developer Studio Class Wizard generates the COleFont class, it may be used to change the control's font as follows. The IFontDisp interface is a system interface and it handles fonts. In the following sample we are using the exEdit component. The idea is the same for any component. 

The following sample changes the size of the control's font:

COleFont font = m_edit.GetFont();
COleCurrency cur( 9, 0 );
font.SetSize( cur.m_cur );

The sample uses the MFC COleCurrency class. This class encapsulates the CURRENCY (CY) data type used in Automation. CURRENCY is implemented as an 8-byte, two's-complement integer value scaled by 10,000. This gives a fixed-point number with 15 digits to the left of the decimal point and 4 digits to the right. The CURRENCY data type is extremely useful for calculations involving money, or for any fixed-point calculation where accuracy is important. It is one of the possible types for the VARIANT data type of Automation. The Size member of the IFontDisp interface gets the CY type.

How can I convert a color expression for a <fgcolor> built-in HTML tag?

A color expression is a value being returned by the RGB function. The <fgcolor> built-in HTML tag requires the color data being passed as <fgcolor=RRGGBB>, where the RR is the hexa representation of the red value of the color, GG is the hexa representation of the green value of the color, and BB is the hexa representation of the blue value of the color. In order to convert a color expression to a RRGGBB string expression you need something like: StrReverse(Right("000000" + Hex(color), 6)), where
  • StrReverse returns a string in which the character order of a specified string is reversed
  • Right returns a specified number of characters from the right side of a string.
  • Hex returns a string representing the hexadecimal value of a number

I've got a new version of the component but I am not able to see any new functions. What I am doing wrong?

  • The wrapper classes of the component need to be refreshed when VC++ is used. The simplest way to refresh the wrapper classes is to insert the component to a new project, and to copy the generated wrapper classes to your old project.  
  • In case you are using .NET environment, C# or VB.NET you need to replace the wrapper files AxInterop.NAMEOFTHETYPELib.dll and Interop.NAMEOFTHETYPELib.dll ( for instance, AxInterop.EXCOMBOBOXLib.dll or Interop.EXCOMBOBOXLib.dll for exComboBox control ) in the bin and obj folders with the new versions. TO get the new versions, just create a new project, insert the component to the form, and build the project. Please copy the wrapper files from new bin\Debug folder to old bin\Debug folder, and so on.

Almost all of our components expose a Version property that specifies the control's version. The control's release notes specify all changes to the component for each version.

I use your control into an ATL dialog and I have problems using the GetDlgControl method. What do you suggest?

The GetDlgControl method uses the AtlAxGetControl method, so it implies using AtlAx functions documented in the MSDN. There are few tips that you should know about:

  • the AtlAxWinInit method should be called before using any AtlAx function. The AtlAxWinInit function initializes ATL's control hosting code by registering the "AtlAxWin7" and "AtlAxWinLic7" window classes plus a couple of custom window messages.
  • the dialog that hosts the componet should be derived from CAxDialogImpl class. By default, the dialog class is derived from CDialogImpl.
  • The AtlAdviseSinkMap method should be called on dialog's OnInitDialog message like following: AtlAdviseSinkMap( this, TRUE );
  • The GetDlgControl function should be called like follows: ( in this case we have used the ExComboBox control. but it is similar for other controls too ):
	EXCOMBOBOXLib::IComboBoxPtr spCombo;
	GetDlgControl( IDC_COMBOBOX1, __uuidof(EXCOMBOBOXLib::IComboBox), (LPVOID*)&spCombo );

I have your control on my VC dialog and it flickers when I click the control. Any suggestion?

By default, your dialog styles doesn't include the WS_CLIPCHILDREN and WS_CLIPSIBLINGS styles. 
  • WS_CLIPCHILDREN Excludes the area occupied by child windows when you draw within the parent window. Used when you create the parent window
  • WS_CLIPSIBLINGS Clips child windows relative to each other; that is, when a particular child window receives a paint message, the WS_CLIPSIBLINGS style clips all other overlapped child windows out of the region of the child window to be updated. (If WS_CLIPSIBLINGS is not given and child windows overlap, when you draw within the client area of a child window, it is possible to draw within the client area of a neighboring child window.) 

You have to open the project resource editor, and to check the "Clip children" and "Clip Siblings" check boxes that can be found in the dialog's Properties\Styles tab. Shortly, when a dialog is shown, the Windows OS sends a WM_ERASE message to the dialog, and then a WM_PAINT message. The WM_ERASE message carries a device context that should be used when dialog erases its background. If the style of the dialog doesn't include the styles like WS_CLIPCHILDREN and WS_CLIPSIBLINGS the passed device context includes the area of the dialog child windows too. So, the dialog erases the area that belongs to its child windows. If the styles of the dialog includes the WS_CLIPCHILDREN and WS_CLIPSIBLINGS, the device context passed to the WM_ERASE message excludes the area occupied by the child windows of the dialog, and so each child window can paint its background for itself. Some controls use the parent's device context, and the problem could not appear.  It depends on the window class style. See also the Q280801 (Avoid WS_CLIPCHILDREN and ActiveX Control Painting Problems) in your MSDN.

Check also:

Including the component to an ATL project.

Any component can be inserted to your ATL project. In the following sample we have inserted an ExPropertiesList control but the things are the same for all components.
  • In the resource editor, insert the component to the dialog
  • Derive the base dialog class from CAxDialogImpl. By default, the base dialog class is derived from the CDialogImpl.
  • Include the definitions for all interfaces of the component using the #import keyword like ( by default, all components are installed on your system folder, so from case to case you need to replace the path with the folder where the component was installed )   :
    #import "c:\\winnt\\system32\\ExPropertiesList.dll"
  • Handle the dialog's WM_INITDIALOG message like bellow. The snippet uses the followings ATL functions defined in the ATL.LIB library
    • AtlAxGetControl. Obtains a direct interface pointer to the control contained inside a specified window given its handle
    • AtlAxGetHost. Obtains a direct interface pointer to the container for a specified window (if any), given its handle

How can I load a bitmap resource using the Images method?

The following sample assigns at runtime a list of icons from the application's resources. The sample uses the CImageList class to load the list of icons. 
CImageList imageList;
imageList.Create( IDB_ICONS, 16, 0, RGB(255,255,255) );

m_explorerTree.BeginUpdate();
m_explorerTree.Images( COleVariant( (long)imageList.operator HIMAGELIST() ) );
CGroups groups = m_explorerTree.GetGroups();
CGroup group = groups.Add( "Group 1" );
group.SetImage( 1 );
m_explorerTree.EndUpdate();

where the m_explorerTree is a member of CExplorerTree class ( wrapper class for Exontrol's ExplorerTree control ). You can use also the CreateFromImage method of the CImageList, like CreateFromImage( IDB_ICONS, 16, 0, CLR_NONE, IMAGE_BITMAP, LR_CREATEDIBSECTION ).

Thanks to  Jin Ho Tan, who submitted the note.

How can I declare a missing parameter?

In C++, a missing member should be a VARIANT of VT_ERROR type like in the following sample:
COleVariant v;
V_VT( &v ) = VT_ERROR;

How to drag and drop data from your ActiveX control to a standard Windows control (listbox, ... )?

The RegisterDragDrop API function registers the specified window as one that can be the target of an OLE drag-and-drop operation and specifies the IDropTarget instance to use for drop operations. Shortly, you need an object that implements the IDropTarger interface, and to call the RegisterDragDrop API function. 

Using the MFC, the COleDropTarget class already implements the IDropTarget instance for a CView derived class, but it can be changed slightly to work for other type of windows like shown in the following snippet:

class COleListBox : public COleDropTarget
{
public:

	virtual DROPEFFECT OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject,
		DWORD dwKeyState, CPoint point)
	{
		return DROPEFFECT_COPY;
	}

	virtual DROPEFFECT OnDragOver(CWnd* pWnd, COleDataObject* pDataObject,
		DWORD dwKeyState, CPoint point)
	{
		return DROPEFFECT_COPY;
	}

	virtual BOOL OnDrop(CWnd* pWnd, COleDataObject* pDataObject,
		DROPEFFECT dropEffect, CPoint point)
	{
		FORMATETC cfFormat = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
		if ( HGLOBAL h = pDataObject->GetGlobalData( CF_TEXT, &cfFormat ) )
		{
			LPCTSTR szData = (LPCTSTR)GlobalLock(h);
			if (pWnd->IsKindOf(RUNTIME_CLASS(CListBox)))
				((CListBox*)pWnd)->AddString( szData );
			GlobalUnlock( h );
		}
		return TRUE;
	}
};

You need to declare a member of the COleListBox type, and to call the Register member of the COleListBox class to register your list box control. The OnDrop method is called when the user drags data to your listbox control. The OnDrop method verifies if the target window is of ListBox type, and then calls the AddString method to add the data to the listbox control.

How can I get a long expression from a VARIANT value?

A VARIANT expression is a structure that may hold a long, a string, an object, an array and so on.

The V2I (MFC) function converts a VARIANT expression to a long expression.

static long V2I( VARIANT vtValue, long nDefault = 0 )
{
	TRY
	{
		COleVariant vtLong;
		vtLong.ChangeType( VT_I4, &vtValue );
		return V_I4( &vtLong );
	}
	CATCH_ALL(e)
	{
	}
	END_CATCH_ALL
	return nDefault;
}

Whenever you have a VARIANT expression you may convert it using V2I( variantvalue ) function.

The V2I (non MFC) function converts a VARIANT expression to a long expression.

static long V2I( VARIANT* pv, long nDefault = 0 )
{
	if ( pv )
	{
		if ( ( pv->vt == VT_ERROR ) || ( pv->vt == VT_EMPTY ) )
			return nDefault;

		CComVariant vt;
		if ( SUCCEEDED( vt.ChangeType( VT_I4, pv ) ) )
			return V_I4( &vt );
	}
	return nDefault;
}

How can I get a string expression from a VARIANT value?

A VARIANT expression is a structure that may hold a long, a string, an object, an array and so on.

The V2S (MFC) function converts a VARIANT expression to a string expression.

static CString V2S( VARIANT vtValue, LPCTSTR szDefault = _T("") )
{
	TRY
	{
		COleVariant vtString;
		vtString.ChangeType( VT_BSTR, &vtValue );
		return V_BSTR( &vtString );
	}
	CATCH_ALL(e)
	{
	}
	END_CATCH_ALL
	return szDefault;
}

Whenever you have a VARIANT expression you may convert it to a string using V2S( variantvalue ) function.

The V2S (non MFC) function converts a VARIANT expression to a string expression.

static std::string V2S( VARIANT* pv, LPCTSTR szDefault = _T("") )
{
	if ( pv )
	{
		if ( ( pv->vt == VT_ERROR ) || ( pv->vt == VT_EMPTY ) )
			return szDefault;

		CComVariant vt;
		if ( SUCCEEDED( vt.ChangeType( VT_BSTR, pv ) ) )
		{
			USES_CONVERSION;
			return OLE2T( V_BSTR( &vt ) );
		}
	}
	return szDefault;
}

We have found memory leaks on using your component on C++ / MFC. Is there something we can look for?

A memory leak is a particular type of unintentional memory consumption by a computer program where the program fails to release memory when no longer needed. This condition is normally the result of a bug in a program that prevents it from freeing up memory that it no longer needs. Generally, we use C++ to build our software products, so we are very carefully on avoiding having them, as we manage the memory consumptions for our components, C++ does not have a garbage collector or similar models. Generally, these memory holes appear when using wrapper classes generated using the MFC class wizard as these are not generate using smart pointers or _variant_t objects as #import directive does.  If you are using the classes being generated using the #import directive, you do not have to follow these rules, as the generated classes uses _variant_t for VARIANT types, that automatically release objects when no longer needed, and smart pointers for IDispatch or IUnkown pointers.

Here's some rules that you have to follow:

  • Whenever a generated property or method retrieve a VARIANT type call the VariantClear() API to release the variant value. For instance, the VARIANT value may refer to an object (IDispatch*), interface ( IUnknown*) , safe arrays, a string and so on, that must be released when no longer need them.

        For instance, we have the generated method VARIANT Add(LPCTSTR ColumnCaption); so once we have called the Add method we must call the VariantClear to release the object, as 

    VariantClear( &cols.Add("Column 1") );

    instead

    cols.Add("Column 1")

     

  • If a property or method retrieves an IDispatch* (LPDISPATCH), IUnknown*, (LPUNKNOWN) the Release method must be called when no longer need.

        For instance, we have the generated method: LPDISPATCH GetUserEditorObject(), so the code must look as:

    if ( IDispatch* pEditor = edCell.GetUserEditorObject() )
    {
    	MaskEditLib::IMaskEditPtr spMaskEdit( pEditor );
    	if ( spMaskEdit != NULL )
    	{
    		spMaskEdit->put_MaskFloat( TRUE );
    		spMaskEdit->put_Mask( (bstr_t)Format );
    	}
    	pEditor->Release();
    }

    instead

    MaskEditLib::IMaskEditPtr spMaskEdit( edCell.GetUserEditorObject() );
    if ( spMaskEdit != NULL )
    {
    	spMaskEdit->put_MaskFloat( TRUE );
    	spMaskEdit->put_Mask( (bstr_t)Format );
    }

How can I use the CImageList with your component?

Most of our UI components provide the Images method that can be used to use an ImageList for displaying icons in the component. The Images method can take a a long expression that identifies a handle to an Image list ( the Handle should be of HIMAGELIST type ) or a string expression that indicates the base64 encoded string that holds the icons list.

If you are using the CImageList, you need to pass the operator HIMAGELIST() of the CImageList object to Images method like in the following sample:

CImageList myImage;
 myImage.Create( 16, 16, ILC_COLOR32 | ILC_MASK , 0, 0 );
 myImage.Add( LoadIcon( AfxGetInstanceHandle(), MAKEINTRESOURCE( IDR_MAINFRAME ) ) );
 m_spGrid->Images( (long)myImage.operator HIMAGELIST() );

where the m_spGrid is the member of EXGRIDLib::IGridPtr type which is type that's defined once you add the #import <exgrid.dll>  for the eXGrid/COM component. For instance, you have EXG2ANTTLib::IG2anttPtr for eXG2antt/COM when importing the type library using the #import <exg2antt.dll>

If no icons are displayed, we would suggest to check for the following:

  • operator HIMAGELIST() MUST returns a not-zero value.
  • GetImageCount() functions returns a non-zero value, or use the ImageList_GetImageCount API in the COMCTL32.DLL dll.

The following sample assigns the Explorer's ImageList to the eXGrid component:

SHFILEINFO sfi = {0};
m_spGrid->Images((long)(HIMAGELIST)SHGetFileInfo( _T("C:\\"),  0, &sfi, sizeof (SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_SMALLICON ));

where the m_spGrid is the member of EXGRIDLib::IGridPtr type which is type that's defined once you add the #import <exgrid.dll>  for the eXGrid/COM component.

How can I instantiate the component using the IClassFactory or IClassFactory2?

Bellow you will find the definitions for createFactory functions that can be used to instantiate our /COM objects using the IClassFactory or IClassFactory2 interfaces. 
#include <atlbase.h>

//   FUNCTION: createFactory( const IID&, BSTR, IUnknown* , IUnknown** )
//
//   PURPOSE: Instantiates the giving component giving its IID, and returns the result in the last parameter.
//
//   COMMENTS:
//
//		The createFactory method uses the available interfaces: IClassFactory or IClassFactory2
//
//			- iidControl: specifies the IID of the object to be instanciated. For instance, __uuidof(EXGRIDLib::Grid), __uuidof(EXG2ANTTLIB::G2antt), and so on
//			- strRuntimeKey: indicates the obejct's runtime license key. Please pay attention that this is NOT the control's developer license.
//			- pUnkOuter: if the object is being created as part of an aggregate, specify a pointer to the controlling IUnknown interface of the aggregate. Otherwise, this parameter must be NULL.
//			- ppResult: holds the result of the createFactory method, if the returned result is S_OK
//
HRESULT createFactory( const IID& iidControl, BSTR strRuntimeKey, IUnknown* pUnkOuter, IUnknown** ppResult )
{
	HRESULT hResult = E_FAIL;
	CComPtr<IClassFactory> spClassFactory;
	if ( SUCCEEDED( hResult = CoGetClassObject( iidControl, CLSCTX_ALL, NULL, IID_IClassFactory, (LPVOID*)&spClassFactory ) ) )
		if ( FAILED( hResult = spClassFactory->CreateInstance( pUnkOuter, IID_IUnknown, (LPVOID*)ppResult ) ) )
		{
			CComPtr<IClassFactory2> spClassFactory2;
			if ( SUCCEEDED( hResult = CoGetClassObject( iidControl, CLSCTX_ALL, NULL, IID_IClassFactory2, (LPVOID*)&spClassFactory2 ) ) )
				hResult = spClassFactory2->CreateInstanceLic( pUnkOuter, NULL, IID_IUnknown, strRuntimeKey, (LPVOID*)ppResult );
		}
	return hResult;
}

//   FUNCTION: createFactory( BSTR, BSTR, IUnknown* , IUnknown** )
//
//   PURPOSE: Instantiates the giving component giving its ProgID (short for PROGrammatic IDentifier), and returns the result in the last parameter.
//
//   COMMENTS:
//
//		The createFactory method uses the available interfaces: IClassFactory or IClassFactory2
//
//			- iidControl: specifies the IID of the object to be instanciated. For instance, __uuidof(EXGRIDLib::Grid), __uuidof(EXG2ANTTLIB::G2antt), and so on
//			- szControlKey: indicates the obejct's runtime license key. Please pay attention that this is NOT the control's developer license.
//			- pUnkOuter: if the object is being created as part of an aggregate, specify a pointer to the controlling IUnknown interface of the aggregate. Otherwise, this parameter must be NULL.
//			- ppResult: holds the result of the createFactory method, if the returned result is S_OK
//
HRESULT createFactory( BSTR strProgID, BSTR strRuntimeKey, IUnknown* pUnkOuter, IUnknown** ppResult )
{
	HRESULT hResult = E_FAIL;
	CLSID clsid = CLSID_NULL;
	if (SUCCEEDED( hResult = CLSIDFromProgID( strProgID, &clsid ) ) )
		hResult = createFactory( clsid, strRuntimeKey, pUnkOuter, ppResult );
	return hResult;
} 

The following code shows how to use the first declaration of the createFactory function, on eXGrid/COM object.:

#import <exgrid.dll>

CComPtr<IUnknown> spUnkGrid = NULL;
if ( SUCCEEDED( createFactory( __uuidof(EXGRIDLib::Grid), L"XXXXXXXX", NULL, &spUnkGrid) ) )
{
	EXGRIDLib::IGridPtr spGrid = spUnkGrid.p;
	if ( spGrid != NULL )
	{
		MessageBox( NULL, _T("Created using the CLSID"), NULL, NULL );
	}
}		

The following code shows how to use the second declaration of the createFactory function, on eXGrid/COM object.:

#import <exgrid.dll>

CComPtr<IUnknown> spUnkGrid = NULL;
if ( SUCCEEDED( createFactory( L"Exontrol.Grid", L"XXXXXXXX", NULL, &spUnkGrid) ) )
{
	EXGRIDLib::IGridPtr spGrid = spUnkGrid.p;
	if ( spGrid != NULL )
	{
		MessageBox( NULL, _T("Created using the ProgID"), NULL, NULL );
	}
}

The XXXXXXXX is not a valid runtime license key. Your development license key can NOT be used as runtime license key. You can get the control's runtime license key only if you own a developer license key. You can ask or find your runtime license key as explained here.

Opening Property Page Results in "Class Not Registered"

When you open the property page for a control, the following error message appears:

Class Not Registered.
Looking For Object With CLSID {7EBDAAE1-8120-11CF-899F-00AA00688B10}

The 

  • {7EBDAAE0-8120-11CF-899F-00AA00688B10} indicates MS Stock Font Property Page Object
  • {7EBDAAE1-8120-11CF-899F-00AA00688B10} indicates MS Stock Color Property Page Object
  • {7EBDAAE2-8120-11CF-899F-00AA00688B10} indicates MS Stock Picture Property Page Object

All these are implemented by the msstkprp.dll file of Microsoft Windows System. 

You need to manually register the msstkprp.dll file.

  • Copy the msstkprp.dll file from \OS\System folder on your Visual Basic/C++ CD to your Windows\System folder (or the System32 folder if you are using Microsoft Windows NT).
  • Use the RegSvr32 utility to manually register the msstkprp.dll. On the Windows Start menu, click Run, and then type the following command:
    regsvr32 C:\Windows\System\msstkprp.dll

On x64 systems:

  • Copy the msstkprp.dll file from \OS\System folder on your Visual Basic/C++ CD to your Windows\SysWOW64 folder
  • Use the RegSvr32 utility to manually register the msstkprp.dll. On the Windows Start menu, click Run, and then type the following command: regsvr32 C:\Windows\SysWOW64\msstkprp.dll

Source: http://support.microsoft.com/KB/188331

Where I can find the identifiers of the events I can put on ON_EVENT macro?

In order to find out the identifier of any of the control's event, you can choose any of the following:
  • If you are using the #import directive to include the component into your C++ project, locate the TLI file in your project's folder, where you will find all the information you might need to use the component in the C++ environment, including the identifiers of the events. For instance, let's say we are looking for identifier in the eXGrid control. So, locate the EXGRID.TLI file, opens it using the NOTEPAD or any other text viewer, and search for the name of the event you are looking for until you get something like ( we were looked for AddColumn event )
    inline HRESULT _IGridEvents::AddColumn ( struct IColumn * Column ) {
        return _com_dispatch_method(this, 0x6, DISPATCH_METHOD, VT_EMPTY, NULL, 
            L"\x0009", Column);
    }

    where the 0x6 is the identifier of the AddColumn event.

  • If you are using the class generated by a MFC Class Wizard, locate the CI___EVENTS.H file where you can find definitions of the events exported by the control. The ___ should be the name of the control such as Grid, G2antt, and so on. For instance, let's say we are looking for identifier in the eXGrid control. So, locate the CIGridEvents.h file, opens it using the NOTEPAD or any other text viewer, and search for the name of the event you are looking for until you get something like ( we were looked for AddColumn event )
    void AddColumn(LPDISPATCH Column)
    {
    	static BYTE parms[] = VTS_DISPATCH ;
    	InvokeHelper(0x6, DISPATCH_METHOD, VT_EMPTY, NULL, parms, Column);
    }
    

    where the 0x6 is the identifier of the AddColumn event. 

  • If you are familiar with OLEView Tool, you can run it, go to Object Classes\All Objects and open the key. Now, look or search for ExGrid, by typing the exgrid, once you locate the library, right click and select "View Type Information". The "ITypeLib Viewer" tool displays all the information that the type library exports, including the identifier of the events. For instance, of we are looking for events in the eXGrid component, search for "dispinterface _IGridEvents" item and select it, so now the right panel shows all the events that control supports. The id(number) in the IDL definition indicates the identifier of the event, as for the AddColumn event you will get something like:
    [id(0x00000006), helpstring("Fired after a new column has been added."), helpcontext(0x000002cd)]
    void AddColumn(IColumn* Column);

    where the 0x00000006 is the identifier of the AddColumn event. 

In the samples are generated interface files (Grid.h/cpp, Column.h/cpp et al). How can I re-generate them myself, using the Visual Studio 2010?

Here's the steps you need to follow to generate the classes using the MFC Class Wizard, in Visual Studio 2010
  • Open the dialog where the control is hosted.
  • Select the control, right click and select "Class Wizard Ctrl+Shift+X"
  • In the MFC Class Wizard, select the Add Class\MFC  Class From TypeLib...
  • Select the "Add Class From File"
  • Click ... of the "Location" field to locate and select the DLL you need to insert ( exgrid.dll, exg2antt.dll, exontrol.NETHost.tlb, and so on, usually located in your system folder )
  • Click ">> (Add all classes)"
  • Click Finish button

and so the MFC Class Wizard generated / inserted the wrapper-files to your project.

How can I build Isolated Application using your DLL as Isolated COM (Registration-Free Activation of COM)?

This is applicable only to COM objects.

An application is considered an isolated application if all of its components are side-by-side assemblies. A side-by-side assembly is a collection of resources—a group of DLLs, windows classes, COM servers, type libraries, or interfaces—available for an application to use at runtime. Typically, a side-by-side assembly is one to several DLLs.  

Isolated COM allows your application to use ActiveX components without having to register them. The original vision of this was to allow copy deployment of the application, but Isolated COM has many benefits. You can have a private copy of the DLL without worrying that another application will install an older or newer copy that breaks your application. Isolated COM also allows you to successfully install and run on non-Administrator accounts.

You can choose from one of the two samples: A) which uses the eXPrint only, while B) the second sample uses the eXG2antt and eXPrint as Isolated COMs.

A) You can download the following C++ sample here, which uses the eXPrint/COM component as an Isolated COM, and built using C++ on VS 2005.

  1. Prepare the TLB (Type Library file ) and DLL files for Isolated COM(s) you use, in our case exprint.tlb and exprint.dll. Actually you need only the DLL as the TLB can be extracted from the DLL. Open the resources of the DLL, locate the "TYPELIB" resource, and export the item 1 as binary. Rename the 1.bin to exprint.tlb, and so you have the TLB file.
  2. Use the #import directive to include the type definition for isolated com as #import "exprint.tlb" or #import "exprint.dll"
  3. Create any isolated component using the program identifier instead control identifier, which here means __uuidof( EXPRINTLib::Print ) instead "Exontrol.Print".
  4. Include the TLB and DLL in the "Isolated COM" of the "Manifest Tool" on the project's property.

    In VS 2005 do the following:

    • Open the property sheet for the project.
    • Click the plus sign next to "Manifest Tool"
    • Click on "Isolated COM".
    • Next to "Type Library File" enter "exprint.tlb"
    • Next to "Component File Name" enter "exprint.dll".

     

  5. Build the project

Once the EXE is built, unregister the DLL, copy it in the same folder where the EXE is. This way the EXE will use always the DLL being found in the same folder, no matter if any other application install a newer or older version of the component.

B) You can download the following C++ sample here, which uses the eXPrint/COM and eXG2antt/COM components as Isolated COMs and built using C++ on VS 2005.

See also: How to Generate Assembly Manifest File (Registration-Free)?

You can find additional information about isolated applications, registration-free activation here:

Is there any reason that on a CViewForm/MDI the control is not initially drawn?

There are several options to prevent this, like listed bellow.
  • Locate the dialog that hosts the component and change the Clip Children and Clip Siblings properties to True ( properties of the dialog ). This also, will prevent flickering the components in the form while it is resized. 

Also, you can check: I have your control on my VC dialog and it flickers when I click the control. Any suggestion?, for more details on Clip Children / Clip Siblings properties.

  • Call the DoEvents during the CFormView::OnInitialUpdate(). Locate the handler of the OnInitialUpdate and do the modifications as follows:

By default, your's CFormView's OnInitialUpdate method looks as:

void CYourFormView::OnInitialUpdate()
{
	CFormView::OnInitialUpdate();
	ResizeParentToFit();
}

and replace with the following:

void CYourFormView::OnInitialUpdate()
{
	CFormView::OnInitialUpdate();
	ResizeParentToFit();
	DoEvents();
}

and defines the DoEvents somewhere in your code such as:

static void DoEvents()
{
	MSG m = {0};
	while ( PeekMessage( &m, NULL, NULL, NULL, PM_REMOVE ) )
	{
		TranslateMessage( &m );
		DispatchMessage( &m );
	}
}

 

Right-To-Left Reading-Order / RTL Layout

While using language that supports reading-order alignment you can:

  • Change the control's RightToLeft property to True, so it: 

  • displays the vertical scroll bar on the left side of the control ( Scrollbars property )
  • flips the order of the columns ( Position property )
  • change the column's alignment to right, if the column is not centered ( Alignment property, HeaderAlignment property, HeaderImageAlignment property ) 
  • reverse the order of the drawing parts in the cells ( Def(exCellDrawPartsOrder) property to "caption,picture,icons,icon,check" ) 
  • aligns the locked columns to the right ( CountLockedColumns property )
  • aligns the control's group-by bar / sort bar to the right ( SortBarVisible property )
  • the control's filter bar/prompt/close is aligned to the right ( FilterBarPromptVisible property )
  • the control's chart is aligned to the left ( for eXG2antt, eXGantt, and so on )

The following C++ sample changes the RightToLeft property to True:

m_spGrid->PutRightToLeft(VARIANT_TRUE);

  • Add the DT_RTLREADING (0x00020000) style to layout the captions in right-to-left reading order for bi-directional text when the font is a Hebrew or Arabic font. The default reading order for any text is left-to-right, and for other languages this style is ignored.

The Column.HeaderAlignment, Column.Alignment and Items.CellHAlignment property of the eXList / eXTree / eXGantt / eXGrid / eXG2antt control specifies the caption's alignment/reading-order when displaying it in the control. 

The following C++ shows how you can call these properties to support the DT_RTLREADING style (using eXGrid):

m_spGrid->BeginUpdate();
m_spGrid->PutTreeColumnIndex(-1);
EXGRIDLib::IColumnPtr var_Column = ((EXGRIDLib::IColumnPtr)(m_spGrid->GetColumns()->Add(L"RTL - אבגד Header Caption")));
	var_Column->PutHeaderAlignment(EXGRIDLib::AlignmentEnum(0x20000 | EXGRIDLib::RightAlignment));
	var_Column->PutAlignment(EXGRIDLib::AlignmentEnum(0x20000 | EXGRIDLib::RightAlignment));
EXGRIDLib::IItemsPtr var_Items = m_spGrid->GetItems();
	var_Items->AddItem("RTL - אבגד Text Right"); // Inherits the alignment from the Column.Alignment property
	var_Items->PutCellHAlignment(var_Items->AddItem("RTL - אבגד Text Center"),vtMissing,EXGRIDLib::AlignmentEnum(0x20000 | EXGRIDLib::CenterAlignment));
	var_Items->PutCellHAlignment(var_Items->AddItem("RTL - אבגד Text Left"),vtMissing,EXGRIDLib::AlignmentEnum(0x20000));
m_spGrid->EndUpdate();
  • Add the WS_EX_RTLREADING (0x00002000) extended style for built-in editors. The following sample shows you how you can change the EDIT's style/extended style for control's built-in editors. For instance, if the shell language is Hebrew, Arabic, or another language that supports reading-order alignment, the WS_EX_RTLREADING extended style indicates that the edit text is displayed using right-to-left reading-order properties. For other languages, the style is ignored.

The Editing property of the eXGrid / eXG2antt control specifies the handle of the built-in editor. While the control is not in running mode, the Editing property returns 0. The EditOpen event notifies your application once the control creates the built-in editor, so during this event, the Editing property returns a valid handle. 

The following C++ sample changes the EDIT's style/extended, using the SetWindowLong / GetWindowLong API function, when control enters in edit mode ( using EditOpen event of the eXGrid ):

// CYourFormView message handlers
BEGIN_EVENTSINK_MAP(CYourFormView, CFormView)
	ON_EVENT(CYourFormView, IDC_GRID1, 27, CYourFormView::EditOpenGrid, VTS_NONE)
END_EVENTSINK_MAP()

void CYourFormView::EditOpenGrid()
{
	_ASSERTE( m_spGrid != NULL );
	{
		HWND hEditing = (HWND)m_spGrid->Editing;
		DWORD dwExStyle = GetWindowLong( hEditing, GWL_EXSTYLE );
		SetWindowLong( hEditing, GWL_EXSTYLE, dwExStyle | WS_EX_RTLREADING );
		//DWORD dwStyle = GetWindowLong( hEditing, GWL_STYLE );
		//SetWindowLong( hEditing, GWL_STYLE, dwStyle | ES_RIGHT );
	}
}

Copyright 1999-2017 Exontrol. All rights reserved.