Autor Beitrag
schitho
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 288

XP Home SP2
D2005 Prof
BeitragVerfasst: 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 :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???

_________________
(Sorry! Leider ewiger Delphi-Neuling)
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 288

XP Home SP2
D2005 Prof
BeitragVerfasst: 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



BeitragVerfasst: 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.


Zuletzt bearbeitet von MathiasSimmack am Do 04.09.03 22:34, insgesamt 1-mal bearbeitet
schitho Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 288

XP Home SP2
D2005 Prof
BeitragVerfasst: 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:

ausblenden volle Höhe 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
 // Mutex erzeugen
  hm := CreateMutex(nil,false,szUniqueId);
  if(GetLastError = ERROR_ALREADY_EXISTS) then begin
    // Fenster-Handle der bereits aktiven Instanz holen
    aWnd := findwindow(nil,'UPX UI 2003');

    // den Parameter (= Datei) an die aktive Instanz senden!
    if(paramcount = 1and
      (fileexists(paramstr(1))) and
      (aWnd <> 0then
    begin
      cpydata.dwData := 0;
      cpydata.cbData := length(paramstr(1)) + 1;
      cpydata.lpData := @paramstr(1)[1];

      SendMessage(aWnd,WM_COPYDATA,0,LPARAM(@cpydata));
    end;

    // diese laufende Instanz in den Vordergrund holen
    if(aWnd <> 0then begin
      SendMessage(aWnd,WM_SYSCOMMAND,SC_RESTORE,0);
      SetForegroundWindow(aWnd);
    end;

    // und den Mehrfachstart unterbinden!
    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:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
(* Nachricht von einer anderen Instanz *)
    WM_COPYDATA:
      begin
        // Dateinamen aus dem übermittelten Puffer lesen, ...
        ZeroMemory(@buf,sizeof(buf));
        lstrcpyn(buf,PCopyDataStruct(lp)^.lpData,PCopyDataStruct(lp)^.cbData);

        // & ggf. in die Liste einfügen
        if(buf[0] <> #0and
          (SendDlgItemMessage(hwndDlg,IDC_FILELIST,LB_FINDSTRINGEXACT,-1,
            LPARAM(@buf)) = LB_ERR) and
          (lstrcmpi(buf,@paramstr(0)[1]) <> 0then
        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!

_________________
(Sorry! Leider ewiger Delphi-Neuling)
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: 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.
ausblenden 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
ausblenden Delphi-Quelltext
1:
2:
ZeroMemory(@buf,sizeof(buf));
lstrcpyn(buf,PCopyDataStruct(lp)^.lpData,PCopyDataStruct(lp)^.cbData);

Wenn das erste Zeichen ungleich Null ist (pchar)
ausblenden Delphi-Quelltext
1:
if(buf[0] <> #0and					

dann wird geguckt, ob´s den String evtl. schon in meiner Liste gibt
ausblenden 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
ausblenden Delphi-Quelltext
1:
  (lstrcmpi(buf,@paramstr(0)[1]) <> 0then					

dann (und nur dann!) wird er in die Listbox eingefügt
ausblenden 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



BeitragVerfasst: Do 04.09.03 22:48 
Noch eine andere Variante, gleich mit praktischem Beispiel:
ausblenden volle Höhe 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 288

XP Home SP2
D2005 Prof
BeitragVerfasst: 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?

ausblenden 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); //funktioniert nicht
  ShowMessage(buf); // funktioniert
end;


Ich bekomm zwar keine Fehlermeldung, allerdings wird im Memofeld auch nicht angezeigt. Versteh ich nicht :(

_________________
(Sorry! Leider ewiger Delphi-Neuling)
schitho Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 288

XP Home SP2
D2005 Prof
BeitragVerfasst: Sa 06.09.03 22:39 
Hab den Code nun geändert! Und er funktioniert trotzdem nicht :bawling:

Hier der Code:

ausblenden volle Höhe 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
    { Private-Deklarationen }
    protected 
    procedure WndProc(var Message: TMessage); override

  public
    { Public-Deklarationen }
  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
    // Dateinamen aus dem übermittelten Puffer lesen, ...
        ZeroMemory(@buf,sizeof(buf));
        lstrcpyn(buf,PCopyDataStruct(Message.LParam)^.lpData,PCopyDataStruct(Message.LParam)^.cbData);

        // & ggf. in die Liste einfügen
        if(buf[0] <> #0and
          (SendDlgItemMessage(self.Handle,Memo.ControlCount,LB_FINDSTRINGEXACT,-1,
            LPARAM(@buf)) = LB_ERR) and
          (lstrcmpi(buf,@paramstr(0)[1]) <> 0then
        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;
  // Mutex erzeugen
  hm:=0;
  hm := CreateMutex(nil,false,szUniqueId);
  if(GetLastError = ERROR_ALREADY_EXISTS) then begin
    // Fenster-Handle der bereits aktiven Instanz holen
    aWnd := findwindow(nil,'Test');

    // den Parameter (= Datei) an die aktive Instanz senden!
    if(paramcount = 1and (aWnd <> 0then
    begin
      cpydata.dwData := 0;
      cpydata.cbData := length(paramstr(1)) + 1;
      cpydata.lpData := @paramstr(1)[1];
      SendMessage(aWnd,WM_COPYDATA,0,LPARAM(@cpydata));
    end;

    // diese laufende Instanz in den Vordergrund holen
    if(aWnd <> 0then begin
      SendMessage(aWnd,WM_SYSCOMMAND,SC_RESTORE,0);
      SetForegroundWindow(aWnd);
    end;

    // und den Mehrfachstart unterbinden!
    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:

_________________
(Sorry! Leider ewiger Delphi-Neuling)
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: 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.
ausblenden 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;

  // hier kommt der ganze Form-bezogene Code
  Application.Initialize;
  { ...}


  // am Schluss: nicht vergessen -->
  CloseHandle(hm);
end;



2. Die Variante mit einem String anstelle eines Char-Puffers. Um vorzugreifen: ich hab´s getestet, und es funktioniert.
ausblenden 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) = -1then
    Memo1.Lines.Add(szBuf);
end;



3. Eine Anmerkung noch dazu:
schitho hat folgendes geschrieben:
ausblenden 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
ausblenden 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
ausblenden Delphi-Quelltext
1:
aWnd := findwindow('TFormTest',nil);					


Gruß.
schitho Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 288

XP Home SP2
D2005 Prof
BeitragVerfasst: 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:

ausblenden volle Höhe 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' {FormMutexTest},
  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');

    // den Parameter (= Datei) an die aktive Instanz senden! 
    if(paramcount = 1and (aWnd <> 0then 
    begin 
      cpydata.dwData := 0
      cpydata.cbData := length(paramstr(1)) + 1
      cpydata.lpData := @paramstr(1)[1]; 
      SendMessage(aWnd,WM_COPYDATA,0,LPARAM(@cpydata));
    end

    // diese laufende Instanz in den Vordergrund holen 
    if(aWnd <> 0then begin 
      SendMessage(aWnd,WM_SYSCOMMAND,SC_RESTORE,0); 
      SetForegroundWindow(aWnd); 
    end

    // und den Mehrfachstart unterbinden! 
    Halt; 

  end;

  Application.Initialize;
  Application.CreateForm(TFormMutexTest, FormMutexTest);
  Application.Run;
  CloseHandle(hm); 

end.


und hier der Rest:

ausblenden volle Höhe 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
    { Private-Deklarationen }
    procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA;
  public
    { Public-Deklarationen }
  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) = -1then
    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:

_________________
(Sorry! Leider ewiger Delphi-Neuling)
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: 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
ausblenden 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
ausblenden Delphi-Quelltext
1:
aWnd := findwindow('TFormMutextTest',nil);					

Gruß.
schitho Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 288

XP Home SP2
D2005 Prof
BeitragVerfasst: So 07.09.03 16:52 
So ist es - peinlich, peinlich :oops:

Jetzt funktioniert es :D

Danke, Danke, Danke!!!!!!

_________________
(Sorry! Leider ewiger Delphi-Neuling)
webrage
Hält's aus hier
Beiträge: 14



BeitragVerfasst: 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

ausblenden 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



BeitragVerfasst: Mo 08.09.03 12:41 
Titel: ok habs :)
so geht es :D

ausblenden volle Höhe 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' {FormMutexTest},
  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);

   // warten bis Anwendung vorhanden ist
       while(paramcount = 1and (aWnd=0do begin
        aWnd := findwindow('TFormMutexTest',nil);
        Application.ProcessMessages;
        sleep(5);
       end;

    // den Parameter (= Datei) an die aktive Instanz senden!
    if(paramcount = 1and (aWnd <> 0then begin
      cpydata.dwData := 0;
      cpydata.cbData := length(paramstr(1)) + 1;
      cpydata.lpData := @paramstr(1)[1];
      SendMessage(aWnd,WM_COPYDATA,0,LPARAM(@cpydata));
    end;

    // diese laufende Instanz in den Vordergrund holen
    if(aWnd <> 0then begin
      SendMessage(aWnd,WM_SYSCOMMAND,SC_RESTORE,0);
      SetForegroundWindow(aWnd);
    end;

    // und den Mehrfachstart unterbinden!
    Halt;

end;
    CloseHandle(hm1);
    CloseHandle(hm2);
end.


ausblenden volle Höhe 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
    { Private-Deklarationen }
    procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA; 
  public
    { Public-Deklarationen }
  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 = 1then 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) = -1then Memo.Lines.Add(IntToStr(memo.Lines.Count+1)+': '+szBuf);
end

procedure TFormMutexTest.Button1Click(Sender: TObject);
begin
reg:=tregistry.create();
Reg.RootKey:=HKEY_CLASSES_ROOT;

  // Dateien ins Context Menü
  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); // adress="c:\test\meinprogramm.exe %1"
  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;
  // Dateien aus Context Menü entfernen
   if Reg.OpenKey('*\shell',false) then begin
    Reg.DeleteKey(RegName);
    Reg.CloseKey;
   end;
end;

end.


Moderiert von user profile iconTino: Code- durch Delphi-Tags ersetzt.
schitho Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 288

XP Home SP2
D2005 Prof
BeitragVerfasst: 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



BeitragVerfasst: 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



BeitragVerfasst: 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
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
procedure TForm1.FormCreate(Sender: TObject);
begin
  if(paramcount = 1then
    szFileName := paramstr(1);

  // usw.
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:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
   aWnd := findwindow('TFormMutexTest',nil);

   // warten bis Anwendung vorhanden ist
       while(paramcount = 1and (aWnd=0do 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



BeitragVerfasst: 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



BeitragVerfasst: 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. Suche in: Delphi-Forum, Delphi-Library PARAMSTR und Suche in: Delphi-Forum, Delphi-Library 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ß.