Delphi 7: an abstract class through VFI - delphi

Is it possible in Delphi 7 to create an abstract class that can be inherited through the Visual Form Inheritance technique? If so, please, provide an example.
The task is this. I want to create a form that will serve as a base form for two other forms that will inherit all the properties of that form. The two inheriting forms will be used for adding new stuff to the database (creating a product item, for example) and editing that stuff. So, I guess the base form should be thought of as an abstract class that should have okay and cancel buttons and things like that which all inheriting classes will share. Well, it is obviously an abstract class, because there is no other use of the form other than being a form to base other forms on.
Here's a simple diagram to make the point clearer:

First of all we need to define what abstract class means. There appear to me to be two competing definitions:
An abstract class is one that cannot be instantiated. This is the most commonly used definition.
An abstract class is one that contains more than one abstract method.
Since Delphi no language mechanism for enforcing definition 1, it would appear that definition 2 is the definition that applies to this question.
And the answer to the question is that classes that contain abstract methods can be used with Visual Form Inheritance.
Modern versions of Delphi do allow you to decorate classes with the abstract keyword. However, this has no effect. You can still instantiate such a class. It is my understanding that the abstract keyword was added for the benefit of the Delphi .net compiler.
Again, in modern versions of Delphi, you can configure the compiler to treat instantiation of classes with abstract methods as a compilation error. That's probably the closest you can get in Delphi to definition 1.
However, even that does not fully adhere to definition 1 since those classes can be instantiated through RTTI or virtual constructors. And the mechanism by which a designed component is instantiated is a perfect example.
Take this class for example:
type
TForm1 = class(TForm)
public
procedure Boo; virtual; abstract;
end;
Even if you set the option for W2000 Constructing instance containing abstract method to Error, you can still let the framework instantiate the class. It's only if you write TForm1.Create that the compiler objects.

You use the term abstract, but reading your question, I seriously doubt you really mean it the way abstract within Delphi is defined. I think you mean the term abstract in the general plain spoken way: you want to design a form with parts that have to be altered or added by descendants. Abstract methods in Delphi mean class routines without implementation. It does not matter though, because it is perfectly possible to design a base form, with or without abstract methods.
You can create a setup as shown in your picture/diagram as follows:
Design a form TBaseForm with 2 Edits, 2 Labels, 2 Buttons and 1 ActionList,
Add 3 actions to the ActionList: Create, Save and Cancel,
Assign the Cancel action to CancelButton.Action in advance,
Save the form,
Design a new form, inherited from TBaseForm, with the menu command: File > New > Other > [Project Name] > BaseForm
You will have a new form that has the edits, labels, buttons and actions,
Assign the Save action to the other button's action property,
Give it a "Edit Item" caption,
Save the form, and repeat it for the "Create New Item"-form.
The base form may have abstract methods, if you want to. When you create a TBaseForm instance at runtime, the compiler will give a warning constructing instance of 'TBaseForm' containing abstract method 'TBaseForm.MethodName'. It remains a warning, until you invoke the method at runtime which will produce an abstract error. Creating a descendant form which implements that method, then there will be no warning. Creating forms with abstract methods in the designer does not produce warnings. Runtime errors then may still occur though.

No, you cannot create an "abstract" base form in Delphi Visual Form Inheritance in the strict Delphi sense of the word "abstract".
However, from your description it doesn't sound like you actually need a strictly abstract base form. You don't mention a requirement for defining abstract methods at all.
It sounds like you just need a base form from which you create multiple differently specialized descendants that can share UI and implementation with the base form.
That's what VFI is for, so yes, you can do that.

I have Succeed to create a Demo with a BaseForm has Abstract Methodes and is work as charm ...
my IDE is Rad Studio RIO
my BaseForm code:
unit UBaseForm;
interface
uses
Winapi.Windows,
Winapi.Messages,
System.SysUtils,
System.Variants,
System.Classes,
Vcl.Graphics,
Vcl.Controls,
Vcl.Forms,
Vcl.Dialogs,
Vcl.ExtCtrls, Vcl.StdCtrls;
type
TBaseForm = class(TForm)
Lbl_IndexPage: TLabel;
procedure Abstracted_Event(Sender: TObject); virtual; abstract;
procedure Abstracted_Proc; virtual; abstract;
function Abstracted_Func: string; virtual; abstract;
private
{ Private declarations }
public
{ Public declarations }
end;
procedure Get_SubForm(var Ref; AFormClass: TFormClass;
aOwner: TComponent; aParent: TWinControl);
var
BaseForm: TBaseForm;
implementation
{$R *.dfm}
procedure Get_SubForm(var Ref; AFormClass: TFormClass;
aOwner: TComponent; aParent: TWinControl);
var
Instance: TBaseForm;
begin
if not Assigned(TBaseForm(Ref)) then
begin
Instance := TBaseForm(AFormClass.NewInstance);
TBaseForm(Ref) := Instance;
Instance.Create(aOwner);
end
else Instance := TBaseForm(Ref);
Instance.Parent := aParent;
Instance.Align := alClient;
Instance.BorderStyle := bsNone;
Instance.OnShow := Instance.Abstracted_Event;
Instance.Show;
end;
end.
in my APP i have three Forms can inherit all the properties of that BaseForm Above Even the Methodes ...
unit UFirstPage;
interface
uses
Winapi.Windows,
Winapi.Messages,
System.SysUtils,
System.Variants,
System.Classes,
Vcl.Graphics,
Vcl.Controls,
Vcl.Forms,
Vcl.Dialogs,
UBaseForm,
Vcl.StdCtrls, Vcl.ExtCtrls;
type
TFrmFirstPage = class(TBaseForm)
Pnl_1: TPanel;
Edt_Abst_Msg_Event: TEdit;
Pnl_2: TPanel;
Pnl_3: TPanel;
Btn_Get_Abstract_Func: TButton;
Btn_Get_Abstract_Proc: TButton;
procedure Btn_Get_Abstract_FuncClick(Sender: TObject);
procedure Btn_Get_Abstract_ProcClick(Sender: TObject);
published
procedure Abstracted_Event(Sender: TObject); override;
procedure Abstracted_Proc; override;
function Abstracted_Func: string; override;
private
{ Private declarations }
public
{ Public declarations }
end;
var
FrmFirstPage: TFrmFirstPage;
implementation
{$R *.dfm}
{ TFrmFirstPage }
{$REGION ' Overridden Abstract Methodes ..'}
procedure TFrmFirstPage.Abstracted_Event(Sender: TObject);
begin
inherited;
Pnl_1.Color := clBlue; Pnl_2.Color := clGray; Pnl_3.Color := clRed;
Edt_Abst_Msg_Event.Text := 'All this Properties can changed using [Abstracted_Event] | (Owner Form is: ['+ Self.ClassName +'])';
end;
function TFrmFirstPage.Abstracted_Func: string;
begin
Result := 'I''m Just an Override of Abstracted_Func ['+ Self.ClassName +']';
end;
procedure TFrmFirstPage.Abstracted_Proc;
begin
inherited;
ShowMessage('I''m Just an Override of Abstracted_Proc ['+ Self.ClassName +']');
end;
{$ENDREGION}
procedure TFrmFirstPage.Btn_Get_Abstract_FuncClick(Sender: TObject);
begin
ShowMessage(Abstracted_Func);
end;
procedure TFrmFirstPage.Btn_Get_Abstract_ProcClick(Sender: TObject);
begin
Abstracted_Proc;
end;
end.
my Second Form:
unit USecondPage;
interface
uses
Winapi.Windows,
Winapi.Messages,
System.SysUtils,
System.Variants,
System.Classes,
Vcl.Graphics,
Vcl.Controls,
Vcl.Forms,
Vcl.Dialogs,
UBaseForm,
Vcl.StdCtrls;
type
TFrmSecondPage = class(TBaseForm)
Lbl_Abst_Msg_Event: TLabel;
Lbl_1: TLabel;
Lbl_2: TLabel;
Lbl_3: TLabel;
Btn_Do_Abst_Proc: TButton;
Btn_Get_Abst_Func: TButton;
procedure Btn_Do_Abst_ProcClick(Sender: TObject);
procedure Btn_Get_Abst_FuncClick(Sender: TObject);
published
procedure Abstracted_Event(Sender: TObject); override;
procedure Abstracted_Proc; override;
function Abstracted_Func: string; override;
private
{ Private declarations }
public
{ Public declarations }
end;
var
FrmSecondPage: TFrmSecondPage;
implementation
{$R *.dfm}
{ TFrmSecondPage }
{$REGION ' Overridden Abstract Methodes ..'}
procedure TFrmSecondPage.Abstracted_Event(Sender: TObject);
begin
inherited;
Lbl_1.Font.Color := clBlue; Lbl_2.Font.Color := clGray; Lbl_3.Font.Color := clRed;
Lbl_Abst_Msg_Event.Caption := 'All this Properties can changed using [Abstracted_Event] | (Owner Form is: ['+ Self.ClassName +'])';
end;
function TFrmSecondPage.Abstracted_Func: string;
begin
Result := 'I''m Just an Override of Abstracted_Func ['+ Self.ClassName +']';
end;
procedure TFrmSecondPage.Abstracted_Proc;
begin
inherited;
ShowMessage('I''m Just an Override of Abstracted_Proc ['+ Self.ClassName +']');
end;
{$ENDREGION}
procedure TFrmSecondPage.Btn_Do_Abst_ProcClick(Sender: TObject);
begin
Abstracted_Proc;
end;
procedure TFrmSecondPage.Btn_Get_Abst_FuncClick(Sender: TObject);
begin
ShowMessage(Abstracted_Func);
end;
end.
my third Form:
unit UThirdPage;
interface
uses
Winapi.Windows,
Winapi.Messages,
System.SysUtils,
System.Variants,
System.Classes,
Vcl.Graphics,
Vcl.Controls,
Vcl.Forms,
Vcl.Dialogs,
UBaseForm,
Vcl.StdCtrls;
type
TFrmThirdPage = class(TBaseForm)
published
procedure Abstracted_Event(Sender: TObject); override;
procedure Abstracted_Proc; override;
function Abstracted_Func: string; override;
private
{ Private declarations }
public
{ Public declarations }
end;
var
FrmThirdPage: TFrmThirdPage;
implementation
{$R *.dfm}
{ TFrmThirdPage }
{$REGION ' Overridden Abstract Methodes ..'}
procedure TFrmThirdPage.Abstracted_Event(Sender: TObject);
begin
inherited;
// your Code Goes Here ..
// call this methode or fill it with code Not a Mandatory :)
// The Mandatory thing is to implement this Methodes Exactly where BASEFORM HAS & without Missing any one of them from the Base Class...
// Enjoy ...
end;
function TFrmThirdPage.Abstracted_Func: string;
begin
// your Code Goes Here ..
// call this methode or fill it with code Not a Mandatory :)
// Enjoy ...
end;
procedure TFrmThirdPage.Abstracted_Proc;
begin
inherited;
// your Code Goes Here ..
// call this methode or fill it with code Not a Mandatory :)
// Enjoy ...
end;
{$ENDREGION}
end.
my Main Form:
unit UMain;
interface
uses
Winapi.Windows,
Winapi.Messages,
System.SysUtils,
System.Variants,
System.Classes,
Vcl.Graphics,
Vcl.Controls,
Vcl.Forms,
Vcl.Dialogs,
Vcl.StdCtrls,
Vcl.ExtCtrls,
// My Abstracted Views ..
UFirstPage,
USecondPage,
UThirdPage;
type
TFrmMain = class(TForm)
Pnl_ToolBar: TPanel;
Pnl_StatusBar: TPanel;
Btn_Previous: TButton;
Btn_Next: TButton;
Notebook_SubForms: TNotebook;
Pnl_First_PAGE: TPanel;
Pnl_Second_PAGE: TPanel;
Pnl_Third_PAGE: TPanel;
procedure FormCreate(Sender: TObject);
procedure Notebook_SubFormsPageChanged(Sender: TObject);
procedure Btn_NextClick(Sender: TObject);
procedure Btn_PreviousClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
FrmMain: TFrmMain;
implementation
uses
// My Abstracted Base Template ..
UBaseForm;
{$R *.dfm}
procedure TFrmMain.Btn_NextClick(Sender: TObject);
begin
case Notebook_SubForms.PageIndex of
0:begin
Notebook_SubForms.PageIndex := 1;
end;
1:begin
Notebook_SubForms.PageIndex := 2;
end;
2:begin
Notebook_SubForms.PageIndex := 0;
end;
end;
end;
procedure TFrmMain.Btn_PreviousClick(Sender: TObject);
begin
case Notebook_SubForms.PageIndex of
0:begin
Notebook_SubForms.PageIndex := 2;
end;
1:begin
Notebook_SubForms.PageIndex := 0;
end;
2:begin
Notebook_SubForms.PageIndex := 1;
end;
end;
end;
procedure TFrmMain.FormCreate(Sender: TObject);
begin
Get_SubForm(FrmFirstPage, TFrmFirstPage, Self, Pnl_First_PAGE);
end;
procedure TFrmMain.Notebook_SubFormsPageChanged(Sender: TObject);
begin
case Notebook_SubForms.PageIndex of
0:begin
Get_SubForm(FrmFirstPage, TFrmFirstPage, Self, Pnl_First_PAGE);
if Assigned(FrmSecondPage) then FreeAndNil(FrmSecondPage);
if Assigned(FrmThirdPage) then FreeAndNil(FrmThirdPage);
end;
1:begin
Get_SubForm(FrmSecondPage, TFrmSecondPage, Self, Pnl_Second_PAGE);
if Assigned(FrmFirstPage) then FreeAndNil(FrmFirstPage);
if Assigned(FrmThirdPage) then FreeAndNil(FrmThirdPage);
end;
2:begin
Get_SubForm(FrmThirdPage, TFrmThirdPage, Self, Pnl_Third_PAGE);
if Assigned(FrmSecondPage) then FreeAndNil(FrmSecondPage);
if Assigned(FrmSecondPage) then FreeAndNil(FrmSecondPage);
end;
end;
end;
end.
When Compile 0 error and 0 Warning..
Result:
link to download the whole demo from my Github Repo here.
the Demo can work Also in Delphi 7 (i test it) :)

Related

Delphi error while returning TList

I have made a very simple application but I have an issue that I really cannot understand. Look at this basic code:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, generics.collections, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
test: TList<integer>;
aList: TList<integer>;
public
{ Public declarations }
function testGenerics: TList<integer>;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
test := testGenerics;
test.Sort;
showmessage(test[0].tostring);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
test := TList<integer>.Create;
aList := TList<integer>.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
aList.Free;
test.Free;
end;
function TForm1.testGenerics: TList<integer>;
begin
aList.Add(4);
result := aList;
end;
end.
Basically when the Form opens I am going to create test and aList and then when I press the button the function testGenerics is called. Why do I have the Invalid pointer operation error?
I really cannot understand since I am creating and destroying the objects (I guess) properly. This code instead works fine:
function TForm1.testGenerics: TList<integer>;
begin
Result := TList<integer>.Create;
Result.Add(4);
end;
In this case I am returning an instance of TList<integer> but also in the case above I am returning an instance of aList (which is a TList).
If I'm correct in the first case test := testGenerics is like test := aList (because I am returning aList in fact) so I am going to give test the same reference as aList. Am I correct?
In the first example, whenever you call testGenerics(), you are re-assigning test to point at the aList object. You are losing track of the original test object created in the OnCreate event, so it is leaked. And then in the OnDestroy event, when you call test.Free, it crashes because you already freed the aList object beforehand, so you are trying to free the same object a second time, which is an invalid operation.
In the second example, you are still leaking the original test object (and every TList you allocate and assign to test, except for the last one), but you are not re-assigning test to point at the aList object anymore, so there is no crash in the OnDestroy event because both variables are pointing at separate objects.
What are you trying to accomplish in the first place? Returning objects in this manner is not good practice. Nor does it make sense to call Sort() on 1-element lists.
If you are trying to populate test with multiple values over time, you should pass test as an input parameter to testGenerics() (or just let testGenerics() access test directly via Self), don't use the return value at all.
And in any case, get rid of your aList private member, as you are not doing anything with it anyway.
Try this:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, generics.collections, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
test: TList<integer>;
public
{ Public declarations }
procedure testGenerics(aList: TList<integer>);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
testGenerics(test);
test.Sort;
ShowMessage(test[0].tostring);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
test := TList<integer>.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
test.Free;
end;
procedure TForm1.testGenerics(aList: TList<integer>);
begin
// FYI, a better way to exercise Sort()
// would be to use RandomRange() instead
// of a hard-coded number...
aList.Add(4);
end;
end.

Delphi Form in DLL works, but Delphi Frame - not

I am trying to create a Form and a Frame in Delphi-made DLL using handles only. The form appears in host application normally, but the frame doesn't appear at all.
What could be wrong?
Below I provide a piece of code that creates both Frame and Window:
library DLL1;
{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters. }
uses
System.SysUtils,
System.Classes,
DllMain in 'DllMain.pas',
Winapi.Windows,
Vcl.Forms,
Vcl.Controls {DLLFrame1: TFrame},
DllForm in 'DllForm.pas' {Form1};
{$R *.res}
type
TSingleton = class
private
fra: TDLLFrame1;
frm: TForm1;
class var __instance: TSingleton;
class function __getInstance(): TSingleton; static;
public
class property Instance: TSingleton read __getInstance;
procedure CreateDLLFrame(AppHandle, ParentWindow: HWND);
procedure CreateDLLForm(AppHandle, ParentWindow: HWND);
procedure DestroyDLLFrame();
procedure DestroyDLLForm();
end;
procedure CreateDLLFrame(AppHandle, ParentWindow: HWND); stdcall;
begin
TSingleton.Instance.CreateDLLFrame(AppHandle, ParentWindow);
end;
procedure CreateDLLForm(AppHandle, ParentWindow: HWND); stdcall;
begin
TSingleton.Instance.CreateDLLForm(AppHandle, ParentWindow);
end;
procedure DestroyDLLFrame(); stdcall;
begin
TSingleton.Instance.DestroyDLLFrame();
end;
procedure DestroyDLLForm(); stdcall;
begin
TSingleton.Instance.DestroyDLLForm();
end;
exports
CreateDLLFrame,
CreateDLLForm,
DestroyDLLFrame,
DestroyDLLForm;
procedure TSingleton.CreateDLLFrame(AppHandle, ParentWindow: HWND);
begin
Application.Handle := AppHandle;
fra := TDLLFrame1.CreateParented(ParentWindow);
fra.Show();
end;
procedure TSingleton.DestroyDLLForm();
begin
frm.Free();
end;
procedure TSingleton.DestroyDLLFrame();
begin
fra.Free();
end;
procedure TSingleton.CreateDLLForm(AppHandle, ParentWindow: HWND);
begin
Application.Handle := AppHandle;
frm := TForm1.CreateParented(ParentWindow);
frm.Show();
end;
class function TSingleton.__getInstance(): TSingleton;
begin
if __instance = nil then
__instance := TSingleton.Create();
Result := __instance;
end;
end.
The DLLFrame:
unit DllMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TDLLFrame1 = class(TFrame)
mmoText: TMemo;
pnlSend: TPanel;
edtSend: TEdit;
btnSend: TButton;
private
public
constructor Create(AOwner: TComponent); override;
end;
implementation
{$R *.dfm}
{ TDLLFrame1 }
constructor TDLLFrame1.Create(AOwner: TComponent);
begin
inherited;
if AOwner = nil then
MessageBox(0, 'Frame owner is NIL', 'Debug', 0)
else
MessageBox(0, PWideChar(AOwner.Name), 'Debug', 0);
end;
end.
Delphi TFrame descend from TWinControl (and thus, TControl), they have an Owner and they have a Parent (often these are the same). The Owner controls the Frame's lifetime while the Parent controls where it's displayed (i.e. which Window handle is to be used). For example, in a VCL app with 2 form units and a frame unit, you could instantiate a Frame having it's owner be the Application object or the the first Form while having it's parent be the second form; the Frame would be displayed on the second form even though it's owner was the first frame.
What is the difference between Owner and Parent of a control?
This little example doesn't use DLLs, but it shows how the frame won't be displayed without a Parent being assigned:
unit CreateFrameAtRunTimeForm;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm2 = class(TForm)
Label1: TLabel;
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
uses CreateFrameAtRunTimeFrame;
{$R *.dfm}
procedure TForm2.Button1Click(Sender: TObject);
var
F : TFrame3;
begin
F := TFrame3.Create(self);
F.Name := 'Frame'+Random(1000000).ToString;
F.Panel1.Caption := 'Frame '+F.Name;
F.Left := 200;
F.Top := 100;
end;
procedure TForm2.Button2Click(Sender: TObject);
var
F : TFrame3;
begin
F := TFrame3.Create(self);
F.Name := 'Frame'+Random(1000000).ToString;
F.Panel1.Caption := 'Frame '+F.Name;
F.Left := 200;
F.Top := 100;
F.Parent := self;
end;
end.
I'm sure your problem is that the Frame doesn't have a Parent control and I don't think it's possible to set one if you are only passing window handles around.

Scoping issue with Set types

I have 3 units This is my main unit....
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Panel1: TPanel;
Panel2: TPanel;
Panel3: TPanel;
procedure FormShow(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
Uses Unit2;
{$R *.dfm}
procedure TForm1.FormShow(Sender: TObject);
begin
Panel1.Visible := oiNone in form2.Settings.OptVars;
Panel2.Visible := oiHint in form2.Settings.OptVars;
Panel3.Visible := oiStat in form2.Settings.OptVars;
end;
end.
This is the second unit - used by the first.
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Unit3;
type
TForm2 = class(TForm)
CheckBox1: TCheckBox;
CheckBox2: TCheckBox;
CheckBox3: TCheckBox;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
Settings: TSettings;
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.FormCreate(Sender: TObject);
begin
Settings := TSettings.Create;
Checkbox1.Checked := oiNone in Settings.OptVars;
CheckBox2.Checked := oiHint in Settings.OptVars;
CheckBox3.Checked := oiStat in Settings.OptVars;
end;
procedure TForm2.FormDestroy(Sender: TObject);
begin
Settings.Free;
end;
end.
and this is the unit which is used by the second unit and contains the Set type which is used by both other units.
unit Unit3;
interface
Uses
winAPI.windows,system.classes;
Type
TOptions = Set Of (oiNone, oiHint, oiStat);
TSettings = class
private
fMyOption: TOptions;
public
Constructor Create;
Destructor Destroy;
property OptVars: TOptions read fMyOption write fMyOption;
end;
implementation
constructor TSettings.Create;
begin
fMyOption := [oiNone, oiHint, oiStat];
end;
destructor TSettings.Destroy;
begin
end;
end.
In the second unit, the items oiNone,oiHint, oiStat are accessible and within scope.
In the first unit, the items oiNone, oiHint and oiStat are not accessible and within scope although the Settings.OpVars which is a TOption data type is accessible.
I cannot think of a better way to describe my problem. If you put these into a project you should see the issue.
Update
After the latest edit it is clear that my first hunch was correct. You weren't using the unit that declares the type. The names have changed, but with the current edit your problem is that the main unit does not use Unit3.
You need to reference the unit that declares the set type, and its enumerated type. Add UnusedItemsReg to your uses clause in the third unit.
If you have done that then the only other explanation I can imagine is that you compile with scoped enums enabled. But I'm clutching at straws now.
I would definitely recommend that you declare the enumerated type as a named type in its own right. Certainly if you use scoped enums then you'll need to do that. But sooner or later you'll want to have that type available.
type
TOption = (oiNone, oiHint, oiStat);
TOptions = set of TOption;
If you do use scoped enums then you'll refer to then like this:
TOption.oiNone
You need Unit3 in the (a) uses clause of the main unit. Otherwise it cannot see type TOptions. That is a requirement of Delphi for visibility. It does not use the implicit references that you seem to be seeking.

Invalid Pointer Operation freeing an object

Being new to OOP, I'm curious why Delphi XE7 generated an invalid pointer operation on a logging class I was using whenI try to free it. So I created a simple test to create an object and then free it. I'm not sure what I'm missing here and why it throws this exception when MyObject.Free is called.
In the first unit, I create an instance of this object as shown here.
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Unit2;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
MyObject: TMyObject;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
MyObject := TMyObject.Create;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
MyObject.Free;
end;
end.
In the 2nd unit, I have the object defined as follows.
unit Unit2;
interface
uses System.Classes;
type
TMyObject = class
public
constructor Create;
destructor Free;
end;
implementation
constructor TMyObject.Create;
begin
inherited Create;
end;
destructor TMyObject.Free;
begin
inherited Free;
end;
end.
Any help is appreciated.
Always implement a destructor by overriding the virtual destructor named Destroy.
type
TMyObject = class
public
constructor Create;
destructor Destroy; override;
end;
constructor TMyObject.Create;
begin
inherited;
end;
destructor TMyObject.Destroy;
begin
inherited;
end;
To destroy an instance call the method named Free in TObject. This calls the virtual destructor Destroy only if the instance is not nil.
Learn more from the documentation:
http://docwiki.embarcadero.com/Libraries/en/System.TObject.Free
http://docwiki.embarcadero.com/Libraries/en/System.TObject.Destroy
The name MyObject is weak. Object is used for instances. Class is used for classes.

Calling a procedure with objects as arguments

Trying to save code. I want to display text etc on an image on the form at OnActivate then print the same text on clicking button (Real program is more complicated). To save writing code twice I tried the enclosed code but it won't compile at the "Obj.Canvas" line. If I comment out this line and the enclosed line the program runs but the Obj value is ().
I've tried several other approaches but none work. Can anyone tell me where I'm going wrong.
Badger
unit Unit7;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls, printers;
type
TForm7 = class(TForm)
Print: TButton;
Image1: TImage;
PrintDialog1: TPrintDialog;
procedure FormActivate(Sender: TObject);
procedure PrintClick(Sender: TObject);
private
{ Private declarations }
public
DH,DW:Extended;
Procedure DoLayout(Obj:TObject);
{ Public declarations }
end;
var
Form7: TForm7;
implementation
{$R *.dfm}
procedure TForm7.FormActivate(Sender: TObject);
begin
DoLayout(Image1);
end;
procedure TForm7.PrintClick(Sender: TObject);
begin
if PrintDialog1.Execute then
begin
printer.BeginDoc;
DoLayout(Printer);
Printer.EndDoc;
end;
end;
procedure TForm7.DoLayout(Obj:TObject);
begin
if Obj =Printer then //when you run the program Obj is ()
begin
DW:=Printer.PageWidth/Image1.Width;
DH:=Printer.PageHeight/Image1.Height;
end
else
begin
DH:=1;
DW:=1;
end;
With Obj.canvas do //Error here when compiled - tried commenting it out
begin
TextOut(Int(DH*50),Int(DW*30),'This is the text'); //commented this out too
end;
end;
end.
The TPrinter class and the TImage class don't share a common ancestor class except for TObject, as a result that's what you're passing in.
A suggested refactoring is to change the DoLayout code to accept the canvas that you want to use, as well as an parameter to determine if it's a printer or an image that you're passing in e.g.
procedure TForm7.DoLayout(aCanvas : TCanvas; bPrinter : boolean);
begin
if bPrinter then //when you run the program Obj is ()
begin
DW:=Printer.PageWidth/Image1.Width;
DH:=Printer.PageHeight/Image1.Height;
end
else
begin
DH:=1;
DW:=1;
end;
With aCanvas do
begin
TextOut(Int(DH*50),Int(DW*30),'This is the text');
end;
end;
then when you call it, use the printer canvas explicitly, or the image canvas:
DoLayout(Printer.canvas, true);
or
DoLayout(Image1.canvas, false);
this is just a rough estimation based on your code; I don't have a delphi compiler to hand to verify it.

Resources