Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - TJvAppInstances - Mehrere Instanzen, verschiedene Benutzer


galagher - Mi 26.07.17 21:15
Titel: TJvAppInstances - Mehrere Instanzen, verschiedene Benutzer
Hallo!

Ich benutze eine TJvAppInstances-Komponente in meinem Programm und beobachte folgende unerwünschte Effekte:

Ich arbeite unter einem eingeschränkten Benutzerkonto und starte mehrere Instanzen meines Programms, wobei (a) das aktuelle, eingeschränkte Konto ist und (b) ein Administratorenkonto ist.

Wenn ich (a) und danach (b) starte, hängt (b), wenn ich dort versuche, auf JvAppInstances1.AppInstances.InstanceCount zuzugreifen, solange (a) noch läuft. Wenn (a) beendet wird, klappt es.

Wenn ich zuerst (b) und dann (a) starte, hängt (b) beim Aufruf von JvAppInstances1.AppInstances.InstanceCount und (a) öffnet sich erst gar nicht, wird aber im Taskmanager aufgelistet und öffnet sich erst, wenn (b) per Taskmanager abgeschossen wird.

JvAppInstances1.AppInstances.InstanceCount ist in jedem Fall zugewiesen ("Assigned").
Mehrere Instanzen unter verschiedenen Admin-Konten oder unter dem eingeschränkten Benutzerkonto verursachen keine Probleme.

Kann ich das im Programm irgendwie abfangen?

//Edit:
Es geht nicht ums Beenden per Taskmanager, es geht nur um JvAppInstances1.AppInstances.InstanceCount, das kommt im OnClose vor. Deaktiviere ich es dort, funktioniert alles wieder.


Delete - Mi 26.07.17 22:01

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Do 27.07.17 18:46

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Guten Abend galagher,

das hängt mit den Eigenschaften .Active und .AutoActive zusammen. Solange diese True sind, hängt sich die Anwendung auf.
Jeweils nur eines auf False, dann beides auf False: Funktioniert leider nicht!


Delete - Do 27.07.17 19:23

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Do 27.07.17 19:52

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Was willst du mit der Komponente erreichen?
Ich möchte einfach in der Caption die Anzahl der Instanzen anzeigen, sonst nichts!

Kann man irgendwie abfangen, dass eine Instanz unter einem anderen Benutzerkonto gestartet wurde? Ich weiss, wie ich den Benutzernamen der jeweiligen Instanz ermittlen kann, ich brauche aber den Benutzernamen der aktuellen Windows-Sitzung. Wenn ich den erstmal habe und er sich von jenem Benutzernamen unterscheidet, mit dem ich die neue Instanz starten möchte (per Windows "Als Administrator ausführen"), komme ich schon weiter!


Delete - Do 27.07.17 20:31

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Do 27.07.17 22:10

Ich habe hier etwas gefunden: http://www.tek-tips.com/faqs.cfm?fid=7523
Funktioniert, die Instanzen werden auch unter mehreren Benutzerkonten korrekt gezählt. Nur das Aktualisieren ist ein bisschen unelegant, weil ich ja nicht so einfach Daten zwischen den Instanzen austauschen kann. (Habe zwar auch dafür eine Prozedur parat, muss ich aber nicht erst einbauen).

Ich weise einmalig beim Programmstart die Anzahl der Instanzen einer Variablen iInstance zu. In FormActivate und ApplicationEvents1Idle rufe ich ProcessCount auf. So habe ich stets aktuelle Werte. Wenn iInstance > InstanceCount(ExtractFileName(Application.ExeName)) ist, dekrementiere ich iInstance.

Muss noch testen, klappt aber bisher fehlerfrei!


Delete - Do 27.07.17 22:30

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Do 27.07.17 22:36

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Such mal hier im Forum nach "Kommunikation zwischen 2 Formularen/Anwendungen".
Mache ich am Samstag!


galagher - Sa 29.07.17 10:22

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
"Kommunikation zwischen 2 Formularen/Anwendungen".
Das läuft letzendlich alles auf FindWindow hinaus, dazu brauche ich die exakte Caption des Fensters. Diese ist aber jedesmal anders.


Delete - Sa 29.07.17 11:43

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Sa 29.07.17 12:12

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Die Caption des Fensters kann man auch indirekt ermitteln:
Das gibt mir die Caption des aufrufenden Fensters zurück, ich brauche aber alle Captions aller Fenster, die zB. den Text "SynEdit" enthalten. Dann schreibe ich die in eine TStringList und sende anhand dieser Liste an alle betreffenden Fenster eine Nachricht.

Aber zuerst brauche ich die Captions!


Delete - Sa 29.07.17 16:19

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Di 01.08.17 20:56

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
SubText ... Ein Wort, welches irgendwo in der Caption enthalten sein soll
Das ist perfekt! Darauf muss man aber auch erst kommen! Danke! :D


Delete - Di 01.08.17 21:31

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Do 03.08.17 19:04

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Es könnte im Anschluss eine weitere Frage auftauchen, wie man nun im besagten Fenster auf ein bestimmtes Element/Control zugreift, wie z.B. ein TSynEdit, welches nachträglich ein paar Textteilen hinzugefügt bekommen soll.

Ich mache das so:
Die sendende Form muss zunächst klarerweise senden können, dazu erstelle ich eine Liste mit den Fenster-Captions, an die ich senden will:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
sCaptionSubText := 'SynEdit ';  //Oder was man eben in der Caption sucht!
//...

procedure TForm1.MakeCaptionList(const iD: Integer);
var
  i: Integer;
  List: TStringList;
begin
  List := TStringList.Create;
  try
    GetSpecificWindowCaption(List, sCaptionSubText);

    for i := 0 to List.Count-1 do
      SendText(Handle, iD, sCaptionSubText, List[i]);
  finally
    List.Free;
  end;
end;


Die empfangende Form bekommt eine Prozedur ReceiveData (https://www.entwickler-ecke.de/viewtopic.php?t=109547&highlight=cbdata+strlen):

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
private
  procedure ReceiveData(var Msg: TWMCopyData); message WM_COPYDATA;
//...

{----------------- ReceiveData - Text empfangen, Beispiel: --------------------}
procedure TForm1.ReceiveData(var Msg: TWMCopyData);
begin
(*//Optional kann der Identifier-Integer ausgewertet werden:
  if Msg.CopyDataStruct.dwData = 0 then [...] //Wenn 0 oder was auch immer, tu was!

  //Optional kann der Integerwert der Stringlänge ausgewertet werden:
  if Msg.CopyDataStruct.cbData = [...] //tu was!
*)


  {Nun den gesendeten Text verarbeiten:}
  if PChar(Msg.CopyDataStruct.lpData) = sCaptionSubText then
  begin
    if (Msg.CopyDataStruct.dwData = iCaptionUpdate) or
       (Msg.CopyDataStruct.dwData = iCaptionDecCount) then
      SetMainFormCaption(Msg.CopyDataStruct.dwData);
  end;
end;

Um auf Komponenten zuugreifen, kann man alles mögliche machen, zB Memo1.Lines.Add(PChar(Msg.CopyDataStruct.dwData)); - oder man könnte mit FindComponent arbeiten, was auch immer man möchte.
Im Wesentlichen kommt es nur auf Msg.CopyDataStruct.dwData an, denn das enthält den zu sendenden Text. Was das empfangende Fenster damit macht, steht in ReceiveData.


Delete - Fr 04.08.17 07:54

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Fr 04.08.17 19:13

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Die etwas andere Möglichkeit wäre gewesen:
Muss ich unbedingt testen, gefällt mir auch!


Delete - Sa 05.08.17 15:06

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Mo 07.08.17 21:57

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Gut, nur mach es besser als ich., also mach eine procedure daraus und initialisiere die Liste bitte vor dem try. :)
Ok, habe ich gemacht. Vor dem try, klar.
Mit TSynEdit kann ich das momentan nicht testen, da mein Programm "SynEdit" heisst. Also verwende ich zum Testen ein TMemo, da wird aber nur an das Memo des "eigenen" Fensters gesendet, und da auch nur an das zuletzt erstellte (Memo1 (nein), Memo2 (nein), Memo3 (ja)). Da kann ich gleich Memo3.Text := 'Neuer SynEdit Text' angeben!

Ich verwende eine Komponente, die ich von TJvWideHLEditor abgeleitet habe, und mit dieser oder dem Typnamen meiner Komponente - TWideHLEditorX - funktioniert es überhaupt nicht. :gruebel: :nixweiss:


Delete - Di 08.08.17 05:42

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Di 08.08.17 18:20

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Man muss also beim Filtern der Fenster sogleich auch die momentanen Fenster-Handle-Werte mitschreiben:
Mit meiner Komponente TWideHLEditorX funktioniert es immer noch nicht, wobei ich dazu sagen muss, dass diese Kompenenten dynamisch zur Laufzeit erzeugt werden.
Mit TMemo oder TEdit wird der Text zwar in allen Instanzen eingefügt, aber jeweils nur in die zuerst erstellte Komponente, und dann auch nur, wenn diese direkt auf der Form liegt. Wenn sie auf einer Toolbar oder auf einem Panel liegt, klappt es nicht.

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
PS: Wenn dein Projekt (T)SynEdit heißt und du außerdem die TSynEdit [https://github.com/SynEdit/SynEdit] Komponente darin nutzt, sind Probleme vorgrogrammiert.
Ich verwende u.a. aus genau diesem Grund nicht TSynEdit, sondern eine von TJvWideHLEditor abgeleitete Komponente: TWideHLEditorX!

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Warum es mit der TJvWideHLEditor Komponente nicht so funktioniert, müsste man es sich genauer anschauen. Ihren Ursprung hat sie im TCustomControl, ebenso wie die bereits angesprochene TSynEdit Komponente.
Weil dynamisch erzeugt? Obwohl: Parent ist Form1...


Delete - Di 08.08.17 19:15

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Di 08.08.17 19:37

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Ich kann nicht so gut hellsehen oder vermuten, wo du deine Controls genau hast, oder gar wieviele. :twisted:
Man kann es natürlich auf deine Gegebenheit anpassen, wenn du bereit bist sie zu nennen. :mrgreen:
War ja kein Vorwurf, nimm's nicht persönlich!

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Noch besser: Ich lasse dir den Vortritt und du darfst Verbesserungen daran durchführen, als Übungszweck.
Damit wären wir bei:
user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Wenn sich ein Control z.B. auf einem Panel befindet, dann musst du zunächst das Panel mit FindWindowEx() suchen, den Handle-Wert dir merken, dann erneut mit FindWindowEx() nach dem besagten Control suchen, wobei dieses FindWindowEx() im ersten Parameter den Handle-Wert des Panels bekommt.
Wenn die "Ziel"-Komponente auf einer weiteren Komponente liegt, und - aus welchem Grund auch immer - diese wiederum auf einer weiteren Komponente, muss ich a) wissen, nach welchen Komponenten ich suchen muss und b) die Suche für jede der Komponenten durchführen, bis ich bei der Komponente angelangt bin, die ich eigentlich meine! :eyecrazy: Naja... :?

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Wenn es Probleme mit den deckungsgleichen Klassennamen gibt, wie in deinem Projekt zu TSynEdit, dann nenn' deine Hauptklasse z.B. TgaSynEdit und alles ist gut.
Es gibt keine Probleme mit TSynEdit, das kommt in meinem Programm gar nicht vor!


Delete - Di 08.08.17 20:07

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Mi 09.08.17 21:11

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Ganz kurz gesagt: Mit FindWindowEx() suchst du von der Form weg, die nächst-untere Ebene auf, bis du an dein Ziel angelangst.
Erst dachte ich, wozu soll ich mir die ganze Arbeit machen? Aber jetzt werde ich mich wohl doch damit beschäftigen! Deine Lösung hat was! Ich wünschte nur, man könnte das allgemeiner halten, also statt nach TPanel zu suchen, sucht man nach TObject oder so ähnlich. Mal sehen, was ich rausfinde! Wird aber dauern.

Wenn ich es mit meiner Lösung, also mit Prozedur ReceiveData, mache, kann ich dort rein schreiben, was immer ich will. Nachteil: Die Fenster müssen zwingend eine Prozedur haben, die als Empfänger fungiert. Fenster, bei denen zwar die Caption stimmt, die aber keinen "Empfangsteil" haben, machen klarerweise nichts.


Delete - Do 10.08.17 00:32

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Do 10.08.17 17:58

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Man könnte es schon allgemeiner halten, wenn man sich den Zugriff zum Hauptfenster verschafft. Dann geht man mit .ControlCount alle Controls durch und sucht nach einem bestimmten Typen/Klasse:
Es funktioniert nicht, auch nicht mit if wndCtrl.Controls[j].ClassNameIs(ControlClassName) then statt if string(wndCtrl.Controls[j].ClassName) = ControlClassName then. (Hier kann man string doch weglassen!)
Das Control wird nur gefunden, wenn es direkt auf dem Fenster liegt. Wenn also ein Memo auf einem Panel liegt, welches auf dem Fenster liegt, dann klappt es mit dem Panel, wenn man 'TPanel' angibt: Die Caption ändert sich. Wenn man 'TMemo' angibt, tut sich mit dem Memo aber nichts.

Ich verstehe den Code, und eigentlich müsste ja alles stimmen...


Delete - Do 10.08.17 18:02

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Do 10.08.17 18:35

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Hast du den Code nun wirklich verstanden?
Ich denke schon, und mir fällt kein Fehler auf.
Mit Kommentaren versehen:

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:
procedure SendText2(const CaptionSub, ControlClassName, Msg: string);
var
  i, j: Integer;
  list: TStringList;
  wndCtrl: TWinControl;
  hnd: HWND;
begin
  list := TStringList.Create;
  try
    GetSpecificWindowCaption(list, CaptionSub);  //Liste aller in Frage kommender Fenster samt Handles erstellen
    for i := 0 to list.Count - 1 do
    begin
      hnd := StrToInt(list[i]);  //Handle zuweisen
      wndCtrl := FindControl(hnd);  //Control suchen und zuweisen
      for j := 0 to wndCtrl.ControlCount - 1 do  //Alle Controls des Fensters durchgehen
      begin
        if string(wndCtrl.Controls[j].ClassName) = ControlClassName then  //Wenn Classname des Controls mit dem des 
        begin                                                         //angegebenen Controls übereinstimmt, dann
          hnd := TWinControl(wndCtrl.Controls[j]).Handle;             //Handle des Controls an hnd zuweisen und
          SendMessage(hnd, WM_SETTEXT, 0, Integer(PChar(Msg)));       //senden. Bei mehreren Fenstern Zugriffsverletzung.
        end;
      end;
    end;
  finally
    list.Free;
  end;
end;


Delete - Do 10.08.17 19:02

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Do 10.08.17 19:16

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Das Problem stellt FindControl() dar, und ich hätte gern gewusst, warum?!
Ich auch, aber vor allem möchte ich wissen, warum nur Controls gefunden werden, die auf dem Fenster liegen!


Delete - Do 10.08.17 19:47

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Do 10.08.17 20:50

Ich möchte den Code so allgemein gestalten, dass ich ihn wieder verwenden kann, ohne ihn jedesmal anpassen zu müssen. Ein oder mehrere TEdit(s) zB. kann/können ohne Weiteres auf einem TPanel liegen.

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Geht man nun den Zähler durch, kann man mit der Eigenschaft .Controls[i] auf alle Controls zugreifen, die auf dem Ausgangs-Control zur Verfügung stehen, TPanel inklusive, ist ja auch ein (sichtbares) Control. ;)
Kann man offensichtlich nicht, denn es werden nur solche Controls berücksichtigt, die direkt auf der Form liegen.
"auf alle Controls, die auf dem Ausgangs-Control zur Verfügung stehen" - das bedeutet, es müsste funktionieren. Wenn ich also TEdit angebe, und es liegt ein TEdit auf einem TPanel, warum funktioniert es dann nicht? Steht doch eindeutig da:

Delphi-Quelltext
1:
2:
3:
for j := 0 to wndCtrl.ControlCount - 1 do  //Alle Controls des Fensters durchgehen
begin
  if string(wndCtrl.Controls[j].ClassName) = ControlClassName then


wndCtrl.Controls[j] ist doch das mit Index j "gelistete" TControl von wndCtrl, also von einem TWinControl. Wo es rumliegt, muss doch egal sein! Ich verstehe das nicht.


Delete - Do 10.08.17 21:07

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Sa 12.08.17 07:17

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Das gibt's doch nicht! Na schön, dann ersetz' ControlCount durch ComponentCount, und Controls[i] durch Components[i].
Das habe ich zwar gemacht, aber dabei vergessen, auch
hnd := TWinControl(wndCtrl.Controls[j]).Handle; anzupassen, es also zu
hnd := TWinControl(wndCtrl. Components[j]).Handle; zu ändern, und natürlich hat das dann nicht funktioniert ("Listenindex außerhalb des gültigen Bereichs"). :oops:

Bleibt noch die Zugriffsverletzung bei mehreren Fenstern zu lösen!


galagher - Sa 12.08.17 10:25

Bist du sicher, dass es an FindControl liegt? Die Zugriffsverletzung tritt erst bei SendMessage auf:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
for j := 0 to wndCtrl.ComponentCount - 1 do  //Alle Components des Fensters durchgehen
begin
  if wndCtrl.Components[j].ClassName = ControlClassName then
  begin
    hnd := TWinControl(wndCtrl.Components[j]).Handle;
    SendMessage(hnd, WM_SETTEXT, 0, Integer(PChar(Msg)));  //<-- Bei zwei Fenstern hält Delphi das Programm, wenn ich hier einen Haltepunkt setze, 2x an
  end;
end;

Alles bis zum SendMessage funktioniert also.

Andererseits kann man die Fehlermeldung verhindern, indem man wndCtrl auf nil überprüft, was mir sagt, es legt also doch an FindControl: Da ist also 1x etwas an wndCtrl zugewiesen, 1x nicht:

Delphi-Quelltext
1:
2:
3:
4:
hnd := StrToInt(list[i]);  //Handle zuweisen
wndCtrl := FindControl(hnd);  //Control suchen und zuweisen
if wndCtrl <> nil then
  for j := 0 to wndCtrl.ComponentCount - 1 do  //Alle Components des Fensters durchgehen

Dennoch funktioniert es nur bei jener Instanz, aus der der Code aufgerufen wurde.
Ratlosigkeit macht sich breit...


Delete - Di 15.08.17 07:27

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Mi 16.08.17 18:21

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Die Lösung wäre z.B. mit EnumChildWindows() [https://msdn.microsoft.com/de-de/library/windows/desktop/ms633494(v=vs.85).aspx] an die Controls zu kommen, oder wie bereits erwähnt, mit FindWindowEx() [https://msdn.microsoft.com/en-us/library/windows/desktop/ms633500(v=vs.85).aspx] alle Controls durchgehen und gegen den gesuchten Typ/Klasse prüfen.
Aber in Prozedur GetSpecificWindowCaption wird doch schon EnumWindows verwendet! Das erstellt doch schon eine Liste aller gefundenen Fenster samt deren Handles: AList.AddObject(wndCaption, TObject(hWnd)); // <-- Fenstertitel und zugehörige Handle mitschreiben


Delete - Mi 16.08.17 18:26

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Mi 16.08.17 18:59

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Richtig, in der GetSpecificWindowCaption() Methode haben wir die Funktion EnumWindows() aufgerufen. EnumChildWindows() ist wiederum etwas anderes, wie der Name schon sagt.
Wer lesen kann, ist klar im Vorteil! :mrgreen:

Nein danke, das ist mir zu kompliziert:
Win API Hilfe hat folgendes geschrieben:
Die Funktion EnumChildWindows zählt die untergeordneten Fenster des festgelegten übergeordneten Fensters auf, indem das Handle jedes untergeordneten Fensters an die anwendungsunterstützte Callback-Funktion übergeben wird.
[...]
WndParent Legt das übergeordnete Fenster fest, dessen untergeordnetes Fenster aufgelistet werden sollen.
EnumFunc Ist die Adresse der Callback-Funktion. Diese Adresse muß mit Hilfe der Funktion MakeProcInstance erzeugt worden sein. [...]
lParam Legt den Wert fest, der an die Callback-Funktion zur Verwendung in der Anwendung übergeben wird. [...]
[...]
Diese Funktion listet nicht obenliegende Fenster, die zu dem übergeordneten Fenster gehören, auf.
Hat ein Sekundär-Fenster eigene Sekundär-Fenster erzeugt, listet die Funktion diese Fenster ebenfalls auf.
Ein Sekundär-Fenster, das während des Auflistungsprozesses in der Z-Richtung bewegt oder repositioniert wird, wird korrekt aufgelistet. Die Funktion listet ein Sekundär-Fenster, das zerstört wird, bevor es erfaßt wird, oder das während des Auflistungsprozesses erzeugt wird, nicht auf.

Was ist unter Windows das "festgelegte übergeordnete Fenster"? Hier also wohl der Desktop. Wie komme ich an das Handle des Desktops? Wie erhalte ich die anderen Parameter?
Ich mache hier Schluss. :eyecrazy:


Delete - Mi 16.08.17 19:19

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Mi 16.08.17 19:37

Mit EnumChildWindows(hWnd(0), @GetAllWindows, Integer(tmp)); kompiliert es zwar, aber mit dem gleichen Effekt: Nur im aufrufende Fenster finden irgendwelche Änderungen zB. in TEdit statt.
Der Aufruf sieht so aus: SendText2('Form1''TEdit''Neuer Edit-Text');


Delete - Mi 16.08.17 20:00

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Mi 16.08.17 20:56

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
hWnd(0) sieht merkwürdig aus, allein schon wegen den runden Klammern.
Ja, es wird aber ein (Integer-)Wert verlangt. Welcher? Keine Ahnung, hab's halt mit 0 versucht!
Ohne Wert in Klammern gibt Delphi die Meldung "[dcc32 Fehler] [...]: E2029 '(' erwartet, aber ',' gefunden" aus.

EnumChildWindows erwartet andererseits als ersten Parameter hWndParent: HWND, also verstehe ich nicht, warum da ein Wert in Klammern verlangt wird...


Delete - Mi 16.08.17 21:05

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Mi 16.08.17 21:17

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Da wird auch kein Wert in Klammern verlangt, sonder das Handle des jeweiligen Anwendung-Fensters. :D
Also könnte man vorerst zum Testen einfach Application.MainForm.Handle angeben, oder auch gleich dieses Handle mit übergeben? Ja? - Hab ich gemacht (SendText2(Handle, 'Form1''TEdit''Neuer Edit-Text');), und jetzt passiert wieder gar nichts. Klarerweise kommt Handle auch bei Prozedur GetSpecificWindowCaption an und wird dort verwertet:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
procedure GetSpecificWindowCaption(aHandle: THandle; const AList: TStrings; const SubText: string;
  bHandle: Boolean);
var
  tmp: TStringList;
  i: Integer;
begin
//...
EnumChildWindows(aHandle, @GetAllWindows, Integer(tmp));
//...


galagher - Do 17.08.17 19:48

Ich habe mir jetzt eine Funktion GetHandle gebaut:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
function GetHandle(const SubText: string): THandle;
var
  aHandle: THandle;
begin
  aHandle := FindWindow(nil, PChar(SubText));

  if aHandle <> 0 then
    Result := FindWindowEx(aHandle, 0, PChar(ControlClassName), PChar(SubText))
  else
    Result := 0;
end;


Die rufe ich für EnumChildWindows so auf:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
begin  {GetSpecificWindowCaption}
  if (AList <> niland (SubText <> ''then
  begin
    tmp := TStringList.Create;
    try
      aHandle := GetHandle(SubText);
      EnumChildWindows(aHandle, @GetAllWindows, Integer(tmp));

Mit dem Ergebnis, dass es immer noch nur bei genau dem aufrufenden Fenster funktioniert, bei anderen nicht, da ist es nach wie vor nil. :autsch: