Otwieranie jednej instancji programu zewnętrznego (ShellExecute)

0

Program obsługuje bazę efektów dźwiekowych.
Kiedy użytkownik chce przesłuchać wybrany efekt, klika pozycję na liście. Program sam określa, który program ma dany dźwięk otworzyć. Problem polega na tym, że kiedy uruchamiam wybrany program poleceniem:

exename:=VlcAppPath; // ścieżka do aplikacji odwarzającej; tu: VLC Media player
Parms:=Filename;
res:=ShellExecute(MainF.handle,'open',PChar(exename+''),PChar('"'+Parms+'"'),'',sw_ShowNormal);

za każdym uruchomieniem otwiera sie nowe okno odtwarzacza. Niekiedy daje to niezły efekt mieszania dźwięku, ale w podstawowym trybie wolałbym, żeby w chwili kolejnego wywołania program odtwarzający przeerywał odtwarzanie bieżącego dźwięku i zaczynał własnie kliknięty (w tej samej instancji odtwarzacza). Dokładnie tak, jak to robi eksplorator Windows, kiedy nie czekając na koniec poprzedniego dźwięku kliknę kolejny plik *.flv;
Próbowałem różnych parametrów wywołania (nShowCmd), ale żadne nie powoduje opisanego dzialania.
Przeszukałem 4programerz.net i delphi.about.com (wiele sie przy tym nauczyłem), ale nie napotkałem odpowiedzi. Może ktoś cos wie na ten temat?

0

Poczytaj o komunikatach...
To dość proste, bierzesz podczas uruchamiania appki sprawdzasz czy nie ma już uruchomionej jgko kopii jeżeli jest to wysyłasz do niej ścieżkę do pliku który chcesz otworzyć i zamykasz tą instancję, a tamta odbiera wiadomość i wykonuje stosowne akcje.

0

Jeżeli dobrze zrozumiałem, to kiedy chcesz osiągnąc efekt jak dla Exploratora. To należało by wywoływać plik, a nie program i parametr. O ile w ogóle VLC Player działa ok. Ponieważ nawet kiedy w WinAMPie ustawiłem opcję aby uruchamiał się w wielu instancjach i zrobiłem taki kod programu, jak widać poniżej. To WinAMP nie wywołał mi się dwa razy. A klikałem przyciski naprzemiennie. Może jeszcze jakieś dodatkowe wpisy akurat dla niego w rejestrze o tym decydują, ale wątpie. To ważne jak program chyba jest stworzony. Na przykład czy nie tworzy Mutex i nie obsługuje nowych parametrów przy podaniu kiedy jest już uruchomiony.

//...
procedure TForm1.Button1Click(Sender : TObject);
begin
  ShellExecute(Handle, 'open', PChar('C:\PROGRAM FILES\WINAMP\winamp.exe'), PChar('"' + 'C:\PROGRAMY\MP3\Weekend - Ona Tanczy Dla Mnie.mp3' + '"'), '', SW_SHOWNORMAL);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  ShellExecute(Handle, 'open', PChar('C:\PROGRAM FILES\WINAMP\winamp.exe'), PChar('"' + 'C:\PROGRAMY\MP3\Sobota & Weekend - Ona Tanczy Dla Mnie (2sty Blend).mp3' + '"'), '', SW_SHOWNORMAL);
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  ShellExecute(Handle, 'open', PChar('"' + 'C:\PROGRAMY\MP3\Weekend - Ona Tanczy Dla Mnie.mp3' + '"'), '', '', SW_SHOWNORMAL);
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
  ShellExecute(Handle, 'open', PChar('"' + 'C:\PROGRAMY\MP3\Sobota & Weekend - Ona Tanczy Dla Mnie (2sty Blend).mp3' + '"'), '', '', SW_SHOWNORMAL);
end;

Jeżeli jednak Explorator zachowuje się ok to myślę, że powinno pomóc wywołanie pliku. Ponieważ kiedy robisz dwuklik na pliku, to o ile się orientuje robisz właśnie tak jakbyś wywoływał sam plik jako program, a parametr pusty. Ewentualnie zobacz w Rejestrze czy wywołanie plików .flv przez VLC nie odbywa się z jakimis dodatkowymi parametrami i tym podobne. Jeżeli to i reinstall nie pomoże to nie wiem jak można by jeszcze kombinować. Nie mam zamiaru instalować VLC na chwile i psuć sobie skojarzenia dla plików filmów. Natomiast przedwczesne jest raczej "wytaczanie takich dział" jak próba napisania wrappera dll (o ile taki da się łatwo napisac, bo VLC obsluguje jakąś udokumentowaną dllkę systemową przez małą ilośc eksportów), co jest moją jedną z moich ulubionych metod i w miarę łatwych do zaimplementowania. Jeżeli oczywiście chcemy łatwo ingerować w zachowanie kodu oryginalnego programu bez jego ręcznego patchowania albo inwazyjnego injectowania dllek. Wtedy takim wrapperem można by spróbować najprościej tworzyć Mutex o unikalnej nazwie. Tylko kwestia, co zrobić jeżeli on by istniał. Jak przekazać nowy plik do VLC. Chyba tylko symulacją komunikatu o przeciąganiu pliku na okno, ewentualnie mechanizmem DDE jeżeli jest obsługiwany i udokumentowany dla tego playera. W ostateczności można szukać pomocy w supporcie VLC.

0

Obawiam się że takiego czegoś nie uzyskasz gdyż to zależy w jaki sposób dany program który ma odtwarzać jest napisany czyli to program musi pozwalać się otworzyć tylko w jednej instancji i odtwarzać podany jako parametr plik (jeżeli jest już otarty to przerywać odtwarzanie poprzedniego a odtwarzać ten nowy). Jeżeli dany odtwarzacz ma udostępnia API (jak np Winamp) to mógłbyś spróbować sterować nim poprzez to API (oczywiście o ile jest taka możliwość bo nie zajmowałem się w zabawy z API Winampa ale raczej powinno się dać).

0

Sprawdź, czy uruchamiany player ma w ustawieniach (włączoną) opcję w stylu "Uruchamiaj tylko jedną instancję" lub wyłączoną opcję w stylu "Pozwól na wiele instancji" lub też poszukaj w helpie playera, czy można go uruchomić z określonym parametrem, który pozwala mu działać tylko w jednej instancji.

0

Propozycja @hzmzp jest według mnie za bardzo inwazyjna, bo zamykamy i otwieramy ponownie program co może potrwać. Natomiast @kAzek napisał o WinAMPie albo sugerując się jego działaniem albo moim kodem. Natomiast pytanie jest o VLC. Który może faktycznie działać inaczej. Chociaż jeżeli przez dwuklik z exploratora jest ok. To podejrzewał bym albo wywoływanie z odpowiednimi parametrami poza nazwą pliku. Albo inną domyślną czynnośc niż open. Albo obsługę DDE. Niestety w nowszych Windowsach o ile się orientuje skopano względem XP trochę ustawianie i podgląd skojarzonych rozszerzeń. Także takie operacje jak podgląd albo zmiana domyślnej czynności i innych ustawień najlepiej dokonać w Rejetrze - konkretnie dla HKEY_CLASSES_ROOT\.FLV, a później dla klucza, który jest w nazwie domyślnej oczywiście.

EDIT: o - poniżej elegancki kod na zasymulowanie przerzucenia plików. Trzeba sobie tylko ustalić uchawyt okna VLC za pewne. Bo chyba obsługuje ono Drag and Drog. To może będzie to jakieś rozwiązanie: http://www.vbforums.com/showthread.php?632732-Simulate-Drop-Files-with-SendMessage-WM_DROPFILES o ile się nie uda inaczej.

EDIT #2: zgodnie z informacją zawartą w treści wątku na: http://us.generation-nt.com/an[...]les-message-help-57315072.html - powyższy kod na symulowanie WM_DROPFILES zadziała, tylko trzeba użyć PostMessage zamiast SendMessage.

EDIT #3: powyższe kombinacje jednak źle działają dla więcej niż jednego pliku, a dodawanie do długości zmiennej Run nawet +1 powoduje obsługe ok po pierwszym uruchomieniu WinAMP'a. Nie analizowałem zmian, ale ten kod, który pochodzi ze strony: http://www.delphi-zone.com/2010/02/how-to-send-data-to-another-program-by-auto-dragdrop sprawdza się znakomicie, a i ma ten plus, że jest już przyszłościowo pod kątem WinAMP ;) Oczywiście nie problem prxerobić go na skorzystanie z TStringList i VCL. Poniżej przykład dla szybkich testów. Zakładam, że takie pliki jak ja macie w media. Przynajmniej pod siódemką takowe są.

//...
uses
  ShellApi;

function MakeDrop(const FileNames : array of string) : THandle;
var
  P : PChar;
  Data : PDragInfoA;
  I, Size : Integer;
begin
  Size := SizeOf(TDragInfoA) + 1;
  for I := 0 to High(FileNames) do
  begin
    Inc(Size, Length(FileNames[I]) + 1);
  end;
  Result := GlobalAlloc(GHND or GMEM_SHARE, Size);
  if Result <> 0 then
  begin
    Data := GlobalLock(Result);
    if Data <> nil then
      try
        Data.uSize := SizeOf(TDragInfoA);
        P := PChar(@Data.grfKeyState) + 4;
        Data.lpFileList := P;
        for I := 0 to High(FileNames) do
        begin
          Size := Length(FileNames[I]);
          Move(Pointer(FileNames[I])^, P^, Size);
          Inc(P, Size + 1);
        end;
      finally
        GlobalUnlock(Result);
      end
    else
    begin
      GlobalFree(Result);
      Result := 0;
    end;
  end;
end;

procedure TForm1.FormCreate(Sender : TObject);
begin
  Application.Title := Caption;
end;

procedure TForm1.Button1Click(Sender : TObject);
var
  H : HWND;
  Drop : HDrop;
begin
  H := FindWindow('Winamp v1.x', nil);
  if H > 0 then
  begin
    Drop := MakeDrop
      (['C:\WINDOWS\MEDIA\flourish.mid',
      'C:\WINDOWS\MEDIA\town.mid',
        'C:\WINDOWS\MEDIA\chord.wav']);
    if Drop <> 0 then
    begin
      PostMessage(H, WM_DropFiles, Drop, 0);
    end;
    GlobalFree(Drop);
  end;
end;
0

Program obsługuje bazę efektów dźwiekowych.
Kiedy użytkownik chce przesłuchać wybrany efekt, klika pozycję na liście.
No ok, ale po co do tego zewnętrzny program przez ShellExecute, zamiast odtworzyć dźwięk wewnątrz programu, dowolnym działającym sposobem?

0

@Azarien: jakby to był typowy format i chodzilo tylko o dźwięk to wiadomo, że chyba najprościej o najlepiej było by posiłkować się bass.dll. Ja jednak kiedy widzę flv i chęć użycia VLC Playera, to podejrzewam że może chodzić również o wyświetlenie ruchomego obrazu. A z pokazywaniem video poza TMediaPlayer i avi osobiście nie mam doświadczenia w Delphi. Wydaje mi się też, że chyba nie ma łatwego do ogarnięcia rozwiązania w postaci dllka, wyświetlenie na oknie o podanym handle i wygodna nawigacja. Czyli bez kombinowania z kodekami i tym podobnymi. Co nie oznacza, że używanie zewnętrznego programu do tego co można spróbować zrobić samodzielnie jest rozwiązaniem godnym polecenia. Bo można inaczej, także jeżeli pytającemy na pewno chodzi tylko o dźwięk to najlepiej przekonwertować flv na mp3, ponazywać sensownie. I albo wrzucić do podkatalogu jako dane i ogarnąć bass.dll albo ewentualnie jeżeli chcemy mieć wszystko w postaci jednego exeka to polecam bass.dll i moduł dllloader.pas z funkcją FindExport. Wpakować wszystko w zasób, ewentualnie mp3ki też jako zasoby tylko wczytywane i zapisywane po ID odpowiednim konstruktorem dla TResourceSream (czyli w pliku *.rc # i liczba jako nazwa zasobu). Wczytywanie po ID zgodnie z tym co piszą w helpie do Delphi, ma znaczenie nie tyle jeżeli chodzi o wielkość sumaryczną zasobów co ewentualną ich ilośc ponad kilkaset sztuk w jednym exeku. Oczywiście można jeszcze kombinować z dllką jako pliko zasobem zewnętrznym. Ale jeśli chcemy sobie zrobić ładny i zgrabny program wszystko w jednym to faktycznie chyba tak najlepiej. Anyway, już pytający otrzymał w tym wątku tyle konkretnych rad, że na pewno spokojnie można siąść i zakować coś porządnego bez "męczenia" VLC do dźwięku :)

0

W pomocy programu VLC media player-a: https://wiki.videolan.org/VLC_command-line_help jest opisany parametr dotyczący uruchamiania pojedynczej instancji: --one-instance , co przekłada się na takie wywołanie ShellExecute:

ShellExecute(MainF.handle, 'open', PChar(exename), PChar('--one-instance "'+Parms+'"'),'',sw_ShowNormal);

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