AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Win32/Win64 API (native code) Delphi Problem mit Dateiaufruf per paramstr
Thema durchsuchen
Ansicht
Themen-Optionen

Problem mit Dateiaufruf per paramstr

Ein Thema von wb32 · begonnen am 7. Aug 2003 · letzter Beitrag vom 23. Sep 2003
Antwort Antwort
Seite 2 von 5     12 34     Letzte »    
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#11

Re: Problem mit Dateiaufruf per paramstr

  Alt 7. Aug 2003, 19:41
Hier also die lauffähige Version. Einfach diese Unit in das Projekt einbinden und die Methode TSingleInstance.OnStartUp mit eigenen Leben füllen. Nicht vergessen den Wert sTitle mit einem eigenen eindeutigen Wert zu ändern.

Gruß Hagen

Delphi-Quellcode:
unit SingleInstance;

interface

implementation

uses Windows, SysUtils, Controls, Messages, Dialogs, Forms;

type
  TSingleInstance = class
    class procedure WndProc(var Msg: TMessage);
    class procedure Start;
    class procedure Stop;
    class function GetParamStr(P: PChar; var Param: string): PChar;
    class function ParamCount: Integer;
    class function ParamStr(Index: Integer): string;

    class procedure OnStartup;
  end;

const
  sTitle = 'my_ProgramXYZ$123456789'; // dieser Wert MUSS individuell angepasst werden

class procedure TSingleInstance.OnStartup;
// diese Methode muß mit eigenen Inhalt gefüllt werden,
// als Beispiel wird hier die 1. Instance sichtbar gemacht
// und der ParamStr() der 2. Instance angezeigt.
var
  S: String;
  I: Integer;
begin
  Application.Minimize;
  Application.Restore;

  S := '';
  for I := 0 to ParamCount do
    S := S + ParamStr(I) + #10;
  ShowMessage(S);
end;

// ab hier Implementierung

const
  cMagic = $BADF00D; // dient zur Idententifizierung der Message wm_CopyData
  cResult = $DAED;

var
  WndHandle: hWnd = 0; // die 1. Instance erzeugt ein Fensterhandle
  CmdLine: PChar = nil; // ParamStr() der 2. Instance per wm_CopyData transportiert

class function TSingleInstance.GetParamStr(P: PChar; var Param: string): PChar;
// diese funktion musste aus System.pas kopiert werden für unser
// ParamStr() udn ParamCount() nötig
var
  Len: Integer;
  Buffer: array[0..4095] of Char;
begin
  while True do
  begin
    while (P[0] <> #0) and (P[0] <= ' ') do Inc(P);
    if (P[0] = '"') and (P[1] = '"') then Inc(P, 2) else Break;
  end;
  Len := 0;
  while (P[0] > ' ') and (Len < SizeOf(Buffer)) do
    if P[0] = '"then
    begin
      Inc(P);
      while (P[0] <> #0) and (P[0] <> '"') do
      begin
        Buffer[Len] := P[0];
        Inc(Len);
        Inc(P);
      end;
      if P[0] <> #0 then Inc(P);
    end else
    begin
      Buffer[Len] := P[0];
      Inc(Len);
      Inc(P);
    end;
  SetString(Param, Buffer, Len);
  Result := P;
end;

class function TSingleInstance.ParamCount: Integer;
// diese Funktion musste aus System.pas kopiert werden für unser
// ParamStr() und ParamCount() nötig da System.pas NICHT auf die
// globale Variable System.CmdLine zugreift sondern per Funktion GetCommandLine() arbeitet.
var
  P: PChar;
  S: string;
begin
  P := GetParamStr(CmdLine, S); // CmdLine statt GetCommandLine
  Result := 0;
  while True do
  begin
    P := GetParamStr(P, S);
    if S = 'then Break;
    Inc(Result);
  end;
end;

class function TSingleInstance.ParamStr(Index: Integer): string;
// siehe ParamCount
var
  P: PChar;
  Buffer: array[0..260] of Char;
begin
  if Index = 0 then
    SetString(Result, Buffer, GetModuleFileName(0, Buffer, SizeOf(Buffer)))
  else
  begin
    P := CmdLine; // CmdLine statt GetCommandLine
    while True do
    begin
      P := GetParamStr(P, Result);
      if (Index = 0) or (Result = '') then Break;
      Dec(Index);
    end;
  end;
end;

class procedure TSingleInstance.WndProc(var Msg: TMessage);
// das ist die Fensterprocedure von WndHandle, sie empfängt innerhalb
// der 1. Instance die wm_CopyData Message mit der CommandLine der
// 2. Instance
begin
  with Msg do
    if (Msg = wm_CopyData) and (PCopyDataStruct(lParam).dwData = cMagic) then
    begin
      Result := cResult;
      CmdLine := PCopyDataStruct(lParam).lpData;
      OnStartup;
    end else Result := DefWindowProc(WndHandle, Msg, wParam, lParam);
end;

class procedure TSingleInstance.Start;
var
  PrevWnd: hWnd;
  Data: TCopyDataStruct;
begin
  if MainInstance = GetModuleHandle(nil) then // nur in EXE's möglich, nicht in DLL's oder packages
  begin
    PrevWnd := FindWindow('TPUtilWindow', sTitle); // suche unser Fenster
    if IsWindow(PrevWnd) then
    begin
    // 1. Instance läuft also schon, sende CommandLine an diese
      Data.dwData := cMagic;
      Data.cbData := StrLen(GetCommandLine) +1;
      Data.lpData := GetCommandLine;
      if SendMessage(PrevWnd, wm_CopyData, 0, Integer(@Data)) = cResult then Halt;
    end;
   // keine 1. Instance gefunden, wir sind also die 1. Instance
    WndHandle := AllocateHWnd(WndProc);
    SetWindowText(WndHandle, sTitle);

// falls auch bei der 1. Instance OnStartup aufgerufen werden soll
// CmdLine := System.CmdLine;
// OnStartup;
  end;
end;

class procedure TSingleInstance.Stop;
begin
  if IsWindow(WndHandle) then DeallocateHWnd(WndHandle);
end;

initialization
  TSingleInstance.Start;
finalization
  TSingleInstance.Stop;
end.
  Mit Zitat antworten Zitat
wb32

Registriert seit: 4. Jul 2003
143 Beiträge
 
#12

Re: Problem mit Dateiaufruf per paramstr

  Alt 7. Aug 2003, 22:43
tut mir leid das is mir zu hoch kann sich mal jemand meinen source ansehen bitte... es funktioniert einwandfrei ausser das beim zuerst gestarteten programm der falsche paramstr ankommt...


Code:

procedure TForm1.WndProc(var msg : TMessage);
begin
  if msg.Msg = dwMessage then begin
    if msg.lParam <> 0 then begin
      m1.Lines.Add(PChar(msg.lParam));
    end else begin
      if msg.WParam > -1 then begin
        m1.Lines.Add(IntToStr(msg.wParam));
      end else begin
         m1.Lines.Add('Keine Daten...');
      end;
    end;
  end else begin
    inherited WndProc(msg);
  end;
end;

function FindSyncDemo(const p_hWindow : DWORD;const p_szCaption : PChar) : Boolean; stdcall;
var
  dwLen : DWORD;
  szWork : PChar;
begin
  Result := true;
  dwLen := SendMessage(p_hWindow,WM_GETTEXTLENGTH,0,0) + 1;
  if Length(p_szCaption) <> (dwLen-1) then exit;
  szWork := AllocMem(dwLen);

 try
   SendMessage(p_hWindow,WM_GETTEXT,dwLen,lParam(szWork));
    if StrLComp(szWork,p_szCaption,dwLen) = 0 then begin
     Form1.m1.Lines.Add('Sende '+Pfad);
     SendMessage(p_hWindow, dwMessage,0, lParam(Pfad));
    end;
 finally
    FreeMem(szWork,dwLen);
  end;
end;

procedure TForm1.FormShow(Sender: TObject);
var
FName: String;
begin
FName:='myprogramm';

if ParamCount > 0 then begin
 Pfad:= paramstr(1);
 Form1.Caption:= FName;

 mHandle  := CreateMutex(nil,True, 'FKILLER-36545tf456-34tf34tf23-23r3');

 if GetLastError=ERROR_ALREADY_EXISTS then begin
  Form1.Caption:= 'xxxxxxxxxxxxxxxxxxxx';

  // jetzt paramstr(1) an exe1 senden
  dwMessage := RegisterWindowMessage(3654-5tf-456-34tf3-4tf23);
  EnumWindows(@FindSyncDemo,integer(FName));

 end;
end;
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#13

Re: Problem mit Dateiaufruf per paramstr

  Alt 7. Aug 2003, 23:18
Zitat:
tut mir leid das is mir zu hoch
Hast du überhaupt mal versucht meinen Vorschlag zu testen und zu begreifen ?

In deinem Source gibt es gleich mehrere Probleme.
1.) CreateMutex() kann fehlschlagen wenn unter Win9x der Prozess der diesen Mutex angelegt hat gecrasht ist. Dann besteht dieser Mutex im System weiterhin obwohl es diesen Prozess nicht mehr gibt. Normalerweise würde eine Anwendung die auf CreateMutex() aufbaut dann nicht mehr starten. In deinem Falle würde die 1. Instance dann per EnumWindow() nach dem Fenster suchen, aber keines finden. Somit ist der CreateMutex() Aufruf sinnlos.
2.) Benötigt man zwecks Datenaustausch von ParamStr() ein Fensterhandle dann kann auch CreateMutex() verzichtet werden.
3.) Daten die von einem Prozess zu einem anderen Prozess, über Fensterhandles, ausgetauscht werden sollen müssen per wm_CopyData gesendet werden. wm_CopyData stellt sicher das der Speicherbereich in TCopyDataStruct aus der Gültigkeit des sendenden Prozesses auch gültig im empfangenden Prozess ist. Deshalb wird wm_CopyData immer NUR mit SendMessage() benutzt. Und deshalb funktioniert dein SendMessage() eben nicht.
4.) Dein EnumWindows() kann leicht durch FindWindow() ersetzt werden.
Statt also mit EnumWindows() über alle TopLevel Fenster zu iterieren reicht FindWindow(ClassName, WindowText) völlig aus.
5.) die Überprüfung in .FormShow() und das Erzeugen des Mutex ist an dieser Stelle denkbar ungünstig. Der beste Zeitpunkt, und auch der frühestmögliche, ist innerhalb einer Unit-Initialization. Wird meine obige Unit im Projektquelltext an 1. Stelle eingefügt dann wird sie noch vor der Erstellung jeglicher Forms ausgeführt. Damit würde also die 2. Instance der Anwendung erst garnicht langwierige Initialisierungen durchführen.

Aber im Grunde macht mein Vorschlag genau das was du versucht zu erreichen.
1.) TSingleInstance.Start sucht im System nach einem Fenster vom Typ "TPUtilWindow" + Caption "my_programXYZ12345". Dies erhöt die Sicherheit.
2.) wenn es gefunden wird wird der aktuelle ParamStr() = GetCommandLine an dieses Fenster per wm_CopyData gesendet. Wir senden also nicht nur ParamStr(1) sondern die komplette Kommandozeile.
3.) sollte das Fenster korrekt antworten so wird die aktuelle Anwendung angehalten und terminiert
4.) sollte kein Fenster "PrevWnd" gefunden worden sein, oder das Fenster hat nicht korrekt geantwortet dann wird die aktuelle Anwendung zur 1. Instance. Sie erzeugt ein Fenster vom Typ "TPUtilWindow" + Caption "my_programxyz123445"
5.) TSingleInstance.WndProc() wartet nun auf die Message wm_CopyData die durch eine 2. Instance der Anwendung aufgerufen wird. Punkte 1.) bis 3.) oben.
6.) sollte TSingleInstance.WndProc() eine gültige wm_CopyData Message erhalten so setzt sie die globale Variable CmdLine auf die Daten die per wm_CopyData empfangen wurde. CmdLine entspricht in diesem Moment der Kommandozeile der zweiten Instance der Anwendung.
7.) nun wird TSingleInstance.OnStartup() aufgerufen, die DEINEN Code enthalten sollte. Innerhalb .OnStartup() kannst du über die Methoden .ParamCount und .ParamStr() auf die Kommandozeile der zweiten Anwendung zugreifen.
8.) TSingleInstance.Stop wird bei der Terminierung der Anwendung aufgerufen und zerstört unser Helperfenster.

So was ist daran kompliziert ? besonders weil es auch sauberer und lesbarer Source ist !

D.h. für dich:

1.) füge Unit Singleinstance zu deinem Projekt hinzu
2.) ändere den Wert von sTitle auf einen eindeutigen String
3.) ändere den Code in .OnStartup so wie du ihn brauchst. Innerhalb von .OnStartup nutzt du ParamCount und ParamStr() so als würdest du ganz normal damit wie in deiner Anwendung arbeiten.

Das wars.

Gruß Hagen
  Mit Zitat antworten Zitat
wb32

Registriert seit: 4. Jul 2003
143 Beiträge
 
#14

Re: Problem mit Dateiaufruf per paramstr

  Alt 8. Aug 2003, 10:47
Sooo danke noch mal für die Hilfe gestern abend, heute morgen usw *gg*...

war gar nich so schwer wie ich dachte aber manchmal muss man nach stundenlangem verzweifeln einfach ne Pause machen und dann später nochmal schauen...

soweit so gut jetzt taucht das nächste problem auf ...

wenn ich mit deiner Unit alle dateien fein nacheinander auswäle und öffne dann geht es wunderbar, wenn ich aber 3 markiere und sage mit myprogramm öffnen dann öffnet sich myprogramm 3x ...

woran könnte das liegen ?
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#15

Re: Problem mit Dateiaufruf per paramstr

  Alt 8. Aug 2003, 11:13
Jay, jetzt's wirds kritisch. Das lässt sich so ohne weiteres nicht ändern, leider. Es liegt an der Vorgehensweise wie die Shell = Explorer eine Anwendung startet. Sie hat die 3 Dateien und erzeugt mit CreateProcess() 3 mal deinen Prozess. ABER! sie gibt diesen 3 neuen Prozessen erst dann eine Chance anzulaufen wenn alle 3 Prozesse schon erzeugt wurden. Deshalb ist es enorm wichtig den "Einmal-Startcode" so früh und so effizient wie möglich einzubauen. Leider bewirkt die Benutzung von Fensterhandles in unserem "Einmal-Startcode" das auch die beiden anderen Prozesse Rechenzeit bekommen, noch bevor überhaupt ein Fensterhandle vollständig erzeugt wurde.
Man kann dies ändern, aber das wird vom Verständnis echt komplizierter. Dafür müssen wir auf Semaphores und Interprozesskommunikation ausweichen. D.h. alle 3 Prozesse laufen korrekt an, wissen aber welche die 1. 2. und 3. Instance ist. Wenn die 1. Instance ordentlich läuft senden die zwei nachfolgenden Prozesse ihre Parameter an die 1. Instance und terminieren. D.h. falls die 2 nachfolgenden Prozesse anhand der Semaphore bemerken das sie nicht die 1. Instance sind müssen die solange warten bis sie mit der 1. Instance kommunizieren können.

Allerdings, irgendwie verstehe ich dein Problem noch nicht so recht. Wenn ich mich richtig erinnere kann man den Registryschlüssel für ShellEx/Commmand so anlegen das der Explorer multiple Files an EINE Instance der Anwendung sendet. Er startet dann nur eine Instance und übergibt alle Files als Params.

Ansonsten, gibts noch den Weg über COM Interfaces ein eigenes Shell-Interface zu programmieren. Statt dann über ParamStr() und ein EXE zu gehen, codet man eine DLL als Handler. In dieser kann über die Shellextension direkt auf die ausgewählte Dateiliste im Explorer zugegriffen werden. Bei Delphi sind zwei Beispiel dafür vorhanden IShellContextMenu und IShellExecuteHook.

Gruß Hagen
  Mit Zitat antworten Zitat
wb32

Registriert seit: 4. Jul 2003
143 Beiträge
 
#16

Re: Problem mit Dateiaufruf per paramstr

  Alt 8. Aug 2003, 11:27
Zitat:
Allerdings, irgendwie verstehe ich dein Problem noch nicht so recht. Wenn ich mich richtig erinnere kann man den Registryschlüssel für ShellEx/Commmand so anlegen das der Explorer multiple Files an EINE Instance der Anwendung sendet. Er startet dann nur eine Instance und übergibt alle Files als Params.

????????????????????????????????????????????


das geht ? dann hätten wir uns die Aktion doch sparen können ich habe doch mehrmals geschrieben wofür ich das benutzen will

wie geht das mit der einstellung für multiple files ?
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#17

Re: Problem mit Dateiaufruf per paramstr

  Alt 8. Aug 2003, 11:42
Zitat:
das geht ? dann hätten wir uns die Aktion doch sparen können ich habe doch mehrmals geschrieben wofür ich das benutzen will
Jo ich weiß aber dann hätten wir nicht diese schöne Exkursion machen können.

Zitat:
wie geht das mit der einstellung für multiple files ?
Shit, jetzt haste mich erwischt. Da muß ich selber erstmal nachschauen. Ich glaube etwa so "C:\MyProgram.EXE %1 %2 %3" oder so ähnlich. Wichtig dabei waren die doppelten Anführungsstriche.

Vielleicht weiß ja ein anderer DP'ler schnell die Antwort.

Gruß Hagen
  Mit Zitat antworten Zitat
wb32

Registriert seit: 4. Jul 2003
143 Beiträge
 
#18

Re: Problem mit Dateiaufruf per paramstr

  Alt 8. Aug 2003, 11:48
Zitat:
Jo ich weiß aber dann hätten wir nicht diese schöne Exkursion machen können.
stimmt war sehr lehrreich *gg*...


Zitat:
Shit, jetzt haste mich erwischt
wurde ja auch langsam Zeit

Zitat:
C:\MyProgram.EXE %1 %2 %3"
für jedes file ein %x ??? nee oder ?

also ich hab schon in der msdn gesucht bin aber nich so wirklich fündig geworden
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#19

Re: Problem mit Dateiaufruf per paramstr

  Alt 8. Aug 2003, 11:59
Ich habe mal auf die schnelle meine Registry gescannt.
Es gäbe den Weg über DDE, ein sehr alter Weg, oder eben über die Shell Extensionen.

Lass mir ein bißchen Zeit, am Wochenende werde ich mal TSingleInstance erweitern um die Semaphores, eventuell mit Memory Mapped Files + Events in Threads. Auf jeden Fall habe ich noch nie einen absolut sauber arbeitenden Single Instance Code gefunden.

Zur Zeit muß ich erstmal Geld verdienen.

Gruß Hagen
  Mit Zitat antworten Zitat
wb32

Registriert seit: 4. Jul 2003
143 Beiträge
 
#20

Re: Problem mit Dateiaufruf per paramstr

  Alt 8. Aug 2003, 12:03
hmm falls jemand anderes weiß wie man mutltiple files an eine instance senden kann bitte trotzdem posten mal schauen ob wir was finden und was letztendlich dann der beste weg ist

@Hagen danke erstmal, mal schauen ob du es hinbekommst
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 5     12 34     Letzte »    


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:27 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz