AppDomain.CurrentDomain.AssemblyResolve 请求.resources 程序集?

发布于 2024-10-06 03:31:18 字数 1743 浏览 11 评论 0原文

使用代码如何嵌入卫星程序集到 csharptest.net 提供的 EXE 文件 中,我创建了一个自定义程序集解析器并将程序集嵌入到我的资源中。

我可以成功解析我使用的程序集,但不知何故 AppDomain.CurrentDomain.AssemblyResolve 要求一个名为“AppName.resources”的程序集,特别是“MyProgram.resources,Version = 0.15.3992.31638,Culture = en-US,PublicKeyToken = null”,我不这样做不知道如何解决?

我尝试禁用从资源加载自定义程序集(将所有程序集 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;
}

using the code How to embed a satellite assembly into the EXE file provided by csharptest.net, I've created a custom assembly resolver and embedded my assemblies in my resources.

I can successfully resolve my assemblies used in but somehow AppDomain.CurrentDomain.AssemblyResolve asks for an assembly called 'AppName.resources' specifically "MyProgram.resources, Version=0.15.3992.31638, Culture=en-US, PublicKeyToken=null" which i don't know how to resolve?

I've tried to disable loading my custom assemblies from resources (placed all my assembly dll's in program directory) and just enabled AppDomain.CurrentDomain.AssemblyResolve, but it was still asking for it.

I'm a bit confused about this, will appreciate a lot if you can help me on this.

Here's my code for interested ones;

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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

梦里梦着梦中梦 2024-10-13 03:31:18

我自己回答;

将此行添加到 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 */
                ...
            }
      }

Answering on my own;

Adding this line to AssemblyInfo.cs solves it and resolver will not get asked for resources any-more.

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

Though this is a work-around should be carefully considered multi-language applications.

More Info:

This approach fails for machines with non en-US cultures. A better approach is ignoring resources on assembly resolver;

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 */
                ...
            }
      }
ぽ尐不点ル 2024-10-13 03:31:18

我的情况有点复杂,上述解决方案对我不起作用。 (即更改 AssemblyInfo.cs 文件)

我已将所有表单和图像资源移动到单独的 dll 中,并且使用任何图像时都会引发“filenotfoundexception”异常。

重要信息如下:
从 .NET Framework 4 开始,为所有程序集(包括资源程序集)引发 ResolveEventHandler 事件。请参阅以下参考

https:// msdn.microsoft.com/en-us/library/system.appdomain.assembleresolve(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;
        }

My situation was a bit more complex and the above solution did not work for me. (That is changing the AssemblyInfo.cs file)

I have moved all my form and image resources to a seperate dll and the moment any of the images are used the 'filenotfoundexception' exception is thrown.

The important information is the following:
Beginning with the .NET Framework 4, the ResolveEventHandler event is raised for all assemblies, including resource assemblies. See the following reference

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

The solution turned out to be very simple. If a resource file is requested in the form 'dllname.resources.dll' always return null;

Here is the event code that I have adapted from other samples found. (I have commented the debugging lines - un-comment them if you have a problem using the code.

Add this line in your class. It is used to prevent loading a dll more than once

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

This is the event method.

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;
        }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文