27

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

2 に答える 2

40

自分で答えます。

この行を 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 */
                ...
            }
      }
于 2011-02-12T11:23:04.940 に答える
5

私の状況はもう少し複雑で、上記の解決策はうまくいきませんでした。(それは 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;
        }
于 2015-02-20T08:24:45.427 に答える