Как я могу удалить DLL SQLite, когда я закончу с ней, если ОС считает, что она все еще используется? - PullRequest
2 голосов
/ 16 мая 2011

Как я могу разблокировать или удалить используемый файл, чтобы я мог его удалить?Данный файл используется моим собственным приложением.

В частности, мое приложение использует бесплатную версию Zeos Lib.При открытии и сохранении моей базы данных файл sqlite3.dll должен находиться в том же каталоге, что и мое приложение, для правильной работы.

Я хочу, чтобы мое приложение было на 100% автономным, поэтому я добавил sqlite3.dll как RC_DATA.в мой проект, и всякий раз, когда мне нужно его использовать (т.е. открывать или сохранять базу данных), я извлекаю его в ту же папку, что и мое приложение.После того, как операция открытия или сохранения завершена, я хотел бы удалить файл sqlite3.dll, и никто даже не узнает, что он там был, или беспокоиться о отсутствующих библиотеках и т. Д. (Хотя я могу оценить, что некоторым из вас может не понравитьсяИдея хранения библиотек внутри приложения, у меня есть свои причины для этого: я не хочу, чтобы мои конечные пользователи знали, что стоит за функционированием моего приложения (SQL), и им также не нужно беспокоиться оотсутствуют библиотеки динамических ссылок.)

Проблема в том, что я могу успешно извлечь файл sqlite3.dll и использовать его для своих операций, но файл блокируется моим приложением (пока я не закрываю свое приложение), что приводит менялибо:

  1. Принудительно разблокировать файл sqlite3.dll, не закрывая мое приложение

  2. Принудительно удалить файл 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 ]

2 голосов
/ 17 мая 2011

Вам лучше использовать статическое связывание движка SQLite3 вместо того, чтобы полагаться на внешнюю DLL.Включение .obj в ваш модуль .dcu SQLite3.

Это также добавит некоторые полезные функции, такие как возможность использовать FastMM4 в качестве диспетчера памяти для движка SQlite3 (ускорение) и, возможно, некоторые приятныефункции уровня (например, шифрование).

См. например:

1 голос
/ 16 мая 2011

Просто, не делай этого. Есть причина, по которой файл заблокирован. Обычно это очень веская причина , например, какой-то другой процесс (или даже ваш собственный), все еще использующий его. Узнайте, что держит файл заблокированным (например, с помощью Process Explorer), и, пока это ваш процесс, убедитесь, что вы все освободили. Например. FreeLibrary после LoadLibrary и т. Д. *

Если вам абсолютно необходимо удалить файл, попробуйте DeleteFile, а в случае неудачи вызовите MoveFileEx с помощью MOVEFILE_DELAY_UNTIL_REBOOT, чтобы удалить файл при перезагрузке.

Вы можете иметь еще один MoveFile или MoveFileEx между ними, чтобы «переименовать» файл, когда он используется (это обычно работает на том же разделе). Но это необходимо только в том случае, если вы полагаетесь на имя файла, и поэтому другой экземпляр вашей программы может быть заблокирован, если старый (заблокированный) устаревший файл все еще существует.

- это методы для выполнения того, что вы хотите, но они не должны заканчиваться кодом, который выпускается для конечных пользователей. В основном это сводится к хакерской атаке, которая, например, использует внедренные потоки для закрытия / разблокирования файлов в объекте, который удерживает их заблокированными. Но это плохая форма. Старайтесь избегать этого.

0 голосов
/ 17 мая 2011

Существует пара доступных решений, которые позволяют вам скомпилировать библиотеки SQLite в вашего exe-файла, а не использовать DLL.Я думаю, что это будет намного лучше подход, если вы действительно должны иметь один исполняемый файл.То, что вы в основном пытаетесь продублировать, извлекая / удаляя DLL, - это функциональность установщика, и вы действительно не должны этого делать.Этот подход просто требует проблем с поддержкой.

Также имейте в виду, что вашему приложению может потребоваться запуск с правами администратора, чтобы иметь возможность извлечь DLL и сохранить ее в каталоге программных файлов.

Если вы можете жить с установленной DLL вместе с исполняемым файлом, вы можете переименовать файл DLL во что-то другое (например, Database.dll) и внести изменения в код Zeos, чтобы он указывал нановое имя DLL.Тогда функциональность SQLite не будет сразу очевидна.

0 голосов
/ 16 мая 2011

Сначала убедитесь, что файл не используется.В вашем случае, все еще используется, потому что библиотека базы данных загрузила DLL и еще не освободила ее.Поскольку вы уже отключились от базы данных, она, вероятно, все еще не используется active , но ОС не знает об этом - если загружена DLL, ОС предполагает, что она все еще необходима, и запрещает удаление.

Когда вы подключаетесь к базе данных, Zeos находит соответствующий драйвер базы данных (в данном случае TZSQLiteDriver, из ZDbcSqLite.pas ) и просит его загрузить его функции.Но когда вы отключаете от базы данных, Zeos не просит драйвер базы данных выгрузить его функции.Это было бы расточительно в типичной программе, где может быть несколько соединений, установленных и разрушенных за время существования программы.

Если вы уверены, что нет открытых соединений с базой данных, вы можете выгрузитьфункционирует сам.Загрузчик для SQLite находится в ZPlainSqLite3.pas .Хотя вы можете полностью освободить объект загрузчика, это может вызвать проблемы позже, так как есть другие части Zeos, которые ожидают, что он все еще будет, когда им это понадобится.Вместо этого просто скажите, чтобы он выгружался:

ZPlainSqLite3.Loader.FreeNativeLibrary;

Это заставляет объект устанавливать флаги, указывающие, что он выгружен, поэтому, если вам нужно будет использовать его снова, он перезагрузит все.


Если вы действительно хотите получить фантазию, вы можете попробовать написать собственного потомка TZNativeLibraryLoader, который автоматически загружает DLL из ресурса при загрузке и удаляет файл при выгрузке.Тогда вам не нужно будет беспокоиться о времени жизни соединения с базой данных в остальной части вашей программы.Вы можете просто подключиться и отключиться, как и все остальные.

0 голосов
/ 16 мая 2011

Похоже, что LibraryLoader в ZPlainSqLite3.pas - это то, что загружает DLL.Вы можете попробовать запустить код из раздела финализации, прежде чем пытаться удалить DLL:

  if Assigned(LibraryLoader) then
     LibraryLoader.Free;
0 голосов
/ 16 мая 2011

Хотя вы не можете удалить файл, который используется, вы можете переименовать его. Просто переименуйте dll во что-то вроде SQLITE3.DELETE.guid. При запуске ваше приложение может попытаться удалить любой файл sqlite.delete. *, Поэтому после его освобождения он исчезнет. -don

...