Autor Beitrag
UweK
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 51
Erhaltene Danke: 1

Win 7
Delphi Enterprise XE6
BeitragVerfasst: Do 30.06.22 15:12 
Hallo,

Ich habe ein Miniprogramm geschrieben, das von einem LabView-Programm eine Nachricht über einen gerade neu entstandenen Datenfile direkt an ein ebenfalls von mir geschriebenes Auswerteprogramm schickt. Das funktioniert einwandfrei. Das Miniprogramm wird von LabView mit den Kommadozeilenparametern aufgerufen, die die Information enthalten. Das Miniprogramm sendet diese Informationen per DDE an das Auswerteprogramm. Danach - soll - das Miniprogramm sich selbst gleich wieder beenden, tut es aber oft nicht. Bei mehreren aufeinander folgenden Versuchen schließt es sich manchmal, und manchmal nicht. Ob ich dabei "Exit" oder "Application.Terminate" verwende, scheint egal zu sein.

An DDE kann das nicht liegen. Selbst wenn ich die einzige Zeile mit "PokeDataLines", die wirklich etwas tut, auskommentiere, bleibt das Verhalten so. Das musss doch irgend etwas ganz Simples sein??? Kann mir jemand helfen?

Hier ist der komplette Quelltext des Miniprogramms:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
program FlexCopyStarter;

uses
  Forms,
  Main in 'Main.pas' {MainForm};

{$R *.res}

begin
  Application.Initialize;
  Application.Title := 'FlexCopyStarter';
  Application.CreateForm(TMainForm, MainForm);
  Application.Run;
end.




ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
unit Main;

interface

uses
  Classes, Forms, Vcl.DDEMan;
type
  TMainForm = class(TForm)
    MainDDEClientConv: TDdeClientConv;
    MainDDEClientItem: TDdeClientItem;
    procedure MainFormOnShow(ASender: TObject);
  private
  public
  end{class}
var
  MainForm: TMainForm;

{$R *.DFM}

procedure TMainForm.MainFormOnShow(ASender: TObject);
var
  TempStringList: TStringList;
begin
  TempStringList:= TStringList.Create;
  try
    TempStringList.Add(ParamStr(1));
    TempStringList.Add(ParamStr(2));
    MainDDEClientConv.PokeDataLines('MainDDEServerItem', TempStringList);
  finally
    TempStringList.Free;
  end{finally}

//  Application.Terminate;

Exit;

end;

end.



Moderiert von user profile iconNarses: Topic aus Delphi Language (Object-Pascal) / CLX verschoben am Di 05.07.2022 um 22:31
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Do 30.06.22 16:40 
Wäre es nicht besser, du schließt einfach per Close das Fenster (und dann wird ja automatisch die Anwendung geschlossen)?

Macht dein Programm denn mehr als die Daten nur zu schicken (also zeigt es irgendwelche Infos an)? Weil sonst wäre wohl ein Konsolenprogramm geeigneter.
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Do 30.06.22 16:59 
DDE braucht 'ne Message Loop. Ich vermute mal auch in Delphi hat eine Konsolenanwendung keine.
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Do 30.06.22 17:23 
Aber wenn die Anwendung sofort im OnShow-Ereignis nach dem Senden geschlossen wird, dann wird auch keine Message-Loop mehr durchlaufen.

Dann hätte ich wohl besser "fensterlose Applikation" schreiben sollen.
Sinspin
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1321
Erhaltene Danke: 117

Win 10
RIO, CE, Lazarus
BeitragVerfasst: Fr 01.07.22 13:39 
Das geht nicht. Im OnShow kann man nicht schließen, egal wie.
Ich würde einen Timer auf die Form packen, gleich aktiv lassen oder im OnShow anschalten, eingestellt auf ein paar ms.
Im Timer machst du den DDE Call.
Keine Ahnung ob DDE fertig sendet oder das Programm noch ein bisschen leben muss. Sonst könntest du gleich Close (Self.Close) für das Fenster aufrufen. Dann ist auch dein Programm wieder weg.
Das Exit bewirkt nie ein Programmende, sondern immer nur das verlassen der aktuellen Procedure.

_________________
Wir zerstören die Natur und Wälder der Erde. Wir töten wilde Tiere für Trophäen. Wir produzieren Lebewesen als Massenware um sie nach wenigen Monaten zu töten. Warum sollte unser aller Mutter, die Natur, nicht die gleichen Rechte haben?

Für diesen Beitrag haben gedankt: UweK
UweK Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 51
Erhaltene Danke: 1

Win 7
Delphi Enterprise XE6
BeitragVerfasst: Mo 04.07.22 12:10 
Danke, funktioniert. Die DDE-Übertragung bleibt in "OnShow". Danach starte ich den Timer, in dessen "OnTimer" Ereignisbehandlung nur das Schließen steht.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Di 05.07.22 09:35 
Hast du es denn einmal ganz simpel so versucht? Denn wozu soll das Fenster gut sein, wenn du es gar nicht verwendest?
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
program FlexCopyStarter;

uses
  System.Classes, Vcl.DDEMan;

{$R *.res}

var
  MainDDEClientConv: TDdeClientConv;
  TempStringList: TStringList;
begin
  MainDDEClientConv := TDdeClientConv.Create(nil);
  try
    TempStringList:= TStringList.Create;
    try
      TempStringList.Add(ParamStr(1));
      TempStringList.Add(ParamStr(2));
      MainDDEClientConv.PokeDataLines('MainDDEServerItem', TempStringList);
    finally
      TempStringList.Free;
    end;
  finally
    MainDDEClientConv.Free;
  end;
end.
UweK Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 51
Erhaltene Danke: 1

Win 7
Delphi Enterprise XE6
BeitragVerfasst: Di 05.07.22 10:20 
Zuerst: Meine gestrige Erfolgsmeldung muss ich wieder zurückziehen. Auch in der Variante mit dem Timer schließt das Fenster nicht richtig. Außerdem gibt es Verzögerungen bei der DDE-Übertragung. Mit dem erfolglosen "Close"-Versuch direkt in "MainForm.OnShow", blieb das Fenster öfters (aber nicht immer) offen, aber der übertragene Text wurde zumindest immer sofort im Server angezeigt. Mit dem "Close" im Timerereignis wird es eher schlechter. Das Fenster bleibt auch manchmal offen, aber dann wird der Text erst angezeigt, wenn ich das Client-Fenster von Hand schließe.

@Jaenicke:
Ganz ohne Fenster wäre natürlich ideal, denn dann würde das unerwünschte Aufblitzen des Fensters auch entfallen. So etwas habe ich auch schon mal probiert:

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
program FlexCopyStarter;

uses
  System.Classes, Vcl.DDEMan;

{$R *.res}

var
  MainDDEClientConv: TDDEClientConv;
  MainDDEClientItem: TDDEClientItem;
  TempStringList: TStringList;
begin
  MainDDEClientConv:= TDDEClientConv.Create(nil);
  MainDDEClientItem:= TDDEClientItem.Create(nil);
  with MainDDEClientConv do
  begin
    DDEService:= 'FlexCopy'{Serverprogramm: "FlexCopy.exe".}
    DDETopic:= 'MainDDEServerConv'{Variablenname in "FlexCopy".}
    ConnectMode:= ddeAutomatic;
  end;
  with MainDDEClientItem do
  begin
    DDEConv:= MainDDEClientConv;
    DDEItem:= 'MainDDEServerItem'{Variablenname in "FlexCopy".}
  end;

  try
    TempStringList:= TStringList.Create;
    try
      TempStringList.Add(ParamStr(1));
      TempStringList.Add(ParamStr(2));
      MainDDEClientConv.PokeDataLines('MainDDEServerItem', TempStringList);
    finally
      TempStringList.Free;
    end;
  finally
    MainDDEClientItem.Free;
    MainDDEClientConv.Free;
  end;
end.

Aber in dieser Form ohne Fenster habe ich gar keine Kommunikation zustande bekommen, nicht einmal das automatische Starten des Servers durch "ConnectMode:= ddeAutomatic" passierte. Mit dem Fenster gehr es aber. So ganz erschließt sich mir auch nicht, was in der Version mit Fenster und den aus der Palette hineingezogenen DDE-Komponenten der Objektinspektor im Hintergrund tut. Die entsprechenden Werte kann ich dort nur eintragen, während der Server-Exefile läuft. Dann klappt es aber. Die vorliegende Version ohne Fenster habe ich sicherheitshalber auch bei laufendem Server compiliert, aber das bringt auch keine Verbesserung.
jasocul
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 6386
Erhaltene Danke: 146

Windows 7 + Windows 10
Sydney Prof + CE
BeitragVerfasst: Di 05.07.22 10:32 
Wenn du gar keine Form brauchst, dann musst die Aktionen aus dem FormShow in eine Prozedur verlagern. Anschließend veränderst du den Source in der dpr-Datei wie folgt:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
program FlexCopyStarter;

uses
  Forms,
  Main in 'Main.pas' {MainForm};

{$R *.res}

begin
  Application.Initialize;
  Application.Title := 'FlexCopyStarter';
  Application.CreateForm(TMainForm, MainForm);
  Application.ShowMainForm := False; // Neu
  MainForm.DeineNeueProzedur; // Neu
//  Application.Run; // Muss dann weg
end.

Da ich gerade kein Delphi zur Verfügung habe, hoffe ich, dass ich nichts vergessen habe. Ist schon ein paar Jahre her, dass ich sowas gebraucht habe. :wink:

Man kann das auch über eine Konsolenanwendung lösen, aber so solltest du den wenigsten Aufwand haben.
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Di 05.07.22 11:04 
Hallo zusammen,

user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
DDE braucht 'ne Message Loop. Ich vermute mal auch in Delphi hat eine Konsolenanwendung keine.

Ohne Application.Run wird aber auch keine Message Loop ausgeführt (und daher klappt es wohl auch in einer Konsolenanwendung nicht).

@UweK: Um kein Fenster anzuzeigen, kannst du aber
ausblenden Delphi-Quelltext
1:
Application.ShowMainForm := False;					

übernehmen (das meinte ich auch mit "fensterloser Applikation").

Welches Intervall hast du denn beim Timer eingestellt (unter 15ms macht keinen Sinn, am besten einige Hundert ms oder sogar einige Sekunden)?
Denn wenn die Anwendung zu schnell automatisch geschlossen wird, dann kann ja auch keine DDE-Message mehr übermittelt werden.

Vllt. ist es aber auch so, daß gerade noch nicht versendete Messages verhindern, daß die Form (und damit die Applikation) automatisch geschlossen werden?!
Sinspin
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1321
Erhaltene Danke: 117

Win 10
RIO, CE, Lazarus
BeitragVerfasst: Di 05.07.22 11:18 
Ich hatte nicht umsonst geschrieben das die Arbeit im Timer gemacht werden soll und nicht im OnShow!

_________________
Wir zerstören die Natur und Wälder der Erde. Wir töten wilde Tiere für Trophäen. Wir produzieren Lebewesen als Massenware um sie nach wenigen Monaten zu töten. Warum sollte unser aller Mutter, die Natur, nicht die gleichen Rechte haben?
UweK Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 51
Erhaltene Danke: 1

Win 7
Delphi Enterprise XE6
BeitragVerfasst: Di 05.07.22 12:02 
Hallo allerseits,
Hier der aktuelle Zwischenstand:

@jasocul:
Deine Variante führt die Übertragung aus, aber läuft danach auf einen Laufzeitfehler "216" (Zugriffsverletzung).

@Th69:
Einfügen dieser Zeile bei sonst unverändertem Programm hat keine Wirkung. Das Fenster öffnet sich trotzdem und bleibt dann stehen, bis ich es von Hand schließe. Die Übertragung bleibt ebenso hängen wie ohne diese Zeile. Der übertragene Text wird im Server erst angezeigt, wenn ich das Fenster von hand schließe.

Dieser Verzögerungseffekt scheint nur daran zu liegen, dass ich das "Close" jetzt in "OnTimer" verlagert habe. In der früheren Version mit dem "Close" in "MainForm.OnShow" kam der Text immer sofort an.

@Sinspin:
Zur Abkürzung habe ich jetzt mal "die Arbeit" in eine separate Prozedur verpackt, um sie bequemer an verschiedene Stellen verschieben zu können, und um auch die Quelltexte in unserer weiteren Unterhaltung zu kürzen. Diese Einheit in sich funktioniert, wenn sie im TForm steht und damit auf die DDE-Komponenten aus der Komponentenliste zugreift. Sie funktioniert (wie ich bereits vorher schrieb) aber nicht mehr in der Version von jaenicke, wenn die DDE-Komponenten ganz ohne TForm von Hand erzeugt werden:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
procedure TMainForm.DoIt;
var
  TempStringList: TStringList;
begin
  TempStringList:= TStringList.Create;
  try
    TempStringList.Add(ParamStr(1));
    TempStringList.Add(ParamStr(2));
    MainDDEClientConv.PokeDataLines('MainDDEServerItem', TempStringList);
  finally
    TempStringList.Free;
  end{finally}
end{procedure TMainForm.DoIt}

Ob ich "DoIt" in TForm.OnShow oder in "TTimer.OnTimer" ausführe, scheint ebenfalls keinen Unterschied zu machen.
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Di 05.07.22 12:15 
Hast du schon in die Doku dazu geschaut: Applikation.ShowMainForm ?
Zitat:
Um das Hauptformular beim Start der Anwendung auszublenden, setzen Sie ShowMainForm vor dem Aufruf der Methode Run in der Haupt-Projektdatei auf false und stellen sicher, dass die Eigenschaft Visible des Formulars ebenfalls den Wert false hat.
(Hervorhebungen von mir)
Dann wird aber selbstverständlich auch nicht mehr OnShow aufgerufen (aber es gibt ja noch andere Möglichkeiten: Konstruktor, OnCreate).

Und wie ist jetzt der Wert vom Timer.Interval?
jasocul
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 6386
Erhaltene Danke: 146

Windows 7 + Windows 10
Sydney Prof + CE
BeitragVerfasst: Di 05.07.22 13:33 
user profile iconUweK hat folgendes geschrieben Zum zitierten Posting springen:

@jasocul:
Deine Variante führt die Übertragung aus, aber läuft danach auf einen Laufzeitfehler "216" (Zugriffsverletzung).

216 bedeutet, dass irgendwas noch nicht freigegeben ist.
Vielleicht genügt es in dem Fall, wenn du nach deinem DoIt noch MainForm.Free machst. Das sollte eigentlich automatisch beim Beenden des Programms erfolgen, wenn ich mich richtig erinnere. Kannst du ja mal ausprobieren. Eventuell hast du ja noch was in der Form, das nur verzögert freigegeben werden kann. Das könnte eventuell dann auch erklären, warum deine eigene Programmvariante mal beendet wird und manchmal nicht.
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10181
Erhaltene Danke: 1254

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Di 05.07.22 22:28 
Moin zusammen!

Jetzt mal allen ernstes: warum eine VCL-Anwendung schreiben, wenn man doch gar keine will?! :zwinker:

Wenn ich das richtig verstehe, dann ist die "Minianwendung" lediglich ein "Adapter", um einer anderen Anwendung vom gleichen Entwickler eine Nachricht zukommen zu lassen, weil die Hostanwendung (LabView?) offensichtlich nix anderes kann, als einen Prozess mit Parametern zu starten.

Wie wäre es denn mit einem anderen IPC-Ansatz, wenn DDE nicht der passende ist? Windows-Message registrieren und an die "große" Anwendung senden? TCP-Socket aufmachen und darüber IPC abwickeln? Das dürfte noch nicht vollständig sein, was da geht... :idea:

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
UweK Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 51
Erhaltene Danke: 1

Win 7
Delphi Enterprise XE6
BeitragVerfasst: Mi 06.07.22 14:21 
@Narses:
Genau das ist der Zweck dieser Mini-Anwendung. Das Problem war dabei aber nicht DDE an sich, sondern der Ablauf 1. "Irgendwie" die Botschaft übertragen (egal ob DDE, Windows Message oder ...) UND 2. Die Mini-Anwendung sofort von selbst wieder beenden. Entweder das Eine oder das Andere ging bei den verschiedenen Versionen nicht, obwohl es nach aller Logik eigentlich hätte gehen müssen.

@Alle:
Nun geht es. Vielen Dank für eure verschiedenen Tipps! Daraus konnte ich jetzt das unten komplett abgedruckte Programm zusammensetzen. Das funktioniert aber nur mit dieser Aufteilung der Funktionen zwischen Projektdatei und TForm. Erzeugte ich die DDE-Komponenten von Hand in der Projektdatei mit denselben Einstellungen wie im Objektinspektor und ließ dafür das eigentlich überflüssige TForm weg, kam keine Kommunikation mit dem Server zustande. Startete ich den TTimer in "MainFormOnShow", wurde die Anwendung in "MainTimerOnTimer" nicht geschlossen. Warum? Das muss ich in meiner Gehaltsklasse vielleicht gar nicht verstehen...

Uwe

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
program FlexCopyStarter;
uses
  Forms,
  Main in 'Main.pas' {MainForm};
{$R *.res}

begin
  Application.Initialize;
  Application.Title := 'FlexCopyStarter';
  Application.CreateForm(TMainForm, MainForm);
  Application.ShowMainForm:= false;
  MainForm.MainTimer.Interval:= 1000;
  MainForm.MainTimer.Enabled:= true;
  Application.Run;
end.

unit Main;
interface
uses
  Classes, Forms, ExtCtrls, DDEMan;
type
  TMainForm = class(TForm)
    MainTimer: TTimer;
    MainDDEClientConv: TDdeClientConv;
    MainDDEClientItem: TDdeClientItem;
    procedure MainTimerOnTimer(ASender: TObject);
  private
  public
  end;
var
  MainForm: TMainForm;

implementation
{$R *.DFM}

procedure TMainForm.MainTimerOnTimer(ASender: TObject);
var
  TempStringList: TStringList;
begin
  MainTimer.Enabled:= false;
  TempStringList:= TStringList.Create;
  try
    TempStringList.Add(ParamStr(1));
    TempStringList.Add(ParamStr(2));
    MainDDEClientConv.PokeDataLines('MainDDEServerItem', TempStringList);
  finally
    TempStringList.Free;
  end;
  Close;
end;

end.
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Mi 06.07.22 16:04 
Der Code sieht ja jetzt auch gut aus. :zustimm:

Einzig das Setzen der MainForm.Timer-Eigenschaften solltest du in den Konstruktor/OnCreate packen (bzw. gleich im Form-Designer setzen).