Entwickler-Ecke
Windows API - ContextMenuHandler
schitho - Mi 03.09.03 21:10
Titel: ContextMenuHandler
Hi,
ihr kennt sicher die Kontext-Menüerweiterungen von WinZip.
Genau soetwas möchte ich für mein Programm auch haben :D
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. :cry:
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 :wink: ) 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???
Delete - 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. :wink:
schitho - 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????
Delete - Mi 03.09.03 23:45
Ich dachte, du hast schon ein Programm von mir gekl... äh ... adaptiert. :wink: Die UPX UI gibt´s auch da.
schitho - Do 04.09.03 21:55
Hab mir Dein Programm angesehen. Puhhh! Ist das Delphi? :wink:
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:
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:
| 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 :cry: Ich glaub ich lern das nie :nixweiss:
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!
Delete - Do 04.09.03 22:36
:mrgreen:
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)
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? :)
Delete - Do 04.09.03 22:48
Noch eine andere Variante, gleich mit praktischem Beispiel:
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:
| 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 - 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 :(
schitho - Sa 06.09.03 22:39
Hab den Code nun geändert! Und er funktioniert trotzdem nicht :bawling:
Hier der Code:
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: 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....... :oops:
Delete - 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 - So 07.09.03 12:31
Hallo Matthias,
Danke für Deine Geduld 8)
Irgendwie bin ich aber scheinbar nicht mal fähig Deinen Code richtig abzutippen :autsch:
Es funktioniert noch immer nicht richtig :oops:
Hier nun der Projektquelltext:
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:
| 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:
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: 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 :cry:
Delete - So 07.09.03 16:02
| schitho hat folgendes geschrieben: |
| Es funktioniert noch immer nicht richtig :oops: |
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 - So 07.09.03 16:52
So ist es - peinlich, peinlich :oops:
Jetzt funktioniert es :D
Danke, Danke, Danke!!!!!!
webrage - 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 - Mo 08.09.03 12:41
Titel: ok habs :)
so geht es :D
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: 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. |
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: 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 - 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?
webrage - 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
Delete - 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 - 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 :)
Delete - 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à. :wink:
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ß.
webrage - Di 09.09.03 13:48
Titel: man man
du bist ja nen toller :)
| Zitat: |
| du dich noch nicht so richtig mit der Suchfunktion in diesem Forum beschäftigt hast. |
wer lesen kann ist klar im vorteil oder ???
es geht doch dabei nicht um mich ?! ich habe doch den quelltext dazu gepostet wie man es machen kann wozu soll ich da die suche benutzen ?
| Zitat: |
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.
|
und wieder falsch :lol: ich habs bisher mit den DDE's gemacht das war mir zu langsam und aus diesem grund wollte ich es damit probieren...
wenn du so grantig bist weil du dich angeriffen fühlst tut es mir leid, ich kann dir versichern ich wollte dich in keiner weise beleidigen ...
UGrohne - Di 09.09.03 13:54
Titel: Re: man man
| webrage hat folgendes geschrieben: |
| wenn du so grantig bist weil du dich angeriffen fühlst tut es mir leid, ich kann dir versichern ich wollte dich in keiner weise beleidigen ... |
Ich konnte nicht feststellen, dass sich Mathias in irgendeiner Weise angegriffen fühlte. Lasst am besten solche Gefühlssachen raus, das gibt nur Ärger :wink:
webrage - Di 09.09.03 14:00
Titel: ok
ich war mir nicht sicher... darum habe ich mich schonmal vorsichtshalber entschuldigt :)
Delete - Di 09.09.03 18:16
Kein Problem. Ich nehm so was nicht krumm. Es soll ja schließlich jeder seine Meinung sagen dürfen, und ich habe die Allwissenheit nicht gepachtet ... noch nicht ... *g*
Aber nebenbei: Ja, ich bin ein ganz Toller. :)
schitho - Di 09.09.03 22:42
Hab noch eine Frage zu
Delphi-Quelltext
1: 2:
| const szUniqueId = 'MUTEXTEST_D311E2A0-3084-11D7-BD2C-FC698EE3C743'; |
Wie erstellt man eigentlich die UniqueId. Hat die ein bestimmtes Format?
Delete - Di 09.09.03 23:17
Kannst du dir selbst ausdenken. Ist ein ganz normaler String. Ich nehme meist den Namen der Anwendung und drücke dann Shift+Strg+G und habe ´ne GUID, die ich anhänge (ohne die geschweiften Klammern) - das ist eigentlich immer recht eindeutig ... um nicht einzigartig zu sagen. :D
Kurz gesagt: so gibt´s keine Probleme mit den Mutexen anderer Anwendungen.
schitho - Di 09.09.03 23:35
Cool. 8)
Die Tastenkombination Shift+Strg+G kannte ich noch nicht.
Nochmals Danke!
Delete - Mi 10.09.03 08:25
Ach so, damit nicht wieder einer kommt und sagt: "Hey, du musst solche Sachen auch für weniger versierte Leute erwähnen" ... :wink: ...
Es ist hoffentlich klar, dass die gewählte Bezeichnung in jeder Version deines Programms immer identisch bleiben muss. Will sagen: du hast jetzt die Version 1.0 von deinem Programm, dann schreibst du die 1.1 -> 1.5 -> 2.0 -> ...
Jede neue Version sollte den selben Mutex und am besten auch den gleichen Klassennamen (= Form-Name in dem Fall) verwenden. Ich benutze solche Spielereien eigentlich hauptsächlich, um das bereits geladene Fenster in den Vordergrund zu holen, wenn jemand versucht, das selbe Programm noch mal zu starten. Und dazu brauchst du ja den Mutex (zum Herausfinden, ob das Programm schon läuft) und das Fenster-Handle (zum in den Vordergrund holen).
Oder ich habe mal ein Konkurrenzprogramm damit ausgeschaltet und gehässigerweise dessen Mutex zusätzlich zu meinem registriert, wodurch es nicht mehr gestartet werden konnte. :twisted:
Na ja, das übliche eben ... *g*
webrage - Mi 10.09.03 09:44
Titel: lol
so was freches ...
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!