2010-03-15 8 views
26

¿Cómo descargo una DLL que se ha cargado usando DllImport en C#?Descargar una DLL cargada con DllImport

+0

Si tiene la intención de hacerlo solo para guardar unos pocos KB de memoria de un módulo cargado, entonces no vale la pena. CLR lo hace por sí solo siempre que se descargue appDomain. Me gustaría saber si tiene algún motivo real para intentar descargar el dll nativo. – RBT

Respuesta

21

La forma más confiable de descargar una DLL no administrada de un proceso que se cargó mediante una declaración de [DllImport] pinvoke es cargarla usted mismo, nuevamente, pinvoking LoadLibrary(). Eso le da un manejo confiable a la DLL y funciona correctamente incluso si el nombre del módulo de la DLL es ambiguo. No tiene ningún efecto en tiempo de ejecución, que no sea el cargador de Windows aumentar el número de referencia interna en la DLL de 1 a 2.

A continuación, puede PInvoke FreeLibrary() dos veces para disminuir la cuenta de referencia a 0, pasando el IntPtr que obtuvo de LoadLibrary(). Eso descarga la DLL, así como cualquier DLL dependiente que se cargó.

Mira que obtendrá el fracaso muy desagradable cuando intenta PInvoke cualquier función exportada de la DLL de nuevo, cualquier momento después de hacer esto. El marshaller pinvoke no sabe que la DLL ya no está presente y llamará a la función a la dirección que cree que todavía es válida. Que bombardea su programa con una excepción AccessViolation si tiene suerte. O ejecuta un código de código completamente aleatorio si no tiene tanta suerte y el espacio de direcciones anteriormente ocupado por el DLL se volvió a utilizar por otro archivo DLL. Todo puede pasar entonces, nada de eso es bueno.

+1

-1: Está hablando claramente de un dll nativo. – leppie

+4

Aún no respondiste la pregunta de subbu. Estaba preguntando cómo descargar una DLL que cargó DllImport, no cargándola manualmente a través de LoadLibrary(). – Ants

+1

@Antes: buen punto. Siéntase libre de llenar los huecos ... –

7

Esto debería liberar un módulo cargado previamente cuando llamó a la función P/Invocar.

[DllImport("kernel32", SetLastError=true)] 
static extern bool FreeLibrary(IntPtr hModule); 

public static void UnloadModule(string moduleName) 
{ 
    foreach(ProcessModule mod in Process.GetCurrentProcess().Modules) 
    { 
     if(mod.ModuleName == moduleName) 
     { 
      FreeLibrary(mod.BaseAddress); 
     } 
    } 
} 
+3

Eso * normalmente * funciona. Pero los nombres de los módulos pueden ser ambiguos, podría liberar la DLL incorrecta. –

+0

Hans tiene razón. Entonces quizás mejor usando Path.GetFileName (mod.FileName) en lugar de mod.ModuleName? – Peter

+0

O agárrate al puntero que obtienes de LoadLibrary y compara BaseAddress con eso. – yoyo

2

Sobre la base de Peters recomendación Esto funciona para mí:

[DllImport("kernel32", SetLastError = true)] 
    private static extern bool FreeLibrary(IntPtr hModule); 

    public static void UnloadImportedDll(string DllPath) 
    { 
     foreach (System.Diagnostics.ProcessModule mod in System.Diagnostics.Process.GetCurrentProcess().Modules) 
     { 
      if (mod.FileName == DllPath) 
      { 
       FreeLibrary(mod.BaseAddress); 
      } 
     } 
    } 
-1

Desde que llegué a través de la información aquí, mientras yo estaba mirando a su alrededor para obtener información Calculo que contribuiré de vuelta lo que terminé haciendo para arreglar un problema con el Sixense SDK en OSX IN UNITY. Verá allí una implementación de cargar dinámicamente/descarga de una dylib en OSX:

https://gist.github.com/amirebrahimi/d7b02c01bcd3ca7da144

-1

Sólo en caso de si eres fan de la programación funcional, entonces usted puede utilizar LINQ para lograr lo que @ IllidanS4 ha sugerido:

[DllImport("kernel32", SetLastError=true)] 
static extern bool FreeLibrary(IntPtr hModule); 

public static void UnloadModule(string moduleName) 
{ 
    var loadedAssemblyModule = 
      Process.GetCurrentProcess().Modules.OfType<ProcessModule>() 
       .FirstOrDefault(x => x.ModuleName == moduleName); 

    if (loadedAssemblyModule != null) 
     FreeLibrary(loadedAssemblyModule.BaseAddress); 
} 
Cuestiones relacionadas