| Autor |
Beitrag |
schitho
      
Beiträge: 288
XP Home SP2
D2005 Prof
|
Verfasst: Mi 03.09.03 21:10
Hi,
ihr kennt sicher die Kontext-Menüerweiterungen von WinZip.
Genau soetwas möchte ich für mein Programm auch haben
Also wenn man mit der rechten Maustaste auf eine oder mehrere Dateien klickt soll im Kontextmenü der Name meines Programmes aufscheinen. Und wenn man nun auf diesen Namen klickt, soll mein Programm geöffnet werden und die Namen (mit Pfad) der Dateien übergeben werden.
Ich weiß, dass ich dies mit HKEY_CLASSES_ROOT\*\shell\MeinProgramm\Command und dem Eintrag MeinProgramm "%1" machen könnte.
Doch leider wird das Programm dann mehrmals gestartet, wenn mehrer Dateien ausgewählt wurden.
WinZip verwendet daher stattdessen - soweit ich das verstanden habe - shellex und einen eigenen ContexMenuHandler.
Es gibt dazu auch mehrer Registry-Einträge. Und so weit ich in Erfahrung bringen konnte muss man eben so einen COM-Handle (oder so ähnlich) dafür programmieren.
Da ich diesbezüglich aber vollkommen ahnungslos bin (wie ihr sicher schon bemerkt habt  ) suche ich nach diesbzüglichen Infos, wie ich so etwas programmieren kann, damit ich im Kontextmenü mein Programm anzeigen kann und es nicht doppelt gestartet wird.
Oder gibt es vielleicht eine Komponente, die einem das abnimmt bzw. wesentlich erleichtert???
_________________ (Sorry! Leider ewiger Delphi-Neuling)
|
|
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mi 03.09.03 23:00
Nein, aber eine Demo von Borland im Ordner "ShellExt", die auf diese Weise den Punkt Compile in das Kontextmenü von Dateien schreibt. Das läuft allerdings auf eine DLL hinaus. Sprich: du müsstest dann doch irgendwie wieder dein Programm aufrufen ...
Du musst aber keine Shell-Erweiterung machen. Dein Problem mit dem mehrfachen Aufruf lässt sich durch einen Mutex oder Semaphore umgehen, der dein Programm nur einmal startet. Wird der Mutex/Semaphore noch einmal gefunden, könntest du den Parameter der zweiten Instanz bspw. über WM_COPYDATA an die erste Instanz weiterreichen.
Dazu könnte ich dich an die UPX-Shell auf meiner Seite verweisen. Da habe ich das gemacht, denn die registriert sich mit beiliegender INF-Datei auch im System.
Langsam sollte ich Lizenz- bzw. Adaptionsgebühren verlangen. 
|
|
schitho 
      
Beiträge: 288
XP Home SP2
D2005 Prof
|
Verfasst: Mi 03.09.03 23:18
| MathiasSimmack hat folgendes geschrieben: | Du musst aber keine Shell-Erweiterung machen. Dein Problem mit dem mehrfachen Aufruf lässt sich durch einen Mutex oder Semaphore umgehen, der dein Programm nur einmal startet. Wird der Mutex/Semaphore noch einmal gefunden, könntest du den Parameter der zweiten Instanz bspw. über WM_COPYDATA an die erste Instanz weiterreichen.
|
Das hab ich auch schon ausprobiert funktioniert aber nicht richtig (zumindest unter W98). Ich bekomm dann immer die Fehlermeldung, dass nicht genügend Arbeitsspeicher zur Verfügung steht (trotz 512 MB RAM).
Trotzdem würde ich mir mal gerne Deine Seite ansehen. Aber wo find ich diese????
_________________ (Sorry! Leider ewiger Delphi-Neuling)
|
|
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mi 03.09.03 23:45
Ich dachte, du hast schon ein Programm von mir gekl... äh ... adaptiert.  Die UPX UI gibt´s auch da.
Zuletzt bearbeitet von MathiasSimmack am Do 04.09.03 22:34, insgesamt 1-mal bearbeitet
|
|
schitho 
      
Beiträge: 288
XP Home SP2
D2005 Prof
|
Verfasst: Do 04.09.03 21:55
Hab mir Dein Programm angesehen. Puhhh! Ist das Delphi?
Also was ich (halbwegs) verstanden habe ist:
Mit diesem Code verhinderst Du, dass Dein Programm doppelt gestartet wird und dass die Parameter an die andere Instanz gesendet werden:
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:
| const szUniqueId = 'UPX-UI-2003_D311E2A0-3084-11D7-BD2C-FC698EE3C743'; var hm : THandle = 0; aWnd : HWND; cpydata : TCopyDataStruct; iccex : TInitCommonControlsEx = ( dwSize:sizeof(TInitCommonControlsEx); dwICC:ICC_BAR_CLASSES;); begin hm := CreateMutex(nil,false,szUniqueId); if(GetLastError = ERROR_ALREADY_EXISTS) then begin aWnd := findwindow(nil,'UPX UI 2003');
if(paramcount = 1) and (fileexists(paramstr(1))) and (aWnd <> 0) then begin cpydata.dwData := 0; cpydata.cbData := length(paramstr(1)) + 1; cpydata.lpData := @paramstr(1)[1];
SendMessage(aWnd,WM_COPYDATA,0,LPARAM(@cpydata)); end;
if(aWnd <> 0) then begin SendMessage(aWnd,WM_SYSCOMMAND,SC_RESTORE,0); SetForegroundWindow(aWnd); end;
Halt; end; |
Diesen Code kann ich fast 1:1 in mein Programm einbauen. Wenn ich es richtig verstanden habe, muss ich nur den Fensternamen ändern und szUniqueId adaptieren. Ist es bei der szUniqueId eigentlich egal, was da drinnen steht???
Hab den Code adaptiert und unter OnCreate integriert. Damit wird der Doppelstart verhindert.
Um die Parameter abzufangen (zu erhalten), verwendest Du (scheinbar) folgenden Code:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| WM_COPYDATA: begin ZeroMemory(@buf,sizeof(buf)); lstrcpyn(buf,PCopyDataStruct(lp)^.lpData,PCopyDataStruct(lp)^.cbData);
if(buf[0] <> #0) and (SendDlgItemMessage(hwndDlg,IDC_FILELIST,LB_FINDSTRINGEXACT,-1, LPARAM(@buf)) = LB_ERR) and (lstrcmpi(buf,@paramstr(0)[1]) <> 0) then SendDlgItemMessage(hwndDlg,IDC_FILELIST,LB_ADDSTRING,0,LPARAM(@buf)); |
Und jetzt steig ich endgültig aus.
Zu welchen Ereignis muss den Code einfügen? Wie bekomm ich die Parameter in ein Memofeld?
Versteh nur Bahnhof  Ich glaub ich lern das nie
Kannst Du mir sagen, wie der Code aussehen müsste, wenn sich in meinem Programm in der Form ein Memofeld (Memo1) befindet und ich die Parameter dort anzeigen möchte?
Recht herzlich Dank!
_________________ (Sorry! Leider ewiger Delphi-Neuling)
|
|
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Do 04.09.03 22:36
Also, wie du richtig erkannt hast, kannst du den "Sende"-Teil quasi 1:1 übernehmen. Die UniqueId ist nicht egal. Du solltest bitte eine andere benutzen. Du kannst dir zwar ausdenken, was du willst, aber du solltest nach Möglichkeit nicht meine nehmen. Es könnte ja sein, dass jemand mal zufälligerweise dein und mein Programm benutzt. Dann würde u.U. mein Programm reagieren, weil deins den Mutex nicht mehr erzeugen kann.
Das "Empfangen" läuft über die Nachricht WM_COPYDATA. Die kannst du auch in der VCL abfangen, indem du bspw. die "WndProc" direkt bearbeitest.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| type TForm1 = class(TForm) protected procedure WndProc(var Message: TMessage); override; end;
procedure TForm1.WndProc(var Message: TMessage); begin if(Message.Msg = WM_COPYDATA) then begin end else inherited WndProc(Message); end; |
Du musst nur beachten, dass "uMsg", "wParam" (wp) und "lParam" (lp) bei der VCL Teil von "Message" sind. Na ja, und was den Code angeht. Das ist eben nonVCL. Hier wird der Name aus dem übermittelten Puffer ausgelesen
Delphi-Quelltext 1: 2:
| ZeroMemory(@buf,sizeof(buf)); lstrcpyn(buf,PCopyDataStruct(lp)^.lpData,PCopyDataStruct(lp)^.cbData); |
Wenn das erste Zeichen ungleich Null ist (pchar)
Delphi-Quelltext
dann wird geguckt, ob´s den String evtl. schon in meiner Liste gibt
Delphi-Quelltext 1: 2:
| (SendDlgItemMessage(hwndDlg,IDC_FILELIST, LB_FINDSTRINGEXACT,-1,LPARAM(@buf)) = LB_ERR) and |
und er darf auch nicht dem Namen der UPX-Shell selbst entsprechen
Delphi-Quelltext 1:
| (lstrcmpi(buf,@paramstr(0)[1]) <> 0) then |
dann (und nur dann!) wird er in die Listbox eingefügt
Delphi-Quelltext 1: 2:
| SendDlgItemMessage(hwndDlg,IDC_FILELIST, LB_ADDSTRING,0,LPARAM(@buf)); |
Damit kannst du den Code wohl jetzt auch 1:1 kopieren? 
|
|
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Do 04.09.03 22:48
Noch eine andere Variante, gleich mit praktischem Beispiel:
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:
| type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA; end;
procedure TForm1.WMCopyData(var Msg: TWMCopyData); var buf : array[0..MAX_PATH]of char; begin inherited;
ZeroMemory(@buf,sizeof(buf)); lstrcpyn(buf,Msg.CopyDataStruct^.lpData,Msg.CopyDataStruct^.cbData);
ShowMessage(buf); end;
procedure TForm1.Button1Click(Sender: TObject); var cpydata : TCopyDataStruct; begin cpydata.dwData := 0; cpydata.cbData := length('Hallo, schitho') + 1; cpydata.lpData := pchar('Hallo, schitho');
SendMessage(self.Handle,WM_COPYDATA,0,LPARAM(@cpydata)); end; |
Bitte schön.
|
|
schitho 
      
Beiträge: 288
XP Home SP2
D2005 Prof
|
Verfasst: Sa 06.09.03 14:03
Cool. Jetzt funktioniert es (halbwegs).
Allerdings bekomm ich die Nachricht nicht ins Memofeld. Mit ShowMessage kann ich sie jedoch anzeigen. Wieso funktioniert Memo.Lines.Add bzw. Append nicht?
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| procedure TFormTest.WMCopyData(var Msg: TWMCopyData); var buf : array[0..MAX_PATH]of char; begin inherited;
ZeroMemory(@buf,sizeof(buf)); lstrcpyn(buf,Msg.CopyDataStruct^.lpData,Msg.CopyDataStruct^.cbData); Memo.Lines.Append(buf); ShowMessage(buf); end; |
Ich bekomm zwar keine Fehlermeldung, allerdings wird im Memofeld auch nicht angezeigt. Versteh ich nicht 
_________________ (Sorry! Leider ewiger Delphi-Neuling)
|
|
schitho 
      
Beiträge: 288
XP Home SP2
D2005 Prof
|
Verfasst: Sa 06.09.03 22:39
Hab den Code nun geändert! Und er funktioniert trotzdem nicht
Hier der Code:
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: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107:
| unit test;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type TFormTest = class(TForm) Label1: TLabel; Memo: TMemo; Button1: TButton; LMutex: TLabel; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject);
private protected procedure WndProc(var Message: TMessage); override;
public end;
var FormTest: TFormTest; Zeit : TDateTime;
implementation
{$R *.dfm}
procedure TFormTest.WndProc(var Message: TMessage); var buf : array[0..MAX_PATH]of char; begin if(Message.Msg = WM_COPYDATA) then begin ZeroMemory(@buf,sizeof(buf)); lstrcpyn(buf,PCopyDataStruct(Message.LParam)^.lpData,PCopyDataStruct(Message.LParam)^.cbData);
if(buf[0] <> #0) and (SendDlgItemMessage(self.Handle,Memo.ControlCount,LB_FINDSTRINGEXACT,-1, LPARAM(@buf)) = LB_ERR) and (lstrcmpi(buf,@paramstr(0)[1]) <> 0) then SendDlgItemMessage(self.Handle,Memo.ControlCount,LB_ADDSTRING,0,LPARAM(@buf));
end else inherited WndProc(Message); end;
procedure TFormTest.FormCreate(Sender: TObject); const szUniqueId = 'TEST_D311E2A0-3084-11D7-BD2C-FC698EE3C743'; var hm : THandle; aWnd : HWND; cpydata : TCopyDataStruct; begin Zeit:=now; hm:=0; hm := CreateMutex(nil,false,szUniqueId); if(GetLastError = ERROR_ALREADY_EXISTS) then begin aWnd := findwindow(nil,'Test');
if(paramcount = 1) and (aWnd <> 0) then begin cpydata.dwData := 0; cpydata.cbData := length(paramstr(1)) + 1; cpydata.lpData := @paramstr(1)[1]; SendMessage(aWnd,WM_COPYDATA,0,LPARAM(@cpydata)); end;
if(aWnd <> 0) then begin SendMessage(aWnd,WM_SYSCOMMAND,SC_RESTORE,0); SetForegroundWindow(aWnd); end;
Halt; end;
end;
procedure TFormTest.Button1Click(Sender: TObject); var cpydata : TCopyDataStruct; begin cpydata.dwData := 0; cpydata.cbData := length('Hallo, schitho') + 1; cpydata.lpData := pchar('Hallo, schitho');
SendMessage(self.Handle,WM_COPYDATA,0,LPARAM(@cpydata));
end;
end. |
Wahrscheinlich voller Fehler....... 
_________________ (Sorry! Leider ewiger Delphi-Neuling)
|
|
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: So 07.09.03 09:12
1. Du solltest den Code mit der Mehrfachabfrage im Projektquelltext (*.dpr) unterbringen, bevor ein Formular erzeugt wird. Die Units "Windows" und "Messages" bitte nicht vergessen.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| begin hm := CreateMutex(nil,false,szUniqueId); if(GetLastError = ERROR_ALREADY_EXISTS) then begin end;
Application.Initialize;
CloseHandle(hm); end; |
2. Die Variante mit einem String anstelle eines Char-Puffers. Um vorzugreifen: ich hab´s getestet, und es funktioniert.
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:
| type TForm1 = class(TForm) private procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA; end;
procedure TForm1.WMCopyData(var Msg: TWMCopyData); var szBuf : string; begin inherited;
SetLength(szBuf,Msg.CopyDataStruct^.cbData + 1); ZeroMemory(@szBuf[1],length(szBuf)); lstrcpyn(@szBuf[1], Msg.CopyDataStruct^.lpData, Msg.CopyDataStruct^.cbData);
if(szBuf <> '') and (Memo1.Lines.IndexOf(szBuf) = -1) then Memo1.Lines.Add(szBuf); end; |
3. Eine Anmerkung noch dazu:
| schitho hat folgendes geschrieben: | Delphi-Quelltext 1:
| aWnd := findwindow(nil,'Test'); | |
Du solltest lieber den Klassennamen abfragen, denn der richtet sich bei der VCL netterweise nach dem Namen der Form. Das heißt, wenn du x Programme hast, deren Formulare alle "TForm1" heißen, dann hast du ein Problem. Wenn du deinem Programm aber einen passenden Namen gibst, dann kannst du mit
Delphi-Quelltext 1:
| aWnd := findwindow('TMeineGesuchteForm',nil); |
das Fenster relativ einfach und unabhängig vom Text in der Titelleiste finden. Im Fall deines Beispielcodes also
Delphi-Quelltext 1:
| aWnd := findwindow('TFormTest',nil); |
Gruß.
|
|
schitho 
      
Beiträge: 288
XP Home SP2
D2005 Prof
|
Verfasst: So 07.09.03 12:31
Hallo Matthias,
Danke für Deine Geduld
Irgendwie bin ich aber scheinbar nicht mal fähig Deinen Code richtig abzutippen
Es funktioniert noch immer nicht richtig
Hier nun der Projektquelltext:
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:
| program mutextest;
uses Forms, test in 'test.pas' , Windows, Messages;
{$R *.res}
const szUniqueId = 'MUTEXTEST_D311E2A0-3084-11D7-BD2C-FC698EE3C743'; var hm : THandle; aWnd : HWND; cpydata : TCopyDataStruct; begin hm := CreateMutex(nil,false,szUniqueId); if(GetLastError = ERROR_ALREADY_EXISTS) then begin aWnd := findwindow(nil,'TFormMutexTest');
if(paramcount = 1) and (aWnd <> 0) then begin cpydata.dwData := 0; cpydata.cbData := length(paramstr(1)) + 1; cpydata.lpData := @paramstr(1)[1]; SendMessage(aWnd,WM_COPYDATA,0,LPARAM(@cpydata)); end;
if(aWnd <> 0) then begin SendMessage(aWnd,WM_SYSCOMMAND,SC_RESTORE,0); SetForegroundWindow(aWnd); end;
Halt;
end;
Application.Initialize; Application.CreateForm(TFormMutexTest, FormMutexTest); Application.Run; CloseHandle(hm);
end. |
und hier der Rest:
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: 53: 54: 55: 56: 57: 58: 59: 60:
| unit test;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type TFormMutexTest = class(TForm) Label1: TLabel; Memo: TMemo; Button1: TButton; LMutex: TLabel; procedure Button1Click(Sender: TObject);
private procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA; public end;
var FormMutexTest: TFormMutexTest;
implementation
{$R *.dfm}
procedure TFormMutexTest.WMCopyData(var Msg: TWMCopyData); var szBuf : string; begin inherited;
SetLength(szBuf,Msg.CopyDataStruct^.cbData + 1); ZeroMemory(@szBuf[1],length(szBuf)); lstrcpyn(@szBuf[1], Msg.CopyDataStruct^.lpData, Msg.CopyDataStruct^.cbData);
if(szBuf <> '') and (Memo.Lines.IndexOf(szBuf) = -1) then Memo.Lines.Add(szBuf); end;
procedure TFormMutexTest.Button1Click(Sender: TObject); var cpydata : TCopyDataStruct; begin cpydata.dwData := 0; cpydata.cbData := length('Hallo, schitho') + 1; cpydata.lpData := pchar('Hallo, schitho');
SendMessage(self.Handle,WM_COPYDATA,0,LPARAM(@cpydata));
end;
end. |
Irgendwas stimmt da noch nicht 
_________________ (Sorry! Leider ewiger Delphi-Neuling)
|
|
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: So 07.09.03 16:02
| schitho hat folgendes geschrieben: | Es funktioniert noch immer nicht richtig  |
Und was heißt das genau? Was funktioniert nicht?
Ich sehe im Moment nur einen Fehler
Delphi-Quelltext 1:
| aWnd := findwindow(nil,'TFormMutexTest'); |
Laut Deklaration am Anfang deiner DPR-Datei ist "TFormMutexTest" der Klassenname - nicht der Fenstertitel. Der Klassenname ist der erste Parameter von "findwindow". Richtig, und das hast du in meinem Punkt #3 überlesen, ist also
Delphi-Quelltext 1:
| aWnd := findwindow('TFormMutextTest',nil); |
Gruß.
|
|
schitho 
      
Beiträge: 288
XP Home SP2
D2005 Prof
|
Verfasst: So 07.09.03 16:52
So ist es - peinlich, peinlich
Jetzt funktioniert es
Danke, Danke, Danke!!!!!!
_________________ (Sorry! Leider ewiger Delphi-Neuling)
|
|
webrage
Hält's aus hier
Beiträge: 14
|
Verfasst: Mo 08.09.03 11:33
Titel: hmmm ich hab das jetzt auch mal probiert...
das problem ist dabei aber das natürlich erst die daten gesendet werden können wenn die
Quelltext 1:
| aWnd := findwindow('TFormMutexTest',nil); |
die anwendung auch findet...
so wie es jetzt ist empfängt mein programm bei 3 markierten dateien nur 1 oder mal gar keine wenn die exe zu spät startet
|
|
webrage
Hält's aus hier
Beiträge: 14
|
Verfasst: Mo 08.09.03 12:41
Titel: ok habs :)
so geht es
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: 53: 54: 55: 56: 57:
| program mutextest;
uses Forms, test in 'test.pas' , Windows, Messages;
{$R *.res} const szUniqueId = 'MUTEXTEST_D311E2A0-3084-11D7-BD2C-FC698EE3C743'; var hm1,hm2 : THandle; aWnd : HWND; cpydata : TCopyDataStruct; begin
hm1 := CreateMutex(nil,false,szUniqueId);
if Not(GetLastError = ERROR_ALREADY_EXISTS) then begin Application.Initialize; Application.CreateForm(TFormMutexTest, FormMutexTest); Application.Run; end;
hm2 := CreateMutex(nil,false,szUniqueId); if (GetLastError = ERROR_ALREADY_EXISTS) then begin aWnd := findwindow('TFormMutexTest',nil);
while(paramcount = 1) and (aWnd=0) do begin aWnd := findwindow('TFormMutexTest',nil); Application.ProcessMessages; sleep(5); end;
if(paramcount = 1) and (aWnd <> 0) then begin cpydata.dwData := 0; cpydata.cbData := length(paramstr(1)) + 1; cpydata.lpData := @paramstr(1)[1]; SendMessage(aWnd,WM_COPYDATA,0,LPARAM(@cpydata)); end;
if(aWnd <> 0) then begin SendMessage(aWnd,WM_SYSCOMMAND,SC_RESTORE,0); SetForegroundWindow(aWnd); end;
Halt;
end; CloseHandle(hm1); CloseHandle(hm2); end. |
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: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88:
| unit test;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Registry;
type TFormMutexTest = class(TForm) Memo: TMemo; Button1: TButton; Button2: TButton; Button3: TButton; procedure Button1Click(Sender: TObject); procedure Button3Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure FormCreate(Sender: TObject);
private procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA; public end;
var FormMutexTest: TFormMutexTest; Reg : TRegistry; RegName,Title,Adress : String; implementation
{$R *.dfm}
procedure TFormMutexTest.FormCreate(Sender: TObject); begin RegName := 'TestMutex'; Title := 'Öffnen mit TestMutexTitle'; Adress := ExtractFilePath(Application.ExeName)+'mutextest.exe'+ ' %1'; IF (paramcount = 1) then Memo.Lines.Add(IntToStr(memo.Lines.Count+1)+': '+paramstr(1)); end;
procedure TFormMutexTest.WMCopyData(var Msg: TWMCopyData); var szBuf : string; begin inherited; SetLength(szBuf,Msg.CopyDataStruct^.cbData + 1); ZeroMemory(@szBuf[1],length(szBuf)); lstrcpyn(@szBuf[1], Msg.CopyDataStruct^.lpData, Msg.CopyDataStruct^.cbData); if(szBuf <> '') and (Memo.Lines.IndexOf(szBuf) = -1) then Memo.Lines.Add(IntToStr(memo.Lines.Count+1)+': '+szBuf); end;
procedure TFormMutexTest.Button1Click(Sender: TObject); begin reg:=tregistry.create(); Reg.RootKey:=HKEY_CLASSES_ROOT;
Reg.OpenKey('*\shell',true); Reg.CloseKey; Reg.OpenKey('*\shell\'+RegName,true); Reg.WriteString('',Title); Reg.CloseKey; Reg.OpenKey('*\shell\'+RegName+'\command',true); Reg.WriteString('',Adress); Reg.CloseKey; reg.free; end;
procedure TFormMutexTest.Button3Click(Sender: TObject); begin memo.Clear; end;
procedure TFormMutexTest.Button2Click(Sender: TObject); begin reg:=tregistry.create(); Reg.RootKey:=HKEY_CLASSES_ROOT; if Reg.OpenKey('*\shell',false) then begin Reg.DeleteKey(RegName); Reg.CloseKey; end; end;
end. |
Moderiert von Tino: Code- durch Delphi-Tags ersetzt.
|
|
schitho 
      
Beiträge: 288
XP Home SP2
D2005 Prof
|
Verfasst: Mo 08.09.03 21:52
Hi,
also bei mir funktionieren beide Versionen!
Nur wenn wenig System-Resourcen zur verfügung stehen, dann funktionieren (meist) auch beide Versionen nicht.
Getestet unter Window 98, Athlon 1 GHz und 512 MB RAM
Wie sieht deine Konfiguration aus?
_________________ (Sorry! Leider ewiger Delphi-Neuling)
|
|
webrage
Hält's aus hier
Beiträge: 14
|
Verfasst: Di 09.09.03 00:19
echt bei dir gehen beide manchmal nicht ???
theoritisch und eigentlich auch logischerweise müsste meine variante aber unabhängig der systemleistung arbeiten da lle mehrfach aufgerufenen instancen warten bis die erste vollständig geladen wurde...
vieleicht hat ja jemand anders ne idee ???
1. wieviele dateien hast du markiert ?
2. hast du es 1:1 übernommen oder etwas geändert?
512 MB Ram
1,2 Athlon
bei mir läuft es mit meiner Variante perfekt egal viele Dateien ich auswähle, die erste Variante hat immer den ersten Wert verschluckt...
logischerweise:
ich markiere 10 Dateien und sage öffnen mit xxx
1 öffnet und muss da bleiben paramstr(1) in die listbox schreiben
9 öffnen und müssen ihre paramstr an die 1 senden
|
|
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Di 09.09.03 10:01
| webrage hat folgendes geschrieben: | | bei mir läuft es mit meiner Variante perfekt egal viele Dateien ich auswähle, die erste Variante hat immer den ersten Wert verschluckt... |
Das ist dein Denkfehler! Für mich war und ist es vollkommen normal und darum überhaupt nicht erwähnenswert, dass das Programm seinen Parameter normalerweise im "OnCreate" bearbeitet
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7:
| procedure TForm1.FormCreate(Sender: TObject); begin if(paramcount = 1) then szFileName := paramstr(1);
end; |
(@schitho: Schau mal in den Code der UPX-Shell. Du müsstest etwas ähnliches im WM_INITDIALOG-Teil finden. ) Nur die Parameter jeder weiteren Instanz werden über WM_COPYDATA an die bereits laufende Anwendung übergeben. Und darum ist dein Codevorschlag a) umständlich, und b) auch mit einer potentiellen Bombe versehen
| Zitat: | Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| aWnd := findwindow('TFormMutexTest',nil);
while(paramcount = 1) and (aWnd=0) do begin aWnd := findwindow('TFormMutexTest',nil); Application.ProcessMessages; sleep(5); end; | |
Stell dir vor, dass aus irgendeinem Grund dein Programm hängen bleibt. Es erzeugt noch den Mutex (passiert ja im Projektquelltext) und will dann die Formulare erzeugen und laden, stürzt dabei jedoch ab. Das Hauptfenster (= Hauptform) ist noch nicht erzeugt, und jede weitere Instanz wartet durch die while-Schleife dann ewig.
Du solltest also entweder einen Zähler einbauen, der die Übergabe des Parameters nach x Versuchen abbricht. Oder du erhöhst den Sleep-Wert, oder du nimmst (wie ich) in Kauf, dass im ungünstigsten Fall evtl. ein paar Dateien nicht berücksichtigt werden, wenn die allererste Instanz nicht schnell genug lädt.
Letztlich hängt das aber auch wieder vom Programm ab. Wenn es seinerseits weitere Formulare erstellt oder andere zeitintensive Dinge beim Start ausführt, dann -ja!- dann hast du schon recht: es kann passieren, dass einige Dateien verloren gehen.
|
|
webrage
Hält's aus hier
Beiträge: 14
|
Verfasst: Di 09.09.03 11:02
| Zitat: | Für mich war und ist es vollkommen normal und darum überhaupt nicht erwähnenswert, dass das Programm seinen Parameter normalerweise im "OnCreate" bearbeitet
|
das für dich und für mich und für schitho klar ist kann ich mir denken, hast du aber vielleicht mal daran gedacht das weniger versierte user irgendwann mal vielleicht den code benutzen wollen und wir nicht davon ausgehen sollten das es sich jeder denken kann ???
| Zitat: | | Du solltest also entweder einen Zähler einbauen, der die Übergabe des Parameters nach x Versuchen abbricht. Oder du erhöhst den Sleep-Wert, oder du nimmst (wie ich) in Kauf, d |
da hast du natürlich recht, das kann passieren ...
ich werde also am besten noch einen zähler integrieren der nach x versuchen abbricht...
in meinem fall benötige ich nur die paramstr(1) in einer listbox daher ist das wenig rechenintensiv, ich habe aber leider keine möglichkeit das auf einem langsamen rechner zu testen, daher sollte man eine sicherheit (zähler) einbauen, da stimme ich uneingeschränkt zu 
|
|
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Di 09.09.03 12:51
| webrage hat folgendes geschrieben: | | Zitat: | Für mich war und ist es vollkommen normal und darum überhaupt nicht erwähnenswert, dass das Programm seinen Parameter normalerweise im "OnCreate" bearbeitet
|
das für dich und für mich und für schitho klar ist kann ich mir denken, hast du aber vielleicht mal daran gedacht das weniger versierte user irgendwann mal vielleicht den code benutzen wollen und wir nicht davon ausgehen sollten das es sich jeder denken kann ??? |
Das beweist mir, dass du dich noch nicht so richtig mit der Suchfunktion in diesem Forum beschäftigt hast. PARAMSTR und PARAMCOUNT bringen diverse Treffer, teilweise sogar mit Vorschlägen, wo man den Parameter am besten abfragt.
Abgesehen davon ist dein Denkansatz unglücklich. Du bist in diesen Beitrag gekommen und hast wahrscheinlich gedacht, WM_COPYDATA ist der einzig mögliche Weg, Parameter abzufragen bzw. zu übergeben.
Tatsächlich läuft es aber so ab: Man möchte, dass das eigene Programm Dateien als Parameter akzeptiert und kümmert sich dabei nicht um andere Sachen. Und dadurch geht man so vor, wie ich das als normal und üblich erachte - man prüft im "OnCreate", ob ein Parameter benutzt wurde und lädt ggf. die Datei.
Meist kommt erst dann die Idee: "Hey, das kann ich ja ins System einbauen, damit sich beim Klick auf XYZ-Dateien mein Programm öffnet." Gesagt, getan - funktioniert.
Und dann kommt der Gedanke, der auch schitho zu Beginn bewegt hat: "Moment mal, jetzt starten hier zig Instanzen des Programms. Wie kann ich das verhindern?"
Voilà.
Nichts für ungut, aber speziell die Sache mit dem Parameter ist für mich (wie ich schon schrieb) ein alter Hut und nicht erwähnenswert, weil´s der eigentlich typische Weg ist. Ich will nichts falsches behaupten, aber ich glaube, selbst in der Delphi-Hilfe ist ein entsprechendes Beispiel. Und, wie gesagt, du findest hier im Forum auch mehr als genug, in denen das vorgemacht wird. Das kann man also durchaus in Erfahrung bringen. So gesehen: warum soll ich´s also zum x-ten Mal erwähnen?
Gruß.
|
|
|