AppDomain.CurrentDomain.AssemblyResolve请求一个<AppName>.resources程序集是什么意思?

27
使用由csharptest.net提供的代码如何将卫星程序集嵌入exe文件,我创建了一个自定义程序集解析器并将我的程序集嵌入到资源中。
我可以成功解析在其中使用的程序集,但是 AppDomain.CurrentDomain.AssemblyResolve 会请求特定为“AppName.resources”且版本号为“0.15.3992.31638”,区域为“en-US”,公钥标记为“null”的程序集“MyProgram.resources”,我不知道如何解决它。
我尝试禁用从资源中加载我的自定义程序集(将所有程序集dll放置在程序目录中),只启用 AppDomain.CurrentDomain.AssemblyResolve,但仍然要求该程序集。
我对此有点困惑,如果您能帮助我,我将非常感激。
这是我感兴趣的人的代码;
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;
}
2个回答

39

回答我的问题;

在AssemblyInfo.cs文件中添加这一行代码即可解决问题,解析器将不再请求资源。

[assembly: NeutralResourcesLanguageAttribute("en-US", UltimateResourceFallbackLocation.MainAssembly)]
尽管这是一个解决方法,但对于多语言应用程序需要仔细考虑。
更多信息: 这种方法在有非 en-US 区域设置的计算机上会失败。更好的方法是在程序集解析器上忽略资源。
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 */
                ...
            }
      }

我在某个地方读到,返回 null 在这种情况下没有帮助;你能确认一下吗? - lysergic-acid
1
更好的方法对我起作用了。让我有点困惑。谷歌一下,直接找到了一个SO答案。我喜欢它。 - Tony Hopkinson
我不理解“中性”检查背后的逻辑。如果文化是“中性”,它将尝试像正常情况下一样加载程序集,但是这样做不会失败,因为没有该名称的程序集吗? - osexpert

6
我的情况有点复杂,上述解决方案对我没有用(即更改AssemblyInfo.cs文件)。我已经将所有的表单和图像资源移动到一个单独的dll中,当使用任何图像时,都会抛出“filenotfoundexception”异常。重要的信息如下:
从.NET Framework 4开始,ResolveEventHandler事件会为所有程序集(包括资源程序集)引发。请参阅以下参考链接:https://msdn.microsoft.com/en-us/library/system.appdomain.assemblyresolve(v=vs.110).aspx。解决方法非常简单。如果在形式上请求资源文件“dllname.resources.dll”,则总是返回null。以下是我从其他示例中找到并适应的事件代码(如果使用该代码存在问题,请取消注释调试行)。 在类中添加此行。它用于防止多次加载dll。
    readonly static Dictionary<string, Assembly> _libs = new Dictionary<string, Assembly>();

这是事件方法。
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;
        }

我建议将测试 if (keyName.Contains(".resources")) 更改为 if (keyName.EndsWith(".resources")),因为如果您有一个名为“myApp.resourcesystem”的程序集,这将给您带来意外的结果。 - Neil Moss

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接