Przechwytywanie parametrów wysłanych do innej instancji

0

Witam.

Mam mały problem z przechwytywaniem parametrów do programu.
Mianowicie, utworzyłem odpowiedni wpis w rejestrze Windows: HCR\MojProgram\shell\open\command -> "C:\MojProgram.exe" "%1"; dla poprawnego skojarzenia pliku z aplikacją.
Następnie otwieram Windows Explorer, zaznaczam 3 pliki, których rozszerzenie zadeklarowałem we wpisie w rejestrze, klikam ENTER i uruchamiają się 3 instancje programów.

Zapobieganie uruchomieniu kolejnej instancji obsłużyłem za pomocą MUTEX'ów, natomiast przekazane parametry wysyłam komunikatem do już istniejącej (pierwszej) instancji.
Możnaby powiedzieć "program działa, jest dobrze, ale mogłoby być lepiej".

Teraz moje pytanie... co zrobić, aby wywołane 3 pliki z Windows Explorer'a były przekazane bezpośrednio do programu pierwszego, tzn. aby uruchomiła się tylko i wyłącznie jedna instancja, a deklaracja MUTEX'ów oraz wysyłania komuniktów była zbędna.
Przykładowo, tak jak AIMP, BESTplayer czy WINAMP otwiera pliki mp3 (po wywołaniu 100 plików, nie uruchamia 100 instancji programu, nie przekazują one parametrów komunikatami, tylko jedna instancja zczytuje wszystkie 100 parametrów pętlą)?

Udało mi się wyczytać, że można to zrobić za pomocą DDE? Jednak nie bardzo wiem jak ten temat ugryźć.
Szukałem po różnych forach (również na tym), część linków nt. DDE już wygasła i ciężko coś znaleźć.

Będe wdzięczny za wszelką pomoc, proszę o treściwe odpowiedzi, mogą być przykłady kodu w języku PASCAL/DELPHI.

Pozdrawiam,
MD.

0

Było ostatnio na forum, podałem link do źródeł. Przeanalizuj sobie kod projektu dpr oraz main.pas ktory jest
na: http://odsiebie.com/plik/5497919---25da.html - tylko jeśli chcesz obsluzyc wiecej niz jeden parametr to:
W pliku .dpr:

var
  I : integer;
  ParamSL : TSTringList;
  DataStruct : CopyDataStruct;
begin
  CreateMutex(nil, False, Mutex_Name);
  if GetLastError = ERROR_ALREADY_EXISTS then
  begin
    SendMessage(HWND_BROADCAST, RegisterWindowMessage(Msg_Name), 0, 0);
    if ParamCount > 0 then
    begin
      ParamSL := TSTringList.Create;
      for I := 1 to ParamCOunt do
      begin
        ParamSL.Add(ParamStr(I));
      end;
      DataStruct.dwData := 0;
      DataStruct.cbData := Length(ParamSL.Text) + 1;
      DataStruct.lpData := Pchar(ParamSL.Text);
      SendMessage(HWND_BROADCAST, WM_COPYDATA, 0, Integer(@DataStruct));
      ParamSL.Free;
    end;
    Halt;
  end;
  Application.CreateForm(TMainForm, MainForm);
  // Tutaj tworzone pozostale formatki:
  Application.Run;
end.

W module głownym pas:

//...
private
    procedure WMCopyData(var Msg : TWMCopyData); message WM_COPYDATA;
//...
procedure TMainForm.WMCopyData(var Msg : TWMCopyData);
var
  S : string;
  I : integer;
  Dir : string;
  SL : TStringList;
begin
  S := PChar(Msg.CopyDataStruct.lpData);
  SL := TStringList.Create;
  SL.Text := S;
  for I := 0 to SL.Count - 1 do
  begin
  // Otwarcie pliku o nazwie w SL[I];
  end;
  SL.Free;
  Msg.Result := 2006;
end;

Pozostałe stałe oraz sposób na ogranicznie programu do jednej instancji, a w przypadku jej działania resume
okna programu masz w kodzie w spakownym rarze z powyzszego linka. Przeanalizuj sobie wszystko. Ów kod
pochodzi w oryginalne z google, ale nie pamiętam jak szukałem. O ile w ogóle o takie rozwiązanie Ci chodzi,
bo niebardzo wiem jak wczytać kilka plików inaczej jak przez dodanie lub umieszczenie skrotu do swojego
programu w folderze %homepath%\sendto - z tego co sprawdziłem na jednym zmodyfikowanym przeze mnie
programie to działa z powyższym kodem (kod w rarze otwiera tylko jeden plik z linii poleceń, ale dzięki temu
kodowi poniżej będziesz wiedział jak zrobić dla wielu plików). No miał być treściwy post, to ten chyba jest :P

0

Dziękować... ale nie w tym problem.

Obsługę MUTEX'ów mam już zaimplementowaną.
Interesuje mnie tylko i wyłącznie jak przekazać te wszystkie 'osobne' parametry jako 'wspólne', tak aby program uruchomił się raz i nie było potrzeby wysyłania komunikatów WM_COPYDATA.

Problemu do teraz nie rozwiązałem, tak więc nadal oczekuję pomocy w temacie :)

0

Problem częściowo rozwiązany... tzn. udało mi się poprzez kontrolkę DDE (JvAppDdeCmd1 z JEDI-VCL - http://jvcl.delphi-jedi.org/) uzyskać listę parametrów DDE.

Na formatkę wystarczy ułożyć komponent JvAppDdeCmd (zakładka JV System), oraz dodać wpisy w rejestrze, aby były powiązane z naszą aplikacją, np.:

HCR\MojProgram\shell\open\ddeexec -> domyślna wartość: [list("%1")]
HCR\MojProgram\shell\open\ddeexec\application -> domyślna wartość: MojProgram
HCR\MojProgram\shell\open\ddeexec\topic -> domyślna wartość: System

Następnie wystarczy edytować zdarzenie OnExecParsedCmd komponentu:

procedure TMainForm.PvAppDdeCmd1ExecParsedCmd(Sender: TObject;
  const Command: string; Parameters: TStrings);
var
  I: Integer;
  S: String;
begin
  with Memo1.Lines do
  begin
    Add(Format('Command: %s', [Command]));
    for I := 0 to Parameters.Count - 1 do
    begin
      S := #9 + Parameters.Strings[I];
      //if Assigned(Parameters.Objects[I]) then S := S + ' <- Integer Value';
      Add(S);
    end;
    Add('');
  end;
end;

Napisałem częściowo gdyż o ile mi wiadomo... istnieją inne metody obsługi parametrów, i możliwe jest, że niepotrzeba dodatkowych kontrolek... co wydaje się być bardziej pożądanym rozwiązaniem...

Pozdrawiam,
MD.

p.s. może ktoś ma inne propozycje, prosiłbym o zamieszczenie kodu ;)

0

Proponuję wykorzystać rozszerzenie powłoki ContextMenuHandler, w połączeniu z Mutexem i WMCopyData. Co prawda też mamy tu do czynienia z mutexem i przekazywaniem danych przez komunikaty, jednak jest to rozwiązanie na tyle odmienne, że może warto zwrócić na nie uwagę.

Rozszerzenie powłoki jest specjalną biblioteką DLL, którą trzeba zainstalować w systemie poprzez odpowiednie wywołanie zaimplementowanej w niej funkcji DllRegisterServer lub np. za pomocą systemowego regsvr32.exe. W bibioltece takiej można dodawać własne polecenia do menu kontekstowego plików i/lub folderów. Jeśli użytkownik zaznaczy w Eksploratorze wiele elementów i z menu kontekstowego wybierze dodane przez nas polecenie, do odpowiedniej funkcji w tej bibliotece zostaną przekazane nazwy (ścieżki) wszystkich zaznaczonych plików/folderów. Teraz wystarczy tylko uruchomić właściwą aplikację i przekazać do niej listę tych plików (np. zapisaną w pliku tekstowym). Różnica pomiędzy wywołaniem polecenia z menu kontekstowego dodanego za pomocą rozszerzenia ContextMenuHandler oraz metody standardowej (...shell\open\command itd.) polega na tym, że w pierwszym przypadku lista wszystkich zaznaczonych plików zostanie przekazana do odpowiedniej funkcji w bibliotece DLL (funkcja ta zostanie wywołana tylko jeden raz), zaś w drugim przypadku aplikacja podana w shell\open\command zostanie uruchomiona tyle razy, ile plików/folderów było zaznaczonych w Eksploratorze, a do każdej instancji przekazana zostanie ścieżka do kolejnego zaznaczonego pliku/folderu.

Rozszerzenia powłoki są często wykorzystywane przez aplikacje operujące na plikach; chociażby w archiwizatorze WinRAR. Program ten instaluje w systemie rozszerzenia powłoki (ContextMenuHandler oraz DragDropHandler) zaimplementowane w bibliotece RarExt.dll. Biblioteka ta dodaje do menu kontekstowego polecenia "Dodaj do archiwum", "Dodaj do archiwum i wyślij..." itp. Po wybraniu jednego z tych poleceń, w katalogu z plikami tymczasowymi tworzony jest plik, którego nazwa rozpoczyna się od Rar$, a końcówka jest generowana losowo (chyba) (np. Rar$LS41.29734). Jest to zwykły plik tekstowy, w którym zapisana jest lista plików/folderów zaznaczonych przez użytkownika w Eksploratorze. Następnie RarExt.dll uruchamia właściwy program, tj. WinRARa, i przekazuje do niego nazwę pliku z zapisaną listą. WinRAR wczytuje z tego pliku nazwy zaznaczonych plików, po czym kasuje plik tekstowy Rar$...

Jeżeli jesteś zainteresowany takim rozwiązaniem, przejrzyj źródła przykładowego projektu MyShellExtension, które są dostępne online na stronie http://www.pazera-download.com/_pub/delphi/MyShellExtension_src.html. Ilość kodu jest dosyć duża, dlatego nie mogę podać go w poście.
Skompilowany projekt z wszystkimi plikami źródłowymi: http://www.pazera-download.com/_pub/delphi/MyShellExtension.zip (354 KB).

Krótki opis działania i instrukcja oobsługi:

  1. Uruchom program MyApp.exe i kliknij przycisk "Zarejestruj MyShellExtension.dll".
    MyApp.exe zarejestruje wówczas w systemie rozszerzenie powłoki ContextMenuHandler zaimplementowane w bibliotece MyShellExtension.dll (procedura RegisterShellExtension).

  2. Zaznacz kilka plików w Eksploratorze, kliknij prawy klawisz myszy i wybierz polecenie "MyShellExtension test".
    Eksplorator przekaże wtedy listę wszystkich zaznaczonych plików do naszego DLLa, który zapisze ją w systemowym katalogu przeznaczonym na pliki tymczasowe w pliku tekstowym, którego nazwa będzie się rozpoczynać od "MyShellExtension_", a końcówkę będzie stanowić ciąg liczb szesnastkowych generowanych losowo i rozszerzenie ".txt". Następnie uruchomiana jest właściwa aplikacja (MyApp.exe), w parametrze 1-szym zostanie do niej przekazany łańcuch "AddFiles", a w 2-gim ścieżka do utworzonego przed chwilą pliku tekstowego z listą plików zaznaczonych w Eksploratorze. W tym miejscu należy zaznaczyć, że nasz DLL musi znać ścieżkę do pliku MyApp.exe (w przykładzie tym wykorzystałem rejestr). Trzeba pamiętać, że MyShellExtension.dll nie jest samodzielną aplikacją, jedynie "dodatkiem" do Eksploratora i nawet jeśli umieścimy naszego DLLa i plik MyApp.exe w jednym katalogu, po wywołaniu ExtractFileDir(ParamStr(0)) wewnątrz DLLa, nie otrzymamy ścieżki do katalogu, w którym znajdują się oba pliki, tylko do katalogu z Eksploratorem Windows (z plikiem explorer.exe).

  3. Uruchamiana jest aplikacja MyApp.exe, która - wykorzystując CreateMutex - sprawdza czy jest już uruchomiona instancja programu. Jeśli nie, działanie MyApp.exe jest kontynuowane, jeśli jednak aplikacja została już wcześniej uruchomiona, wysyłany zostaje do niej komunikat WM_COPYDATA, w którym przekazywana jest nazwa pliku tekstowego z listą plików zaznaczonych w Eksploratorze, po czym instancja wysyłająca komunikat kończy działanie. MyApp.exe, po odebraniu takiego komunikatu, wczytuje listę zaznaczonych plików z pliku tesktowego, wypisuje nazwy tych plików w Memo1, a następnie - podobnie jak WinRAR - usuwa z katalogu tymczasowego już niepotrzebny plik tekstowy.

Zainteresowanym polecam także analizę kodów źródłowych programów demonstracyjnych dołączanych do Delphi: ContextM, CopyHk i TRegSvr.

0
kenny198p napisał(a)

Problem częściowo rozwiązany... tzn. udało mi się poprzez kontrolkę DDE (JvAppDdeCmd1 z JEDI-VCL - http://jvcl.delphi-jedi.org/) uzyskać listę parametrów DDE.

Na formatkę wystarczy ułożyć komponent JvAppDdeCmd (zakładka JV System), oraz dodać wpisy w rejestrze, aby były powiązane z naszą aplikacją, np.:

HCR\MojProgram\shell\open\ddeexec -> domyślna wartość: [list("%1")]
HCR\MojProgram\shell\open\ddeexec\application -> domyślna wartość: MojProgram
HCR\MojProgram\shell\open\ddeexec\topic -> domyślna wartość: System

Następnie wystarczy edytować zdarzenie OnExecParsedCmd komponentu:

procedure TMainForm.PvAppDdeCmd1ExecParsedCmd(Sender: TObject;
  const Command: string; Parameters: TStrings);
var
  I: Integer;
  S: String;
begin
  with Memo1.Lines do
  begin
    Add(Format('Command: %s', [Command]));
    for I := 0 to Parameters.Count - 1 do
    begin
      S := #9 + Parameters.Strings[I];
      //if Assigned(Parameters.Objects[I]) then S := S + ' <- Integer Value';
      Add(S);
    end;
    Add('');
  end;
end;

Napisałem częściowo gdyż o ile mi wiadomo... istnieją inne metody obsługi parametrów, i możliwe jest, że niepotrzeba dodatkowych kontrolek... co wydaje się być bardziej pożądanym rozwiązaniem...

Pozdrawiam,
MD.

p.s. może ktoś ma inne propozycje, prosiłbym o zamieszczenie kodu ;)

A mi to nie działa :(

0

Witam

Mam podobny problem co kenny198p.
Nie wiem jak to jest rozwiązane w Winampie, ale na pewno się da. Nawet przez DDE przesyła mi każdą ścieżkę osobno :( A nie da rady bo potrzebuje listę.

Wygląda to tak: Przez przeciągnięcie plików na ikonę programu działa elegancko dostaje listę plików z której wybieram ścieżkę o indeksie 0 i ją odtwarzam. Resztę dodaje do listy (tak jak w Winampie)
A W PRZYpadku np zaznaczenia kilku plików i dania polecenia Otwórz wywala mi każdą ścieżke osobno. I wygląda to tak iż zaczyna odtwarzac każdą po kolei

Z góry dziękuje i proszę o pomoc

Pozdrawiam, Damian

1 użytkowników online, w tym zalogowanych: 0, gości: 1