Autor Beitrag
kalle345
Hält's aus hier
Beiträge: 8



BeitragVerfasst: Sa 30.06.12 20:20 
Ich bin gerade dabei ein LAN fähiges Mühle Spiel zu implementieren.
Für die Kommunikation von Server und Client verwende ich Data-Snap.
docwiki.embarcadero...._Anwendung_verwenden

Jedoch erhalte ich beim Auffrufen der Funktion im Client:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
function TClientVerbindungClient.verbunden_c: Boolean;
begin
  if Fverbunden_cCommand = nil then
  begin
    Fverbunden_cCommand := FDBXConnection.CreateCommand;
    Fverbunden_cCommand.CommandType := TDBXCommandTypes.DSServerMethod;
    Fverbunden_cCommand.Text := 'TClientVerbindung.verbunden_c';
    Fverbunden_cCommand.Prepare;
  end;
  Fverbunden_cCommand.ExecuteUpdate;
  Result := Fverbunden_cCommand.Parameters[0].Value.GetBoolean;
end;


die Fehlermeldung:
datasnap

der Aufruf sieht folgendermaßen aus:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
procedure TGUI.B_verbindenClick(Sender: TObject);
var
  Temp: TClientVerbindungClient;
begin
  SQLConnect_muehle_client.Params.Values['HostName'] := E_verbinden.Text;
  SQLConnect_muehle_client.Connected := true;

  Temp:=TClientVerbindungClient.Create(SQLConnect_muehle_client.DBXConnection);
    //try
      if Temp.verbunden_c()=true then begin;
        ShowMessage('Verbunden...');
        B_verbinden.Enabled := false;
        L_status.Caption:='Du bist an der Reihe';
        derChat.schreibeIP(E_verbinden.Text);
        dieSteuerung.verbunden();
      end;
    {except
      ShowMessage('Verbindungsfehler!');
    end; }

  Temp.Free;
end;


die Prozedur im Server sieht folgendermaßen aus:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
function TClientVerbindung.verbunden_c():boolean;
begin
  AnzSteineImSpiel:=0;
  dieGUI.verbunden();
  result := true;
end;


Wenn ich <dieGUI.verbunden();> auskommentiere funktioniert alles Problemlos..

Hoffe mir kann jemand weiterhelfen. Ich komme einfach nicht weiter.
Gruß

Moderiert von user profile iconNarses: Bild als Anhang hochgeladen.
Einloggen, um Attachments anzusehen!
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19285
Erhaltene Danke: 1743

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 30.06.12 20:51 
Wo wird denn "dieGUI" initialisiert? Entweder ist diese Variable selbst nicht zugewiesen oder irgendetwas darin geht schief.
Hast du denn einmal einen Haltepunkt an die Stelle im Server gesetzt und geschaut was da passiert? Also was dieGUI ist usw.?

Zudem befindest du dich an der Stelle in einem Thread, d.h. Zugriffe auf gemeinsame oder visuelle Elemente müssen synchronisiert werden!
kalle345 Threadstarter
Hält's aus hier
Beiträge: 8



BeitragVerfasst: So 01.07.12 19:54 
wenn ich im server im debugging durchsteppe kommt der Fehler direkt beim ersten Befehl in der Procedure <TGUI.verbunden();>.

dieGUI wird im Constructor von TClientVerbindung initialisiert:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
constructor TClientVerbindung.erzeugeClientVerbindung(p_GUI:TGUI);
begin
  dieGUI:=p_GUI;
end;


der Aufruf des Constructor erfolgt in FormCreate:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TGUI.FormCreate(Sender: TObject);
begin
  dieSteuerung := TSteuerung.CreateSteuerung(self);
  TClientVerbindung.erzeugeClientVerbindung(self);
end;


Die Klasse TGUI kennt die Klasse TClientVerbindung nicht.
Aber die Klasse TClientVerbindung kennt die Klasse TGUI.

Und noch was: Ich habe TClientVerbindung nicht in der Forward-Deklaration (direkt nach type).
Ist das von Bedeutung?

Das mit dem synchronisieren hab ich noch gar nicht gelesen/gehört wie macht man das?

Gruß
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19285
Erhaltene Danke: 1743

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: So 01.07.12 20:34 
Ach du Schande... ein Konstruktor, der nicht Create heißt. Das kann nur ins Auge gehen, wenn man nicht sehr genau weiß was man da tut... (und wer es weiß macht sowas nicht :zwinker:)
Ich rate dir dringend den immer Create zu nennen. Tust du das nicht, wird dieser z.B. ausschließlich benutzt, wenn er direkt aufgerufen wird. Das kann gerade bei DataSnap ins Auge gehen, wenn auch vor allem bei übertragenen Objekten. Zudem fehlt dort der Aufruf des vererbten Konstruktors, es sei denn dein Objekt ist direkt von TObject abgeleitet.

Die Forward-Deklaration ist egal, wenn es kompiliert. Die braucht nur der Compiler.

Synchronisieren geht z.B. mit TThread.Synchronize.

Zum weiteren Debuggen:
user profile iconkalle345 hat folgendes geschrieben Zum zitierten Posting springen:
wenn ich im server im debugging durchsteppe kommt der Fehler direkt beim ersten Befehl in der Procedure <TGUI.verbunden();>.
Und welchen Wert hat dann in verbunden (vor dem Ausführen des Befehls) Self? Wenn das nil ist, ist deine GUI nicht zugewiesen.

Wenn du möchtest, kannst du das Projekt ja mal anhängen oder mir per PN schicken, wenn du es nicht veröffentlichen möchtest.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19285
Erhaltene Danke: 1743

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: So 01.07.12 21:34 
Nachdem ich das Projekt nun per PN erhalten habe, habe ich beim Debuggen einmal die Maus drüber gehalten: ;-)

dieGUInil

Ein Blick in den Quelltext hat mir auch gezeigt warum. Einmal zur Erklärung:
TDSServerClass verwaltet wie du denke ich weißt die Verbindungsklassen. Wenn nun ein Client anfragt, wird die Klasse mit OnGetClass ermittelt. Für jede neue Verbindung wird diese nun ggf. neu erzeugt.
Aber wenn "DataSnap" das tut, weiß das ja nichts von deinem Parameter. Stattdessen wird der Standardkonstruktor aufgerufen, den du aber nicht überschrieben hast.

Du hast also ein neues Verbindungsobjekt, das nun aber die GUI gar nicht bekommen hat.

Lösung:
Benutze auch OnCreateInstance der TDSServerClass und erzeuge das Verbindungsobjekt selbst:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
procedure TGUI.DSMuehleClass_serverCreateInstance(DSCreateInstanceEventObject: TDSCreateInstanceEventObject);
begin
  DSCreateInstanceEventObject.ServerClassInstance := TClientVerbindung.Create(Self);
end;


Dann zur Synchronisation:
Die fehlt in der Tat. Das sieht z.B. so aus, wenn es asynchron passieren soll (mit Synchronize würde der Aufruf warten bis der Aufruf ausgeführt ist):
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
  TThread.Queue(nil,
    procedure
    begin
      dieGUI.verbunden();
    end);
Dass es verschiedene Threads sind, siehst du in den beiden Bildern im Anhang. Der eine Screenshot ist beim Aufruf von TThread.Queue, der zweite bei der Ausführung darin. In der Threadliste siehst du, dass du jeweils in verschiedenen Threads bist wie ich ja schon geschrieben hatte. Aber ich wollte dir auch noch zeigen wie man das sehen kann.

(// EDIT: Wenn die Ausführung angehalten ist, ist man in dem Thread, bei dem Haltepunkt steht.)

Die GUI so fest mit der Steuerung zu verbandeln ist aber ohnehin nicht gut. Besser wären Events, die du mit Methoden in der GUI verbindest. Also im Prinzip sowas:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
type
  TOnConnect = procedure(...) of object;

  property OnConnect: TOnConnect read FOnConnect write FOnConnect;

  if Assigned(FOnConnect) then
    TThread.Synchronize(nil,
      procedure
      begin
        FOnConnect(...);
      end;
Das macht es auch einfacher alles in unterschiedliche Units zu packen. Denn das ist auch sehr sinnvoll. :zwinker:
Einloggen, um Attachments anzusehen!

Für diesen Beitrag haben gedankt: kalle345
kalle345 Threadstarter
Hält's aus hier
Beiträge: 8



BeitragVerfasst: So 01.07.12 23:11 
Ich muss dir an dieser Stelle einfach mal ein großes Lob und Dankeschön aussprechen. Du gibst dir immer sehr viel Mühe beim Helfen.

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:

Die GUI so fest mit der Steuerung zu verbandeln ist aber ohnehin nicht gut. Besser wären Events, die du mit Methoden in der GUI verbindest. Also im Prinzip sowas:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
type
  TOnConnect = procedure(...) of object;

  property OnConnect: TOnConnect read FOnConnect write FOnConnect;

  if Assigned(FOnConnect) then
    TThread.Synchronize(nil,
      procedure
      begin
        FOnConnect(...);
      end;
Das macht es auch einfacher alles in unterschiedliche Units zu packen. Denn das ist auch sehr sinnvoll. :zwinker:

Das glaube ich dir gerne.
Jedoch muss ich es nach diesem Muster bei meinem Lehrer abliefern...


Mit deinem letzten Beitrag habe ich es jetzt geschafft dass ich aus dem Client die Funktion <TClientVerbindung.verbunden_c():boolean;> des Servers problemlos aufrufen kann.

Wenn ich jetzt jedoch versuche die <TClientVerbindung.spielen_setzphase_c> mit dem Client aus dem Servers aufzurufen (sprich: nach dem verbinden im Client auf das Spielfeld klicken) spuckt er folgende Fehlermeldung aus:
---------------------------
muehle_p
---------------------------
Remote-Fehler: Methode TClientVerbindung.spielen_setzphase_c in der Servermethodenliste nicht gefunden.
---------------------------
OK
---------------------------

Gruß
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19285
Erhaltene Danke: 1743

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: So 01.07.12 23:41 
user profile iconkalle345 hat folgendes geschrieben Zum zitierten Posting springen:
Ich muss dir an dieser Stelle einfach mal ein großes Lob und Dankeschön aussprechen. Du gibst dir immer sehr viel Mühe beim Helfen.
Danke ;-)

user profile iconkalle345 hat folgendes geschrieben Zum zitierten Posting springen:
Das glaube ich dir gerne.
Jedoch muss ich es nach diesem Muster bei meinem Lehrer abliefern...
Da könnt ich jetzt noch einiges zu schreiben, wenn er das genau so meinte, aber ich lass es besser. Dem Lehrer zuliebe. :zwinker:
Wobei ich es aber trotzdem schön finde, dass überhaupt so etwas wie DataSnap im Unterricht vorkommt. :zustimm:

user profile iconkalle345 hat folgendes geschrieben Zum zitierten Posting springen:
Remote-Fehler: Methode TClientVerbindung.spielen_setzphase_c in der Servermethodenliste nicht gefunden.
Dort gibt es ja (in der Version hier jedenfalls) auch nur spielen_setzphase_s.

Und dieser Kommentar ist schon einmal sehr schlecht:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
//
// Erzeugt vom DataSnap-Proxy-Generator.
// 24.06.2012 15:24:31
// bearbeitet!!!!!!!!!!!!
Diese Client-Klassen sollten immer automatisch erzeugt werden. (SQLConnection verbinden, rechts draufklicken und auf Client-Klasse erzeugen klicken.)
Sonst machst du Änderungen, vergisst die auf der anderen Seite, ...
kalle345 Threadstarter
Hält's aus hier
Beiträge: 8



BeitragVerfasst: Mo 02.07.12 17:03 
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:

Und dieser Kommentar ist schon einmal sehr schlecht:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
//
// Erzeugt vom DataSnap-Proxy-Generator.
// 24.06.2012 15:24:31
// bearbeitet!!!!!!!!!!!!
Diese Client-Klassen sollten immer automatisch erzeugt werden. (SQLConnection verbinden, rechts draufklicken und auf Client-Klasse erzeugen klicken.)
Sonst machst du Änderungen, vergisst die auf der anderen Seite, ...


Anfangs habe ich die Data-Snap Klasse selbst erweitert, weil die automatische Erzeugung nicht alle procedures/functions des Servers auch wirklich erzeugt hatte. Jedoch habe ich das Problem jetzt behoben indem ich anstatt cardinal‘s integer’s verwende. Anscheinen mag Data-Snap cardinal’s nicht...
---------------------------------------------------------------------------------------------
Nun stehe ich aber schon vor einem weiteren Problem:
Wenn ich beispielsweise im Konstruktor von TSteuerung folgendes deklariere:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
constructor TSteuerung.Create(p_GUI:TGUI);
var
  I: Integer;
begin
  for I := 1 to 9 do begin
    derStein_weiss[I] := TStein.Create();
    derStein_schwarz[I] := TStein.Create();
  end;

  for I := 1 to 8 do begin
    dasSpielfeld_innen[I] := TSpielfeld.Create();
    dasSpielfeld_mitte[I] := TSpielfeld.Create();
    dasSpielfeld_aussen[I] := TSpielfeld.Create();

    dasSpielfeld_innen[I].aendereBesetzung(keine_f);
    dasSpielfeld_mitte[I].aendereBesetzung(keine_f);
    dasSpielfeld_aussen[I].aendereBesetzung(keine_f);
  end;
end;


und im im Konstruktor von TClientVerbindung auch:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
constructor TClientVerbindung.Create(p_GUI:TGUI; p_Steuerung: TSteuerung);
var
  I:cardinal;
begin
  for I := 1 to 9 do begin
    derStein_weiss[I] := TStein.Create();
    derStein_schwarz[I] := TStein.Create();
  end;

  for I := 1 to 8 do begin
    dasSpielfeld_innen[I] := TSpielfeld.Create();
    dasSpielfeld_mitte[I] := TSpielfeld.Create();
    dasSpielfeld_aussen[I] := TSpielfeld.Create();

    dasSpielfeld_innen[I].aendereBesetzung(keine_f);
    dasSpielfeld_mitte[I].aendereBesetzung(keine_f);
    dasSpielfeld_aussen[I].aendereBesetzung(keine_f);
  end;
end;


Greife ich dann auf die gleichen Objekte (z.B. dasSpielfeld_mitte[2]) von den beiden Klassen TClientVerbindung+TSteuerung aus zu? Ich hoffe das versteht man :D..

Gruß