Kiedy program sprawdza obecność dll'ki w bieżącym katalogu?

0

Mój program korzysta z biblioteki bass.dll, która musi być obecna w bieżącym katalogu przy uruchamianiu programu. Dodałem więc tę bibliotekę do zasobów, wkompilowałem w exeka, zaś w zdarzeniu TForm1.FormCreate zrobiłem żeby program wydobywał bibliotekę z zasobu i w bieżącym katalogu zakładał nowy plik właśnie o nazwie bass.dll. Niestety, gdy odpalam program to zanim jeszcze plik zostanie utworzony na dysku to już program sprawdza jego obecność i krzyczy, że go nie ma. W którym więc miejscu programu mam umieścić kod tak aby zdążyć utworzyć plik bass.dll zanim program sprawdzi czy jest on w bieżącym katalogu? Czy nie napisałem zbyt zawile?

2

Jeśli dobrze rozumiem:
Są dwa sposoby ładowania DLL, Ty musisz użyć ładowania
dynamicznego, a prawdopodobnie używasz statycznego.

1

Jeżeli korzystasz z biblioteki z zwykły sposób program statycznie importuje funkcje z biblioteki a więc ładuje ją do pamięci już w momencie uruchamiania programu aby zrobić tak jak chcesz musisz ładować bibliotekę i importować funkcje dynamicznie (funkcje LoadLibrary, FreeLibraryi GetProcAddress) .

1

Taki kod do zapisu z zasobów, co logiczne, należy umieścić przed pierwszym wywołaniem funkcji LoadLibraryA/W. I oczywiście podawać w ścieżce do ładowania pełną lokalizację z wyodrębnien ParamStr(0) czy też Application.ExeName jeśli korzystasz z VCL. Oczywiście należy też pamięrtać o kontroli błędów jak sytuacja, gdy nie ma zasobów, problem z zapisem do danej lokalziacji ze względu na brak praw Admina lub włłączony mechanizm UAC. I dopiero gdy się wszystko uda zapisać i wczyrać to załadować eksportowane funkcje z tej dllki, ewentualnie robić zapis do katalogu %TEMP%, ktory bez problemu można sobie pobrać funkcjami WinAPI. Na Google jest mnóstwo przykładów do tego jak to zastosować.

A i nie wiem też, której wersji Delphi używasz. Ale pod Delphi 7 świetnie sprawdza się moduł, który dołaczyłem do mojej odpowiedzi w tym wątku. Jego obsługa nie powinna sprawić większych problemu nikomu, kto zna podstawy Delphi oraz umie logicznie pomyśleć. Na Google są chyba też wersję od innego autora pod nowsze środowiska. Ja do tej pory jedyny problem z zastosowaniem tego modułu miałem z dllkami, które zakłądają hooka na funkcje API. Jednak z używaniem pod kątem bass/bassmod/sqlite3 jest super i szczerze polecam. Ktoś może doradzi Tobie tutaj jeszcze coś lepszego. A w razie czego, to moduł ten da się dosyć łatwo oraz szybko przerobić do użycia w czystymi WinAPI, używając wycinków kodu klas VCL TResourceStream.

0
olesio napisał(a)

I oczywiście podawać w ścieżce do ładowania pełną lokalizację z wyodrębnien ParamStr(0) czy też Application.ExeName jeśli korzystasz z VCL.

To nie do końca jest dobre rozwiązanie;

Ani ParamStr, ani też właściwość Application.ExeName nie gwarantują poprawności zwracanej ścieżki; Wcale nie jest powiedziane, że zwrócą dokładną i prawidłową ścieżkę pliku wykonywalnego - zresztą sam wielokrotnie przekonałem się o tym, że potrafią zwracać błędne ścieżki; I nie jest to błąd tych elementów, a po prostu specyfika parametrów uruchomieniowych, o czym zostało dokładnie napisane w tym poście;

O ile takie rozwiązanie jest jedynym możliwym w przypadku aplikacji stand alone czy portable, to jest najgorszym rozwiązaniem, jeśli chodzi o aplikacje instalowane lub pseudo-instalowane;

A już horrorem jest próba wyodrębnienia pliku z zasobów i zapisanie bezpośrednio obok pliku wykonywalnego, co oczywiście rozłoży aplikację na łopatki, jeśli zostanie ona uruchomiona z lokalizacji read only (np. nośnik CD-R) lub w katalogu, do którego bieżący użytkownik nie ma uprawnień zapisu (również w przypadku włączonej usługi UAC i uruchomienia aplikacji bez wymuszenia uprawnień administratora);

[...] ewentualnie robić zapis do katalogu %TEMP%, ktory bez problemu można sobie pobrać funkcjami WinAPI.

Nie ewentualnie, a koniecznie; Tylko nie do katalogu %TEMP%, bo ten można różnymi programami opróżnić w dowolnym momencie i może być później problem; Sugeruję katalog LOCAL_APP_DATA, bo ten nigdy nie jest czyszczony i każdy użytkownik ma do niego dostęp;

To jedyny bezpieczny sposób wyodrębnienia pliku z zasobów i zapisanie go na dysku; Rozwiązanie uniwersalne, zarówno dla wszelkich typów aplikacji, poziomów uprawnień oraz wersji systemu operacyjnego;


dindzi napisał(a)

W którym więc miejscu programu mam umieścić kod tak aby zdążyć utworzyć plik bass.dll zanim program sprawdzi czy jest on w bieżącym katalogu?

Po pierwsze skorzystaj z dynamicznego ładowania procedur/funkcji z biblioteki - ułatwi to całą pracę; Po drugie - dobrym miejscem do tego jest główny plik projektu, czyli plik .dpr; Jeżeli projekt jest duży lub bardzo duży, przyda się dodakowy moduł, tak aby nie naśmiecić w pliku .dpr; Takich rzeczy nie robi się w konstruktorze i destruktorze głównej formy - chyba że nie ma się czasu, albo projekt jest malutki;

Przy rozruchu aplikacji ekstrahuj bibliotekę do pliku w katalogu, do którego zawsze ma się dostęp; Wtedy też ładuj bibliotekę i ewentualnie wszystkie importy (jak wolisz); Na zakończenie (po metodzie Application.Run) zwalniaj bibliotekę z pamięci i usuwaj jej plik z dysku.

0

Przepisałem program. Ładuję teraz dynamicznie funkcje z bass.dll. Działają wszystkie oprócz BASS_ErrorGetCode. Gdy przy inicjalizacji bass'a chcę pobrać kod błędu to wywala błąd. Nie ma natomiast tego problemu gdy funkcja BASS_ErrorGetCode jest ładowana statycznie. Nie rozumiem o co chodzi. Dlaczego BASS_Init domaga się natychmiastowego BASS_ErrorGetCode ?

0

Furios Programming, czy wiesz może czy katalog C:\Users\somebody\AppData\Roaming (CSIDL_APPDATA) nadaje się do wypakowywania plików podczas uruchamiania programu? Widzę, że aplikacje, które kiedyś instalowałem tam właśnie tworzą własne katalogi.

0

@didzni - zobacz na poniższy artykuł:

Per user configuration files synchronized across domain joined machines via Active Directory Roaming napisał(a)

Configuration data files that the application uses and are unique per user. This per user data is synchronized across the domain via Active Directory.

Jeśli nie potrzebujesz takich bajerów to użyj po prostu CSIDL_LOCAL_APPDATA:

Local per user configuration files napisał(a)

Configuration data files that the application uses and is unique per user. It stays local to the individual machine and is not synchronized via Active Directory.

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