Access violation when affecting a value to variable? - delphi

The code below is written in unit2 ( form2), it calls the values entered in the email and password boxes ( in form1 ), yesterday the code was working perfectly, i made some changes and now : This code doesn't work, it raises an Access Violation error when i click the Button COMMENCER:
procedure TForm2.Btn_commencerClick(Sender: TObject);
begin
email := form1.ed_Email.Text;// <----- LOOK HERE
password := form1.Ed_typedpass.Text; // <-----AND HERE
MD5 := GetMD5;
MD5.Init;
MD5.Update(TByteDynArray(RawByteString(password)), Length(password));
password := LowerCase(MD5.AsString);
etc.......
But this code works :
email := 'myemail#yahoo.com';
password := 'mypass';
MD5 := GetMD5;
MD5.Init;
etc etc......
The question :
Why ?

Where are you creating your form1 object? Sounds like it haven't initialized before you access it and therefore you get AV.
Your second code works, because you don't have to initialize string variables before accessing or assigning values to them and you are assigning them directly, not through the form1 variable.
But breakpoint to email := form1.ed_Email.Text; and look if form1 is nil or not.

i think you have to create the form1 (as i think its available form in your case)..and you may have closed and freed the form so, your
email := form1.ed_Email.Text;
is giving AV, as form1 doesnt exist(as its freed now) ,so u cannot have the ed_Email.Text value.
make sure your not closing the form1 (freeing) before pressing Btn_commencer

You can always check if the form has been created first to avoid access violations.
if assigned(Form1) then
begin
// assignments
end;

Related

Create a TDataModule only once

I'm trying to build a procedure that creates a TDataModule in Application as its parent.
The problem is, the second time I call the procedure, the dm parameter still nil. I expect something diferent of nil considering that it was created before.
Here is the code I'm trying:
procedure UseDataModule(dm : TDataModule; cClass:TcomponentClass);
begin
if dm = nil then
cClass.Create(Application);
end;
There are some requirements I want for this procedure:
The given TDataModule should be created once
It must to be created by procedure because I want to use It sometimes, that's why I don't put it in auto-create forms
Its parent will be always Application
Try changing your code to this:
procedure UseDataModule(var dm : TDataModule; cClass:TcomponentClass);
// the `var` qualifier is to allow the value of `dm` to be retained
// after `UseDataModule` exits, otherwise the Created instance will be discarded
// and you will have a memory leak
begin
if dm = nil then
dm := cClass.Create(Application);
end;
Imo, it would be better to code UseDataModule as a function, but that is largely a matter of taste. Note also that you could write if notAssigned(dm) instead of if dm = Nil.
I gather from your comment that you have decided to use the following code instead of my initial suggestion:
procedure UseDataModule(var dm : TDataModule; cClass:TcomponentClass);
begin
if dm = nil then begin
dm := cClass.Create(Application) as TDataModule;
end;
end;
which seems fine to me.
Another option is to use the same code used to autocreate forms with a check to see if it is already created.
// Create data module if it doesn't already exist
if DM = nil then Application.CreateForm(TDM, DM);

Failing to update a table

I have a boolean field in cxGrid (represented as a checkbox inside the grid).
I am trying to do an update of the very same record when user sets the checkbox as checked.
I tried this:
procedure TDataModule3.ABSTable2BeforePost(DataSet: TDataSet);
begin
if DataModule3.ABSTable2.FieldByName('DONE').AsBoolean = True then
with Datamodule3.ABSQuery4 do begin
Datamodule3.ABSQuery4.Close;
Datamodule3.ABSQuery4.SQL.Clear;
Datamodule3.ABSQuery4.SQL.Text :='UPDATE MYTABLE SET USER=:a1 where TW_ID =:a2';
Datamodule3.ABSQuery4.Params.ParamByName('a1').AsString := MainForm.AdvOfficeStatusBar1.Panels[3].Text ;
Datamodule3.ABSQuery4.Params.ParamByName('a2').AsInteger := Datamodule3.ABSTable2.FieldByName('TW_ID').AsInteger;
Datamodule3.ABSQuery4.ExecSQL;
end;
end;
I get :
First chance exception at $00B6E33F. Exception class $C0000005 with message 'access violation at 0x00b6e33f: read of address 0x000003a0'. Process Project1.exe (4916)
What am I doing wrong ?
Edit: I tried the suggestion.
/
if ABSTable2.FieldByName('Done').AsBoolean = True then
begin
ABSQuery4.Close;
ABSQuery4.SQL.Clear;
ABSQuery4.SQL.Text :='UPDATE mytable SET user=:a1 where TW_ID=:a2 ';
ABSQuery4.Params.ParamByName('a1').AsString := ABSQuery1.FieldByName('USER').asString ;
ABSQuery4.Params.ParamByName('a2').AsInteger := ABSTable2.FieldByName('TW_ID').AsInteger;
ABSQuery4.ExecSQL;
end;
I have removed the reference to the main form and added the query syntax as the AdvOfficeStatusBar1.Panels[3].Text gets its data from it.
Now I get: Absolute Engine error : The table is locked.
Remove the DataModule3 from the beginning of all of the lines of code.
You're using an instance variable inside the methods of a class. If the instance is named anything other than DataModule3 or DataModule3 hasn't been created, your code will fail.
The proper way to write the code would be (see the NOTE below):
procedure TDataModule3.ABSTable2BeforePost(DataSet: TDataSet);
begin
if Self.ABSTable2.FieldByName('DONE').AsBoolean = True then
begin
Self.ABSQuery4.Close;
Self.ABSQuery4.SQL.Clear;
Self.ABSQuery4.SQL.Text :='UPDATE MYTABLE SET USER=:a1 where TW_ID =:a2';
// See NOTE below
Self.ABSQuery4.Params.ParamByName('a1').AsString := MainForm.AdvOfficeStatusBar1.Panels[3].Text ;
Self.ABSQuery4.Params.ParamByName('a2').AsInteger := Self.ABSTable2.FieldByName('TW_ID').AsInteger;
ABSQuery4.ExecSQL;
end;
end;
NOTES
The Self is optional. You could write each without the Self. and it would work equally as well in this case. Self refers to the current instance of the object, rather than a specific named instance like DataModule3 does.
You should find a way to remove the reference to MainForm and a visual component by adding an instance variable or property to the datamodule class instead that you can refer to in your code. Hard-coding in the MainForm can cause the same kind of problems that hard-coding in DataModule3 - it's a specific instance name rather than just being the current instance of something. If you rename your MainForm to something else, your code won't compile. If you replace MainForm with a different form, but there's another MainForm in scope that hasn't been created yet, your code will also crash.

Passing Tobject to another form?

I'm writing a touchscreen enabled application in Delphi XE2.
I have a form with TEdits. When I click on them, I call the procedure I've written to show another maximized always on top form with a TTouchkeyboard with a label (for caption) and a TEdit for keyboard input.
My procedure (vkeyboard is my form name with the TTouchkeyboard):
procedure TLogin.showkeyboard(numeric,password: Boolean;
caption,value:string;Sender:TObject);
begin
if numeric then
vkeyboard.TouchKeyboard1.Layout := 'NumPad' // make the TTouchkeyboard on the form numeric or alpha
else
vkeyboard.TouchKeyboard1.Layout := 'Standard';
if password then
vkeyboard.input.PasswordChar := '*' //make the TEdit show * or normal characters
else
vkeyboard.input.PasswordChar := #0;
vkeyboard.title.Caption := caption;
vkeyboard.input.Text := value;
vkeyboard.Show;
end;
I'm trying to send Form1.Edit1 object to the form vkeyboard but i don't know how to do it properly!
Why? Because i want to be able to click Done on the input form (vkeyboard) then trace back who was the sender then update the text in the main form edit!
procedure Tvkeyboard.sButton1Click(Sender: TObject);
begin
(temp as TEdit).Text := input.Text; // send back the text to the right object
vkeyboard.Hide;
end;
This little part of course didn't work... I think i need to specified that the temp object belong the X form ?
To be clear, i want to trace back who called the procedure or at least be able to specified it in the procedure and then return back the text (from the 2nd form to the main one) to the right TEdit!
You're welcome to pass whatever arguments you want to whatever function you want. If you need to use the passed value in yet another function, you'll need to save it somewhere so the later function can still access it.
Using your example, you appear to have provided a Sender parameter for your showkeyboard function. I assume that's where you're passing a reference to the TEdit control that triggered the keyboard to show. The Tvkeyboard object stored in vkeyboard will need to use that value later, so give a copy of that value to the Tvkeyboard object. Declare a TEdit field:
type
Tvkeyboard = class(...)
...
public
EditSender: TEdit;
Then, in showkeyboard, set that field:
vkeyboard.EditSender := Sender;
Finally, use that field when you set the text:
procedure Tvkeyboard.sButton1Click(Sender: TObject);
begin
EditSender.Text := input.Text; // send back the text to the right object
Self.Hide;
end;
Since you know it will always be a TEdit control, you can change the type of the Sender parameter in showkeyboard to reflect that specific type:
procedure TLogin.showkeyboard(..., Sender: TEdit);

How can i know if the user define component is created?

I create a memo inside a procedure, using this code:
Global_MemoIni := TMemo.Create(Conf);
Global_MemoIni.Parent := Conf;
Global_MemoIni.Visible := False;
Global_MemoIni.Align := alClient;
Global_MemoIni.WordWrap := False;
When I call the procedure again it creates the global_memoini again.
How can I know if the component is created so I don't need to call it again?
Update : Can I use the Global_MemoIni.Free above the creation code so the next time create
the Global_memoini once... But i want to know if this is created...
Thank you
You can check if Global_MemoIni is Nil and create the TMemo if it is. Otherwise it already exists, you can then free it using Free or FreeAndNil. If you use free be careful that you assign Nil to Global_MemoIni. If you don't, you can't use the Global_MemoIni <> Nil check.
I honestly don't understand the point of using a memo in stead of a TStringList which is more lightweight. just do
unit UnitName;
interface
uses SysUtils, Windows, Classes, ...;
var Global_INI: TStringList; // <-- it's defined in the interface section, therefore
// it can be accessed by any unit which uses this unit
implementation
initialization
Global_INI := TStringList.Create;
Global_INI.LoadFromFile( 'C:\config.ini' ); // <-- replace the file name with the
// one you want
finalization
FreeAndNil( Global_INI );
end;
Don't do this is an arbitrary function. Either create the component in the FormCreate or even the constructor of the form, or make it a read-only property of the form, and use lazy instantiation, i.e.
if not Assigned(Global_MemoIni) then
begin
Global_MemoIni := TMemo.Create(Self);
// rest of your code
end;
Result := Global_MemoIni;
But why is it global? If you make it a field and corresponding read-only property of the form, it is easily accessible and you can protect it in the way shown above.
FWIW, instead of Free-ing the component, let the Owner (the form) do that. That way, it is available as long as the form exists, and no nasty invalid pointer issues can take place.
If you do not know the creation state of object use:
if not Assigned(Global_MemoIni) then
begin
Global_MemoIni := TMemo.Create(Conf);
...
end
And don't forget to use FreeAndNil(Global_MemoIni) when destroying the object.

How can I check whether an object reference is still valid?

I have an issues where I am trying to determine if a reference to an object is valid. But it seems to be returning strange results.
procedure TForm1.Button1Click(Sender: TObject);
var form1 : TForm;
ref2 : TControl;
begin
form1 := TForm.Create(nil);
form1.Name := 'CustomForm';
form1.Parent := self; //Main Form
form1.Show;
ref2 := form1;
showmessage(ref2.ClassName+' - '+ref2.Name+' - '+BoolToStr(ref2.visible,true));
freeandnil(form1);
showmessage(ref2.ClassName+' - '+ref2.Name+' - '+BoolToStr(ref2.visible,true));
end;
The first showmessage returns - "TForm - CustomForm - True" (Just like I would expect it to).
The second showmessage return - "TForm - - False". I was actually hoping for some kind of access violation that I could then trap and know that the reference isn't valid.
In my application I need to compile a list of random TForm descendants as they are created and then check later if they have gone away (or are not visible). Unfortunately it is a plugin based system so I can go change all of these Forms to post a "I'm done Message."
Would code like this be safe to use (assuming I actually am checking for access violations)? Does anybody have any ideas what is happening.
Thanks
The problem is that with a certain likelyhood the memory accessed is still reserved by the Delphi memory manager. In that case Windows does not generate any kind of access violation, because that memory belongs to you!
One possibility is to switch to a different Delphi memory manager which can detect the use of freed objects. FastMM4, for example, has several "memory hygiene" checks, which are very useful for debugging, but even then you won't catch all of these errors immediately.
You can download FastMM4 from SourceForge.
Any TComponent (e.g. a TForm descendant) can register for notifications when other components are destroyed.
In your form, call FreeNotification(form) for each form that you wish to be notified of the destruction of. Then on the same form override the Notification() method. When any form (or other component) for which you have called FreeNotification() is destroyed, your Notification() method will be called with a Component parameter referencing the form and an Operation of opRemove.
If I've understood what it is you are trying to achieve, I think this should be enough information to devise an approach to do what you need.
After
freeandnil(form1);
the Delphi memory manager just marks the memory allocated by form1 as free, but all form1 data is still there, and can be accessed via ref2 until the memory manager reuse the freed memory for some other object(s).
You can't check that way if ref2 references a valid object or not. Code like this can't be safe, it is actually a bug.
If you want to obtain a 100% access violation modify the code as follows (here ref2^ = nil if form1 is freed):
procedure TForm1.Button1Click(Sender: TObject);
var form1 : TForm;
ref2 : ^TControl;
begin
form1 := TForm.Create(nil);
form1.Name := 'CustomForm';
form1.Parent := self; //Main Form
form1.Show;
ref2 := #form1;
showmessage(ref2^.ClassName+' - '+ref2^.Name+' - '+BoolToStr(ref2^.visible,true));
freeandnil(form1);
showmessage(ref2^.ClassName+' - '+ref2^.Name+' - '+BoolToStr(ref2^.visible,true));
end;
There is no reliable way to do what you are trying to do using the technique you're attempting. Forms that have "gone away" may have their memory reused, possibly even for a new form.
At best, you could work some mechanism whereby you cache the results of iterating Screen.Forms, but you can still fall foul of accidental duplicates, where a form gets destroyed and another gets reallocated and gets the same object address. That scenario is less likely than the memory being reused for some other object, however.
In a similar case I am using a singleton object that keeps a list of all the created forms.
Each form has a field with a reference to this Object.
TMyForm = class(TForm)
private
//*** This is the reference to the singleton...
FFormHandler: TFormHandler;
public
...
//*** you might want to publish it as a property:
property FormHandler: TFormHandler read FFormHandler write FFormHandler;
end;
You can set this reference e.g. when calling the constructor:
TMyForm.Create(aFormHandler: TFormHandler; aOwner: TComponent)
begin
FFormHandler := aFormHandler;
inherited Create(aOwner);
end;
(Or you could set the field from outside directly after creating the form if you don't want to change the parameters of the constructor).
When the form ist destroyed it notifies the handler and tells him to remove the form from the list - something like that:
TMyForm.Destroy(Sender: TObject);
begin
FFormHandler.RemoveFromFormList(Self);
inherited;
end;
(The details of the track-keeping are not included in the expample - e.g. a method "AddToFomList" or something alike would be needed)
There is one very interesting memory manager. It is called SafeMM: http://blogs.embarcadero.com/medington/2009/10/16/24839 But still it is for debugging only.
Given that you cannot modify the code that is out there in the plugins, all the good solutions about how to write safer code are not applicable to your case.
You have 1 way of doing it by
checking if an Object reference is
still what it's supposed to be by
looking up the VMT. This idea was
first published by Ray Lischner (who advocated for FreeAndNil for that very reason) and
later by Hallvard Vassbotn: see
this SO answer.
Another, better but introducing major slowdown, is to use FastMM4 in FullDebugmode to have it to replace all the freed objects by a TFreeObject instance instead of simply releasing the memory to the available pool.
Note that both methods do not prevent a false positive if another instance of the same class happens to be created at the same memory address. You get a valid object of the right type, just not the original one. (Unlikely in your case, but possible)
it is as simple as comparing against NIL:
// object declaration
Type object;
object = new Type();
...
// here you want to be sure of the existance of the object:
if (object <> nil )
object.free;
If you cannot test in another manner, you can use this as a last resort±
function IsValidClass( Cls: TClass ): Boolean;
var
i: Integer;
begin
for i := 0 to 99 do begin
Result := ( Cls = TObject ); // note that other modules may have a different root TObject!
if Result then Exit;
if IsBadReadPtr( Cls, sizeof( Pointer ) ) then Break;
if IsBadReadPtr( Pointer( Integer( Cls ) + vmtParent ), sizeof( Pointer ) ) then Break;
Cls := Cls.ClassParent;
end;
Result := False;
end;
function IsValidObject( Obj: TObject ): Boolean;
begin
Result := not IsBadReadPtr( Obj, sizeof( Pointer ) ) and IsValidClass( Obj.ClassType ) and not IsBadReadPtr( Obj, Obj.InstanceSize );
end;
IsBadReadPtr comes from Windows.

Resources