2009-09-02 19 views
15

¿Cuál es la mejor forma de comprobar si un archivo DLL es una DLL Win32 o si es un ensamblado CLR? En el momento en que utilizo este códigoLa mejor manera de comprobar si un archivo DLL es un conjunto CLR en C#

try 
    { 
     this.currentWorkingDirectory = Path.GetDirectoryName(assemblyPath); 

     //Try to load the assembly. 
     assembly = Assembly.LoadFile(assemblyPath); 

     return assembly != null; 
    } 
    catch (FileLoadException ex) 
    { 
     exception = ex; 
    } 
    catch (BadImageFormatException ex) 
    { 
     exception = ex; 
    } 
    catch (ArgumentException ex) 
    { 
     exception = ex; 
    } 
    catch (Exception ex) 
    { 
     exception = ex; 
    } 

    if (exception is BadImageFormatException) 
    { 
     return false; 
    } 

Pero me gusta comprobar antes de la carga, porque no quiero que esas excepciones (tiempo).

¿Hay una manera mejor?

Respuesta

17

comprobar el encabezado PE:

cabecera DOS comienza en 0x0, el valor DWORD en 0x3C contiene un puntero a la firma PE (normalmente 0x80), que es de 4 bytes, los próximos 20 bytes es la COFF cabecera y entonces no es el encabezado PE (en 0x9. el encabezado de PE es 224 bytes y contiene el directorio de datos (a las 96 bytes en la cabecera PE = 0xf. el entrada 15a (en 0x16 es el encabezado CLR Descriptor (a veces llamado COMDescriptor, pero esto no tiene nada que ver con COM). Si esto es vacío (es decir, 0 en los 8 bytes de 0x168 a 0x16f), entonces el archivo no es un ensamblado .NET . Si desea comprobar si es es una DLL COM, entonces debería mirar para ver si exporta GetClassObject.

Ref.

ACTUALIZACIÓN: hay una manera más '.NET' para lograrlo:

Uso Module.GetPEKind método y comprobar el PortableExecutableKinds enumeración:

NotAPortableExecutableImage El archivo no está en el ejecutable portátil (PE) formato de archivo.

ILOnly El ejecutable sólo contiene lenguaje intermedio de Microsoft (MSIL), y es por lo tanto neutral con respecto a plataformas de 32 bits o de 64 bits.

Required32Bit El ejecutable se puede ejecutar en una plataforma de 32 bits, o en las Windows de 32 bits en Windows (WOW) ambiente en una plataforma de 64 bits.

PE32Plus El ejecutable requiere una plataforma de 64 bits.

Unmanaged32Bit El archivo ejecutable contiene código puro no administrado.

+8

que estoy tratando de hacer algo similar a la OP. Esta solución suena genial, pero ¿cómo se puede obtener una instancia del Módulo sin llamar a Assembly.LoadFile (que arroja una BadImageFormatException para dlls que no son CLR antes de poder llamar a GetPEKind)? –

+1

@Simon: por supuesto, si es de su interés, ¿qué tal si escribe y agrega algún código como respuesta? –

+2

@Mitch: Ok tal vez el voto negativo fue un poco apresurado. Traté de deshacerlo, pero el tiempo de espera ha transcurrido. Lo siento. no dude en rechazar algunas de mis respuestas :). Intenté que esto funcionara. Aunque me pareció que la pregunta de Pauls era válida. Module.GetPEKind es instancia. Entonces, para obtener un Módulo, debe llamar al ensamblaje Assembly.Load. que lanzará una excepción. Dado que la pregunta explícitamente dice "sin excepciones", parecería que esta respuesta es incompleta o incorrecta. – Simon

2

Enfrentado con el mismo problema en el pasado, recurrí al uso de su enfoque de reflexión porque la alternativa es leer manualmente el encabezado PE like this. Parecía exagerado para mi escenario, pero puede ser útil para ti.

0

No especificó si tiene que hacer esto en el código, o si solo necesita saber si un archivo que está viendo en su sistema es un ensamblado .NET (que tal vez crea que requiere que escriba tu propio código para hacerlo). Si es el último, puede usar Dependency Walker para ver si tiene una dependencia en MSCOREE.dll, que es el motor de tiempo de ejecución .Net.

+0

Gracias, pero necesito esto para evitar la excepción de reflexión. – schoetbi

-1

puedes leer los dos primeros bytes del archivo, si los bytes son "MZ", entonces intenta leer el nombre de ensamblado para determinar (microsoft slow way) la validez de la asamblea.

public static bool isValidAssembly (string sFileName) 
    { 
     try 
     { 
      using (FileStream fs = File.OpenRead(sFileName)) 
      { 
       if ((fs.ReadByte() != 'M') || (fs.ReadByte() != 'Z')) 
       { 
        fs.Close(); 
        return false; 
       } 
       fs.Close(); 
      } 

      // http://msdn.microsoft.com/en-us/library/ms173100.aspx 
      object foo = SR.AssemblyName.GetAssemblyName(sFileName); 
      return true; 
     } 
     catch 
     { 
      return false; 
     } 
    } 
5

Si un ensamblado se carga por ejemplo Assembly.LoadFile(dotNetDllorExe) y no lanza ninguna excepción, es un ensamblado de .NET válido. Si no es así, arrojará una "BadImageFormatException".

La idea de verificar el estado del tiempo de un archivo es ensamblarlo o no cargándolo y verificando si se produce una excepción o no; no parece estar muy limpio Después de todas las excepciones se supone que se utilizarán excepcionalmente.


Los ensamblados .NET son archivos regulares de Win32 PE, el sistema operativo no distingue entre ensamblados .NET y archivos binarios ejecutables de Win32, son los mismos archivos PE normales. Entonces, ¿cómo funciona el sistema si un DLL o EXE es un ensamblado administrado para cargar el CLR?

Valida el encabezado del archivo para comprobar si se trata de un ensamblaje administrado o no. En la Partición II de Especificaciones de ECMA - Metadatos que se envía junto con .NET SDK, verá que hay un encabezado de CLI por separado en el Formato de PE. Es el 15º directorio de datos en los encabezados opcionales PE. Entonces, en términos simples, si tenemos valor en este directorio de datos, significa que este es un ensamblado .NET válido, de lo contrario no lo es.

internal static class PortableExecutableHelper 
{ 
    internal static bool IsDotNetAssembly(string peFile) 
    { 
     uint peHeader; 
     uint peHeaderSignature; 
     ushort machine; 
     ushort sections; 
     uint timestamp; 
     uint pSymbolTable; 
     uint noOfSymbol; 
     ushort optionalHeaderSize; 
     ushort characteristics; 
     ushort dataDictionaryStart; 
     uint[] dataDictionaryRVA = new uint[16]; 
     uint[] dataDictionarySize = new uint[16]; 


     Stream fs = new FileStream(peFile, FileMode.Open, FileAccess.Read); 
     BinaryReader reader = new BinaryReader(fs); 

     //PE Header starts @ 0x3C (60). Its a 4 byte header. 
     fs.Position = 0x3C; 

     peHeader = reader.ReadUInt32(); 

     //Moving to PE Header start location... 
     fs.Position = peHeader; 
     peHeaderSignature = reader.ReadUInt32(); 

     //We can also show all these value, but we will be  
     //limiting to the CLI header test. 

     machine = reader.ReadUInt16(); 
     sections = reader.ReadUInt16(); 
     timestamp = reader.ReadUInt32(); 
     pSymbolTable = reader.ReadUInt32(); 
     noOfSymbol = reader.ReadUInt32(); 
     optionalHeaderSize = reader.ReadUInt16(); 
     characteristics = reader.ReadUInt16(); 

     /* 
      Now we are at the end of the PE Header and from here, the 
         PE Optional Headers starts... 
       To go directly to the datadictionary, we'll increase the  
       stream’s current position to with 96 (0x60). 96 because, 
         28 for Standard fields 
         68 for NT-specific fields 
      From here DataDictionary starts...and its of total 128 bytes. DataDictionay has 16 directories in total, 
      doing simple maths 128/16 = 8. 
      So each directory is of 8 bytes. 
         In this 8 bytes, 4 bytes is of RVA and 4 bytes of Size. 

      btw, the 15th directory consist of CLR header! if its 0, its not a CLR file :) 
    */ 
     dataDictionaryStart = Convert.ToUInt16(Convert.ToUInt16(fs.Position) + 0x60); 
     fs.Position = dataDictionaryStart; 
     for (int i = 0; i < 15; i++) 
     { 
      dataDictionaryRVA[i] = reader.ReadUInt32(); 
      dataDictionarySize[i] = reader.ReadUInt32(); 
     } 
     if (dataDictionaryRVA[14] == 0) 
     { 
      Console.WriteLine("This is NOT a valid CLR File!!"); 
      return false; 
     } 
     else 
     { 
      Console.WriteLine("This is a valid CLR File.."); 
      return true; 
     } 
     fs.Close(); 
    } 
} 

ECMA Ref, Blog Ref

0

Se podría utilizar algo como:

 AssemblyName assemblyName = null; 

     try 
     { 
      assemblyName = AssemblyName.GetAssemblyName(filename); 
     } 
     catch (System.IO.FileNotFoundException ex) 
     { 
      throw new Exception("File not found!", ex); 
     } 
     catch (System.BadImageFormatException ex) 
     { 
      throw new Exception("File is not an .Net Assembly.", ex); 
     } 

Por favor, también echa un vistazo: https://msdn.microsoft.com/en-us/library/ms173100.aspx

Cuestiones relacionadas