Evitar tener una DLL al lado del EXE?

Iniciado por z3nth10n, 22 Febrero 2017, 17:26 PM

0 Miembros y 1 Visitante están viendo este tema.

z3nth10n

Hola buenas,

el error es simple, tengo un exe que utiliza una dll, que al compilarse (por medio de un batch) lo dejo "aislado" de sus correspondientes DLLs, realmente no queda aislado como tal, la DLL pasa a estar en otra carpeta, entonces, mi pregunta es si se puede hacer un link a esa DLL movida de sitio...

Según he leido con App.config es posible a hacerlo a subdirectorios, pero no a otro sitio:

http://stackoverflow.com/a/3577344/3286975

Y que por temas de seguridad no se puede hacer de otra forma:

https://social.msdn.microsoft.com/Forums/vstudio/en-US/4eb0026c-135b-4f4c-9ec2-0ef39a1b001a/referencing-a-dll-in-a-location-different-from-exe?forum=csharpgeneral

Entonces que me queda, utilizar Reflection para cargar esas DLLs?



Como veis, ahí dentro de la misma solución tengo la DLL (Librería de clases -> Lerp2API), y una aplicación de consola (Lerp2Console) que tiene como referencia a dicha DLL...

Vale, al compilarse, utilizo un Batch como venimos sabiendo dese antaño :xD este batch mueve las DLLs de tal forma que de pasar a estar asi:



Como veis, a la derecha, tenemos la consola con su "Copy Local", la Copy Local será borrada y a cambio se tomará la DLL que está en D:\LERP2DEV\Lerp2Dev Assets\Lerp2API\Build\
formando la jerarquía que podéis observar a la izquierda...

El problema está en que la consola está un nivel por debajo de la DLL que necesita para ejecutarse en vez de ser de al revés que sería como funcionaría...

Provocando este error:



El error y su solución es clara, pero quiero saber si se puede hacer de la forma que yo quiero, el código está en Github:

https://github.com/Lerp2Dev/Lerp2API/blob/c4562481509f874a019d9ea8700abde8069241c3/Project/Lerp2Console/Program.cs

Un saludo.

Interesados hablad por Discord.

Serapis

#1
Bla,bla,bla...

Eleкtro

Hola.

¿Tú lo que quieres es incluir (adjuntar, embedir) esa dll en tu compilación.exe?, ¿o por lo contrario lo que quieres es mantener el archivo.dll aislado de los demás ensamblados y en una carpeta distinta al directorio de trabajo de tu compilación.exe?.

Hay varias soluciones efectivas para ambos casos, pero aclárame ese punto por favor para no explicar cosas distintas en vano...

¡Saludos!








z3nth10n

Me refiero a la segunda opción, tengo el exe en una carpeta y la dll en otro como puedes ver :P

Interesados hablad por Discord.

Eleкtro

#4
Cita de: Ikillnukes en 22 Febrero 2017, 22:22 PMMe refiero a la segunda opción, tengo el exe en una carpeta y la dll en otro

Bueno. Como ya te mencioné por privado, puedes crear un enlace simbólico (SymLink o también llamado SoftLink) con la herramienta mklink.exe de Microsoft:

Sintaxis que debes usar:
MkLink.exe "C:\Enlace Destino.dll" "C:\Archivo Origen.dll"

Esto creará una especie de acceso directo de "cero" bytes con el que no tendrás problemas para cargar el archivo de origen en tu aplicación. Logicamente debes asignarle el mismo nombre al archivo simbólico que el nombre original de tu librería.dll.

Esta es la opción que más te recomiendo bajo mi criterio. Te permite mantener la misma estructura de archivos (reemplazando el archivo original por otro archivo "fantasma" y funcional), el resultado no ocupa nada de espacio, y no supone ningún impacto negativo en el rendimiento del S.O. Solamente el archivo simbólico y tú, tú y el archivo simbólico :xD.

Si quieres crear el enlace de forma programática desde .NET, puedes utilizar la función Win32 CreateSymbolicLink:

Aqui tienes un ejemplo de uso y definiciones para VB.NET (de mi framework ElektroKit que puedes encontrar a la venta en CodeCanyon):

Código (vbnet) [Seleccionar]
NativeMethods.CreateSymbolicLink("C:\Destination Link.ext", "C:\Source File.ext", NativeEnums.SymbolicLinkFlags.File)

Código (vbnet) [Seleccionar]
<SuppressUnmanagedCodeSecurity>
Public NotInheritable Class NativeMethods

   ''' ----------------------------------------------------------------------------------------------------
   ''' <summary>
   ''' Creates a symbolic link.
   ''' </summary>
   ''' ----------------------------------------------------------------------------------------------------
   ''' <remarks>
   ''' <see href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa363866%28v=vs.85%29.aspx"/>
   ''' </remarks>
   ''' ----------------------------------------------------------------------------------------------------
   ''' <param name="dstFilePath">
   ''' The path of the symbolic link to be created.
   ''' </param>
   '''
   ''' <param name="srcFilePath">
   ''' The path of the target for the symbolic link to be created.
   ''' <para></para>
   ''' If <paramref name="srcFilePath"/> has a device name associated with it,
   ''' the link is treated as an absolute link;
   ''' otherwise, the link is treated as a relative link.
   ''' </param>
   '''
   ''' <param name="flags">
   ''' Indicates whether the link target is a file or is a directory.
   ''' </param>
   ''' ----------------------------------------------------------------------------------------------------
   ''' <returns>
   ''' If the function succeeds, the return value is <see langword="True"/>.
   ''' <para></para>
   ''' If the function fails, the return value is <see langword="False"/>.
   ''' <para></para>
   ''' To get extended error information, call <see cref="Marshal.GetLastWin32Error"/>.
   ''' <para></para>
   ''' </returns>
   ''' ----------------------------------------------------------------------------------------------------
   <SuppressMessage("Microsoft.Interoperability", "CA1401:PInvokesShouldNotBeVisible", Justification:="Visible for API")>
   <DllImport("Kernel32.dll", SetLastError:=True)>
   Public Shared Function CreateSymbolicLink(ByVal dstFilePath As String,
                                             ByVal srcFilePath As String,
               <MarshalAs(UnmanagedType.I4)> ByVal flags As NativeEnums.SymbolicLinkFlags
   ) As <MarshalAs(UnmanagedType.I1)> Boolean
   End Function

   ''' <summary>
   ''' Prevents a default instance of the <see cref="NativeMethods"/> class from being created.
   ''' </summary>
   Private Sub New()
   End Sub

End Class


Código (vbnet) [Seleccionar]
Public NotInheritable Class NativeEnums

   ''' ----------------------------------------------------------------------------------------------------
   ''' <summary>
   ''' Indicates whether a symbolic link is a file or is a directory.
   ''' </summary>
   ''' ----------------------------------------------------------------------------------------------------
   ''' <remarks>
   ''' <see href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa363866%28v=vs.85%29.aspx"/>
   ''' </remarks>
   ''' ----------------------------------------------------------------------------------------------------
   Public Enum SymbolicLinkFlags As Integer

       ''' <summary>
       ''' The link target is a file.
       ''' </summary>
       File = &H0

       ''' <summary>
       ''' The link target is a directory.
       ''' </summary>
       Directory = &H1

   End Enum

   ''' <summary>
   ''' Prevents a default instance of the <see cref="NativeEnums"/> class from being created.
   ''' </summary>
   Private Sub New()
   End Sub

End Class


Nota: Recuerda que esta función de la API de Windows, como muchas otras, tiene una versión ANSI y otra Unicode (CreateSymbolicLinkA, CreateSymbolicLinkW) en caso de que necesites usar alguna en específico por el motivo que sea.




Lo que te comentó por encima el compañero @NEBIRE, consistiría en colocar el archivo.dll en uno de los directorios en el que el sistema operativo intentará buscar de forma automática las dependencias de tu executable. Es otra buena opción, pero yo lo recomendaría en otro tipo de circunstancias. Al estar hablando de una aplicación administrada, el cargador de dependencias de .NET lo más probable es que te diese problemas al intentar resolver la ubicación de una dependencia administrada que esté ubicada fuera del directorio de inicio de tu app, aunque la coloques en el directorio del sistema. Si descartas la opción de crear un enlace simbólico, entonces en lugar de esto lo que deberías hacer es registrar la dependencia en el GAC de Windows, pero bueno, te explicaré esta metodología por si quieres probar a ver si en tus circunstancias te funciona bien... o simplemente para que sepas algo más.

El orden de búsqueda de dependencias para aplicaciones de escritorio es el siguiente (el orden variará en el caso de que tengas activado el valor de registro SafeDllSearch):


  • 1. El directorio de inicio de la aplicación {startup dir}. Donde está alojada la aplicación.
  • 2. El directorio de trabajo actual de la aplicación {working dir}.
  • 3. Los directorios de sistema de 32 y 64 Bits.
      C:\Windows\SysWOW64\
      C:\Windows\System32\
  • 4. El directorio de sistema de 16 Bits.
      C:\Windows\System\
  • 5. El directorio de Windows
      C:\Windows\
  • 6. Los directorios que estén listados en las variables de entorno PATH.
      HKCU\Environment PATH
      HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment PATH

En la MSDN tienes toda la info que necesites conocer al respecto (para variar):

¡Saludos!








z3nth10n

#5
Hola buenas,

5 días después de responder y una vez más que resuelta la duda, te respondo. Lo primero gracias por decir lo de los Symbolic Links, y a ti tb Nebire por tu sugerencia.

Me quedé atascado un poco porque al leer por encima, me quedé solo con lo de:

Citar
Código (dos) [Seleccionar]
MkLink.exe "C:\Enlace Destino.dll" "C:\Archivo Origen.dll"

Y no leí lo del acceso directo. Una vez que hicimos ya lo hablamos en privado y le echaste un ojo más de cerca, me explicaste la sintaxis para hacerlo, que era la siguiente:

Código (dos) [Seleccionar]
mklink "D:\LERP2DEV\Lerp2Dev Assets\Lerp2API\Build\Console\Lerp2API.dll" "D:\LERP2DEV\Lerp2Dev Assets\Lerp2API\Build\Lerp2API.dll"

Un saludo.

PD: En el repositorio se han quedado unos cuantos scripts interesantes, que Elektro no recomendaria, pero que a mi me han servido:

https://github.com/Lerp2Dev/Lerp2API/tree/b72b5530c4f5f36fcb2a476df64e03b03f7d6009/Project/Lerp2Console

Más en concreto: Dynamicals.cs, Instancials.cs, NativeLinking.cs y Program - Copia.cs

Es el uso de dynamic para instancias y llamadas estáticas (Instancials sirve para crear de forma rápida y sencilla llamadas (mediante llamadas estáticas o bien mediante creación de nuevas instancias), Dyanicals simplemente alberga la parte de la creación de objetos a partir de llamadas estáticas, Activator (una clase nativa de Reflection) es la que se encarga de la parte de instancias).

Llamada estática:

Código (C#) [Seleccionar]
Console.WriteLine("Hola mundo");

Formato: Clase - Metodo

Llamada a través de una instancia:

Código (C#) [Seleccionar]
Clase clase = new Clase();
clase.Metodo();


Básicamente. NativeLinking.cs lo que hace es lo que nos propuso Elektro, pero de forma nátiva, en C#. :P

Ahora me he quedado trabado con el tema de los Sockets. :-\

Interesados hablad por Discord.