Avatar billede sorenriis Nybegynder
24. juli 2005 - 00:48 Der er 26 kommentarer og
3 løsninger

Program hænger

Når jeg eksekverer en funktion, så hænger programmet - og kommer ingen steder.

Er det ikke muligt at få Delphi til at vise præcist hvilket linje den går ned på?
Mener det er muligt, men har ikke lige kunnet finde ud af hvor/hvordan..
Avatar billede sorenriis Nybegynder
24. juli 2005 - 00:53 #1
Eller er der måske en anden måde at finde hvor fejlen præcist er?
Avatar billede vallemanden Nybegynder
24. juli 2005 - 01:28 #2
jeg plejer at putte nogle
showmessage('1');

ind hist og her
Avatar billede frille Nybegynder
24. juli 2005 - 01:34 #3
Nu kender jeg ikke delphi, men hvad med noget debugning.... eller det eksistere måske ikke i delphi.
Avatar billede kroning Nybegynder
24. juli 2005 - 08:00 #4
Hvis du kikker i menuen under run kan du se at bla. F8 og F7 bruges til debug.
Avatar billede sorenriis Nybegynder
24. juli 2005 - 11:35 #5
Problemet er at F8/F7 ikke viser fejlen.

Jeg har lavet 2 programmer som kommunikerer med hinanden vha. TCP.
Problemet opstår når jeg sender en bestemt kommando fra klienten til server.

Den ekskverer et "klik" på en knap - og når det sker, hænger server-modulet. Og eftersom jeg ingen meddelelser får om hvorfor den hænger, er jeg lidt på bar bund.
Avatar billede sorenriis Nybegynder
24. juli 2005 - 22:18 #6
Skal lige understreges at laver jeg det knap-klik manuelt, så virker det som det skal. Men gør jeg det vha. min remote-software, så hænger server-delen et eller andet sted under knap-klik proceduren..
Avatar billede vallemanden Nybegynder
24. juli 2005 - 23:30 #7
ja men så prøv at sætte nogle stops eller showmessages ind så du kan se hvilken linie der laver fejlen
Avatar billede hrc Mester
26. juli 2005 - 11:04 #8
I stedet for at bruge ShowMessages så kan du måske drage nytte af OutputDebugString, der indsætter en linie i Delphi's event log (ctrl-alt-v). Under Views, Debug windows er der mange muligheder for at se hvor det går galt. Skal det blive rigtigt hardcore, så kan du få den til at vise hvor i den oversatte kode at den dør - hvis altså den dør med fejl og ikke et uendeligt loop eller lignende.

Har du mulighed for at offentliggøre lidt kode? Mest interessant er nok der hvor du forbereder data og sender det - tilsvarende der hvor du læser data igen.
Avatar billede sorenriis Nybegynder
26. juli 2005 - 14:43 #9
Jeg har prøvet at rode med det igen.

Det viser sig at det er følgende kode der får programmet til at hænge (ihvertfald hænger programmet ikke, hvis jeg fjerner den).

for i:=0 to (Length(Tracks)-1) do
begin
Index:=TrackListBox.Items.IndexOf(Tracks[i]);
Index2:=TrackListBox2.Items.IndexOf(Tracks[i]);
if (Index>=0) then TrackListBox.Items.Delete(Index);
if (Index2>=0) then TrackListBox2.Items.Delete(Index2);
TrackListBox2.Items.Add(Tracks[i]);
end;

Det koden gør er at den skulle søge efter om nogle af de linjer jeg sætter ind i TrackListBox2, eksisterer i TrackListBox1. Gør de det, skal den slette dem fra TrackListBox1 (kan det gøres på en smartere måde)?

Hvad jeg ikke forstår er, at det fungerer perfekt hvis jeg laver det manuelle klik.
Gør jeg det gennem remotecontrol'en, så hænger programmet.

Fjerner jeg koden der, så virker det manuelle klik og remotecontrol'en perfekt.
Avatar billede sorenriis Nybegynder
26. juli 2005 - 14:47 #10
Lavede lige lidt om i koden.
Den hænger dog stadig (hvis jeg ikke udelader den):

TrackListBox2.Clear;
for i:=0 to (Length(Tracks)-1) do
begin
Index:=TrackListBox.Items.IndexOf(Tracks[i]);
if (Index>=0) then TrackListBox.Items.Delete(Index);
TrackListBox2.Items.Add(Tracks[i]);
end;
Avatar billede hrc Mester
26. juli 2005 - 15:56 #11
Jeg tror det har noget at gøre med din length(Tracks) at gøre. Kan ikke se hvad det er for en tingest, men antager at det er en TStrings og så er din måde at lave det på forkert.

Jeg har lavet nedenstående procedure som måske kan hjælpe dig videre (<TfrmMain> skal du erstatte med din egen form):

procedure <TfrmMain>.InsertTracks(aTracks : TStrings);
var
  i, Index : integer;
begin
  TrackListBox1.Items.BeginUpdate;
  TrackListBox2.Items.BeginUpdate;
  try
    for i := 0 to aTracks.Count - 1 do begin
      Index := TrackListBox1.Items.IndexOf(aTracks[i]);
      if Index >= 0 then
        TrackListBox1.Items.Delete(Index);
    end;
    TrackListBox2.Items.Assign(aTracks);
  finally
    TrackListBox1.Items.EndUpdate;
    TrackListBox2.Items.EndUpdate;
  end;
end;

Som jeg kalder således:

procedure <TfrmMain>.Button2Click(Sender: TObject);
var
  Tracks : TStringList;
begin
  Tracks := TStringList.Create;
  try
    Tracks.Add('A');
    ...
    Tracks.Add('Z');

    InsertTracks(Tracks);
  finally
    Tracks.Free;
  end;
end;
Avatar billede sorenriis Nybegynder
26. juli 2005 - 16:08 #12
Hrm.. ok...
Jeg forstår ikke helt hvordan det kan være forkert når det nu virker, hvis jeg altså selv trykket på knappen?
Enlighten me please...
Avatar billede hrc Mester
27. juli 2005 - 07:21 #13
Hvis altså at Tracks er en TStrings tingest, så skal man ikke bruge length når den har en property der fortæller oplysningen. Length bruges kun til at returnere længden på en streng eller antallet af felter i et array (Borland undlod, af en eller anden grund, at lave String til et rigtigt objekt fra starten).

Vil skyde på, at det er tilfældigt at den returnerer et brugbart resultat.

Under alle omstændigheder, givet at tracks er en TStrings af en art (TStringList), så virker min procedure - den er af mig testet.
Avatar billede sorenriis Nybegynder
27. juli 2005 - 10:34 #14
Tracks er et array af typen string.

Værdierne læses ind fra en fil og array'et kan så se således ud:
Tracks[0]:='Bane0':
Tracks[1]:='Bane1':
Tracks[2]:='Bane2':
Tracks[3]:='Bane3':

Du skriver at length() netop kan bruges til at tælle antallet af felter i et array, hvilket jeg netop gerne vil.
Men du mener stadig at min kode ikke dur - og jeg skal bruge din metode?
Avatar billede hrc Mester
27. juli 2005 - 11:50 #15
Jeg ville bruge min procedure og indlæse filen via en TStringList. Den har nemlig en LoadFromFile (og en LoadFromStream) og så har du et array hvor både strenglængden og antallet af linier er dynamisk op til 2Gb grænsen.

Arrays er noget som jeg anser som pre-Delphi. TStringListen er smart og hurtig.
Avatar billede doc404 Novice
28. juli 2005 - 23:13 #16
Den TCP ting, der kalder din Click funktion, køre den i en tråd?
Avatar billede sorenriis Nybegynder
29. juli 2005 - 00:03 #17
I en tråd? Hvad mener du?
Avatar billede doc404 Novice
29. juli 2005 - 00:10 #18
Din TCP/IP komponent/kode, der modtager data fra klienten. Kører den i en TThread? Kan du poste den del af koden?
Avatar billede sorenriis Nybegynder
29. juli 2005 - 09:48 #19
Den ser ud som følger:

procedure TForm1.TCPServerExecute(AContext: TIdContext);
var
Command: string;
Command2: string;
Index: Integer;
begin
Command:=Acontext.Connection.IOHandler.ReadLn;
Command2:=Acontext.Connection.IOHandler.ReadLn;

// Load Profile
if (Command='LP') then
  begin
  Index:=LogListBox.Items.IndexOf(AContext.Connection.Socket.Binding.PeerIP);
    If (Index>=0) then
    begin
      if (StrToInt(Command2)>=0) then
        begin
        LoadComboBox.ItemIndex:=StrToInt(Command2);
        Acontext.Connection.IOHandler.WriteLn('2');
        LoadButton.Click;
        end;

      if (StrToInt(Command2)<0) then
        begin
        Acontext.Connection.IOHandler.WriteLn('1');
        end;
    end;
end;

Hvis serveren modtager en en "LP"-kommando, så skal serveren gøre det ovenstående. Det foregår med IdTCP-componenten.

Mener du det er her den går galt (findes der en bedre måde at sende den kommando på?)?
Avatar billede doc404 Novice
29. juli 2005 - 09:59 #20
TCPServer køren i en thread og der er sikkert det, der giver dig problemer. Du 'må' ikke kalde kode i din main thread, der 'skriver' på skærmen. Tag et kig i hjælpen under Synchronize.
Avatar billede frille Nybegynder
29. juli 2005 - 10:31 #21
At en TCP server køre i en tråd er ikke en problem i sig selv. Problemet er vis serveren køre i main tråden, at så vil det se ud som om at programmet hænger, fordi så vil den bare ligge og lytte konstant uden at opdatere ydre ting som fx gui. Nu har jeg ikke lige læst hele tråden, men jeg ved af erfaring. At når en server skal lytte skal man oprette en tråd til den som den selv kan ligge og køre i. Når "serveren" selv får tildelt en tråd, kan man stadig interegere med den, da man ikke benytter main tråden til lytning. Fx kan man lave en tofase terminering, så man kan bestemme hvornår server tråden skal dø.
Avatar billede sorenriis Nybegynder
29. juli 2005 - 10:43 #22
Frille < Der tabte jeg forbindelsen.
Kan du ikke forklare det lidt nærmere? Og hvad er det jeg præcist skal gøre (eksempel ville være godt)?
Avatar billede frille Nybegynder
29. juli 2005 - 15:08 #23
Nu kan jeg ikke delphi men java og c#

men se her i java.


private boolean isRunning = true;

public static void main(String[] argv)
{

    while(isRunning)
      {
    //vi lytter, lytter og lytter
      }
}

her kan du godt se, at det går galt, da når vi først kommer ind i lykken, bruger
vi kun energi på at lytte. Dvs. hvis vi har noget gui fx. Så vil det ikke blive opdateret.

gør istedet på denne led, ved at lade en tråd tage sig af lytte løkken. I c#

public class Server
{
    private Thread server;   

    public Server()
    {
        server = new Thread(ThreadStart(startNyTråd)); //startNyTråd er bare navnet på den metode der skal kaldes når den nye tråd startes.
                (kaldes et delegate)   
        server.Start();//vi starter den nye tråd. Dvs vi returnere omgående samtidig med at den nye tråd går  igang.

        for(int tal=0; tal < 30000; tal++)
        Console.WriteLine(tal)
    }

    public void startNyTråd()
    {
        //her kunne server lytningen finde sted.
    }

    public static void Main()
    {
        new Server();
    }
}

Se her sker det. Vi laver et server objekt i main. I konstruktoren skabes der en ny tråd. Denne tråd startes med start metoden, der
omgående returnere. Dvs. Her ville serveren lytte imens at den talte op fra 0 til 30000. Istedet for at tælle, kan dit program så
fx lytte på gui input eller hvad du nu vil.
Håber det er svar nok :-)
Avatar billede doc404 Novice
29. juli 2005 - 18:47 #24
Java eksemplet viser faktisk, hvad der sker inde i TCPServerSocket. Den opretter en tråd til at modtage client requests og nye tråde til at behandle client requests.

Din OnExecute er altså en anden tråd en den din mainform køre i. Hvis du fra din OnExecute kalder funktioner i din main tråd kan det give virkelige underlige resultater. Især hvis det er noget GUI noget.

Synchronize er løsningen på dit problem. Der er glimrende eksempler i hjælpen.
Avatar billede sorenriis Nybegynder
04. august 2005 - 15:04 #25
Jeg er altså stadig ikke (selv efter at have kigget i hjælpen) i stand til at greje hvordan jeg bruger det synchonize-halløj. Findes der ikke noget mere detaljeret hjælp?
Avatar billede frille Nybegynder
04. august 2005 - 18:45 #26
Forstår ikke hvorfor du snakker om syncronized. Syncronized er noget du normalt bruger til at beskytte en kritisk region. Altså en fælles resource som to eller flere tråde benytter sig af. Tror bare du skal finde ud af hvordan du får lavet en tråd. Det burde løse dit program hænger problem.

Måske kan dette hjælpe dig:
Starting the thread

When creating a thread without the TThread class, always use the BeginThread function from the SysUtils unit. It is specifically written to use Pascal functions and it encapsulates the CreateThread winapi call.

Let's take a look at the declaration and step through the parameters.

BeginThread(SecurityAttributes: Pointer;
    StackSize: LongWord;
    ThreadFunc: TThreadFunc;
        Parameter: Pointer;
    CreationFlags: LongWord;
    var ThreadId: LongWord): Integer; 

    * SecurityAttributes: a pointer to a security record, used only in windows NT, fill in nil
    * StackSize: the initial stack size of the thread. The default value is 1MB. If you think this is too small fill in the desired size, otherwise if not fill in 0.
    * ThreadFunc: This is the function that will be executed while the thread is running. This is mostly a function with a while loop inside. The prototype is function(Parameter: Pointer): Integer
    * Parameter: This is a pointer to a parameter, which can be anything you like. It will be passed to the thread function as the parameter pointer.
    * CreationFlags: This flag determines whether the thread starts immediately or it is suspended until ResumeThread is called. Use CREATE_SUSPENDED for suspended and 0 for an immediate start.
    * ThreadID: This is a var parameter that will return the ThreadID of the thread.


taget fra
http://community.borland.com/article/0,1410,22411,00.html
Avatar billede doc404 Novice
05. august 2005 - 22:45 #27
Java's synchronize er ikke det samme som Delphi's synchronize...

Det er faktisk en smule besværligt det du er igang med. Men prøv at se dette eksempel.

En form, en listbox og en TIdTCPServer komponent sat til og lytte på port 9999. Du kan starte en cmd prompt og med kommandoen telnet 127.0.0.1 9999 kan du sende til den.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, IdBaseComponent, IdComponent, IdTCPServer,IdSYnc, StdCtrls;

type
  TForm1 = class(TForm)
    IdTCPServer1: TIdTCPServer;
    ListBox1: TListBox;
    procedure IdTCPServer1Execute(AThread: TIdPeerThread);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TMySync = class(TIdSync)
  private
    Txt : string;
  public
    constructor Create(AThread : TIdPeerThread);
    procedure DoSynchronize; override;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TMySync }

constructor TMySync.Create(AThread: TIdPeerThread);
begin
  Inherited;
  Txt := AThread.Connection.ReadLn;
end;

procedure TMySync.DoSynchronize;
begin
  // Her kan du gøre hvad som helst GUI relateret...
  Form1.ListBox1.Items.Add(Txt);
end;

procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
begin
  with TMySync.Create(AThread) do
    DoSynchronize;
end;

end.
Avatar billede frille Nybegynder
06. august 2005 - 00:11 #28
Sorry. Det burde jeg have gjort dig opmærksom på, at det ikke var sikker at de to var det samme.
Avatar billede sorenriis Nybegynder
17. august 2005 - 00:19 #29
Nu har jeg brugt noget tid på at rode med det.. Men jeg kommer altså ikke rigtigt nogle vegne.

Jeg kan forstå at det er i min ServerExecute der skal ændres. Herunder er koden - kan I lede mig lidt mere på sporet (selvom jeg har fået meget hjælp allerede)?

procedure TForm1.TCPServerExecute(AContext: TIdContext);
var
Command: string;
Command2: string;
Index: Integer;
begin
Command:=Acontext.Connection.IOHandler.ReadLn;
Command2:=Acontext.Connection.IOHandler.ReadLn;

// Load Profile
if (Command='LP') then
  begin
  Index:=LogListBox.Items.IndexOf(AContext.Connection.Socket.Binding.PeerIP);
    If (Index>=0) then
    begin
      if (StrToInt(Command2)>=0) then
        begin
        LoadComboBox.ItemIndex:=StrToInt(Command2);
        Acontext.Connection.IOHandler.WriteLn('2');
        LoadButton.Click;
        end;

      if (StrToInt(Command2)<0) then
        begin
        Acontext.Connection.IOHandler.WriteLn('1');
        end;
    end;

    If (Index<0) then
    begin
    Acontext.Connection.IOHandler.WriteLn('0');
    end;
  end;
end;
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester