3

アセンブリ内を覗いていくつかの属性メタデータを取得するカスタム MSBuild タスクがあります。

Assembly assembly = Assembly.ReflectionOnlyLoadFrom(AssemblyFile)

これは、自動化されたビルド/リリース プロセスで使用され、クラス ライブラリ、コンソール アプリ、および Web プロジェクトによって使用および参照されるアセンブリに対して完全に機能しています。MSBuild タスクは、別の MSBuild プロセスがプロジェクトをコンパイルした後に呼び出されます。

昨日、この特定のアセンブリ (.NET 3.5 クラス ライブラリ) を参照する WPF プロジェクトを追加したところ、機能しなくなりました。

System.IO.FileLoadException: API restriction: The assembly 'file:///bogus.dll' has already loaded from a different location. 
It cannot be loaded from a new location within the same appdomain. 
at System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection) 
at System.Reflection.Assembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection) 
at System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) 
at System.Reflection.Assembly.InternalLoadFrom(String assemblyFile, Evidence securityEvidence, Byte[] hashValue, AssemblyHashAlgorithm hashAlgorithm, Boolean forIntrospection, StackCrawlMark& stackMark) 
at System.Reflection.Assembly.ReflectionOnlyLoadFrom(String assemblyFile) 
at RadicaLogic.MSBuild.Tasks.GetAssemblyAttribute.Execute() 
at Microsoft.Build.BuildEngine.TaskEngine.ExecuteInstantiatedTask(EngineProxy engineProxy, ItemBucket bucket, TaskExecutionMode howToExecuteTask, ITask task, Boolean& taskResult)

WPF プロジェクトによって参照されていない同じソリューション内の別のアセンブリを指すように AssemblyFile を変更しても例外がスローされないため、WPF 関連であることはわかっています。

例外メッセージには、

... already loaded from a different location.

It cannot be loaded from a new location within the same appdomain.

同じ appdomain に関する部分に注意してください。

そこで、この特定の例外をキャッチして CurrentDomain を調べるようにコードを変更しました。

Assembly assembly = null;
try
{
    assembly = Assembly.ReflectionOnlyLoadFrom(AssemblyFile);
}
catch (FileLoadException)
{
    List<string> searched = new List<string>();
    foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
    {
        if (Path.GetFileName(asm.CodeBase).Equals(Path.GetFileName(AssemblyFile), 
            StringComparison.OrdinalIgnoreCase))
        {
            message = string.Format("Found assembly {0} in current domain", 
                asm.CodeBase);
            MSBuildHelper.Log(this, message, MessageImportance.High);
            assembly = asm;
            break;
        }
        else
        {
            searched.Add(Path.GetFileName(asm.CodeBase));
        }
    }
    if (assembly == null)
    {
        message = string.Format(
            "Unable to find {0} after looking in current domain assemblies {1}",
            Path.GetFileName(AssemblyFile), string.Join(", ", searched.ToArray()));
        MSBuildHelper.Log(this, message, MessageImportance.High);                    
    }
}

問題のアセンブリが現在のドメインになかったことは言うまでもありません (コンパイルを実行する別の MSBuild プロセスが生成されるため、これは理にかなっている可能性があります)。したがって、エラー メッセージが true であると仮定すると、どこを調べればよいでしょうか。それは住んでいますか?私へのエラーメッセージはそれがCurrentDomainであるべきだと示唆しているので、混乱しています。

または、WPF の経験が豊富な人が、ビルドが成功した後も、このアセンブリがアプリ ドメインにまだ残っている理由を説明できますか?

これは、この例外に遭遇した他の誰かからの別の質問です。

4

1 に答える 1

2

私の解決策は、オープンソースに移行することでした:) Cecilを使用して以下から取得AttributeAssemblyFileます:

bool found = false;
string value = string.Empty;
Type attributeType = Type.GetType(Attribute);
AssemblyDefinition assembly = AssemblyFactory.GetAssembly(AssemblyFile);
foreach (CustomAttribute attribute in assembly.CustomAttributes)
{
    if (attribute.Constructor.DeclaringType.Name == attributeType.Name)
    {
        value = attribute.ConstructorParameters[0].ToString();
        found = true;
    }
}

Jays のコメントの更新:

通常、私AssemblyFileVersionは Attribute プロパティの値として: を使用し、不足している部分をセッター ロジックで埋めます :)

プロパティの定義方法は次のとおりです。

string attribute;
[Required]
public string Attribute
{
    get { return attribute; }
    set
    {
        string tempValue = value;
        if (!tempValue.StartsWith("System.Reflection."))
        {
            tempValue = "System.Reflection." + tempValue;
        }
        if (!value.EndsWith("Attribute"))
        {
            tempValue += "Attribute";
        }
        attribute = tempValue;
    }
}

必要な接頭辞または接尾辞がない属性プロパティ値を示す単体テスト:

[Test]
public void Execute_WithoutSystemReflectionPrefixOrAttributeSuffix_ReturnsExpectedResult()
{
    string version = getAssemblyFileVersion();
    Assert.IsNotNull(version, "Expected AssemblyFileVersionAttribute to contain value");

    task.AssemblyFile = assemblyFile;
    task.Attribute = "AssemblyFileVersion";
    task.Value = "Bogus";

    result = task.Execute();

    Assert.IsTrue(result, "Expected execute to pass on valid assembly and attribute name");

    Assert.AreEqual(task.Value, version,
        "Expected task value to match assembly file version attribute value");
}
于 2009-10-30T06:35:00.587 に答える