1

実行時にコードをコンパイルする C# WPF アプリケーションを作成しました。アプリケーションは基本的に次の手順を実行します

  1. ボタンをクリック [コンパイルする]
  2. 経由でコードファイルを作成するStreamWriter
  3. Microsoft.Build.Execution.BuildManagerクラスを使用してコード ファイルをビルドする
  4. リフレクションを使用して DLL ファイルにアクセスします ( Assembly.LoadFrom(filePath))
  5. dll に含まれるクラスのインスタンスを作成します ( assembly.CreateInstance(NamespaceName + "." + ClassName))

正常に動作しますが、1 回だけです (再度実行するには、アプリケーションを再起動する必要があります)。

これは、次の実行中に起こることです

  1. ボタンをクリック [コンパイルする]
  2. 経由でコードファイルを作成するStreamWriter
  3. クラスを使用してコード ファイルをビルドMicrosoft.Build.Execution.BuildManager-> DLL ファイルがロックされていることを示すエラーが生成されます。

別のプロセスで使用されているため、プロセスはファイル 'DLL\generatedflexform.dll' にアクセスできません

コード ファイルが同じであるため、手順 2 を省略しても問題は発生しません。したがって、BuildManagerdll を再作成/コピーしません。

BuildManagerが仕事を終えた後に DLL を解放する方法を理解する必要があります。これは、コード ファイルが頻繁に変更される可能性が高いためです。それ以外の場合は、コードを変更するたびにアプリケーションを閉じて再度開く必要があります。

編集:私の最初の考えは、BuildManager がロックを引き起こすということでしたが、そうではありません。むしろ、DLL をロードしようとするとロックが発生すると思います。シャドウ コピーを試してみます (@granadaCoder で言及されているように)。

private Window LoadWindowFromDll(string filePathToDll)
{
    var assembly = Assembly.LoadFrom(filePathToDll);
    var window = assembly.CreateInstance(NamespaceName + "." + ClassName) as Window;
    return window;
}
4

2 に答える 2

1

解決:

LoadWindowFromDllmethodDLLのロックを回避するために変更する必要がありました

private Window LoadWindowFromDll(string filePathToDll)
{
    byte[] readAllBytes = File.ReadAllBytes(filePathToDll);
    Assembly assembly = Assembly.Load(readAllBytes);

    var window = assembly.CreateInstance(NamespaceName + "." + ClassName) as Window;

    return window;
}

しかし、どういうわけか pdb ファイルがロックされていたため、2 回実行しようとするとビルドが失敗しました。

ビルド ファイルに次の 1 行を追加して、この動作を修正しました。

<DebugType>none</DebugType>

完全なビルド ファイルは次のとおりです。

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <AssemblyName>generatedflexform</AssemblyName>
    <OutputPath>DLL\</OutputPath>
    <OutputType>Library</OutputType>
    <DebugType>none</DebugType>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="System.Xaml">
      <RequiredTargetFramework>4.0</RequiredTargetFramework>
    </Reference>
    <Reference Include="WindowsBase" />
    <Reference Include="PresentationCore" />
    <Reference Include="PresentationFramework" />
  </ItemGroup>
  <ItemGroup>
    <Page Include="MyForm.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Compile Include="MyForm.xaml.cs">
      <DependentUpon>MyForm.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Compile>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

そして、コンパイル マジックを実行するメソッドがここにあります。this.CreateXamlFile(xaml);

        //Erstellen der project file
        this.CreateProjectFile();

        //Build der DLL
        //using (var buildManager = BuildManager.DefaultBuildManager)
        using (var buildManager = new BuildManager())
        {
            var result = buildManager.Build(this.CreateBuildParameters(), this.CreateBuildRequest());

            if (result.OverallResult == BuildResultCode.Success)
            {
                return this.LoadWindowFromDll(FolderPath + DllRelativeFilePath + NamespaceName + DllFileExtension);
            }
        }

        //Error handling
        var stringbuilder = new StringBuilder();

        using (var reader = new StreamReader(DebuggerLogFileName))
        {
            stringbuilder.Append(reader.ReadToEnd());
        }

        throw new CompilerException(stringbuilder.ToString());
    }

ヘルパー メソッド:

private BuildParameters CreateBuildParameters()
{
    var projectCollection = new ProjectCollection();
    var buildLogger = new FileLogger { Verbosity = LoggerVerbosity.Detailed, Parameters = "logfile=" + DebuggerLogFileName };
    var buildParameters = new BuildParameters(projectCollection) { Loggers = new List<ILogger>() { buildLogger } };
    return buildParameters;
}

private BuildRequestData CreateBuildRequest()
{
    var globalProperties = new Dictionary<string, string>();
    var buildRequest = new BuildRequestData(FolderPath + ProjectFileName, globalProperties, null,
                                            new string[] { "Build" }, null, BuildRequestDataFlags.ReplaceExistingProjectInstance);
    return buildRequest;
}
于 2013-03-06T10:12:07.880 に答える
0

「シャドウコピー」をご覧ください。

シャドウ コピーを使用すると、アプリケーション ドメインをアンロードせずに、アプリケーション ドメインで使用されるアセンブリを更新できます。これは、継続的に利用できる必要があるアプリケーションに特に役立ちます。

http://msdn.microsoft.com/en-us/library/ms404279.aspx

以下も参照してください。

http://gotchahunter.net/2010/12/net-how-do-you-load-an-assembly-programmatically-and-avoid-a-file-lock/

http://blogs.msdn.com/b/junfeng/archive/2004/02/09/69919.aspx

于 2013-03-05T19:27:08.320 に答える