c# - studio - DLL infierno con SQLite
sqlite visual studio 2017 (3)
Algunos de nuestros usuarios tienen un problema con la versión de sqlite.interop.dll que se está cargando en tiempo de ejecución, y es un verdadero problema.
Antecedentes: una aplicación WPF creada para AnyCPU, implementada con SQlite .NET y sqlite.interop.dll versión 1.0.89. Implementamos dlls tanto x86 como x64 y empleamos la carga de demora incluida con SQLite. Esto ha estado bien hasta hace poco, cuando comenzamos a obtener algunos problemas de soporte técnico de usuarios que habían comprado, por lo general, nuevas máquinas Dell. Parece que hay una versión anterior de sqlite.interop.dll (v.1.0.80) que, de alguna manera , se está cargando en preferencia a la que enviamos. El error que obtenemos es un punto de entrada que falta, ''sqlite3_changes_interop''.
Lo que hemos intentado:
Cambiando la configuración para simplemente copiar la dll apropiada (x86 / 64) al mismo directorio que el ejecutable durante la instalación (es decir, no hay carpetas x86 / x64 separadas). Esto significa que ya no utilizamos la carga de demora, ya que la dll correcta está disponible en el directorio ejecutable (aunque no hemos deshabilitado explícitamente el mecanismo de carga de demora en sqlite.net). Esto no soluciona el problema ...
Cargar explícitamente sqlite.interop.dll cuando la aplicación se carga por primera vez. Una vez más, esto no parece solucionar el problema.
Parece que el orden de las ubicaciones de carga de dll ha cambiado un poco en los últimos años, y es posible que no tenga un buen control sobre él. Siempre asumí que una dll en el directorio ejecutable obtendría la primera preferencia, y que una dll que se había cargado explícitamente evitaría que la misma dll se vuelva a cargar durante el tiempo de vida de la aplicación, por lo que durante la vida de mi no puedo entender lo que está pasando aquí. .
¿Alguien puede arrojar alguna luz sobre lo que podría estar pasando aquí? El problema se agrava aún más por el hecho de que simplemente no puedo reproducir el problema localmente, por ejemplo, al colocar la versión incorrecta de la DLL en la ruta de mi sistema, etc. ¿Qué me hace pensar que quizás el GAC podría estar en juego?
Realmente atascado en este, por lo que cualquier ayuda sería genial.
Además, como último recurso, podría considerar volver a la misma versión 1.0.80, para que no tengamos este problema. ¿Alguien sabe dónde podríamos obtener versiones anteriores de sqlite.net y sqlite.interop.dll?
Editar - alguna información adicional:
El choque se debe a una copia de sqlite.interop.dll versión 1.0.80 que se instala con Dell Backup and Recovery. Esto se instala en todas las máquinas Dell nuevas, y los usuarios que instalan nuestro software en una máquina así experimentan este problema. Este software de Dell también utiliza System.Data.SQLite.dll.
La versión correcta de sqlite.interop.dll se encuentra en el mismo directorio que nuestro ejecutable, y todo lo que entiendo sobre la carga de dll sugiere que esto se debe cargar con preferencia.
Aunque todavía no hemos podido reproducir el problema localmente, parece que la versión incorrecta de interop.dll no se encuentra en la ruta. Además, la utilidad de copia de seguridad de Dell se ejecuta automáticamente en el inicio. ¿Alguien sabe de algún mecanismo posible por el cual esto podría estar enganchando en las solicitudes de carga dll y sirviendo el archivo incorrecto?
La línea de pensamiento actual es que podríamos construir nuestro propio System.Data.SQLite.dll y cambiar el código de carga de interoperabilidad a una versión específicamente nombrada (por ejemplo, sqlite.interop.1.0.89.dll). No es una buena solución avanzando, pero ...
Estamos tratando este problema exacto y la solución que encontramos es utilizar el paquete del sitio web System.Data.SQlite en lugar del paquete de nuget: https://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki
La dll agrupada tiene los ensamblados administrados y no administrados combinados, por lo que no hay necesidad de cargar dinámicamente el Sqlite.Interop.dll correcto para que no tenga el problema de versiones en conflicto en el dominio de aplicaciones.
Cuando utilice el ensamblaje incluido, debe incluir su propia lógica en el instalador de su aplicación para decidir qué dll copiar (x86 o x64).
No hemos tenido más problemas con las versiones en conflicto desde que usamos el ensamblaje empaquetado.
Nuestra aplicación tiene el mismo problema. Como mencionó, el problema es que Dell Backup and Recovery instala una extensión de shell que usa versiones antiguas de varias dll populares. Juegan al infierno con cualquier aplicación que inicie cuadros de diálogo de archivos y también use esas bibliotecas, porque las extensiones de shell cargan sus archivos DLL en su dominio de aplicación. La única solución que tenemos hasta ahora es decirles a los usuarios que desinstalen Dell Backup and Recovery.
Si obliga a su aplicación a cargar la biblioteca correcta como se menciona en dymanoid, entonces su aplicación se bloqueará cuando muestre un cuadro de diálogo de archivo (porque la extensión del shell se bloqueará). Si no lo haces, entonces tu aplicación se bloqueará cuando intente leer desde su base de datos.
Curiosamente, Dell Backup and Recovery es un infractor reincidente; También rompe QT5 de la misma manera. La solución recomendada de los chicos de QT es compilar su biblioteca de QT con un nombre diferente con la opción -qtnamespace [nombre]. Podríamos ser capaces de manipular algo así con system.data.sqlite, pero luego tendríamos que compilar nuestra propia versión.
Microsoft es consciente del problema , pero se ha negado a solucionarlo.
Desearía que los chicos de Dell hubieran implementado su extensión de shell de esta manera .
La solución de Portroit Pro , SONAR y AutoDesk para este problema también es desinstalar Dell Backup and Recovery.
Un rastro de pila típico del problema se ve en nuestra aplicación:
System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at System.Data.SQLite.UnsafeNativeMethods.sqlite3_open_interop(Byte[] utf8Filename, Int32 flags, IntPtr& db)
at System.Data.SQLite.SQLite3.Open(String strFilename, SQLiteConnectionFlags connectionFlags, SQLiteOpenFlagsEnum openFlags, Int32 maxPoolSize, Boolean usePool)
at System.Data.SQLite.SQLiteConnection.Open()
at STCommonShellIntegration.DataShellManagement.CreateNewConnection(SQLiteConnection& newConnection)
at STCommonShellIntegration.DataShellManagement.InitConfiguration(Dictionary`2 targetSettings)
at DBROverlayIcon.DBRBackupOverlayIcon.initComponent()
Entonces, en respuesta al comentario de Track, si desea detectar este problema en particular y avisar a los usuarios, podría hacer algo como esto:
AppDomain.CurrentDomain.UnhandledException += UEHandler;
//...
static void UEHandler(object sender, UnhandledExceptionEventArgs e){
var ex = e.ExceptionObject as Exception;
if( ex.ToString().Contains( "DBROverlayIcon" ){
//show some dialog here telling users to uninstall DBaR
}
}
SQLite.Interop.dll se carga de una manera difícil.
Al usar cualquier reflector, puede inspeccionar el método UnsafeNativeMethods.Initialize () en System.Data.SQLite.dll por usted mismo.
Algunas notas para demostrar, que es posible obtener algo interesante por reflexión (versión 1.0.89):
- Si SQLite.Interop.dll se coloca en el directorio base, se cargará
- Las variables de entorno PreLoadSQLite_BaseDirectory y PreLoadSQLite_UseAssemblyDirectory pueden afectar el proceso de carga
- SQLite.Interop.dll se puede buscar en subcarpetas predeterminadas (x86, x64, Win32, Itanium, WinCE)
- Se llama a Trace.WriteLine para informar la ruta seleccionada (no siempre)