¿Cómo puedo eliminar la DLL de SQLite cuando termine con ella si el sistema operativo cree que todavía está en uso?

¿Cómo puedo desbloquear o eliminar un archivo que está en uso para poder eliminarlo? El archivo en cuestión es utilizado por mi propia aplicación.

Más específicamente, mi aplicación utiliza el software gratuito Zeos Lib. Al abrir y guardar mi base de datos, el archivo sqlite3.dll debe residir en el mismo directorio que mi aplicación para que funcione correctamente.

Quiero que mi aplicación sea 100% independiente, así que agregué sqlite3.dll como RC_DATA a mi proyecto, y cada vez que necesito usarlo (es decir, abrir o guardar la base de datos), lo extraigo a la misma carpeta que mi aplicación . Una vez que se haya completado la operación de abrir o guardar, me gustaría eliminar el archivo sqlite3.dll, y nadie sabría que estaba allí o tendría que preocuparse por las bibliotecas que faltan, etc. (Aunque puedo apreciar que a algunos de ustedes no les gustará la idea de almacenar las bibliotecas dentro de la aplicación, tengo mis razones para hacer esto: no quiero que mis usuarios finales sepan qué hay detrás del funcionamiento de mi aplicación (SQL), y tampoco necesitan preocuparse por faltan bibliotecas de vínculos dinámicos).

El problema es que puedo extraer con éxito el sqlite3.dll y usarlo para mis operaciones, pero el archivo queda bloqueado por mi aplicación (hasta que cierre mi aplicación), lo que me lleva a:

  1. Forzar el desbloqueo del archivo sqlite3.dll, sin cerrar mi aplicación

  2. Forzar la eliminación del archivo sqlite3.dll

A menos que, por supuesto, haya otra sugerencia.

Aquí hay una muestra de cómo extraerlo y usarlo. No es necesario realizar llamadas directas como LoadLibrary, etc. de mi parte; las unidades Zeos Lib deben encargarse de esto, siempre que sqlite3.dll esté en el mismo directorio que la aplicación.

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;

EDITAR

Creo que lo que estoy intentando hacer no es muy práctico, no es una buena idea hacer esto como STATUS_ACCESS_DENIED comentó anteriormente. He decidido que es mejor no continuar con lo que me propuse.

preguntado el 16 de mayo de 11 a las 16:05

@Craig tal vez no pueda eliminar el archivo porque todavía está usado para su aplicación, si está utilizando LoadLibrary para cargar el dll, recuerda llamar FreeLibrary antes de eliminar. -

El título pregunta cómo eliminar un archivo bloqueado. Body pregunta cómo asegurarse de que un archivo esté desbloqueado. El cuerpo es la solución preferible. ¿Por qué crees que todo debe ser forzado ¿aún? ¿Por qué el archivo sigue bloqueado y qué ha hecho para intentar desbloquearlo? -

no, no necesito hacer ninguna llamada directa a la biblioteca, siempre que exista en la misma carpeta que la aplicación. Editaré la pregunta para mostrar el código que utilicé. -

Craig, algo obviamente lo llama, o de lo contrario no lo necesitaría en absoluto. ¿Como lo usas? Sospecho que descubriremos que tiene un código que usa SQLite que no escribió, y carga la biblioteca pero no la libera. Tal vez sea porque nunca lo publica, o tal vez porque no se lo ha dicho usted. Espectáculo que código, por favor. -

Escribí el código, lo único que pensé que podría haberlo bloqueado es TZConnection. Pero creo esto así: Connector: TZConnection.Create (nil); y desconectarlo Connector.Disconnect; y Connector.Free. -

6 Respuestas

Simple, no lo hagas. Hay una razón por la que el archivo está bloqueado. Por lo general, es un muy buena razón, como algún otro proceso (o incluso el suyo) que todavía lo usa. Descubra qué mantiene el archivo bloqueado (por ejemplo, utilizando Process Explorer) y, siempre que sea su proceso, asegúrese de haber liberado todo. P.ej FreeLibrary Si lo envía después LoadLibrary etc ...

Si es absolutamente necesario eliminar el archivo, intente DeleteFile y cuando eso falla llama MoveFileEx con MOVEFILE_DELAY_UNTIL_REBOOT para eliminar el archivo al reiniciar.

Puedes tener otro MoveFile or MoveFileEx en el medio para "renombrar" el archivo mientras está en uso (esto generalmente funciona en la misma partición). Pero esto solo es necesario si confía en el nombre del archivo y, por lo tanto, otra instancia de su programa podría bloquearse si el archivo antiguo (bloqueado) aún existe.

Aún así, son métodos para hacer lo que desee, pero no deben terminar en un código que se libera a los usuarios finales. Básicamente se reduce a la piratería que, por ejemplo, usaría subprocesos inyectados para cerrar / desbloquear archivos en la entidad que los mantiene bloqueados. Pero es de mala forma. Trate de evitarlo.

contestado el 16 de mayo de 11 a las 21:05

Tal vez tengas razón y realmente no debería hacer esto. En lo que respecta a sus comentarios sobre mover / eliminar el archivo, como le dije a Don, hacer esto después del arranque frustra el propósito de lo que estaba tratando de hacer. - usuario741875

@Craig: como comenté allí, falta el código importante en el medio. Básicamente lo que mantiene el archivo bloqueado. Y deberías publicarlo, porque es relevante. Es probable que haya un LoadLibrary y debería haber uno FreeLibrary para cada LoadLibrary. Y cuando llamas LoadLibrary varias veces, esto también puede afectar el recuento de referencias. - 0xC0000022L

Y uno debe esperar unos segundos después de la última biblioteca gratuita antes de intentar eliminar. (o al menos vuelva a intentarlo unos segundos más tarde si falla la primera vez). Los bloqueos a veces persisten, especialmente en unidades de red. - Marco van de Voort

Deberías usar mejor enlace estático del motor SQLite3 en lugar de depender de una dll externa. Incluyendo el .obj en su unidad .dcu SQLite3.

También agregará algunas características agradables como la capacidad de usar FastMM4 como el administrador de memoria para el motor SQlite3 también (acelerar), y potencialmente algunas características agradables de bajo nivel (como el cifrado).

Ver por ejemplo:

contestado el 17 de mayo de 11 a las 11:05

Aunque no puede eliminar un archivo que está en uso, puede cambiarle el nombre. Simplemente cambie el nombre de la dll a algo como SQLITE3.DELETE.guid. En el inicio, puede hacer que su aplicación intente eliminar cualquier archivo sqlite.delete. * Para que, una vez que el archivo se libere, desaparezca. -don

contestado el 16 de mayo de 11 a las 21:05

Mejor aún, una eliminación retrasada después de cambiar el nombre del archivo. Es decir MoveFile seguido por MoveFileEx con MOVEFILE_DELAY_UNTIL_REBOOT. Yo personalmente prefiero DeleteFile seguido por MoveFileEx con MOVEFILE_DELAY_UNTIL_REBOOT, aunque. - 0xC0000022L

Gracias por el consejo, pero necesito eliminarlo de inmediato, no programado para ser eliminado después del arranque. - usuario741875

@Craig: en este caso, todavía no ha publicado el código relevante que contiene el "misterio" de por qué el archivo está bloqueado en primer lugar. Probablemente sea tan simple como FreeLibrary. - 0xC0000022L

Parece que la LibraryLoader en ZPlainSqLite3.pas es lo que está cargando la DLL. Puede intentar ejecutar el código de la sección de finalización antes de intentar eliminar la DLL:

  if Assigned(LibraryLoader) then
     LibraryLoader.Free;

contestado el 16 de mayo de 11 a las 21:05

Primero debe asegurarse de que el archivo no esté todavía en uso. En tu caso, is todavía en uso porque la biblioteca de la base de datos cargó la DLL y aún no la ha liberado. Dado que ya se ha desconectado de la base de datos, es probable que aún no esté en lector activo usar, pero el sistema operativo no lo sabe: si se carga una DLL, el sistema operativo asume que todavía es necesaria y no permite la eliminación.

Cuando se conecta a la base de datos, Zeos encuentra el controlador de la base de datos correspondiente (en este caso, TZSQLiteDriver, de ZDbcSqLite.pas) y le pide que cargue sus funciones. Pero cuando tu desconectar de la base de datos, Zeos no solicita al controlador de la base de datos que descargue sus funciones. Eso sería un desperdicio en un programa típico, donde puede haber varias conexiones establecidas y destruidas durante la vida útil del programa.

Si está seguro de que no hay conexiones abiertas a la base de datos, puede descargar las funciones usted mismo. El cargador para SQLite está en ZPlainSqLite3.pas. Aunque podría liberar el objeto cargador por completo, eso puede causar problemas más adelante, ya que hay otras partes de Zeos que esperan que aún esté disponible cuando lo necesiten. En su lugar, dígale que se descargue:

ZPlainSqLite3.Loader.FreeNativeLibrary;

Eso hace que el objeto establezca banderas que indican que está descargado, por lo que si necesita usarlo nuevamente, recargará todo.


Si realmente quisieras ponerte elegante, podrías intentar escribir el tuyo propio. TZNativeLibraryLoader descendiente que obtiene automáticamente la DLL del recurso al cargar y elimina el archivo al descargar. Entonces no tendría que preocuparse por la duración de la conexión a la base de datos en el resto de su programa. Podrías conectarte y desconectarte como todos los demás.

contestado el 16 de mayo de 11 a las 21:05

Parece que no tengo ZPlainSqLite3.pas, ¿Windows Search tampoco devuelve el archivo? no importa, parece estar en ZPlainSqLiteDriver.pas - user741875

¿Me equivoco de nuevo, por alguna razón no tengo el archivo en mi instalación? Puedo ver cómo se ve aquí: koders.com/delphi/… pero no tengo esa unidad - user741875

Basado en el tarball que acabo de descargar hace media hora, está en zeosdbo_rework \ src \ plain. El archivo del controlador contiene el controlador, no el cargador. El conductor no le permite controlar el cargador, que es lo que necesita. Busque en su código fuente "sqlite3.dll"; ese es el archivo que debe contener el cargador. - Rob Kennedy

@Rob Gracias por la información, pero siguiendo el consejo de STATUS_ACCESS_DENIED creo que lo que intento hacer no es muy práctico. Sin embargo, he votado tu comentario por los consejos que también has compartido. - usuario741875

Derecha. Lo que eras tratando de que hacer era eliminar el archivo mientras aún estaba cargado, o con fuerza descargarlo. Mi respuesta te da una forma de evitar hacer eso. Te permite descargar la DLL graciosamente, de manera que el resto de su programa pueda ser plenamente consciente y manejado sin la posibilidad de fallar. - Rob Kennedy

Hay un par de soluciones disponibles que le permiten compilar las bibliotecas SQLite dentro su exe, en lugar de usar la DLL. Creo que sería un mucho mejor enfoque si realmente debe tener un solo archivo ejecutable. Lo que básicamente está tratando de duplicar extrayendo / eliminando la DLL es la funcionalidad de un instalador, y realmente no debería hacerlo. Ese enfoque solo pide problemas de soporte.

También tenga en cuenta que es posible que su aplicación deba ejecutarse con derechos de administrador para poder extraer el dll y guardarlo en el directorio de archivos del programa.

Si puede vivir con una DLL instalada junto con el ejecutable, puede cambiar el nombre del archivo DLL a otra cosa (es decir, Database.dll) y hacer cambios en el código zeos para que apunte al nuevo nombre de la DLL. Entonces, la funcionalidad de SQLite no sería evidente de inmediato.

contestado el 17 de mayo de 11 a las 09:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.