2010-12-06 23 views
21

usando el código How to embed a satellite assembly into the EXE file provisto por csharptest.net, he creado un solucionador de conjuntos personalizado e incrustó mis ensamblajes en mis recursos.AppDomain.CurrentDomain.AssemblyResolve solicitando un ensamblado de <AppName> .resources?

Puedo resolver con éxito mis ensamblados utilizados en pero de alguna manera AppDomain.CurrentDomain.AssemblyResolve solicita un ensamblado llamado 'AppName.resources' específicamente "MyProgram.resources, Version = 0.15.3992.31638, Culture = en-US, PublicKeyToken = null "que no sé cómo resolver?

He intentado deshabilitar la carga de mis ensamblajes personalizados desde los recursos (coloqué todos los dll de mi ensamblado en el directorio de programas) y acabo de habilitar AppDomain.CurrentDomain.AssemblyResolve, pero aún estaba pidiéndolo.

Estoy un poco confundido acerca de esto, apreciaré mucho si me pueden ayudar en esto.

Aquí está mi código para los interesados;

static Assembly ResolveAssemblies(object sender, ResolveEventArgs args) 
{ 
    Assembly assembly = null; 
    string name = args.Name.Substring(0, args.Name.IndexOf(',')); 
    if (name == "MyProgram.resources") return null; 
    else name = string.Format("MyProgram.Resources.Assemblies.{0}.dll", name); 

    lock (_loadedAssemblies) 
    { 
     if (!_loadedAssemblies.TryGetValue(name, out assembly)) 
     { 
      using (Stream io = Assembly.GetExecutingAssembly().GetManifestResourceStream(name)) 
      { 
       if (io == null) 
       { 
        MessageBox.Show("MyProgram can not load one of it's dependencies. Please re-install the program", string.Format("Missing Assembly: {0}", name), MessageBoxButtons.OK, MessageBoxIcon.Error); 
        Environment.Exit(-1); 
       } 
       using (BinaryReader binaryReader = new BinaryReader(io)) 
       { 
        assembly = Assembly.Load(binaryReader.ReadBytes((int)io.Length)); 
        _loadedAssemblies.Add(name, assembly); 
       } 
      } 
     } 
    } 

    return assembly; 
} 

Respuesta

32

Respondiendo por mi cuenta;

Al agregar esta línea a AssemblyInfo.cs, se resuelve y ya no se le pedirán recursos a resolver.

[assembly: NeutralResourcesLanguageAttribute("en-US", UltimateResourceFallbackLocation.MainAssembly)] 

Aunque se trata de una solución temporal, se deben considerar cuidadosamente las aplicaciones de varios idiomas.

Más información:

Este enfoque no para máquinas con cultivos no en-US. Un mejor enfoque es ignorar los recursos en el resolver ensamblador;

public Assembly Resolver(object sender, ResolveEventArgs args) 
     { 
      lock (this) 
      { 
       Assembly assembly; 
       AssemblyName askedAssembly = new AssemblyName(args.Name); 

       string[] fields = args.Name.Split(','); 
       string name = fields[0]; 
       string culture = fields[2]; 
       // failing to ignore queries for satellite resource assemblies or using [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.MainAssembly)] 
       // in AssemblyInfo.cs will crash the program on non en-US based system cultures. 
       if (name.EndsWith(".resources") && !culture.EndsWith("neutral")) return null; 

       /* the actual assembly resolver */ 
       ... 
      } 
     } 
+1

+1 muchas gracias, muy útil! – smirkingman

+0

Leí en alguna parte que devolver nulo no ayudará en este caso; puedes confirmar ? –

+1

"El mejor enfoque funcionó para mí". Me hizo sacudir mi cabeza eso por un momento. Google y directamente a una respuesta SO. Me gusta. –

3

Mi situación era un poco más compleja y la solución anterior no funcionó para mí. (Eso está cambiando el archivo AssemblyInfo.cs)

He movido todos mis recursos de formulario y de imagen a un dll independiente y, en el momento en que se utiliza cualquiera de las imágenes, se lanza la excepción 'filenotfoundexception'.

La información importante es la siguiente:
A partir de .NET Framework 4, se genera el evento ResolveEventHandler para todos los ensamblados, incluidos los ensamblados de recursos. Ver la siguiente referencia

https://msdn.microsoft.com/en-us/library/system.appdomain.assemblyresolve(v=vs.110).aspx

La solución resultó ser muy simple. Si se solicita un archivo de recursos con el formato 'dllname.resources.dll', siempre devuelve null;

Aquí está el código del evento que he adaptado de otras muestras encontradas. (He comentado las líneas de depuración - descomprímalas si tiene algún problema al usar el código.

Agregue esta línea en su clase. Se usa para evitar cargar un dll más de una vez

readonly static Dictionary<string, Assembly> _libs = new Dictionary<string, Assembly>(); 

Este es el método del evento.

private static Assembly OnAssemblyResolve(object sender, ResolveEventArgs args) 
     { 
      Assembly assembly = null; 
      string keyName = new AssemblyName(args.Name).Name; 
      if (keyName.Contains(".resources")) 
      { 
       return null; // This line is what fixed the problem 
      } 
      if (_libs.ContainsKey(keyName)) 
      { 
       assembly = _libs[keyName]; // If DLL is loaded then don't load it again just return 
       return assembly; 
      } 

      string dllName = DllResourceName(keyName); 
      //string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames(); // Uncomment this line to debug the possible values for dllName 
      using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(dllName)) 
      { 
       if (stream == null) 
       { 
        Debug.Print("Error! Unable to find '" + dllName + "'"); 
        // Uncomment the next lines to show message the moment an assembly is not found. (This will also stop for .Net assemblies 
        //MessageBox.Show("Error! Unable to find '" + dllName + "'! Application will terminate."); 
        //Environment.Exit(0); 
        return null; 
       } 

       byte[] buffer = new BinaryReader(stream).ReadBytes((int) stream.Length); 
       assembly = Assembly.Load(buffer); 

       _libs[keyName] = assembly; 
       return assembly; 
      } 
     } 

     private static string DllResourceName(string ddlName) 
     { 
      if (ddlName.Contains(".dll") == false) ddlName += ".dll"; 

      foreach (string name in Assembly.GetExecutingAssembly().GetManifestResourceNames()) 
      { 
       if (name.EndsWith(ddlName)) return name; 
      } 
      return ddlName; 
     } 
Cuestiones relacionadas