[Delphi 5] Parameter doorgeven aan actieve applicatie

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • One_Gandalf
  • Registratie: April 2002
  • Laatst online: 02-08 14:54
Ik heb een viewer geschreven en die gekoppeld aan de extensie .JPG. Via de functie ParamStr haal ik de naam van het te bekijken JPG-plaatje op.

Als ik nu nogmaals op een JPG-plaatje klik wil ik dat het plaatje in de eerder gestarte viewer wordt getoond en dus het getoonde plaatje vervangt.

Het voorkomen dat de viewer dubbel wordt gestart heb ik reeds opgelost.

Mijn vraag is : Hoe zorg ik er voor dat de naam van het aangeklikte JPG-plaatje wordt opgepakt door de openstaande viewer?

Ik vraag me met name het volgende af :
  • Welke methode wordt het meest gebruikt door programmeurs ?
  • Welke methode wordt gebruikt binnen Windows zelf ?

[ Voor 28% gewijzigd door One_Gandalf op 06-07-2008 10:42 ]


Acties:
  • 0 Henk 'm!

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 12-08 12:13

Creepy

Tactical Espionage Splatterer

Je zult in je tweede opgestarte instantie moeten controleren of er al een eerder opgestarte instantie van je app. draait (bijv. m.b.v. FindWindow of een named mutex). Zo ja, dan een bericht versturen (SendMessage) naar je eerste instantie dat ie een andere afbeelding moet laden.

[ Voor 10% gewijzigd door Creepy op 06-07-2008 09:14 ]

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


Acties:
  • 0 Henk 'm!

  • One_Gandalf
  • Registratie: April 2002
  • Laatst online: 02-08 14:54
Creepy schreef op zondag 06 juli 2008 @ 09:12:
Je zult in je tweede opgestarte instantie moeten controleren of er al een eerder opgestarte instantie van je app. draait (bijv. m.b.v. FindWindow of een named mutex). Zo ja, dan een bericht versturen (SendMessage) naar je eerste instantie dat ie een andere afbeelding moet laden.
Deel 1 : Gebruik Mutex
Is dat de 'best practice' ? Op Internet http://delphi.about.com/od/windowsshellapi/l/aa100703a.htm vond ik namelijk een tekst waarin het gebruik van een Mutex niet als beste oplossing werd gezien. Ik heb de daar aangereikte oplossing met 'File Mapping' geïmplementeerd. Ik heb wel de functie 'RaiseLastOSError' moeten vervangen door 'RaiseLastWin32Error' maar daarna werkte het als een zonnetje.

Deel 2 : SendMessage
Ik heb zelf een werkende oplossing gebouwd via de registry en een timer. Ik heb SendMessage overwogen maar vond dit te tijdrovend om uit te zoeken. Is SendMessage de meest gebruikte methode om deze problematiek op te lossen? Maakt de standaard Windows Viewer bijvoorbeeld ook gebruik van SendMessage om een plaatje te verversen?

Acties:
  • 0 Henk 'm!

  • Cypher87
  • Registratie: Oktober 2004
  • Laatst online: 06-07 12:44
Je kan ook de CreateSemaphore api call gebruiken. Geeft je weinig code en is verschrikkelijk simpel te programmeren.
Delphi:
1
2
3
4
5
6
7
8
9
10
11
  Sem: THandle

  ..

  Sem := CreateSemaphore(nil, 0, 1, 'unieke naam hier');
  if ((Sem <> 0) and (GetLastError = ERROR_ALREADY_EXISTS)) then
  begin
    // sendmessage gedoe
    CloseHandle(Sem);
    application.Terminate;
  end;


Dit stukje code moet je in je project source plaatsen (de .dpr dus)

Acties:
  • 0 Henk 'm!

  • One_Gandalf
  • Registratie: April 2002
  • Laatst online: 02-08 14:54
Cypher87 schreef op zondag 06 juli 2008 @ 10:28:
Je kan ook de CreateSemaphore api call gebruiken. Geeft je weinig code en is verschrikkelijk simpel te programmeren.
Delphi:
1
2
3
4
5
6
7
8
9
10
11
  Sem: THandle

  ..

  Sem := CreateSemaphore(nil, 0, 1, 'unieke naam hier');
  if ((Sem <> 0) and (GetLastError = ERROR_ALREADY_EXISTS)) then
  begin
    // sendmessage gedoe
    CloseHandle(Sem);
    application.Terminate;
  end;


Dit stukje code moet je in je project source plaatsen (de .dpr dus)
toon volledige bericht
Bedankt voor deze tip. Ik ben echter vooral geïnteresseerd in het 'sendmessage gedoe' of eventuele andere oplossingen voor communicatie met de reeds actieve instance van de viewer.

Acties:
  • 0 Henk 'm!

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 12-08 12:13

Creepy

Tactical Espionage Splatterer

De SendMessage methode wordt uitgelegd in de link die je geeft evenals het gebruik van een named mutex :)
Alles in Windows werkt met Messages waarbij SendMessage behoorlijk vaak wordt gebruikt voor het versturen ervan. Delphi vangt dit soort dingen allemaal voor je af normaal gesproken maar ja, dit is de manier om een event door te geven. Hoef je niet in het register te lopen rommelen en met timers e.d. te gaan lopen pollen.

Dat een filemapping beter zou zijn dan een mutex (of een semephore) haal ik niet uit je link, wel dat ze een filemapping gebruiken om meer dan 1 instantie toe te kunnen staan (i.p.v. maximaal 1).

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Die sendmessage in die link werkt door het naar alle applicaties te sturen, dat lijkt me niet zo handig. Daarnaast is het oversturen van de data ook nog niet geheel triviaal. Dit is een voorbeeld met WM_COPYDATA, wat ervoor bedoeld is.

Het argument dat ze gebruiken dat Findwindow wel eens een andere Form1 tegen kan komen is vrij brak. Je moet je Mainform natuurlijk geen Form1 noemen...

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 12-08 12:13

Creepy

Tactical Espionage Splatterer

Hmja, met een beetje logisch nadenken dan zie je dat de SendMessage een HWND als argument pakt en dat je i.p.v. broadcast daar ook het HWND van je al draaiende app. in kan gebruiken.

Ook zou je een stukje shared geheugen kunnen gebruiken voor het doorgeven van de te openen bestanden of een named pipe.

[ Voor 33% gewijzigd door Creepy op 06-07-2008 11:45 ]

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


Acties:
  • 0 Henk 'm!

  • One_Gandalf
  • Registratie: April 2002
  • Laatst online: 02-08 14:54
Creepy schreef op zondag 06 juli 2008 @ 11:43:
Hmja, met een beetje logisch nadenken dan zie je dat de SendMessage een HWND als argument pakt en dat je i.p.v. broadcast daar ook het HWND van je al draaiende app. in kan gebruiken.

Ook zou je een stukje shared geheugen kunnen gebruiken voor het doorgeven van de te openen bestanden of een named pipe.
Blijkbaar hebben jullie de tekst achter de link beter gelezen dan ik. Ik was met name gefocust op het open houden van maximaal één instance.

Ik wil SendMessage gaan uitproberen. Voor zover ik heb begrepen moet ik dan code schrijven aan de ontvangende en aan de zendende kant.

Zenden vindt plaats in de unit 'CheckPrevious' die wordt gebruikt in de projectsource. In het volgende stukje code is de handle gevonden en zet ik nu een waarde in de registry :

Delphi:
1
2
3
4
5
6
7
8
          if IsIconic(InstanceInfo^.PreviousHandle) then
            ShowWindow(InstanceInfo^.PreviousHandle, SW_RESTORE);
          SetForegroundWindow(InstanceInfo^.PreviousHandle);
          { Schrijf waarde in Registry }
          Reg := TRegistry.Create;
          Reg.OpenKey('Software',True);
          Reg.OpenKey('JpegViewer',True);
          Reg.WriteString('Afbeelding', ParamStr(1))


Ontvangen vindt plaats in een event in de unit van het MainForm (in mijn geval 'Unit1').

Acties:
  • 0 Henk 'm!

  • Cypher87
  • Registratie: Oktober 2004
  • Laatst online: 06-07 12:44
Je kan het beste pedorus zijn oplossing gebruiken.
Nog een handig linkje: http://delphi.about.com/od/windowsshellapi/a/wm_copydata.htm
Denk dat het daarmee wel moet lukken.

Acties:
  • 0 Henk 'm!

  • One_Gandalf
  • Registratie: April 2002
  • Laatst online: 02-08 14:54
Cypher87 schreef op zondag 06 juli 2008 @ 12:31:
Je kan het beste pedorus zijn oplossing gebruiken.
Nog een handig linkje: http://delphi.about.com/od/windowsshellapi/a/wm_copydata.htm
Denk dat het daarmee wel moet lukken.
Ok. Ik ga hier mee aan de slag.

Topic kan worden gesloten.

Acties:
  • 0 Henk 'm!

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 12-08 12:13

Creepy

Tactical Espionage Splatterer

offtopic:
Eeh nee, alleen "foute" topics worden gesloten. Een topic gaat niet dicht omdat de TS vindt dat ie een oplossing heeft, misschien heeft iemand anders nog wel wat toe te voegen ;)

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


Acties:
  • 0 Henk 'm!

  • One_Gandalf
  • Registratie: April 2002
  • Laatst online: 02-08 14:54
Inderdaad ik ben hier mee aan de slag gegaan en loop vast. De applicatie vertaalt maar de message komt niet aan. Per kant gebruik ik de volgende code:

Zender
Delphi:
1
2
3
4
5
copyDataStruct.dwData := 0; //use it to identify the message contents
copyDataStruct.cbData := 1 + Length(ParamStr(1)) ;
copyDataStruct.lpData := PChar(ParamStr(1)) ;
SendMessage(InstanceInfo^.PreviousHandle, WM_COPYDATA,
        Integer(InstanceInfo^.PreviousHandle), Integer(@copyDataStruct));


Ontvanger
Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
  private
    procedure WMCopyData(var Msg : TWMCopyData); message WM_COPYDATA;
  ...
  procedure TForm1.WMCopyData(var Msg: TWMCopyData);
  var s : String;
  begin
    ShowMessage('Ontvangen message');
    s := PChar(Msg.CopyDataStruct.lpData);
    ShowMessage(s);
    //Send something back
    Msg.Result := 1
  end;


De vraag is natuurlijk : Wat doe ik fout ?

Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Op metaniveau: je probeert het wiel opnieuw uit te vinden, terwijl er kant-en-klare, goed werkende code is. (Ik zou de situatie waarbij het zenden faalt nog afvangen in DoPassCommandline, maar voor de rest lijkt het ok.)

Over dit probleem: Je roept SendMessage verkeerd aan. De 3e parameter moet de EIGEN window-handle zijn, indien beschikbaar. Daarnaast zou InstanceInfo^.PreviousHandle wel eens niet-valide kunnen zijn, of zou er een security probleem kunnen zijn (Vista?). Check GetLastError/RaiseLastWin32Error. Ook is meerdere keren Paramstr(1) aanroepen niet handig.

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

  • One_Gandalf
  • Registratie: April 2002
  • Laatst online: 02-08 14:54
pedorus schreef op zondag 06 juli 2008 @ 16:39:
Op metaniveau: je probeert het wiel opnieuw uit te vinden, terwijl er kant-en-klare, goed werkende code is. (Ik zou de situatie waarbij het zenden faalt nog afvangen in DoPassCommandline, maar voor de rest lijkt het ok.)

Over dit probleem: Je roept SendMessage verkeerd aan. De 3e parameter moet de EIGEN window-handle zijn, indien beschikbaar. Daarnaast zou InstanceInfo^.PreviousHandle wel eens niet-valide kunnen zijn, of zou er een security probleem kunnen zijn (Vista?). Check GetLastError/RaiseLastWin32Error. Ook is meerdere keren Paramstr(1) aanroepen niet handig.
Op metaniveau : ik hoopte juist niet het wiel opnieuw uit te vinden.

Over dit probleem : ik had als 3e parameter net zoals in de voorbeeld-code eerst Integer(Handle) staan maar kreeg een foutmelding op de variabele Handle. Ik werk met Windows XP SP 2. Kan je nader toelichten hoe ik GetLastError/RaiseLastWin32Error moet gebruiken?

De variabele InstanceInfo^.PreviousHandle wordt enkele regels terug gebruikt om het reeds bestaande window naar de voorgrond te halen:

Delphi:
1
SetForegroundWindow(InstanceInfo^.PreviousHandle);


Zou het misschien aan de projectcode kunnen liggen :

Delphi:
1
2
3
4
5
6
7
8
begin
  if not CheckPrevious.RestoreIfRunning(Application.Handle, 1) then
  begin
    Application.Initialize;
    Application.CreateForm(TForm1, Form1);
    Application.Run;
  end;
end.


De opdracht SendMessage staat in de unit CheckPrevious echter op dat moment is het Form nog niet aangemaakt.

Ik heb het zender-gedeelte als volgt aangepast :

Zender
Delphi:
1
2
3
4
5
copyDataStruct.dwData := 0; //use it to identify the message contents
copyDataStruct.cbData := 1 + Length(ParamStr(1)) ;
copyDataStruct.lpData := PChar(ParamStr(1)) ;
SendMessage(InstanceInfo^.PreviousHandle, WM_COPYDATA,
        AppHandle, Integer(@copyDataStruct));


De derde parameter van het commando SendMessage is nu AppHandle. Dit lost het probleem nog niet op.

Het resultaat van GetLastError direct na SendMessage toont de waarde 183. Echter ik kan niet achterhalen wat deze waarde betekent. Je zou denken de waarde is niet 0 dus er is vermoedelijk iets aan de hand. De waarde is volgens de help afhankelijk van de message WM_COPYDATA.

Zojuist gevonden : "The return value will be 0(zero) if the message could not be delivered". Dit betekent dat de message wel is afgeleverd, maar waar.

[ Voor 42% gewijzigd door One_Gandalf op 06-07-2008 18:32 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Is er vóór Application.Initialize en de eerste Application.CreateForm wel een geldige handle? Ik programmeer al jaren niet meer in Delphi, maar voor zover ik mij kan herinneren wordt die handle dan gemaakt. Kijk even in de VCL source of het bij Initialize of CreateForm is. Bij de eerste keer dat CreateForm aangeroepen wordt, wordt de handle van dat form de handle van je main form. Je application heeft ook een handle, en die wordt volgens mij in Application.Initialize gemaakt.

Waarom begin je niet met twee applicaties die wat berichten naar elkaar sturen? Een zender en een ontvanger waarbij de zender gewoon één knop heeft en de ontvanger een ShowMessage met je bericht. Dan weet je tenminste waar het probleem ligt.

[ Voor 6% gewijzigd door Verwijderd op 06-07-2008 19:46 ]


Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
One_Gandalf schreef op zondag 06 juli 2008 @ 16:58:
Het resultaat van GetLastError direct na SendMessage toont de waarde 183. Echter ik kan niet achterhalen wat deze waarde betekent. Je zou denken de waarde is niet 0 dus er is vermoedelijk iets aan de hand. De waarde is volgens de help afhankelijk van de message WM_COPYDATA.

Zojuist gevonden : "The return value will be 0(zero) if the message could not be delivered". Dit betekent dat de message wel is afgeleverd, maar waar.
Windows Error 0x000000B7 - 183
Cannot create a file when that file already exists.
ERROR_ALREADY_EXISTS

Dat is de error message die hiervoor is ontstaan met de memory mapped virtual file, dus Sendmessage gaat gewoon goed. SendMessage is waarschijnlijk gewoon gelukt, enkel zal 0 hebben geretourneerd (message niet afgehandeld).

Als derde parameter kun je gewoon 0 gebruiken, zoals in de werkende code. Waarschijnlijk is je Handle niet van het juiste window, waardoor je message niet wordt afgehandeld door de ontvanger. Application.Handle bevat gewoon niet de juiste waarde, test eens met FindWindow('TForm1',nil) zou ik zeggen..

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

Verwijderd

pedorus schreef op zondag 06 juli 2008 @ 20:45:
Application.Handle bevat gewoon niet de juiste waarde, test eens met FindWindow('TForm1',nil) zou ik zeggen..
Application.Handle heeft niet de juiste waarde om de reden die ik noemde. En FindWindow('TForm1', nil) is sowieso onzin. De classname is standaard TApplication en om dat te veranderen moet je handmatig CreateParams van je form overriden en WinClassName van TCreateParams veranderen. Dat zul je sowieso moeten veranderen voor FindWindow, want elke Delphi applicatie gebruikt dus TApplication als standaard classname. Dit kan wel eens de oorzaak van het hele probleem zijn; je stuurt je message naar de verkeerde applicatie.

Maar nogmaals, maak even twee applicaties en test de communicatie. Er zijn nu tal van oorzaken waardoor het fout kan gaan die niet met de IPC zelf te maken hebben.

[ Voor 17% gewijzigd door Verwijderd op 06-07-2008 22:21 ]


Acties:
  • 0 Henk 'm!

  • One_Gandalf
  • Registratie: April 2002
  • Laatst online: 02-08 14:54
Het commando FindWindow('TForm1',nil) was de gouden tip. Ik heb alle handles gecontroleerd. En het bleek dat de handle die ik gebruikte de handle van de applicatie was. Door de handle van TForm1 te gebruiken werd de message in Form1 opgevangen. Voor de volledigheid de code totnogtoe:

Zender
Delphi:
1
2
FindHandle := FindWindow('TForm1',nil);
SendMessage(FindHandle, WM_COPYDATA, AppHandle, LParam(@copyDataStruct));


Ontvanger
Delphi:
1
2
3
4
5
6
procedure TForm1.WMCopyData(var Msg: TWMCopyData);
var s : String;
begin
  ShowMessage('Ontvangen message');
  s := PChar(Msg.CopyDataStruct.lpData);
end;


Het zoeken op TForm1 vind ik nog niet lekker; ik ga waarschijnlijk een unieke naam bedenken voor het Form.

Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Je zou bijna denken dat FindWindow de rest overbodig maakt. Een oplossing met alleen Findwindow kan echter meerdere instanties gewoon toelaten, vanwege synchronisatieproblemen.

Bij die about.com code kan dat trouwens theoretisch ook, als applicatie 2 wordt opgestart terwijl de memory mapped file wel is aangemaakt, maar nog op '0 instanties' staat. Iets dergelijks kan ook gebeuren bij het exact tegelijkertijd afsluiten en opstarten. Bij die Tip op Torry kan dat niet, omdat daar wel goed gecheckt wordt met GetLastError. Daar is het vinden van de juiste handle ook heel mooi opgelost, dus ik zou eigenlijk gewoon alsnog switchen.

Dan nog is er een openstaand probleem: Wat te doen als de ontvanger de boodschap niet kan verwerken vanwege openstaande dialoogjes, afsluiten of een vastloper?

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten

Pagina: 1