c++ - studio - ¿Puedo usar dos versiones incompatibles de la misma DLL en el mismo proceso?
microsoft dll (7)
Estoy usando dos bibliotecas comerciales que son producidas por el mismo proveedor, llamadas VendorLibA y VendorLibB. Las bibliotecas se distribuyen como muchas DLL que dependen de la versión del compilador (por ejemplo, VC7, VC8). Ambas bibliotecas dependen de otra biblioteca, producida por este proveedor, llamada VendorLibUtils y contenida en una DLL.
El problema: VendorLibA usa una versión diferente de VendorLibUtils que VendorLibB. Las dos versiones no son compatibles con binarios, e incluso si lo fueran, sería una mala idea usar una versión incorrecta.
¿Hay alguna manera de que pueda usar las dos bibliotecas en el mismo proceso?
Nota: LoadLibrary no puede resolver esto ya que mi proceso no es el que está importando VendorLibUtils.
EDITAR: Olvidé mencionar lo obvio, no tengo que obtener el código fuente para ninguna de las bibliotecas comerciales y probablemente nunca lo haga ( suspiro ).
EDITAR: La alternativa btw es hacer esto: Cómo combinar aplicaciones GUI en Windows
En realidad, es posible cargar implícitamente diferentes versiones de un dll en un único proceso.
Hacer esto implica:
Creando dos ensambles, cada uno conteniendo una versión de la DLL que debe cargarse varias veces. Suena complicado, pero prácticamente implica poco más que crear (2) subcarpetas con nombre, cada una con un archivo .manifest que contiene algún xml y su propia copia del dll. Entonces, VendorUtilsAssemblyV1 y VendorUtilsAssemblyV2
Hacer que cada dll dependiente use el mecanismo de ensamblaje para resolver la dependencia implícita, al agregar una directiva assemblyDependency que identifica explícitamente VendorUtilsAssemblyV1 o V2.
Hay algunas opciones para el punto 2. Si los archivos VendorLibA y VendorLibB no contienen sus propios manifiestos, puede simplemente agregar los archivos manifest con la directiva dependiente dependiente necesaria llamada VendorLibA.2.dll.manifest y VendorLibB.2.dll.manifest. Si ya contienen manifiestos (probablemente para vincular al VS-200 o VS2008 C-Runtime), utilice la herramienta MT.EXE para fusionarse en la nueva dependencia.
Como no está utilizando VendorLibUtils directamente, asumo que no puede usar LoadLibrary, etc.
Si las DLL de VendorLibUtils solo tienen exportaciones por ordinal, probablemente podría cambiar el nombre de una de las bibliotecas y parchar el VendorLib X correspondiente para usar un nombre de archivo diferente para sus importaciones.
Si los DLL de VendorLibUtils tienen uno o más símbolos exportados con los mismos nombres, es posible que necesite parchear las tablas de importación y exportación, pero ¡ojalá no! :-)
Creo que su opción más prometedora es quejarse en voz alta al vendedor que distribuye productos mutuamente incompatibles. Eso más bien va en contra de la idea de una DLL.
No puedes simplemente poner las DLL en directorios diferentes. Una vez que se carga una DLL con un nombre dado, todos los demás intentos de cargar otra DLL con el mismo nombre de módulo simplemente usarán la que ya está cargada, incluso si las rutas son diferentes.
A partir de eso, podemos concluir que para cargar dos copias de VendorLibUtils, una copia debe tener un nombre diferente. No puede simplemente cambiar el nombre del archivo DLL; el código en su programa no sabrá buscar el archivo diferente. Por lo tanto, tal vez haya una manera de editar la tabla de importación de VendorLibB para hacer que piense que las funciones que necesita están en VendorLibUtilsB.dll en lugar de solo VendorLibUtils.dll. Me temo que no conozco ninguna utilidad que pueda hacer eso, pero tengo pocas dudas de que es posible hacerlo.
No soy un experto en DLL, pero la única forma en que lo veo posible sería usar LoadLibrary()
y cargar explícitamente las DLL. Luego puede colocar las funciones / clases, etc. en espacios de nombres separados usando GetProcAddress()
.
HMODULE v1 = LoadLibrary(_T("libv1_0.dll"));
libv1_0::fun_in_lib = reinterpret_cast<FUNTYPE>(GetProcAddress(v1, _T("fun_in_lib"));
y
HMODULE v2 = LoadLibrary(_T("libv2_0.dll"));
libv2_0::fun_in_lib = reinterpret_cast<FUNTYPE>(GetProcAddress(v2, _T("fun_in_lib"));
Que esto funcione o no todavía depende de la biblioteca, por lo que puede funcionar o no, pero hasta donde sé es la única posibilidad.
Como mencionó otra persona, puede cambiar el nombre de una de las copias de VendorLibUtils y modificar la tabla de importación de la DLL VendorLib asociada para vincularla, en lugar de la VendorLibUtils.dll con la que se creó.
Hay algunas herramientas que le permiten editar archivos EXE / DLL de esta manera. CFF Explorer es bastante decente y permite editar la tabla de importación. Si abre la DLL VendorLib en ella y va a la sección Importar Directorio (en el árbol de la izquierda), verá una lista de módulos en la parte superior de la ventana principal. Puede cambiar el nombre del módulo haciendo doble clic en su nombre. Luego solo guarda la DLL y ahora debería usar su DLL VendorLibUtils renombrada.
Por supuesto, esto supone que VendorLib utiliza la tabla de importación para acceder a VendorLibUtils, que puede no tener; puede usar LoadLibrary
/ GetProcAddress
, en cuyo caso no verá una entrada de tabla de importación para VendorLibUtils.
En realidad, si VendorLib usa la tabla de importación pero también usa LoadLibrary
para acceder al VendorLibUtils DLL en algunos lugares (lo he visto hecho), esos lugares seguirán utilizando el incorrecto. Si cambia el nombre de ambas bibliotecas, al menos puede ver un error si este es el caso (ya que una DLL con el nombre original no existirá ahora). Hay una manera de lidiar con esto si ocurre, pero comienza a ser bastante complicado en este punto, así que no daré más detalles a menos que realmente desee / necesite saber.
¿Quiere decir que tiene una situación similar a MSVCRT80.DLL y MSVCRT90.DLL? Hay una buena razón por la que Microsoft numeró estas DLL. Si ambas se llamaran MSVCRT.DLL, solo una de ellas se cargaría en un solo proceso.
Tuve un problema similar. Específicamente, quería usar un PyQt de un intérprete de Python incrustado en una aplicación que estaba usando una versión incompatible de Qt. Había dos Qt DLL utilizados por la aplicación principal: QtCore.dll y QtGui.dll.
Cuando cargaba PyQt desde el intérprete de Python incorporado, obtenía un error:
ImportError: DLL load failed: The specified procedure could not be found.
Esto ocurrió en la línea:
from PyQt4 import QtGui
El problema es que una vez que se carga un QtGui.dll incompatible en el espacio de proceso de la aplicación principal, cualquier referencia a QtGui.dll (por ejemplo, desde el archivo QtGui.pyd) es incorrecta.
Lo que sucedió después, no me enorgullezco.
Primero
QtGui4.dll
nombre deQtGui4.dll
en la distribución de PyQt aQtGuiX.dll
y luegoQtCore4.dll
el nombre deQtCore4.dll
aQtCoreX.dll
. Tenga en cuenta que el cambio de nombre mantuvo el mismo número de caracteres, esto es importante.Luego abrí el archivo
QtGui.pyd
en Notepad ++ y reemplacé todas las referencias de texto sin formato deQtGui4.dll
aQtGuiX.dll
y deQtCore4.dll
aQtCoreX.dll
. Repetí el proceso para los archivos:QtCore.pyd
,QtGuiX.dll
yQtCoreX.dll
.Finalmente, verifiqué que mi aplicación de prueba PyQt todavía funcionaba. ¡Lo hizo! Luego intenté ejecutar la aplicación de prueba PyQt desde el intérprete de Python incorporado, y funcionó también.
Entonces, parece funcionar en un par de casos triviales. Espero que necesite repetir el proceso para todas las DLL y PYD en la distribución de PyQt.
Probablemente esta no sea la forma correcta de hacer las cosas, pero no puedo pensar en ninguna razón concreta sobre cómo podría explotar (más que si cambio la longitud del nombre del archivo).
Acredita (o culpa) a otros en el hilo por inspirar este terrible cuento.