Как я могу удалить DLL SQLite, когда я закончу с ней, если ОС считает, что она все еще используется?
Как я могу разблокировать или удалить файл, который используется, чтобы я мог удалить его? Данный файл используется моим собственным приложением.
В частности, мое приложение использует бесплатную Zeos Lib. При открытии и сохранении моей базы данных файл sqlite3.dll должен находиться в том же каталоге, что и мое приложение, для правильной работы.
Я хочу, чтобы мое приложение было на 100% автономным, поэтому я добавил sqlite3.dll как RC_DATA в свой проект, и всякий раз, когда мне нужно его использовать (т. Е. Открывать или сохранять базу данных), я извлекаю его в ту же папку, что и мое приложение., После того, как операция открытия или сохранения завершена, я хотел бы удалить файл sqlite3.dll, и никто даже не узнает, что он там был, или должен беспокоиться о отсутствующих библиотеках и т. Д. (Хотя я могу оценить, что некоторым из вас может не понравиться Идея хранения библиотек внутри приложения, у меня есть свои причины для этого: я не хочу, чтобы мои конечные пользователи знали, что стоит за функционированием моего приложения (SQL), и им также не нужно беспокоиться о отсутствуют библиотеки динамических ссылок.)
Проблема в том, что я могу успешно извлечь sqlite3.dll и использовать его для своих операций, но файл блокируется моим приложением (до тех пор, пока я не закрою приложение), что приводит меня к следующему:
Принудительно разблокировать файл sqlite3.dll, не закрывая приложение
Принудительно удалить файл sqlite3.dll
Если конечно нет другого предложения?
Вот пример извлечения и использования. С моей стороны не нужно делать никаких прямых вызовов, таких как LoadLibrary и т. Д.; Об этом должны позаботиться модули Zeos Lib, если sqlite3.dll находится в том же каталоге, что и приложение.
procedure ExtractResource(ResName: String; Filename: String);
var
ResStream: TResourceStream;
begin
ResStream:= TResourceStream.Create(HInstance, ResName, RT_RCDATA);
try
ResStream.Position:= 0;
ResStream.SaveToFile(Filename);
finally
ResStream.Free;
end;
end;
//Open procedure
var
sFileName: String = ExtractFilePath(ParamStr(0)) + 'Sqlite3.dll';
if OpenDialog1.Execute then
begin
ExtractResource('RES_SQLITE3', sFileName);
... //process my database
...
... // finished opening database
if FileExists(sFileName) then
DeleteFile(sFileName);
end;
РЕДАКТИРОВАТЬ
Я думаю, что то, что я пытаюсь сделать, не очень практично, это не очень хорошая идея, как ранее прокомментировал STATUS_ACCESS_DENIED. Я решил, что лучше не продолжать то, что я намеревался сделать.
6 ответов
Просто не делай этого. Есть причина, по которой файл заблокирован. Обычно это очень веская причина, например, какой-то другой процесс (или даже ваш собственный), все еще использующий его. Узнайте, что держит файл заблокированным (например, с помощью Process Explorer), и, пока это ваш процесс, убедитесь, что вы все освободили. Например FreeLibrary
после LoadLibrary
так далее...
Если вам абсолютно необходимо удалить файл, попробуйте DeleteFile
и когда это не удается позвонить MoveFileEx
с MOVEFILE_DELAY_UNTIL_REBOOT
удалить файл при перезагрузке.
Вы можете иметь другой MoveFile
или же MoveFileEx
между тем, чтобы "переименовать" файл, пока он используется (это обычно работает на том же разделе). Но это необходимо, только если вы полагаетесь на имя файла, и поэтому другой экземпляр вашей программы может быть заблокирован, если старый (заблокированный) устаревший файл все еще существует.
Существуют методы, позволяющие делать то, что вы хотите, но они не должны заканчиваться кодом, выпущенным для конечных пользователей. В основном это сводится к хакерской атаке, которая, например, использует внедренные потоки для закрытия / разблокирования файлов в объекте, который удерживает их заблокированными. Но это плохая форма. Постарайся избежать этого.
Вам лучше использовать статическое связывание движка SQLite3, а не полагаться на внешнюю DLL. Включив.obj в ваш модуль.dcu SQLite3.
Он также добавит некоторые приятные функции, такие как возможность использовать FastMM4 в качестве диспетчера памяти для движка SQlite3 (ускорение) и потенциально некоторые приятные низкоуровневые функции (например, шифрование).
Смотрите, например:
- наша оболочка с открытым исходным кодом (которая будет включать в себя шифрование файлов на лету и некоторые полезные функции, такие как виртуальные таблицы, закодированные как классы Delphi);
- DISQlite, который не является бесплатным;
- AnyDAC, который вообще не бесплатен, но открыт для других двигателей БД;
- Aducom бесплатная, но старая версия ( некоторая обновленная информация).
Хотя вы не можете удалить файл, который используется, вы можете переименовать его. Просто переименуйте dll во что-то вроде SQLITE3.DELETE.guid. При запуске ваше приложение может попытаться удалить любой файл sqlite.delete.*, Поэтому после его освобождения он исчезнет. -don
Сначала вы должны убедиться, что файл не используется. В вашем случае он все еще используется, потому что библиотека базы данных загрузила DLL и еще не освободила ее. Поскольку вы уже отключились от базы данных, она, вероятно, еще не активно используется, но ОС не знает об этом - если загружена DLL, ОС предполагает, что она все еще необходима, и запрещает удаление.
При подключении к базе данных Zeos находит соответствующий драйвер базы данных (в этом случае TZSQLiteDriver
, от ZDbcSqLite.pas) и просит его загрузить свои функции. Но когда вы отключаетесь от базы данных, Zeos не просит драйвер базы данных выгрузить его функции. Это было бы расточительно в типичной программе, где может быть несколько соединений, установленных и разрушенных за время существования программы.
Если вы уверены, что нет открытых соединений с базой данных, вы можете самостоятельно выгрузить функции. Загрузчик для SQLite находится в ZPlainSqLite3.pas. Хотя вы можете полностью освободить объект загрузчика, это может вызвать проблемы позже, так как есть другие части Zeos, которые ожидают, что он все еще будет, когда им это понадобится. Вместо этого просто скажите, чтобы выгрузить:
ZPlainSqLite3.Loader.FreeNativeLibrary;
Это заставляет объект устанавливать флаги, указывающие, что он выгружен, поэтому, если вам нужно будет использовать его снова, он перезагрузит все.
Если вы действительно хотите стать модным, вы можете попробовать написать свой собственный TZNativeLibraryLoader
потомок, который автоматически загружает DLL из ресурса при загрузке и удаляет файл при выгрузке. Тогда вам не нужно будет беспокоиться о времени жизни соединения с базой данных в остальной части вашей программы. Вы можете просто подключиться и отключиться, как и все остальные.
Есть пара доступных решений, которые позволяют вам скомпилировать библиотеки SQLite в ваш exe, а не использовать DLL. Я думаю, что было бы намного лучше, если бы у вас действительно был один исполняемый файл. То, что вы в основном пытаетесь продублировать, извлекая / удаляя DLL, - это функциональность установщика, и вы действительно не должны этого делать. Такой подход просто просит о поддержке проблем.
Также имейте в виду, что вашему приложению может потребоваться запустить с правами администратора, чтобы иметь возможность извлечь DLL и сохранить ее в каталоге программных файлов.
Если вы можете жить с установленной DLL вместе с исполняемым файлом, вы можете переименовать файл DLL во что-то другое (например, Database.dll) и внести изменения в код zeos, чтобы он указывал на новое имя DLL. Тогда функциональность SQLite не будет сразу очевидна.
Похоже, LibraryLoader
в ZPlainSqLite3.pas это то, что загружает DLL. Вы можете попробовать запустить код из раздела финализации, прежде чем пытаться удалить DLL:
if Assigned(LibraryLoader) then
LibraryLoader.Free;