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

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

В частности, мое приложение использует бесплатную 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. Я решил, что лучше не продолжать то, что я намеревался сделать.


person Community    schedule 16.05.2011    source источник
comment
@Craig, возможно, вы не можете удалить файл, потому что он все еще используется для вашего приложения, если вы используете LoadLibrary для загрузки dll, не забудьте вызвать FreeLibrary перед удалением.   -  person RRUZ    schedule 16.05.2011
comment
Заголовок спрашивает, как удалить заблокированный файл. Тело спрашивает, как убедиться, что файл разблокирован. Тело является предпочтительным решением. Как вы думаете, почему еще что-то должно быть принудительно? Почему файл все еще заблокирован, и что вы сделали, чтобы попытаться его разблокировать?   -  person Rob Kennedy    schedule 16.05.2011
comment
нет, мне не нужно делать какие-либо прямые вызовы в библиотеку, если она существует в той же папке, что и приложение. Я отредактирую вопрос, чтобы показать код, который я использовал.   -  person    schedule 16.05.2011
comment
Как вы связываетесь с функциями в dll?   -  person Lasse V. Karlsen    schedule 16.05.2011
comment
Крейг, что-то явно его называет, иначе он бы вам вообще не понадобился. Как ты это используешь? Я подозреваю, что мы обнаружим, что у вас есть код, использующий SQLite, который вы не писали, и он загружает библиотеку, но не выпускает ее. Может быть, это потому, что он никогда не выпускает его, или, может быть, потому, что вы не сказали ему об этом. Покажите этот код, пожалуйста.   -  person Rob Kennedy    schedule 16.05.2011
comment
Я написал код, единственное, что, по моему мнению, могло его заблокировать, это TZConnection. Но я создаю это так: Connector: TZConnection.Create(nil); и отключите его Connector.Disconnect; и Connector.Free.   -  person    schedule 16.05.2011


Ответы (6)


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

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

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

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

person 0xC0000022L    schedule 16.05.2011
comment
Может быть, вы правы, и я не должен этого делать на самом деле. Что касается ваших комментариев о перемещении/удалении файла, как я сказал Дону, делая это после загрузки, это противоречит цели того, что я пытался сделать. - person ; 16.05.2011
comment
@Craig: как я там прокомментировал, важная часть кода между ними отсутствует. В основном то, что держит файл заблокированным. И вы должны опубликовать это, потому что это актуально. Вероятно, есть LoadLibrary и должно быть по одному FreeLibrary на каждый LoadLibrary. И когда вы вызываете LoadLibrary несколько раз, это также может повлиять на счетчик ссылок. - person 0xC0000022L; 16.05.2011
comment
И нужно подождать несколько секунд после последней бесплатной библиотеки, прежде чем пытаться удалить. (или, по крайней мере, повторите попытку через несколько секунд, если в первый раз не получилось). Блокировки иногда задерживаются, особенно на сетевых дисках. - person Marco van de Voort; 21.05.2011

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

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

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

person Arnaud Bouchez    schedule 17.05.2011

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

person Don Dickinson    schedule 16.05.2011
comment
А еще лучше отложенное удаление после переименования файла. т.е. MoveFile, затем MoveFileEx и MOVEFILE_DELAY_UNTIL_REBOOT. Лично я предпочитаю DeleteFile, за которым следует MoveFileEx с MOVEFILE_DELAY_UNTIL_REBOOT. - person 0xC0000022L; 16.05.2011
comment
Спасибо за совет, но мне нужно удалить его немедленно, а не по расписанию после загрузки. - person ; 16.05.2011
comment
@Craig: в этом случае вы еще не опубликовали соответствующий код, который содержит загадку того, почему файл вообще заблокирован. Вероятно, это так же просто, как FreeLibrary. - person 0xC0000022L; 16.05.2011

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

  if Assigned(LibraryLoader) then
     LibraryLoader.Free;
person jasonpenny    schedule 16.05.2011

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

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

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

ZPlainSqLite3.Loader.FreeNativeLibrary;

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


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

person Rob Kennedy    schedule 16.05.2011
comment
ZPlainSqLite3.pas у меня вроде нет, поиск тоже не возвращает файл? неважно, кажется, в ZPlainSqLiteDriver.pas - person ; 16.05.2011
comment
Опять неправильно, по какой-то причине у меня нет файла в моей установке?? Я вижу, как это выглядит здесь: koders.com/delphi/ но у меня нет этого юнита - person ; 16.05.2011
comment
Судя по архиву, который я скачал полчаса назад, он находится в папке zeosdbo_rework\src\plain. Файл драйвера содержит драйвер, а не загрузчик. Драйвер не позволяет вам управлять загрузчиком, что вам и нужно. Найдите в исходном коде sqlite3.dll — это файл, который должен содержать загрузчик. - person Rob Kennedy; 16.05.2011
comment
@Rob Спасибо за информацию, но, следуя совету STATUS_ACCESS_DENIED, я считаю, что то, что я пытаюсь сделать, не очень практично. Я проголосовал за ваш комментарий, хотя и из совета, которым вы также поделились. - person ; 16.05.2011
comment
Верно. Вы пытались удалить файл, пока он еще был загружен, или принудительно выгрузить его. Мой ответ дает вам способ избежать этого. Он позволяет выгружать DLL изящно таким образом, чтобы остальная часть вашей программы могла полностью осознавать и обрабатывать ее без риска сбоя. - person Rob Kennedy; 16.05.2011
comment
Что ж, я попытался модифицировать исходные блоки Zeos, пересобрал их, но Windows все равно заблокировала их. Я изменил свой подход к своей первоначальной проблеме, вместо того, чтобы извлекать и пытаться удалить sqlite3.dll, когда он не нужен, я просто проверяю, существует ли файл sqlite3.dll при запуске, и продолжаю как обычно, если он не существует, я извлекаю это и использовать его как обычно. задача решена. - person ; 16.05.2011

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

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

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

person GrandmasterB    schedule 17.05.2011