Personally, I think the limitation of DELPHI's implementation of activex control exists in the fact that the base class of DELPHI's activex control is derived from TAutoObject as follows:
TActiveXControl = class(TAutoObject,
IConnectionPointContainer,
IDataObject,
IObjectSafety,
IOleControl,
IOleInPlaceActiveObject,
IOleInPlaceObject,
IOleObject,
iperPRopertyBrowsing,
IPersistPropertyBag,
IPersistStorage,
IPersistStreamInit,
IQuickActivate,
ISimpleFrameSite,
ISpecifyPropertyPages,
IViewObject,
IViewObject2)
....
....
end;
Anyone who has used DELPHI to create activex controls knows that DELPHI's activex control wizard must specify the VCL form control contained in the control (VCL control derived from TWinControl), so that its implementation must include a VCL form control. To make this VCL form control available at any time, DELPHI also provides a parent window for the control. Look at the code below:
procedure TActiveXControl.Initialize;
begin
inherited Initialize;
FConnectionPoints := TConnectionPoints.Create(Self);
FControlFactory := Factory as TActiveXControlFactory;
if FControlFactory.EventTypeInfo <> nil then
FConnectionPoints.CreateConnectionPoint(FControlFactory.EventIID,
ckSingle, EventConnect);
FPropertySinks := FConnectionPoints.CreateConnectionPoint(IPropertyNotifySink,
ckMulti, nil);
FControl := FControlFactory.WinControlClass.CreateParented(ParkingWindow);
if csReflector in FControl.ControlStyle then
FWinControl := TReflectorWindow.Create(ParkingWindow, FControl) else
FWinControl := FControl;
FControlWndProc := FControl.WindowProc;
FControl.WindowProc := WndProc;
InitializeControl;
end;
This is the limitation of DELPHI's implementation of the activex control. How do we control this ParkingWindow? Even if we can control the size of the entire activex control and expand it a lot, the smaller the activex control on the Internet, the better. At least now DELPHI must implement something similar to vc light Magnitude control (only implements IPersistStreamInit, IOleControl, IOleObject, IOleInPlaceActiveObject, IViewObjectEx, IOleInPlaceObjectWindowless interfaces (COM objects of these interfaces) are very difficult, let alone implementing the interface as needed (depending on whether the activex control is used in IE, Word, or others). One solution is not to derive from TAutoObject but directly from the form control as follows:
TMyActiveXControl=class(TMyControl,
//TMyControl is a general class or a VCL form control class.
//The following interfaces do not always need to be derived and implemented. Can be derived and implemented on demand
IConnectionPointContainer,
IDataObject,
IObjectSafety,
IOleControl,
IOleInPlaceActiveObject,
IOleInPlaceObject,
IOleObject,
IPerPropertyBrowsing,
IPersistPropertyBag,
IPersistStorage,
IPersistStreamInit,
IQuickActivate,
ISimpleFrameSite,
ISpecifyPropertyPages,
IViewObject,
IViewObject2)
....
....
end;
But if so, a new problem arises. How to register this activex control? Because there is no suitable class factory object, DELPHI's class factory has the following relationship:
-->TActiveXControlFactory-->TActiveFormFactory
And TComObjectFactory is implemented like this,
TComObject = class(TObject, IUnknown, ISupportErrorInfo)
. . . .
End;
TComClass = class of TComObject;
TComObjectFactory = class(TObject, IUnknown, IClassFactory, IClassFactory2)
…..
constructor Create(ComServer: TComServerObject; ComClass: TComClass;
const ClassID: TGUID; const ClassName, Description: string;
Instancing: TClassInstancing; ThreadingModel: TThreadingModel = tmSingle);
End;
The constructor of the class factory requires the parameter ComClass, and TComObject is derived from TObject. Therefore, if we cannot find a suitable class factory base class to derive from, unless there is the following delphi implementation:
TMyComObject = class(TMyControl, IUnknown, ISupportErrorInfo)
. . . .
End;
TMyComClass = class of TMyComObject;
TMyComObjectFactory = class(TObject, IUnknown, IClassFactory, IClassFactory2)
…..
constructor Create(ComServer: TComServerObject; MyComClass: TMyComClass;
const ClassID: TGUID; const ClassName, Description: string;
Instancing: TClassInstancing; ThreadingModel: TThreadingModel = tmSingle);
End;
Unfortunately, even if there is such a class, we cannot use it. This limitation comes from DELPHI's implementation of the COM class factory as follows:
TComServer = class(TComServerObject)
Private
. . . .
procedure FactoryFree(Factory: TComObjectFactory);
procedure FactoryRegisterClassObject(Factory: TComObjectFactory);
procedure FactoryUpdateRegistry(Factory: TComObjectFactory);
procedure LastReleased;
end;
The problem lies in these functions. These functions require parameters to be of type TComObjectFactory, which means that the implementation of the COM server provided by Delphi must be used. Then the class factory of the COM object must be derived from TComObjectFactory. I have no idea why. Without using an interface, for example, the following implementation:
TComServer = class(TComServerObject)
Private
. . . .
procedure FactoryFree(Factory: IClassFactory);
procedure FactoryRegisterClassObject(Factory: IClassFactory);
procedure FactoryUpdateRegistry(Factory: IClassFactory);
procedure LastReleased;
end;
Ultimately, the problem lies in the implementation of the COM server and the class factory provided by Delphi. Now there is nothing I can do. At least I haven't found any good solution yet. The only thing I can think of now is to implement the COM server dll and my own ActiveX control class factory (I actually made one, it feels very simple, some Delph methods for implementing COM servers can be used directly).