Link to home
Start Free TrialLog in
Avatar of MauricioGaviria
MauricioGaviria

asked on

multi-thread application

When trying to execute a multi-thread application in Delphi 6, it gives an error with message:
faulted with message: access violation at 0x005517e8: write of address 0x38010b12. Process stopped.
What does this error mean? How can I solve this situation
Avatar of david_barker
david_barker

I think you are going to have to give more details, like where the code breaks.
Avatar of MauricioGaviria

ASKER

ok
this is the source
------------------------------------------------------------------------------------------------------------------------------------------------
main Unit
------------------------------------------------------------------------------------------------------------------------------------------------
unit main;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls;
type
  TserverGui = class(TForm)
    PageControl1: TPageControl;
    TabSheet1: TTabSheet;
    TabSheet2: TTabSheet;
    Button1: TButton;
    RichEdit1: TRichEdit;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    ThreadsRunning: Integer;
    procedure iniciarCopiaArchivo(listaEquipos:TStringList;idCmd:String);
    procedure FOnDoneParsing(Sender: TObject);

  public
    { Public declarations }
  end;

var
  serverGui: TserverGui;

implementation

uses hiloArchivo;

{$R *.dfm}

procedure TserverGui.Button1Click(Sender: TObject);
var
    listaEquipos : TStringList;
    idComando : String;
begin
    listaEquipos.Add('pp');
    listaEquipos.Add('pp2');
    listaEquipos.Add('pp3');
    listaEquipos.Add('pp4');
    listaEquipos.Add('pp5');
    listaEquipos.Add('pp6');
    listaEquipos.Add('pp7');
    listaEquipos.Add('pp8');
    listaEquipos.Add('pp9');
    listaEquipos.Add('pp10');
    listaEquipos.Add('pp11');
    listaEquipos.Add('pp12');
    listaEquipos.Add('pp13');
    iniciarCopiaArchivo(listaEquipos,'540');
end;

procedure TServerGui.iniciarCopiaArchivo(listaEquipos:TStringList;idCmd:String);
begin
    ThreadsRunning := 1;
    with TEscribirArchivo.Create(listaEquipos, idCmd) do
         OnTerminate := FOnDoneParsing;
    Button1.Enabled := False;
end;

procedure TServerGui.FOnDoneParsing(Sender: TObject);
begin
  Dec(ThreadsRunning);
  if ThreadsRunning = 0 then
  begin
    Button1.Enabled := True;
  end;
end;
end.
------------------------------------------------------------------------------------------------------------------------------------------------
unit thread
------------------------------------------------------------------------------------------------------------------------------------------------
unit hiloArchivo;

interface

uses
  Classes;

type
  ThiloEscrituraArchivo = class(TThread)
  private
    { Private declarations }
    listaEqps : TStringList;
    idComando : String;
  protected
    procedure Execute; override;
    procedure mostrarProgreso;
    procedure escribir_a_Archivo(listaPCs:TStringList;idComandoExe:String); virtual; abstract;
  public
    constructor Create(lista: TStringList; idCmd: String);
  end;

  TEscribirArchivo = class(ThiloEscrituraArchivo)
  protected
    procedure escribir_a_Archivo(listaPCs:TStringList;idComandoExe:String);
  end;
implementation
uses excaliburAdminCode;
{ Important: Methods and properties of objects in VCL or CLX can only be used
  in a method called using Synchronize, for example,

      Synchronize(UpdateCaption);

  and UpdateCaption could look like,

    procedure hiloEscrituraArchivo.UpdateCaption;
    begin
      Form1.Caption := 'Updated in a thread';
    end; }

{ hiloEscrituraArchivo }
constructor ThiloEscrituraArchivo.Create(lista: TStringList; idCmd: String);
begin
    listaEqps := TStringList.Create;
    listaEqps := lista;
    idComando := idCmd;
    FreeOnTerminate := True;
    inherited Create(False);
end;
procedure ThiloEscrituraArchivo.Execute;
begin
    { Place thread code here }
    escribir_a_Archivo(listaEqps,idComando);
end;
procedure ThiloEscrituraArchivo.mostrarProgreso;
begin
    serverGui.RichEdit1.Lines.Add('ok');
    serverGui.RichEdit1.Lines.Add(listaEqps.Strings[0]);
end;
procedure TEscribirArchivo.escribir_a_Archivo(listaPCs:TStringList;idComandoExe:String);
begin
    listaPCs.SaveToFile('comandos.txt');
    Synchronize(mostrarProgreso);
end;
end.
Avatar of Wim ten Brink
It means that on address 0x005517e8 there's a function trying to either read or write data on address 0x38010b12. However, this memory address is protected for some reason and the action cannot be executed. You don't have access, thus you get an access violation.
If you want to get a more detailed answer, please give more details about your application. ;-)

It is most likely that some component or object or function is trying to access something in one thread that has been freed in the other thread. You could try debugging the code but it's better to write log information to a log file.
the problem might be because you are passing a reference to all threads to that list

try something like this:

procedure TServerGui.iniciarCopiaArchivo(listaEquipos:TStringList;idCmd:String);
begin
    ThreadsRunning := 1;
    with TEscribirArchivo.Create(listaEquipos, idCmd) do
    begin
         OnTerminate := FOnDoneParsing;
         Sleep(20) //just give time for this thread to assign that list to its internal list
    end;
    Button1.Enabled := False;
end;
ASKER CERTIFIED SOLUTION
Avatar of vadim_ti
vadim_ti

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
One simple advise, which not many people follow anyway: Don't use your native language for class names, variable names and anything else. Try using English names for them. Not that it matters much, but when you need help from people like us at EE who don't understand your native language, your code snippets become a bit easier to read. It also helps if you ever plan to sell the sourcecode to some other party. But as I said, not many people do this anyway.

vadim_ti is right. Use listaEqps.AddStrings(lista);

But the real flaw is here:
constructor ThiloEscrituraArchivo.Create(lista: TStringList; idCmd: String);
begin
    listaEqps := TStringList.Create;
    listaEqps := lista;
    idComando := idCmd;
    FreeOnTerminate := True;
    inherited Create(False);
end;

Should be:
constructor ThiloEscrituraArchivo.Create(lista: TStringList; idCmd: String);
begin
    inherited Create(False);
    listaEqps := TStringList.Create;
    listaEqps := lista;
    idComando := idCmd;
    FreeOnTerminate := True;
end;

Alex:

i do not think you are right, must be:

constructor ThiloEscrituraArchivo.Create(lista: TStringList; idCmd: String);
begin
    listaEqps := TStringList.Create;
    listaEqps.AddStrings(lista);           // <==
    idComando := idCmd;
    FreeOnTerminate := True;
    inherited Create(False);
end;

please tell me what a difference where you place
   inherited Create;
i do not understand
thanks a lot
@vadim_ti, you are wrong... You must FIRST call the inherited Create, then initialize whatever you want to be initialized. If you do it the other way around, you risk that the inherited create will clear some of the data you've just initialized. With destructors, you call the inherited method as last method, but with constructors you MUST call the inherited create first.

Because, do you realise that the default behaviour of TObject.Create is to zero the memory used by the component? It checks the size, then cleans it. Thus if you've added some data in it... Well, you know the risk.

The constructor initializes the data, thus you first want to call the inherited stuff, so that part is correctly initialized, then add your own stuff. With destructors it's the other way around, cleaning your stuff first before the inherited destructor gets the chance to clean the rest.

In most cases I just do this:

constructor ThiloEscrituraArchivo.Create(lista: TStringList; idCmd: String);
begin
    inherited Create(TRUE); // Create it suspended.
    listaEqps := TStringList.Create;
    listaEqps.AddStrings(lista);           // <==
    idComando := idCmd;
    FreeOnTerminate := True;
    RESUME; // Resume the thread.
end;
ok, i regular do the same, but in this case, there are no difference

below is thread constructor with my comments

constructor TThread.Create(CreateSuspended: Boolean);
begin
  inherited Create;                                // do nothing (TObject constructor)
  AddThread;                                       // add thread to threads list
  FSuspended := CreateSuspended;      // it is ok
  FCreateSuspended := CreateSuspended; // it is ok
// we do not use thread  handle
  FHandle := BeginThread(nil, 0, @ThreadProc, Pointer(Self), CREATE_SUSPENDED, FThreadID);
  if FHandle = 0 then
    raise EThread.CreateResFmt(@SThreadCreateError, [SysErrorMessage(GetLastError)]);
end;

what i wanted to say , in this case there is no difference where you call inherited constructor.
i even checked 2 cases to be sure.

i do not understand one thing, why FreeOnTerminate works with this constructor:

constructor ThiloEscrituraArchivo.Create(lista: TStringList; idCmd: String);
begin
    listaEqps := TStringList.Create;
    listaEqps := lista;
    idComando := idCmd;
    FreeOnTerminate := True;
    inherited Create(False);
end;

The TObject.Create isn't doing nothing! At least not with all Delphi versions. The fact that it just happens to work this time isn't really professional, though. That's just my main objection with that method of calling the inherited method as last one. The reason that FreeOnTerminate just happens to work is probably because it is not cleared in the inherited create methods. Basically, that's a bug because it means it could contain any kind of value.

All I'm saying is that you cannot rely that it continues to work with the next Delphi version.
thanks