1

我编写了一个 C# WPF 应用程序来在运行时编译代码。该应用程序基本上执行以下步骤

  1. 点击按钮[编译东西]
  2. 通过创建代码文件StreamWriter
  3. Microsoft.Build.Execution.BuildManager使用类构建代码文件
  4. 使用反射访问 DLL 文件 ( Assembly.LoadFrom(filePath))
  5. 创建包含在 dll ( assembly.CreateInstance(NamespaceName + "." + ClassName))中的类的实例

我工作正常,但只有一次(我需要重新启动应用程序才能再次这样做)

这是下一次执行期间发生的情况

  1. 点击按钮[编译东西]
  2. 通过创建代码文件StreamWriter
  3. 使用类构建代码文件Microsoft.Build.Execution.BuildManager-> 产生一个错误,指出 DLL 文件已被锁定。

该进程无法访问文件“DLL\generatedflexform.dll”,因为它正被另一个进程使用

当我省略第 2 步时,问题不会发生,因为那时代码文件是相同的。因此BuildManager不会重新创建/复制 dll。

我需要弄清楚在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

解决方案:

我不得不更改LoadWindowFromDllmethod以避免 DLL 锁定

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 文件被锁定了,当我尝试执行它两次时,导致构建失败。

我通过在构建文件中添加一行来解决此问题:

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

这是执行编译魔法的方法> public Window BuildXamlWindowFromStrings(string xaml, string codeBehind) { //Erstellen der Codefiles (XAML und CodeBehind) this.CreateCodeFile(codeBehind); 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 回答