Search code examples
delphisqliteunlockzeos

How can I delete the SQLite DLL when I'm finished with it if the OS thinks it's still in use?


How can I unlock or delete a file that is in use, so that I can delete it? The file in question is used by my own application.

More specifically, my application is using the freeware Zeos Lib. When opening and saving my database the sqlite3.dll file must reside in the same directory as my application to work correctly.

I want my application to be 100% standalone, so I have added the sqlite3.dll as RC_DATA to my project, and whenever I need to use it (ie, open or save database), I extract it to the same folder as my application. Once the open or save operation has completed, I would like to delete the sqlite3.dll file, and no one would even know it was there or have to worry about missing libraries etc. (While I can appreciate that some of you may not like the idea of storing the libraries inside the application, I have my reasons for doing this: I don't want my end-users knowing what is behind the functioning of my application (SQL), and they also don't need to worry about missing dynamic link libraries.)

The problem is, I can successfully extract the sqlite3.dll and use it for my operations, but the file becomes locked by my application (until I close my application), which brings me to either:

  1. Force unlock the sqlite3.dll file, without closing my application

  2. Force delete the sqlite3.dll file

Unless of course there is another suggestion?

Here is a sample of extracting and using it. No direct calls need to be made such as LoadLibrary etc from my part; the Zeos Lib units must take care of this, so long as the sqlite3.dll is in the same directory as the application.

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;

EDIT

I think what I am attempting to do is not very practical, it is not a good idea to this as STATUS_ACCESS_DENIED earlier commented on. I have decided it is best not to proceed with what I set out to do.


Solution

  • Simple, don't do it. There is a reason why the file is locked. Usually it's a very good reason, such as some other process (or even your own) still using it. Find out what keeps the file locked (e.g. using Process Explorer) and as long as it's your process make sure you did free everything. E.g. FreeLibrary after LoadLibrary etc ...

    If you absolutely must remove the file, try DeleteFile and when that fails call MoveFileEx with MOVEFILE_DELAY_UNTIL_REBOOT to remove the file upon reboot.

    You can have another MoveFile or MoveFileEx in between to "rename" the file while it is in use (this usually works on the same partition). But this is only necessary if you rely on the file name and therefore another instance of your program could get blocked if the old (locked) stale file still exists.

    There are methods to do what you want, but they should not end up in code that is released to end users. It's basically boiling down to hackery that would - for example - use injected threads to close/unlock files in the entity that holds them locked. But it's bad form. Try to avoid it.