AV When using a Procedure from one Component called by another - delphi

Im not sure if i have explaned this the best i can but, here we go...
I have 2 Custom components on a form, Which are link together at design time through the IDE. Whenever i call a procedure from on of the Component i get the Access violation,
Access violation at address 0049A614
in module 'Project2.exe'. Read of
address 00000034.
This is a small section of my code
TMyClient = class(TClientSocket)
{...}
end;
and...
TPresence = class(TComponent)
private
ftheClient: TMyClient
public
procedure SetStatus(status: string);
published
property UserName : string read fUserName write fUserName;
property theClient: TMyClient read ftheClient write ftheClient;
end;
procedure TPresence.SetStatus(status: string);
begin
try
***** if theClient = nil then
Exception.Create('theClient is Nil');
except
on e:Exception do
MessageDlg(e.classname+', '+e.message, mtWarning, [mbOK], 0);
end;
{...}
end;
0049A614 is at the *****, and the IDE stops here.
I Have also tried to do the assign at run time with
Presence1.theClient := MyClient1;
with no luck
using procedures from Presence1 or MyClient1 that do not rely on each other work fine.
Delphi 7
Follow Up:
from mghie comments, i rethought about it.
I removed the TPresence Component from the form (which caused some strange IDE errors, that might have had something to do with it) and created it design time, assigning everything that was needed. Now it works, but putting the TPresence Component back on the from brings the error back.
Thankyou for your help guys, i should be able to work this one out now, if i can't ill reopen another question :)

You seem to be thinking that the exception is raised because the client field of Presence1 is not set - if you do however get the exception "Read of address 00000034" it means that the Self pointer in the SetStatus() call is nil. That would indicate that you call SetStatus() on an unassigned TPresence reference. It is not really possible to tell the reason for that from the snippet you posted, but it should get you started debugging.
I would still advise you to write a proper setter method for all component references in your own custom components - first because you have a better hook when debugging such problems (you can set a breakpoint there), and second because you should always call TComponent.FreeNotification() on such linked components to be able to track their destruction and set the internal reference to nil.

We probably need more of your code. It is possible you are not correctly creating an instance of TPresence which would give you the error you are experiencing. Try to give us a simple as possible code snippet that causes your error.

Related

how do i create Tstrings with onchange event?

i know how to make Tstringlist onchange event , but what about Tstrings ? i dont want to use VCL like tmemo or something . can i do that ? is it possible to have this event on tstrings and do something when its changed ?
i tried to do something like this but got access violation
//on form show event
stringlist:= TStringList.Create;
stringlist.OnChange := HandleStringListChange;
//implantation
procedure TChatFo.HandleStringListChange(Sender: tObject);
begin
if stringlist.Text <> '' then
ProcessCommands(stringlist.Text);
stringlist.Clear;
end;
exception messages
Project Project1.exe raised exception class $C0000005 with message
'access violation at 0x6d40c92c: read of address 0x00000150'.
Project Project1.exe raised exception class EStringListError with
message 'List index out of bounds (5)'.
Project Project1.exe raised exception class EStringListError with
message 'List index out of bounds (5)'.
this tstringlist should work as command identifier i creat it with my thread
as example
type
TReadingThread = class(TThread)
protected
FConnection : TIdTCPConnection;
FLogResult : TStrings;
procedure Execute; override;
public
constructor Create(AConn: TIdTCPConnection; ALogResult: TStrings); reintroduce;
end;
ListeningThread := TReadingThread.Create( TCPClient, stringlist);
constructor TReadingThread.Create(AConn: TIdTCPConnection; ALogResult: TStrings);
begin
FConnection := AConn;
FLogResult := ALogResult;
inherited Create(False);
end;
procedure TReadingThread.Execute;
Var
strData : String;
begin
while not Terminated do
begin
try
strData := FConnection.IOHandler.ReadLn;
if strData <> '' then
begin
FLogResult.Add( strData );
end;
except
on E: Exception do
begin
FConnection.Disconnect(False);
if FConnection.IOHandler <> nil
then FConnection.IOHandler.InputBuffer.Clear;
Break;
end;
end;
Sleep(10);
end; // While
end;
if i use Tmemo no errors or exception happened.
We're all shooting in the dark here because you haven't provided all the relevant code. That said, the information you have provided has a lot of problems, and I can offer advice to help you solve it.
Debugging 101
Run your code through the IDE. When you get your exception, the debugger stops at the line that caused the exception.
This is usually the most important step in figuring out what went wrong. You have access to this information. Run your program and carefully look at the line that raised the error. You might be able to already figure what caused the error. If not, a other basic techniques can be applied to get more information:
Get values of objects and variables on the error line and other close lines. You can hover your mouse cursor to get tool-tips, or press Ctrl + F7.
You can examine the call stack of lines leading to the one that caused the error by double-clicking the previous line in the call-stack.
Put a breakpoint on the line before the error and re-run the app. The debugger will stop on that line and give you a chance to check values as explained earlier, but before the error happens.
Asking for help 101
Getting help from people is much more effective when you give them all the relevant information. For a start, the line of code where the access violation occurs would be extremely useful.... Tell us!
Give us real code.
Saying "I tried to do something like this" is not particularly useful. Please copy and paste exactly what you tried. If your code is different, your mistake might no longer be there.
Access Violations
You get an access violation in the following situations:
You forgot to create the object you want to use or didn't assign it to the appropriate variable.
You created the object but Destroyed or Freed it already before trying to use it again.
You changed the variable that was referencing the object.
You performed a 'hard-cast' (or unchecked typecast) from one type to an incompatible type.
The above are the basics. There some variations, and a few special edge cases, but these account for the vast majority of mistakes.
So using the above, that's what you need to check. If you had copy-pasted more of your code, we might be able to see what you did wrong.
NOTE: One shot-in-the-dark possibility is that you are destroying your string list in the wrong place. And perhaps the memo works because as a component dropped on the form, you're not trying to destroy it.
Stack overflow
Let's examine what happens in your OnChange event when for example a string is added:
The event fires.
Text is not empty.
So you call ProcessCommands
You then call Clear
The the end of Clear, Changed is called again.
Which fires your event again.
This time Text is empty, so you won't call ProcessCommands
But you do try to Clear the string list again...
This could go on forever; well at least until the call-stack runs out of space and you get a stack-overflow error.
Saved by the bell
The only reason you don't get a stack overflow is because Clear doesn't do anything if the string list is empty:
procedure TStringList.Clear;
begin
if FCount <> 0 then //You're lucky these 2 lines stop your stack-overflow
begin
...
FCount := 0; //You're lucky these 2 lines stop your stack-overflow
SetCapacity(0);
Changed;
end;
end;
I suggest you rethink how to solve you problem because code that leads to unintentional recursion is going to make your life difficult.
Working with threads
You really need to get a handle on the basics of programming before trying to work with threads. Multi-threaded programming throws in a huge amount of complexity.
I can already see a huge possibility of one potential mistake. (Though it depends what you're doing inside ProcessCommands.)
You modify your string list in the context of a thread.
This means your OnChange event handler also fires in the context of the thread. (The fact it's implemented on the form is irrelevant.)
If your ProcessCommands method does anything that requires it to operate on the main thread, you're going to encounter problems.
As a final consideration, I've noted quite a few beginner programmers completely miss the point that code starting a thread can finish before the thread does. E.g. (Going back to the topic on Access Violations.): If you're destroying your string list soon after creating your thread, your thread could suddenly throw an access violation when the object it had earlier is suddenly gone.
The likely explanation for your error is that you are modifying the list in its OnChange event handler. That is simply not allowed. An OnChange handler must not mutate the state of the object.
What is happening is that you have some other code, that we cannot see, that modifies the list, perhaps in a loop. As it modifies the list your event handler clears the list and then the calling code has had the rug pulled from underneath it.
Of course, I'm having to guess here since you did not show complete code. Perhaps the specifics vary somewhat but it seems likely that this is the root of your problem.
You'll need to find another way to solve your problem. With the information available we cannot suggest how to do that.
Looking at your updated code, there's no need for a string list at all. Remove it completely. Instead, where you do:
FLogResult.Add( strData );
do this:
ProcessCommands( strData );
TStrings is an abstract class, ancestor of TStringList. You can create an instance of TStringList and use it as a TStrings.

Delphi Custom Component Knowing Which Referee Called A Method

I have created a custom component which has a property that the user can assign to another custom component..
TComp1 = class(TComponent)
public
property Comp2: TComp2 read GetComp2 write SetComp2;
property Something;
end;
TComp2 = class(TComponent)
public
procedure DoSomething;
end;
There could be multiple TComp1 components that assign the same TComp2. I need TComp2 to know which TComp1 called it because it needs to reference property "something" of that specific referee..
var
comp1a, comp1b: TComp1;
comp2: TComp2;
comp2 := TComp2.create;
comp1a := TComp1.create;
comp1b := TComp1.create;
comp1a.comp2 := comp2;
comp1b.comp2 := comp2;
comp1b.comp2.dosomething; <-- needs to know this was from comp1b not comp1a
obviously, the code above is just to illustrate my point and is not including the notification mechanisms that I have to put in place, etc.
so far, I have considered using the getter for TComp1.Comp2 to set an "activeComponent" property on the assigned TComp2 so that TComp2 can use that property to get the right component. While this should work, I believe it is unsafe and if someone tries to use comp2 directly or passes the reference to another variable entirely (comp := comp1a.comp2; comp.dosomething;), or tries to use it from multiple threads, there could be issues.
has anybody else encountered this issue? what is the best solution?
I hope somebody will be able to help :)
In the line:
comp1b.comp2.dosomething; <-- needs to know this was from comp1b not comp1a
The call to doSomething has absolutely no knowledge of comp1b. You should think of this as two separate lines of code:
LocalComp2 := comp1b.comp2;
LocalComp2.doSomething;
So as per David's comment, you need to pass the other component as a parameter. I.e.
comp1b.comp2.doSomething(comp1b);
This, by the way, is a stock-standard technique used throughout Delphi code. The best example is TNotifyEvent = procedure (Sender: TObject) of object; and is used in calls like:
ButtonClick(Self);
MenuItemClick(MainMenu);
You ask in a comment:
I have thought about that too but it seems somewhat redundant to effectively reference the same component twice.. comp1a.comp2.domsomething(comp1a); is there no better way?
As I said, the bit comp2.domsomething has no knowledge of the comp1a. just before it. So as far as the compiler is concerned it's not redundant. In fact it's also possible to call comp1a.comp2.domsomething(SomeOtherComponent).
However, that said, there is a better way.
Currently your code violates a princple called the Law of Demeter. Users of TComp1 are exposed to details abobut TComp2 even if they don't care about TComp2. This means that you can find yourself repeatedly writing:
comp1a.comp2.doSomething(comp1a);
comp1b.comp2.doSomething(comp1b);
comp1a.comp2.doSomething(comp1a);
comp1c.comp2.doSomething(comp1c);
To avoid that, fix the Law of Demeter violation by writing:
procedure TComp1.doSomething;
begin
comp2.doSomething(Self);
end;
Now your earlier lines become:
comp1a.doSomething;
comp1b.doSomething;
comp1a.doSomething;
comp1c.doSomething;
The obvious solution is to pass the extra information as a parameter. Like this:
comp1b.comp2.dosomething(comp1b);
Expecting comp2 to be able to work out whether it was referenced from comp1a or comp1b is unrealistic, and frankly would be an indication of a poor design.
Parameters are explicit and so demonstrate clear intent to the reader.

Can NIL be used for a Sender object?

Recently I was asked to automate a little sub-routine that presented a series of data records and any two of four potential buttons for the user to select after seeing an analysis of the record. The boss said having the users see the analysis was wasting time since the users invariably selected the number one choice in the button list and he was prepared to live with my guesses for all but the best of his users. So, he wanted a NEW series of buttons added to offer Handle Automatically, Handle Manually and Handle Case by Case. The last button would just run the already existing code. The second button would essentially do nothing and just exit. The first button? Well, that was the rub.
What I decided to do was to use a couple of flags and then have the automatic path just simulate the click of whatever sub-button was best, based on the analysis. The issue was that calling Button1Click(Sender) wasn't possible because the procedure running the Analysis was called RunAnalysis and wasn't attached to a specific object to pass the TObject through. I eventually refactored the guts of the Button1Click method into Button1Pressed and then called THAT from Button1Click. Thus I was able to call Button1Pressed from within RunAnalysis.
The avoided path would have been to call Button1Click(Nil). I didn't try it since I had an easy solution (Thanks Modelmaker, by the way). But my question is, would the nil reference have worked or would it have caused a disaster. Could I have called a higher function (randomly?) that did have a sender, JUST to have a sender object in the procedure call? Just how important IS the Sender object, if I don't use anything that actually REFERENCES the Sender?
System details: Delphi 7 in Win 7 programming environment for use in Windows XP.
Thanks in advance for any wisdom, GM
I tend to put the event handler's code into another method when possible:
procedure TForm1.DoSomething(const Test: Boolean);
begin
// Do Something based on Test
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
DoSomething(False); // init here
end;
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
DoSomething(TCheckBox(Sender).Checked);
end;
So when I have a need to call CheckBox1Click(nil) it's a good sign for me to pull off the code from the event handler into a separate method.
A callback or "event" is no different than any other function. You can pass a NIL reference anywhere you want, as long as you either (a) wrote that code and know it's nil safe, or (b) you have read that code and everything it calls without checking for nil, and know it's nil safe.
Some programmers use NIL freely and some consider using NIL parameter values to be bad style. My style tends to be "don't assume Sender is assigned and don't assume that it is of a particular type", which leads to lots of check-code in my event handlers, but other code than mine, it varies widely and so the First Rule of coding comes in; "Don't make assumptions. Read the code.".
You can use nil as a parameter to a TNotify event (or any other expecting a Sender), as long as the code doesn't reference Sender:
procedure TForm1.Button1Click(Sender: TObject);
begin
DoSomeStuff(nil);
end;
procedure TForm1.DoSomeStuff(Sender: TObject);
begin
// Safe
DoSomeOtherStuff;
// Safe
// Do stuff with Sender
if Sender is TButton then
TButton(Sender).Caption := 'In DoSomeStuff'
// NOT safe!
with TButton(Sender) do
begin
Caption := 'In DoSomeStuff';
end;
end;
Short Answer - yes.
I use it to distinguish whether (say) a menu Event was clicked by a user or called directly by code.

How to set THTTPRio.Converter.Options to soLiteralParams in OnBeforeExecuteEvent

This refer to the post Delphi SOAP Envelope and WCF.
Could someone please post a sample code which can show me how to set soLiteralParams in THTTPRio.Converter.Options in Delphi 7. I have the following code currently.
I have drag-dropped a HTTPRIO component into the document which has created a line HTTPRIO1: THTTPRIO at the beginning of the code. I basically want to understand how I set soLiteralParams in the above component. Following is the code I am trying to execute which is giving me error.
procedure TForm1.CleanUpSOAP(const MethodName: String; var SOAPRequest: WideString);
var RIO: THTTPRIO;
begin
//The following line is giving error
// RIO.Converter.options := [soLiteralParams];
end;
In the above code I have declared a variable RIO of the type THTTPRIO, which I am not sure is correct.
Just guessing, as you provide very little information in your question.
Use the variable assigned to the component you dropped on your form. Don't declare a new local one (which you never created anyway). To set the Converter.Options in code, you'll need to add OPToSOAPDomConv to your uses clause.
implementation
uses
OPToSOAPDomConv;
// BTW, this name might not be a good one if it's the
// OnBeforeExecute event handler as that isn't
// clear from the name.
procedure TForm1.CleanUpSOAP(const MethodName: String; var SOAPRequest: WideString);
begin
// Note this clears any previous options!
HTTPRIO1.Converter.Options := [soLiteralParams];
// If you want to keep the previous options instead of replacing them
// HTTPRIO1.Converter1.Options := HTTPRIO1.Converter1.Options + [soLiteralParams];
end;
If you've dropped the component on the form, I'm not sure why you're not handling this in the Object Inspector instead, however.
If this doesn't solve the problem, edit your question and provide the exact error message you're receiving, including any memory addresses in the case of an exception being raised.
I have cracked this. The issue was that I didn't refer to OPconvert.pas file, which contained the TSOAPConvertOption enumeration. I don't know whether copying this file into the same folder as my project files and referring to this in the "uses" section is the right way, but it worked fine.

Access Violation when assigning component property on Data Module

I'm on Delphi 2009, and my application contains a data module, which has a custom component named 'Globals' on it.
In another form in the application, I'm trying to change a published property (a string) on the Globals component:
dm.Globals.qhProject := _ProjectName.Text; //always gives access violation
The _ProjectName control is a TLabeledEdit descendant. I always get an access violation here if there's any text in the box.
However, if I manually assing a string to the property, I don't get the AV:
dm.Globals.qhProject := 'some text'; //works
Here's the declaration of the qhProject property:
FqhProject: string;
property qhProject: string read FqhProject write FqhProject;
I can't figure out why there's an AV here at all. I've tried moving the auto create order of the forms/data module around, but I always get the AV.
What am I doing wrong here?
Update:
I'm using Billenium Effects TFormContainer to switch between different forms in the application. Both forms that are involved here are in the form container. One form, TdlgSummary, contains the following:
procedure TdlgSummary.SyncVars;
begin
dm.Globals.qhProject := _ProjectName.Text
end;
The other form, TdlgFinalize, contains the following:
dlgSummary.SyncVars;
If I run SyncVars inside of dlgSummary, it works fine. If I call it from dlgFinalize, I get the AV. This seems to be the crux of the problem, but I can't figure out what's going wrong. Even if I switch control to dlgFinalize, dlgSummary hasn't been destroyed, because I can go back to it. Any ideas?
Looks to me like _ProjectName is nil. Try putting a breakpoint at this line and examine _ProjectName, see if it gives a valid reference or not.

Resources