GELÖST: PChar in BroadcastSystemMessage

Antworten
zappa2
Beiträge: 43
Registriert: Do 28. Nov 2013, 09:54

GELÖST: PChar in BroadcastSystemMessage

Beitrag von zappa2 »

Ich hänge an der Übergabe eines Strings an BroadcastSystemMessage. Hierzu ein Beispiel (beide Auszüge unterscheiden sich ausschließlich durch die Anführungszeichen in der Zuweisung p:=PChar(Edit1.Text)):

Das hier (Edit1.Text ist hier als Quasikonstante in Anführungszeichen gesetzt) funktioniert, es kommt beim Empfänger Edit1.Text an:

Code: Alles auswählen

procedure TForm1.SendGetGroupIDs;
var
  P : PChar;
  lp: LPARAM;
  dwRecipient: DWord;
begin
  p:=PChar('Edit1.Text');
  dwRecipient:=BSM_APPLICATIONS;
  lp:=LongInt(Handle);
  BroadcastSystemMessage(BSF_POSTMESSAGE, @dwRecipient, wSetGroupIDsMsg, Integer(p), lp);
end;
 


Das hier (Edit1.Text soll direkt genommen werden) funktioniert nicht, es kommt irgendwas an:


Code: Alles auswählen

procedure TForm1.SendGetGroupIDs;
var
  P : PChar;
  lp: LPARAM;
  dwRecipient: DWord;
begin
  p:=PChar(Edit1.Text);
  dwRecipient:=BSM_APPLICATIONS;
  lp:=LongInt(Handle);
  BroadcastSystemMessage(BSF_POSTMESSAGE, @dwRecipient, wSetGroupIDsMsg, Integer(p), lp);
end;
 


Also ein im Quelltext statisch angegebener String funktioniert, ein erst zur Laufzeit zur Verfügung stehender String funktioniert nicht. Beide sind ja innerhalb ein und derselben Variable. Im Debugger sieht P auch im zweiten Fall richtig beim Sender aus.

Was kann man tun, damit dieser simple kleine Code funktioniert?



Falls es jemand nachvollziehen mag, hier gleich der relevante Teil der Auswertung der Botschaften beim Empfänger als Auszug:


Code: Alles auswählen

procedure TForm1.WndProc(var Message: TMessage);
var
  li: longint;
  GruppenIDs: PChar;
begin
  li:=LongInt(Message.lParam);             // Message empfangen
 
  if Message.msg=wSetGroupIDsMsg then begin    // GroupID empfangen
    if li=LongInt(Handle) then Exit;
    GruppenIDs:=PChar(Message.wParam);
    Memo1.Lines.Add(GruppenIDs);
  end;
 
  inherited WndProc(Message);
end;
 
Zuletzt geändert von zappa2 am Fr 13. Sep 2019, 19:16, insgesamt 1-mal geändert.

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6198
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Konstante oder variable PChar in BroadcastSystemMessage

Beitrag von af0815 »

Dein Problem hängt damit zusammen, das direkt im Quelltext vorkommende Strings vom Compiler zu pchar konvertiert werden können. Bei Properties wie Edit1.text geht das nicht.

Schau dir mal die funktion strcopy an, die sollte das Problem lösen. https://www.freepascal.org/docs-html/cu ... pcopy.html
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

zappa2
Beiträge: 43
Registriert: Do 28. Nov 2013, 09:54

Re: Konstante oder variable PChar in BroadcastSystemMessage

Beitrag von zappa2 »

Schon mal wieder besten Dank für Deine so unglaublich schnelle Antwort!

Leider ändert strcopy nichts am Ergebnis.

Könnte es sein, dass das String-Handling von irgend welchen Compilereinstellungen abhängt?

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1430
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Konstante oder variable PChar in BroadcastSystemMessage

Beitrag von fliegermichl »

Edit1.Text ist ja kein string in dem Sinn. Es ist eine Property von TControl. Wenn lesend darauf zugegriffen wird, so wird TControl.GetText aufgerufen in der Message soll aber ein Pointer direkt auf einen PChar übergeben werden.

Wenn du eine lokale Variable vom Typ string anlegst, kannst du die innerhalb der Methode zu einem PChar casten. Wenn die Methode verlassen wird, so ist der Inhalt aber wieder undefiniert.

Also kann die Lösung mMn. nur eine globale Variable sein.

Code: Alles auswählen

 
implementation
var tempStr : string;
 
procedure TForm1.SendGetGroupIDs;
var
  lp: LPARAM;
  dwRecipient: DWord;
begin
  tempStr := Edit1.Text;
  dwRecipient:=BSM_APPLICATIONS;
  lp:=LongInt(Handle);
  BroadcastSystemMessage(BSF_POSTMESSAGE, @dwRecipient, wSetGroupIDsMsg, PtrUInt(tempStr), lp);
end;
 

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Konstante oder variable PChar in BroadcastSystemMessage

Beitrag von Warf »

Obs der Fehler ist den du suchst keine Ahnung, aber dein Code ist komplett kaputt

Die letzten 2 parameter von BroadcastSystemMessage sind vom typen LPARAM und WPARAM (die als IntPtr oder UIntPtr definiert sein sollten), du verwendest aber einen Cast auf LongInt und Integer (was beides das selbe ist). Unter 32 bit mag das vielleicht funktionieren, aber spätestens auf 64 bit fliegt dir das um die Ohren. Mein Tipp, verwende die richtigen typen (und damit meine ich nicht IntPtr, sondern die richtigen typen LPARAM und WPARAM die auch in der Funktionssignatur verwendet werden):

Code: Alles auswählen

  lp:=LPARAM(Handle);
  BroadcastSystemMessage(BSF_POSTMESSAGE, @dwRecipient, wSetGroupIDsMsg, WPARAM(tempStr), lp);


Also kann die Lösung mMn. nur eine globale Variable sein.

Der string wird eh kopiert sobald er an die WinAPI übergeben wird (ansonsten wäre das ja ein speicherleck da du ja nicht wissen kannst wann windows mit dem broadcast fertig ist und du den memory block wieder freen kannst), daher sollte der string eh nicht länger leben müssen als die aktuelle Funktion.

Außerdem gibts da schönere Varianten, z.B. consts, die verhalten sich wie statics in C:

Code: Alles auswählen

procedure Foo;
const quasiGlobal: String;
begin
 
end;

Diese quasiGlobal variable ist im Grunde eine globale variable, aber nur in der entsprechenden Methode zugänglich, und daher mMn. schöner.
Außerdem, wenn man eh vorhat den string bis zum program ende zu behalten kann man auch einfach mit GetMem den PChar direkt alloziieren und braucht gar keine variable

zappa2
Beiträge: 43
Registriert: Do 28. Nov 2013, 09:54

Re: Konstante oder variable PChar in BroadcastSystemMessage

Beitrag von zappa2 »

Das funktioniert leider alles nicht. :(

Schon mal wieder Stunden verballert, ohne auch nur einen Stich weiter zu kommen. :evil:

Nachtrag: Empfänger und Sender sind unterschiedliche Programme bzw. Instanzen.

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Konstante oder variable PChar in BroadcastSystemMessage

Beitrag von Warf »

Versuch es mal mit custom alloziiertem Speicher:

Code: Alles auswählen

function str2pchar(const str: String): PChar;
begin
  GetMem(Result, str.length + 1);
  Move(str[1], result^, str.length);
  Result[str.length] := #00;
end;

(Achtung, Das gibt nen Memory leak)

Wenn das auch nicht geht liegt es wahrscheinlich am Inhalt deines edits. Enthält das zufällig Sonderzeichen? Wenn ja kann’s ne utf8-Windows utf16 oder gar ascii (keine Ahnung was die Funktion erwartet) Inkompatibilität sein
Zuletzt geändert von Warf am Do 12. Sep 2019, 15:44, insgesamt 1-mal geändert.

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1430
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Konstante oder variable PChar in BroadcastSystemMessage

Beitrag von fliegermichl »

Das ist vermutlich auch der Grund, weshalb man bei allen Windows API Aufrufen die Zeichenketten liefern, in der aufrufenden Routine einen Buffer anlegen und die Adresse dieses Buffers mit seiner Größe an die API Funktion übergeben muß. Diese schreibt das Ergebnis in den Buffer und liefert die Anzahl der geschriebenen Zeichen.

Vielleicht bekommt man das per IPC Client/Server einfacher gelöst.

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Konstante oder variable PChar in BroadcastSystemMessage

Beitrag von Warf »

fliegermichl hat geschrieben:Das ist vermutlich auch der Grund, weshalb man bei allen Windows API Aufrufen die Zeichenketten liefern, in der aufrufenden Routine einen Buffer anlegen und die Adresse dieses Buffers mit seiner Größe an die API Funktion übergeben muß. Diese schreibt das Ergebnis in den Buffer und liefert die Anzahl der geschriebenen Zeichen.


Hauptgrund dafür ist das Speicher der in einer Bibliothek angelegt wird nicht außerhalb der Bibliothek gefreed werden darf ganz dumm gesagt, der Fpc hat nen eignen Memory Manager, und der kennt den Speicherbereiche aus C Bibliotheken gar ned. Daher arbeiten api Funktionen entweder auf Speicher den der Nutzer bereitstellt, oder auf Speicher der komplett von der api gehandelte wird (findfirst findnext und findclose z.b. Bei dem close das free ist)

Vielleicht bekommt man das per IPC Client/Server einfacher gelöst.

Wenn beides laz Programme sind sollte das auf jeden fall die beste Option sein

zappa2
Beiträge: 43
Registriert: Do 28. Nov 2013, 09:54

Re: Konstante oder variable PChar in BroadcastSystemMessage

Beitrag von zappa2 »

Also ich werde das jetzt mit IPC-Server/Client machen. Das funktioniert wenigstens.

Ich dachte eigentlich, dass ich diesen ganzen Aufwand zum Austausch eines einzigen Strings zwischen 2 Programmen nicht brauche, aber mit allem anderen komme ich jetzt nicht weiter.

Ich glaube nicht wirklich, dass man mit BroadcastSystemMessage in der Tat PChars zwischen zwei verschiedenen Programmen übergeben kann. Man kann Integer hin- und herschieben, aber mehr nicht.

Ich habe nun sogar versucht, mittels direkter Speicherzugriffe quasi nur den Ort mitzuteilen, wo der PChar steht.
Dazu habe ich beim Sender mit LPARAM die eigene ProzeßID und im WPARAM die Adresse des PChars übergeben.

Im Empfänger habe ich dann versucht, diese Speicheradresse wie folgt auszulesen:

Code: Alles auswählen

Hndl := OpenProcess(PROCESS_ALL_ACCESS, FALSE, Message.lParam);
ReadProcessMemory(Hndl,Pointer(Message.wParam),p,4,n);
 

Aber obwohl die Adresse als auch die ProzeßID exakt beim Empfänger ankommen, kommt am Ende auch nur Unsinn raus.

Ich werfe jetzt das Handtuch: 2 Tage rumgebastelt, keinen Schritt weiter gekommen. Bin wohl doch zu doof :(

IPC bleibt ja noch übrig.


Nachtrag: Ich danke Euch allen fleißigen Cracks, die zu helfen versucht haben!

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Konstante oder variable PChar in BroadcastSystemMessage

Beitrag von Warf »

zappa2 hat geschrieben:Also ich werde das jetzt mit IPC-Server/Client machen. Das funktioniert wenigstens.

Ich dachte eigentlich, dass ich diesen ganzen Aufwand zum Austausch eines einzigen Strings zwischen 2 Programmen nicht brauche, aber mit allem anderen komme ich jetzt nicht weiter.


Eine Plattformabbhängige System API zu verwenden ist selten einfacher als eine fertige, cross plattform komponenten, vor allem wenn man von dieser API keine ahnung hat

zappa2 hat geschrieben:Im Empfänger habe ich dann versucht, diese Speicheradresse wie folgt auszulesen:

Code: Alles auswählen

Hndl := OpenProcess(PROCESS_ALL_ACCESS, FALSE, Message.lParam);
ReadProcessMemory(Hndl,Pointer(Message.wParam),p,4,n);
 

Aber obwohl die Adresse als auch die ProzeßID exakt beim Empfänger ankommen, kommt am Ende auch nur Unsinn raus.

Ich werfe jetzt das Handtuch: 2 Tage rumgebastelt, keinen Schritt weiter gekommen. Bin wohl doch zu doof :(


ähm... ja..., wenn du im RAM eines anderen programmes rumstöberst ist das normalerweise ein zeichen das du was falsch machst. Falls es dich interresiert warum es nicht get: ASLR, ein sicherheitsfeature das Fremde programme eben nicht im RAM anderer Programme rumstöbern können. Wenn du das umgehen willst, musst du einfach nur das Offset bestimmen und dann alle Pointer + Offset rechnen beim ReadProcessMemory.

Wenn Programme nett ihren speicher Teilen wollen (im gegensatz zum ungefragten reinschauen mit ReadProcessMemory) benutzt man normalerweise dafür auch Shared Memory. So könntest du z.B. einen Block shared Memory haben, dessen addresse du einfach als PChar verwenden kannst um so C-Strings hin und her zu schieben. Das ist aber nur eine von vielen IPC methoden die Windows implementiert, es gibt noch ne ganze reihe mehr: Link. Simple IPC Client/Server benutzen glaube ich Named Pipes, da die auf Windows ähnlich funktionieren zu Posix (Linux, Mac), außerdem völlig ausreichend sind (sind ein bisschen lahm, wenn man also große daten rüberschieben will sollte man shared memory nehmen, für 90% der fälle reichts aber aus)

zappa2
Beiträge: 43
Registriert: Do 28. Nov 2013, 09:54

Re: Konstante oder variable PChar in BroadcastSystemMessage

Beitrag von zappa2 »

Schnauf! Ich habe es geschafft!!!!

Ich arbeite wieder mit

Code: Alles auswählen

 SendMessage(receiverHandle, WM_COPYDATA, h, Integer(@copyDataStruct)); 


Dazu übertrage ich zuvor per BroadcastSystemMessage mein Fenster-Handle, welches der Empfänger in o.g. SendMessage als receiverHandle benutzt.

Ich hatte das schon mal auf meinem Plan, nur hatte Lazarus niemals diese Message empfangen.
Ich bin der Sache jetzt mal nachgegangen. Ursache und Lösung hat Astat recht einfach hier beschrieben: https://www.delphipraxis.net/147640-wm_copydata-zwischen-delphi-und-freepascalprogramm.html

Ich habe in der dort erwähnten win32callback.inc in der

Code: Alles auswählen

function TWindowProcHelper.DoWindowProc: LResult;


das nachgerüstet (was ein Neukompilieren der LCL zur Folge hat), und nun funzt das vom Allerfeinsten.

Mann, war das mal wieder ein Kampf! Grrrh!

Nur eines ist scheußlich: Jetzt muß ich mit jeder Neuinstallation von Lazarus daran denken, die o.g. Nachrüstung auszuführen. Denn leider kommt ja kein Compiler-Fehler wenn das fehlt (ist ja auch kein Fehler), nur kommt dann wieder diese Message nicht mehr an. Ich finde, das sollten die Lazarus-Entwickler mittels Compilerschalter drin lassen; aber wahrscheinlich denke ich da mal wieder zu einfach.

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1430
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Konstante oder variable PChar in BroadcastSystemMessage

Beitrag von fliegermichl »

zappa2 hat geschrieben:Nur eines ist scheußlich: Jetzt muß ich mit jeder Neuinstallation von Lazarus daran denken, die o.g. Nachrüstung auszuführen. Denn leider kommt ja kein Compiler-Fehler wenn das fehlt (ist ja auch kein Fehler), nur kommt dann wieder diese Message nicht mehr an. Ich finde, das sollten die Lazarus-Entwickler mittels Compilerschalter drin lassen; aber wahrscheinlich denke ich da mal wieder zu einfach.


Dann sende doch einen Bugreport an die Lazarusentwickler.

Antworten