Hello i get error E2197: [DCC Error] proj1.pas(34): E2197 Constant object cannot be passed as var parameter:
unit proj1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, AdvEdit;
type
TForm1 = class(TForm)
AdvEdit1: TAdvEdit;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure SetEditText(const instr: string; out outstr: string);
begin
outstr := instr;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
SetEditText('Pippo', AdvEdit1.Text);
end;
end.
Of course, i can solve writing:
procedure TForm1.Button1Click(Sender: TObject);
var sText: string
begin
SetEditText('Pippo', sText);
AdvEdit1.Text := sText;
end;
But when i have many AdvEdit, then it is hard. Then i ask, is possible solve the problem in some mode giving directly TAdvEdit.Text as parameter in mine procedure?
Thanks very much.
I presume that Text is a property. And you cannot pass a property to a var or out parameter. You can only pass variables to parameters of those kinds.
You'll need to find a different way to write your code. You've come up with one such idea, but it seems needlessly complex to me. I cannot see anything simpler than:
AdvEdit1.Text := 'Pippo';
How could there be any code simpler than this? You need to specify at a bare minimum the following:
The target control.
That we are dealing with the Text property.
The fact that we are assigning.
The new value.
The code above does that and nothing more.
Related
Need to access some components right after program start but I have found that doing so from form's onCreate event is bad because at the moment they may still be unavailable (access violation occurs). Can not find onCreate event in any component. Am I missing something?
Here is code. Empty form with ValueListEditor.
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.ValEdit;
type
TForm1 = class(TForm)
ValueListEditor1: TValueListEditor;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
procedure: Load;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
Load;
end;
procedure Load;
begin
(Application.MainForm.FindComponent('ValueListEditor1') as TValueListEditor)
.Strings.LoadFromFile('c:\li');
end;
end.
The problem is not because your component hasn't been created yet, because it has been. The real problem is because the Application.MainForm property hasn't been assigned yet when your main Form's OnCreate event is fired, so you are calling FindComponent() on a nil Form pointer.
Since Load() is merely accessing a member of TForm1 then Load() should also be a member of TForm1 as well, and then you can call it, and thus access your component, via the implicit Self pointer, which is valid during the Form's OnCreate event, eg:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.ValEdit;
type
TForm1 = class(TForm)
ValueListEditor1: TValueListEditor;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure LoadValues;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
LoadValues;
end;
procedure TForm1.LoadValues;
begin
ValueListEditor1.Strings.LoadFromFile('c:\li');
end;
end.
If, for whatever reason, Load() must be a standalone procedure, then at least have it use your global Form1 variable, which the call to Application.CreateForm() will assign before the main Form's OnCreate event is fired, eg:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.ValEdit;
type
TForm1 = class(TForm)
ValueListEditor1: TValueListEditor;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure LoadValues;
public
{ Public declarations }
end;
var
Form1: TForm1;
procedure Load;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
LoadValues;
end;
procedure TForm1.LoadValues;
begin
ValueListEditor1.Strings.LoadFromFile('c:\li');
end;
procedure Load;
begin
if Form1 <> nil then
Form1.LoadValues;
end;
end.
Alternatively, you can fallback to looking for the Form1 object in the Screen.Forms[] array, eg:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.ValEdit;
type
TForm1 = class(TForm)
ValueListEditor1: TValueListEditor;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure LoadValues;
public
{ Public declarations }
end;
var
Form1: TForm1;
procedure Load;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
LoadValues;
end;
procedure TForm1.LoadValues;
begin
ValueListEditor1.Strings.LoadFromFile('c:\li');
end;
procedure Load;
var
I: Integer;
Frm: TForm;
begin
for I := 0 to Screen.FormCount-1 do
begin
Frm := Screen.Forms[I];
if Frm is TForm1 then
begin
TForm1(Frm).LoadValues;
Exit;
end;
end;
end;
end.
Use the form's onShow() -event.
But be aware, that the onShow() -event is called every time the form is shown, not only the first time.
In your form, you should override the DoShow method and insert your code there. Since DoShow is called everytime the form changes from invisible to visible, you should also add a boolean variable in your form and check in the DoShow override, if it is false. In that case, it is the first time DoShow is called. Then set the variable to true and do whatever you need to do the first time.
Please note that within DoShow the form is not visible yet. If you need to do something once the form is made visible, you can post a custom message from DoShow and put your code in the corresponding message handler. At the time it is executed, the form just became visible.
const
WM_APP_STARTUP = WM_USER + 1;
type
TForm1 = class(TForm)
protected
FInitialized : Boolean;
procedure DoShow; override;
procedure WMAppStartup(var Msg: TMessage); message WM_APP_STARTUP;
end;
implementation
procedure TForm1.DoShow;
begin
// Form is NOT visible but will soon be
if not FInitialized then begin
FInitialized := TRUE;
// Insert your code here
PostMessage(Handle, WM_APP_STARTUP, 0, 0);
end;
inherited DoShow;
end;
procedure TForm1.WMAppStartup(var Msg: TMessage);
begin
// The form is now visible
// Insert your code here
end;
I'm a high school student taking programming as one of my subjects, so I'm rather new to Delphi.
I'm writing a game that requires the same (very long) block of code to be run when multiple different events occur. I was wondering if there was a way to write it at the beginning and call it in these different parts of the program, or perhaps get multiple senders to run the same event? The code sets the brush color of 42 different objects to different colors depending on what the user selects (the game is Risk) and when i try using a procedure it get errors for every object telling me it is undeclared.
type
TForm1 = class(TForm)
shpTerr1: TShape;
private
{ Private declarations }
public
procedure CheckOwner;
end;
var
Form1: TForm1;
iArmies, iTemp, i : integer;
iSelected, iSelectedOld : integer;
arrTerrArmies, arrTerrOwners : array[0..41] of integer;
arrPlayerColour : array[0..3] of string;
arrPlayers : array of string;
AttackMode : boolean;
implementation
{$R *.dfm}
procedure CheckOwner;
begin
shpTerr1.Brush.Color := StringToColor('cl' + arrPlayerColour[arrTerrOwners[0]]);
end;
The error is with the TShape.
Any help?
Quick answer:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
shpTerr1: TShape;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure CheckOwner;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.CheckOwner;
begin
shpTerr1.Brush.Color:= Color; // I don't know what is arrPlayerColour[arrTerrOwners[0]]
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
CheckOwner;
end;
end.
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.
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.
Anyone know why I am getting an access violation with the following:
unit TestForm;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Generics.Collections, Vcl.Grids,
Vcl.ValEdit;
type
TClientThing = class
private
iCDic: TDictionary<string, string>;
published
property Dic: TDictionary<string, string> read iCDic write iCDic;
end;
TForm1 = class(TForm)
vleHeader: TValueListEditor;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
ClientThing: TClientThing;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
vCustomParamKey: string;
vCustomValueKey: string;
J: integer;
begin
with ClientThing do
begin
// Get the header params from the config and list edit...
for J := 0 to vleHeader.RowCount - 1 do
begin
vCustomParamKey := vleHeader.Cells[0, J];
vCustomValueKey := vleHeader.Cells[1, J];
Dic.Add(vCustomParamKey, vCustomValueKey);
end;
end;
end;
end.
The Access violation is at the Dic.Add line. The exception is:
I have been up all night and so have probably missed something. The TValueListEditor contents are (code editor view):
X-Application=g9V0rB9a3J5UF8
X-Authentication=kQNvuuimr0yMtEYZtXAZntTScPlvjecEAGtvbnNIU=
JSONRpc=2.0
The form never assigns a value to ClientThing, so it's still nil. Assign it a value, and then assign a value to iCDic. You should have been able to detect this problem when you stepped through the code with the debugger.