Delphi
Frequently Asked Questions (exontrol)
DPH.0:
In RAD Studio Delphi 32-bit you can use any of the following versions:
  • /COM indicates the 32-bit edition of the ActiveX version

The application built using /COM version runs on any Windows 32 or 64-bit machine.

In RAD Studio Delphi 64-bit you can use any of the following versions:

  • /COM/64 indicates the 64-bit edition of the ActiveX version

The application built using /COM/64 version runs on Windows 64-bit machine only. The application built using /COM/64 version cannot run on Windows 32-bit machine.

If you want to use your application on 32 and 64-bit machines, you can go for:

  • /COM/ANY indicates the 32 and 64-bit editions of the ActiveX versions

Should I use a 32-bit or 64-bit version of the control?

DPH.1:

This error may occur when a /COM 32 or 64-bit module is being installed by a Delphi package (2010). The EFilerError exception is thrown because a component has already registered different objects with the same name as the new component you are installing. For instance, a Column object from eXG2antt generates a TColumn, while a Column object from eXGantt also generates a TColumn. Despite having the same name, they are distinct objects.

Typically, the T... classes are automatically generated as wrapper classes in the PAS extension based on the interfaces or objects provided by the control. When multiple controls use the same name for their interface, conflicts can arise, but this can be resolved by renaming.

At the end of each ..._TLB.pas file you intend to install, you will usually find something like:

procedure Register;
begin
  RegisterComponents(dtlOcxPage, [TGantt]);
  RegisterComponents(dtlServerPage, [TColumn, TColumns, TItems, TOleEvent, 
    TOleEventParam, TAppearance, TExDataObjectFiles, TExDataObject, TTemplate, 
    TTemplatePage, TChart, TLevel, TBar, TBars, 
    TConditionalFormat, TConditionalFormats]);
end;

All you need to do is globally replace each word in [] of dtlServerPage (check the Options: Case Sensitive, Whole word only and Scrope: Global) as 'TColumn', 'TColumns', ... with 'TColumn_Gantt', 'TColumns_Gantt', ... until you achieve the following:

procedure Register;
begin
  RegisterComponents(dtlOcxPage, [TGantt]);
  RegisterComponents(dtlServerPage, [TColumn_Gantt, TColumns_Gantt, TItems_Gantt, TOleEvent_Gantt,
    TOleEventParam_Gantt, TAppearance_Gantt, TExDataObjectFiles_Gantt, TExDataObject_Gantt, TTemplate_Gantt, TTemplatePage_Gantt, TChart_Gantt, TLevel_Gantt, TBar_Gantt, TBars_Gantt,
    TConditionalFormat_Gantt, TConditionalFormats_Gantt]);
end;

The installation is considered successful only when the message 'Package ...\Bpl....bpl has been installed. The following new component(s) have been registered: ....' appears.

If you have Visual Studio installed, you can replace all of these simultaneously by following these steps:

  • drag or open the ..._TLB.pas file into Vsiual Studio
  • Press CTRL + H to open the Replace dialog and :
    • at the Find field paste: \b(TColumn|TColumns|TItems|TOleEvent|TOleEventParam|TAppearance|TExDataObjectFiles|TExDataObject|TTemplate|TTemplatePage|TChart|TLevel|TBar|TBars|TConditionalFormat|TConditionalFormats)\b, !!! don't forget to check Match Case (Alt+C), Match Whole Word(Alt+W) and Use Regular Expression(Alt+R)
    • at the Replace field paste: $0_Gantt
    • click Replace All(Ctrl+Alt+Enter)
DPH.2:
This error could appear when a /COM 32 or 64 bits module is hosted by a Delphi application ( 2009, 2010 ).

The solution is disabling the FPU exceptions using the Set8087CW method as follows:

C++Builder

	#include "float.h"; 
	void disableFpu() { 
		_control87(PC_64|MCW_EM,MCW_PC|MCW_EM);        
	}
Delphi 3 or higher
	procedure DisableFpu;
	begin 
	  Set8087CW(Default8087CW or $3F); 
	end; 

"This is not so much a bug as an incompatibility between different compilers. There's a detailed explanation below. In short, what happens is that Delphi and C++Builder set the control word of the processor to a certain state, and applications or dlls written in Visual C++, for example, set it to a different state. This causes crashes (more precisely: floating point division by zero exceptions)."

Other notes:

Thanks to Jamie Rorrison, MindGenius Ltd, who submitted the note.
DPH.3:
This error could appear when a /COM 32 or 64 bits module is hosted by a Delphi application ( 2007, 2009, 2010, 2024 ). This error does not occur in Delphi 8.

The EVariantBadVarTypeError is the exception class for variant type errors. The EVariantBadVarTypeError is raised when a variant operation fails because of a value that isn't a valid variant type

The Delphi is not able to handle the events of Variant type, so the generated TLB file requires some changes for events that carries parameters of the Variant type, events such as BarResize, BarResizing, BarParentChange and so on. Running a sample with such of event handled, fires "Project raised exception class EVariantBadVarTypeError with message 'Invalid variant type'. Process stopped. Use Step or Run to continue." 

In order to prevent this, you can use the

  • Event handler and the EventParam properties to handle the BarResize event instead. For instance, the Event(120) is fired when the BarResize event occurs. The EventParam(0) gets the handle of the item where the bar is resized, and the EventParam(1) gets the key of the bar being resized. The EventParam property has effect only if it is called during the event itself. Calling outside the event, has no effect.
  • Change or make the following adjustments in the BarResize defintion.

So, let's assume that we are handing the eXG2antt's BarResize event which definition is:

event BarResize (Item as HITEM, Key as Variant)

There are three things you need to do so the BarResize is properly fired as follows:

  • change the TG2anttBarResize to TNotifyEvent in TLB file, so there is no parameter of OleVariant type.
  • update the definition for the generated event to procedure G2antt1BarResize(ASender: TObject); with no parameters. You have to use the EventParam property to access the event's parameters.
  • assign the new event to OnBarResize property during the FormCreate event of the form.

Here's the changes in detail. 

The Delphi generates the event BarResize as a property OnBarResize with following syntax:

property OnBarResize: TG2anttBarResize read FOnBarResize write FOnBarResize;

where the TG2anttBarResize is the type for the BarResize event/property which definition is:

TG2anttBarResize = procedure(ASender: TObject; Item: HITEM; Key: OleVariant) of object;

Replace the TG2anttBarResize definition as:

TG2anttBarResize = TNotifyEvent;

Save the TLB file. 

Now, we are going to the change unit where we have used the BarResize event and will do the following changes:

Change the declaration of the generated event from:

procedure G2antt1BarResize(ASender: TObject; Item: Integer; Key: OleVariant);

to:

procedure G2antt1BarResize(ASender: TObject);

and the implementation from:

procedure TForm1.G2antt1BarResize(ASender: TObject; Item: Integer; Key: OleVariant);

to

procedure TForm1.G2antt1BarResize(ASender: TObject);

Once you save you will be asked:

The G2antt1BarResize method referenced by G2antt1.OnBarResize has an incompatible parameter list. Remove the reference?

Click Yes, so the reference is removed as you will assign the event at runtime, in order to prevent showing this message.

Now, the last thing we have to do is to assign the new event handler for the OnBarResize property ( call this on TForm1.FormCreate(Sender: TObject) event )

G2antt1.OnBarResize := G2antt1BarResize; 

Save, and run the project. The BarResize event will be fired, and the EventParam property can be used to access the event's parameters without any error.

In conclusion the BarResize event may show as following:

procedure TForm1.G2antt1BarResize(ASender: TObject);
var s : String;
begin
  with G2antt1 do
  begin
    s := 'Item: ' + VarToStr(EventParam[0]);
    s := s + ' Key: ' + VarToStr(EventParam[1]);
    MessageBox(0,PChar(s),'Parameters',0);
  end
end;
You can use the EventParam to change a specified parameter, if you are calling something like EventParam[Index] := Value; For this, you have to make sure that the parameter is passed by reference ( it is preceded by a var keyword ).

See also: Accessing event parameters declared as OleVariant types

DPH.4:
This error could appear when pasting code from the exontrol's exhelper tool. The problem is that the wrapper of the control renamed the Label property/parameter to Label_.  At the top of the control's TLB file you will find a list of Hint that indicates the changes. For instance for label you will find: "Hint: Member 'Label' of 'IChart' changed to 'Label_' "
DPH.5:
Drag-and-drop is one of the fundamental metaphors underlying the Microsoft� Windows family of operating systems. Users understand that some items can be moved around by holding the mouse down on them, and that they'll get appropriate visual feedback when they're over a spot where the item can be dropped. They expect to be able to move data and images from one spot to another this way. 

By default Delphi implements its own drag and drop events for TControl objects so these events override the OLE drag and drop events of the components.  In conclusion, you have to use the Delphi's default implementation for drag and drop events. 

There are three simple steps required in order to be able to drag and drop items/objects/bars from our /COM components as follows:

Beginning a Drag-and-Drop Operation

To begin a drag-and-drop operation, you have to call the BeginDrag method of the TControl object, when the MouseDown event is fired. 

Accepting Data From a Drag Operation

The control fires the StartDrag event, when the user hovers data to the control so the Accept parameter of the event should be changed accordingly.

Performing the Drop Operation 

The control fires the DragDrop event when the user drags data inside the control, so you need to handle the DragDrop event.

In conclusion, here's a simple Delphi sample that allows moving the items from an item to another, using Drag and Drop:

procedure TForm1.G2antt1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var h : Integer;
var c : Integer;
var hit : EXG2ANTTLib_TLB.HitTestInfoEnum;
begin
  with G2antt1 do
  begin
  h := ItemFromPoint[-1, -1, c, hit];
  if ( h <> 0 ) and ( c <> -1 ) then // Begins drag and drop operation as soon as we drag an item in the list area, not the chart area
  begin
    BeginDrag(False);
  end;
  end;
end;

procedure TForm1.G2antt1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
begin
  Accept := Source IS TG2antt;
end;

procedure TForm1.G2antt1DragDrop(Sender, Source: TObject; X, Y: Integer);
var h, hDragged : Integer;
var c : Integer;
var hit : EXG2ANTTLib_TLB.HitTestInfoEnum;
begin

with G2antt1 do
begin
  hDragged := Items.FocusItem;
  h := ItemFromPoint[-1, -1, c, hit];
  if ( h <> 0 ) then
  begin
    BeginUpdate();
      with Items do
      begin
        SetParent(hDragged,h);
        ExpandItem[h] := True;
        SelectItem[hDragged] := True;
      end;
    EndUpdate();
  end;
end;

end;
DPH.6:
The following trick helps you to forward the message to inside editor, while editing, instead navigating to another cell:
procedure TfmMainForm.G2antt1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
var
AHandle:HWND;
begin
	AHandle:=G2antt1.Editing;
	if AHandle<>0 then
	begin
		if (Key=VK_LEFT) or (Key=VK_RIGHT) then
		begin
			SendMessage(AHandle,256,Key,0);
			Key := 0;
		end;
	end;
end;

The sample checks if the control is in editing mode, and forwards the left or right arrow to inside editor.

The same idea is applied for Backspace key, instead that the KeyPress event must be handled such as:

procedure TForm1.G2antt1KeyPress(Sender: TObject; var Key: Char);
var
  Edit: HWND;
begin
  Edit := G2antt1.Editing;
  if Edit <> 0 then begin
    if Ord(Key) = VK_BACK then begin
      if not FSendingBackspace then begin
        FSendingBackspace := True;
        try
          SendMessage(Edit, WM_CHAR, VK_BACK, 0);
          Key := #0;
        finally
          FSendingBackspace := False;
        end;
      end;
    end else begin
      //handling other keys...
    end;
  end;
end;
The sample checks if the control is in editing mode, and forwards the backspace key to inside editor.
DPH.7:
This is happen, because the TLB files were not re-generated after buying and installing the registered version of the component. 

In order to prevent the problem, you need to 

  • install the registered setup on your development machine
  • re-create the wrappers of the TLB files on the development machine
  • include the new regenerated files to your project

Also you can do the following:

  • remove the control from the form, and then re-add it, so you make sure that the registered version is shown on the form ( no warning bar is shown ).
  • verify that the used TLB file include a @CLicenseKey ( InitControlData procedure ), else it means that the TLB file were generated using the trial version. 

The most common cause of happing this is that you have started developing your application using the trial version of the component, not using the registered version. 

Please do the following test on your DEVELOPMENT machine:

  • Install the registered version you have received from us once you purchased the component.
  • Start Delphi, and creates a new project
  • Select the Component\Import ActiveX Control... and then select the control you want to include in the project, let's say the ExG2antt 1.0 Control Library ( Version 1.0 ), and then click the Create Unit
  • If the message to "Do you want to replace it?" shows up, click Yes, so the new TLB file is regenerated.
  • In the TLB file you can search for the @CLicenseKey, and if this is found, you can go to the next step.
  • Install the component by selecting the Component\Install Component..., click the Into new package, and select the new TLB file you were generated earlier. Type the new name of the package in the Package file name. Click OK.
  • If the message to "Do you want to replace it?" shows up, click Yes
  • If the message to "Package ... will be built then installed. Continue?" shows up, click Yes
  • Go to the Form in design mode, and then select the ActiveX palette tool, so you can select the component to be inserted to the form. Select the component from the toolbar, and place it to the form.
  • Save the project
  • Build All the project, so the EXE file is generated in the folder where you have saved the project,
  • Close Delphi.

On the CLIENT machine do the following:

  • Copy the DLL file ( aka ExG2antt.dll ) and the EXE file you were built earlier from the development machine on the client machine
  • Call the regsvr32 on the DLL (aka regsvr32 ExG2antt.dll ), so the DLL is registered on the client machine. You are NOT allowed to install any development setup on the client machine. Only a regsvr32 it is required, so the message "DllRegisterServer in ....dll succeeded." is displayed.
  • Run the EXE file
DPH.8:
This is happen, if you are setting the DataSource property to the reference of the TADOTable, TADODataSet, ... object ( wrappers ), instead reference to the Recordset property of TADOTable. The Delphi environment provides wrapper objects like TADOConnection, TADODataSet, TADOTable to facilitate accessing a database, tables, and so on. Internally, the TADOTable, TADODataSet, uses "ADODB.Recordset" type library. The Recordset property of the TADOTable object returns a reference to the "ADODB.Recordset" type library which is required by the control's DataSource

The control's DataSource property binds the control to an ADO or DAO recordset. In other words, the columns section of the control requires a table/recordset to fill data from. When the DataSource property is set, the recordset being passed, must be opened or active, so the control can load data from, and sink to the recordset events. 

Calling the following:

G2antt1.DataSource := ADOTable1

gets the error "E2010 Incompatible types: 'IDispatch' and 'TADOTable'",

instead you should use as follows:

ADOTable1.Active := True;
G2antt1.DataSource := ADOTable1.Recordset;

The Active property on True, makes sure that the recordset is opened before calling the DataSource property. If the recordset is not opened, before calling the DataSource, the control loads no data, and it may returns an error: 0x80020009. 

If using the eXGantt or eXG2antt, you want to fill the Chart section too, so all you need is to add an AddItem handler, with a code like:

// AddItem event - Occurs after a new Item has been inserted to Items collection.
procedure TForm1.Gantt1AddItem(ASender: TObject; Item : HITEM);
begin
	with Gantt1 do
	begin
		with Items do
		begin
			AddBar(Item,'Task',CellCaption[OleVariant(Item),OleVariant('Start')],CellCaption[OleVariant(Item),OleVariant('End')],Null,Null);
		end;
	end
end;

which actually loads a Task bar for each record found, using the Start/End columns in the recordset. You should change accordingly the  name of the Columns with your case. In conclusion, the AddItem event is fired once a new item/record is loaded from the recordset. 

  • You can  use the Items.AllowCellValueToItemBar property to associate a column with a property of the bar, so you can have the value of the starting/ending position of the tasks into columns section, and so automatically serialized to the database, once a change occurs.
  • Handle the Error event, to be notified if any ADO or DAO error occurs while using the DataSource property.
DPH.9:
The Delphi XE7 generates the `E2015 Operator not applicable to this operand type` error, if compiling the TLB file

The Delphi XE7 wrapper class generates the following code:

TControlData2(CControlData).FirstEventOfs := UIntPtr(@@FOnHostEvent) - UIntPtr(Self);

which should be replace with:

TControlData2(CControlData).FirstEventOfs := Cardinal(@@FOnHostEvent) - Cardinal(Self);
DPH.10:
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.

The solution is to include the control's manifest file to the application's resource under 24( Manifest Resource Type ) with the identifier 1.

  • Open the Project\Options
  • Go to Application
  • Select at Runtime Themes field the "Use custom manifest"
  • At the Custom Manifest field fills or select the exg2antt.manifest file
  • Build the project

Now, copy the generated EXE and the exg2antt.dll to a client machine, and run the EXE. It will work, as in this case, the application uses the exg2antt as isolated, so it requires no registration ( regsvr32 ).

You can download it here the Delphi XE7 project. In the Release folder, you can find the sample.exe that uses the exg2antt.dll as isolated.

See also: How to Generate Assembly Manifest File (Registration-Free)?
DPH.11:
The eXHelper tool helps you quickly find answers and source code for questions about using our UI components. Currently, the eXHelper tool is not able to generate 100% running code as it lacks of:
  • Declaring variables (like Dim h As Long in VB6 or var h: Integer in Delphi, etc.)
  • Some event code in X-Script isn't fully converted to all programming languages
For instance, if your environment generates errors as:
  • E2003 Undeclared identifier: 'h', You need to declare the specified variable, like var h: Integer
By default, you initialize the control in the FormCreate event, which runs when a form is created. This is where you can set up properties, configure controls, or perform other startup tasks. The following properties of the control: requires the control to be visible, so they won't take effect. Some parts of the code should be moved to the FormShow event, as shown in the following sample:
procedure TForm1.FormShow(Sender: TObject);
begin
with G2antt1 do
begin
  BeginUpdate();
  with Chart do
  begin
    FirstVisibleDate := '9/20/2006';
    PaneWidth[False] := 196;
  end;
  EndUpdate();
end
end;
DPH.12:
The following exceptions or errors occur when accessing an event that includes parameters of the OleVariant type:
  1. "Project ... raised exception class $C0000005 with message 'access violation at ...: read of address 0x00000000'", runtime error
  2. "Invalid variant type" runtime exception
  3. "E2064 Left side cannot be assigned to" compiler error

The Delphi environment fails to properly handle events with OleVariant parameters, or generates incorrect definitions for events that use parameters of type "const OleVariant" when they should be declared as "var OleVariant"

The solution is to make a few changes in the TLB file (component package) and reinstall the package for events that include parameters of type OleVariant, as explained below:

  • for events that carry parameters by value of type OleVariant (error 1 and 2), such as where the Key/StarBarKey/EndBarKey/NewValue parameter passed by value is declared as Variant, you need to:
    • open the *TLB.pas (package) file that defines the component
    • locate the defintion of the event such as "TG2anttBarResize = procedure(ASender: TObject; Item: HITEM; Key: OleVariant) of object" and replace with TG2anttBarResize = TNotifyEvent; or TG2anttBarResize = procedure(ASender: TObject) of object
    • reinstall the package to receive a confirmation message like 'Package ...bpl has been installed.'
    • replace the definition of G2antt1BarResize in the PAS file from G2antt1BarResize(ASender: TObject; Item: HITEM; Key: OleVariant) to G2antt1BarResize(ASender: TObject), or remove and add a new event handler (Property and method ... are not compatible)
    Now, the event works properly, but to access the parameters, you need to use the EventParam property, as shown in the following example:
    procedure TForm1.G2antt1BarResize(ASender: TObject);
    begin
      ShowMessage('The bar "' + VarToStr(G2antt1.EventParam[1]) + '" of "' + G2antt1.Items.CellCaption[G2antt1.EventParam[0], 0] + '" has been moved or resized');
    end;
    where EventParam[0] represents the Item parameter (the first parameter of the event as declared in the help file), and EventParam[1] specifies the Key parameter (the second parameter of the event as declared in the help file)
  • for events that carry parameters by reference of type const OleVariant (error 3), such as where the Cancel/Value/NewValue parameter passed by reference is declared as Variant, you need to replace the "const OleVariant" to "var OleVariant"
    • open the *TLB.pas (package) file that defines the component
    • locate the defintion of the event such as "TG2anttChange = procedure(ASender: TObject; Item: HITEM; ColIndex: Integer; const NewValue: OleVariant) of object;" and replace with "TG2anttChange = procedure(ASender: TObject; Item: HITEM; ColIndex: Integer; var NewValue: OleVariant) of object;"
    • reinstall the package to receive a confirmation message like 'Package ...bpl has been installed.'
    • replace the definition of G2antt1Change in the PAS file from G2antt1Change(ASender: TObject; Item: HITEM; ColIndex: Integer; const NewValue: OleVariant) to "G2antt1Change(ASender: TObject; Item: HITEM; ColIndex: Integer; var NewValue: OleVariant)", or remove and add a new event handler (Property and method ... are not compatible)
    Now, the event works properly, you can change the parameter during the event, as shown in the following example:
    procedure TForm1.G2antt1Change(ASender: TObject; Item: HITEM; ColIndex: Integer; var NewValue: OleVariant);
    begin
      NewValue := '1234';
    end;
    where the NewValue parameter can be modified at runtime, as it is passed by reference.