如何解锁或删除正在使用的文件,以便删除它?有问题的文件由我自己的应用程序使用。
更具体地说,我的应用程序正在使用免费软件Zeos Lib。打开并保存数据库时,sqlite3.dll 文件必须与我的应用程序位于同一目录中才能正常工作。
我希望我的应用程序是 100% 独立的,所以我将 sqlite3.dll 作为RC_DATA添加到我的项目中,每当我需要使用它(即打开或保存数据库)时,我都会将其解压缩到与我的应用程序相同的文件夹中。打开或保存操作完成后,我想删除 sqlite3.dll 文件,甚至没有人会知道它在那里或不必担心缺少库等(虽然我可以理解你们中的一些人可能不喜欢将库存储在应用程序中的想法,但我有这样做的理由: 我不希望我的最终用户知道我的应用程序(SQL)功能背后的原因,他们也不必担心缺少动态链接库。
问题是,我可以成功提取 sqlite3.dll 并将其用于我的操作,但该文件被我的应用程序锁定(直到我关闭我的应用程序),这使我进入:
-
强制解锁 sqlite3.dll 文件,而不关闭我的应用程序
-
强制删除 sqlite3.dll 文件
当然,除非有其他建议?
下面是提取和使用它的示例。无需直接调用,例如我的负载库等;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之前评论的那样,这不是一个好主意。我已经决定最好不要继续我打算做的事情。
你最好使用 SQLite3 引擎的静态链接,而不是依赖外部 dll。通过将 .obj 包含在您的 .dcu SQLite3 单元中。
它还将添加一些不错的功能,例如能够使用FastMM4作为SQlite3引擎的内存管理器(加速),以及可能的一些不错的低级功能(如加密)。
例如,请参阅:
- 我们的开源包装器(将包括即时文件加密和一些不错的功能,如编码为 Delphi 类的虚拟表);
- DISQlite 它不是免费的;
- AnyDAC 根本不是免费的,而是向其他数据库引擎敞开的大门;
- Aducom免费但旧版本(一些更新的信息)。
很简单,不要这样做。文件被锁定是有原因的。通常这是一个很好的理由,例如其他一些进程(甚至您自己的进程)仍在使用它。找出是什么使文件保持锁定(例如使用进程资源管理器),只要它是您的进程,请确保您释放了所有内容。例如 FreeLibrary
LoadLibrary
等之后...
如果绝对必须删除该文件,请尝试DeleteFile
,当失败时,请与MOVEFILE_DELAY_UNTIL_REBOOT
一起调用MoveFileEx
以在重新启动时删除该文件。
您可以在两者之间使用另一个MoveFile
或MoveFileEx
,以便在文件使用时"重命名"文件(这通常适用于同一分区)。但这仅在您依赖文件名时才是必需的,因此如果旧的(锁定的)过时文件仍然存在,则程序的另一个实例可能会被阻止。
有一些方法可以执行所需的操作,但它们不应最终出现在发布给最终用户的代码中。它基本上归结为黑客行为,例如,使用注入的线程来关闭/解锁锁定实体中的文件。但这是糟糕的形式。尽量避免它。
虽然您无法删除正在使用的文件,但可以对其进行重命名。只需将 dll 重命名为 SQLITE3 之类的名称即可。启动时,您可以让应用尝试删除任何 sqlite.delete.* 文件,以便在文件释放后它将消失。-don
看起来ZPlainSqLite3.pas中的LibraryLoader
是加载DLL的内容。在尝试删除 DLL 之前,可以尝试从完成部分运行代码:
if Assigned(LibraryLoader) then
LibraryLoader.Free;
您应该首先确保该文件尚未在使用中。在您的情况下,它仍在使用中,因为数据库库加载了 DLL 并且尚未释放它。由于您已经与数据库断开连接,因此它可能尚未处于活动使用状态,但操作系统不知道这一点 - 如果加载了 DLL,操作系统会假定它仍然需要并不允许删除。
当您连接到数据库时,Zeos 会从 ZDbcSqLite.pas 中找到相应的数据库驱动程序(在本例中为 TZSQLiteDriver
),并要求它加载其函数。但是,当您断开与数据库的连接时,Zeos 不会要求数据库驱动程序卸载其功能。这在典型的程序中是浪费,在程序的生命周期中可能会建立和销毁多个连接。
如果确定没有打开的数据库连接,则可以自行卸载函数。SQLite 的加载器位于 ZPlainSqLite3.pas 中。尽管您可以完全释放加载器对象,但这可能会导致以后出现问题,因为Zeos的其他部分希望在需要时它仍然存在。相反,只需告诉它卸载:
ZPlainSqLite3.Loader.FreeNativeLibrary;
这会导致对象设置指示它已卸载的标志,因此如果您需要再次使用它,它将重新加载所有内容。
如果您真的想花哨,可以尝试编写自己的TZNativeLibraryLoader
后代,该后代在加载时自动从资源中获取 DLL,并在卸载时删除文件。这样,您无需担心程序其余部分中的数据库连接生存期。您可以像其他人一样连接和断开连接。
解决方案允许您将 SQLite 库编译到您的 exe 中,而不是使用 DLL。 我认为如果您真的必须有一个文件可执行文件,那将是一种更好的方法。 您基本上试图通过提取/删除DLL来复制的是安装程序的功能,您真的不应该这样做。 这种方法只是要求支持问题。
另请记住,你的应用可能需要以管理员权限运行,才能提取 dll 并将其保存到程序文件目录中。
如果您可以接受与可执行文件一起安装的 DLL,则可以将 DLL 文件重命名为其他名称(即 Database.dll),并在 zeos 代码中进行更改,使其指向新的 DLL 名称。 那么SQLite功能就不会立即显现出来。