在.NET 6中用Roslyn CSharpCompilation生成带AssemblyInfo的DLL失败
.NET 6中用Roslyn生成的DLL无Assembly属性显示的解决方法
问题分析
你遇到的情况是:通过Roslyn编译生成的DLL,在Windows资源管理器的文件属性中看不到AssemblyInfo里定义的产品、版本等信息,但编译过程无报错。这是因为:
- .NET 6+中,Windows文件属性里的详细信息(如产品名称、公司)依赖Win32资源,仅通过
[assembly: ...]属性不会自动生成该资源,所以资源管理器里看不到,但程序集本身已经包含这些属性,可以通过反射读取。 - 若编译的程序集无任何类型,部分场景下可能被优化,但你的代码中属性本身是会被保留的。
验证程序集属性是否存在
先确认程序集确实包含这些属性,在编译完成后添加以下代码读取生成的DLL:
var assembly = Assembly.LoadFrom($"{assemblyName}.dll"); Console.WriteLine("AssemblyProduct: " + assembly.GetCustomAttribute<AssemblyProductAttribute>()?.Product); Console.WriteLine("AssemblyVersion: " + assembly.GetCustomAttribute<AssemblyVersionAttribute>()?.Version);
运行后会输出你定义的test和1.2.3,说明属性已存在。
让Windows文件属性显示信息(生成Win32资源)
要让资源管理器里看到这些信息,需要手动生成Win32资源并嵌入到程序集中。修改你的Compile方法,添加资源生成逻辑:
修改后的完整代码
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using System.Reflection; using System.Resources; var assemblyName = "test"; var assemblyVersion = "1.2.3"; var dotnetPath = Path.GetDirectoryName(typeof(object).Assembly.Location) + Path.DirectorySeparatorChar; var trees = new List<SyntaxTree>(); // AssemblyInfo var info = Manager.GenerateAssemblyInfo(assemblyName, assemblyVersion); trees.Add(CSharpSyntaxTree.ParseText(info)); Manager.Compile(trees, dotnetPath, assemblyName); // 验证属性 var assembly = Assembly.LoadFrom($"{assemblyName}.dll"); Console.WriteLine("AssemblyProduct: " + assembly.GetCustomAttribute<AssemblyProductAttribute>()?.Product); Console.WriteLine("AssemblyVersion: " + assembly.GetCustomAttribute<AssemblyVersionAttribute>()?.Version); public static class Manager { // AssemblyInfo public static string GenerateAssemblyInfo(string assemblyName, string assemblyVersion) { var code = @$"using System.Reflection; [assembly: AssemblyProduct(""{assemblyName}"")] [assembly: AssemblyCompany(""Test Company"")] [assembly: AssemblyCopyright(""Copyright © {assemblyName} 2022"")] [assembly: AssemblyVersion(""{assemblyVersion}"")]"; return code; } // Compiler public static bool Compile(List<SyntaxTree> trees, string dotnetPath, string assemblyName) { List<MetadataReference> References = new(); References.Add(MetadataReference.CreateFromFile(dotnetPath + "System.Private.CoreLib.dll")); References.Add(MetadataReference.CreateFromFile(dotnetPath + "System.Runtime.dll")); References.Add(MetadataReference.CreateFromFile(dotnetPath + "System.Console.dll")); // 添加必要的引用,确保属性能被正确识别 References.Add(MetadataReference.CreateFromFile(dotnetPath + "System.Reflection.dll")); var settings = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary) .WithOptimizationLevel(OptimizationLevel.Release); // 生成Win32资源临时文件 var resFileName = Path.GetTempFileName() + ".res"; using (var writer = new ResourceWriter(resFileName)) { writer.AddResource("VS_VERSION_INFO", GenerateVersionResource(assemblyName, assemblyVersion)); } CSharpCompilation compilation = CSharpCompilation.Create( assemblyName, trees, References, settings.WithWin32Resource(resFileName)); var result = compilation.Emit( $"{assemblyName}.dll", $"{assemblyName}.pdb"); if (!result.Success) { foreach (var diag in result.Diagnostics.Where(d => d.Severity >= DiagnosticSeverity.Error)) { Console.WriteLine(diag.ToString()); } } // 清理临时资源文件 File.Delete(resFileName); return result.Success; } // 生成版本资源数据 private static byte[] GenerateVersionResource(string productName, string version) { var versionParts = version.Split('.'); var fileVersion = new Version(version); using (var ms = new MemoryStream()) using (var bw = new BinaryWriter(ms)) { // VS_VERSION_INFO 结构的简化实现 bw.Write((ushort)0xfeef); // 签名 bw.Write((ushort)1); // 结构版本 bw.Write((uint)0); // 文件版本占位 bw.Write((uint)0); // 产品版本占位 bw.Write((ushort)0x100); // 文件标志 bw.Write((ushort)0); // 文件OS bw.Write((ushort)0x2); // 文件类型 bw.Write((ushort)0); // 文件子类型 bw.Write((uint)0); // 文件日期占位 // 字符串信息 var info = $"VS_VERSION_INFO\r\nFILEVERSION {fileVersion.Major},{fileVersion.Minor},{fileVersion.Build},{fileVersion.Revision}\r\nPRODUCTVERSION {fileVersion.Major},{fileVersion.Minor},{fileVersion.Build},{fileVersion.Revision}\r\nFILEFLAGSMASK 0x3fL\r\nFILEFLAGS 0x0L\r\nFILEOS 0x40004L\r\nFILETYPE 0x2L\r\nFILESUBTYPE 0x0L\r\nBEGIN\r\n BLOCK \"StringFileInfo\"\r\n BEGIN\r\n BLOCK \"040904B0\"\r\n BEGIN\r\n VALUE \"CompanyName\", \"Test Company\"\r\n VALUE \"ProductName\", \"{productName}\"\r\n VALUE \"LegalCopyright\", \"Copyright © {productName} 2022\"\r\n VALUE \"FileVersion\", \"{version}\"\r\n VALUE \"ProductVersion\", \"{version}\"\r\n END\r\n END\r\n BLOCK \"VarFileInfo\"\r\n BEGIN\r\n VALUE \"Translation\", 0x409, 1200\r\n END\r\nEND"; var bytes = System.Text.Encoding.Unicode.GetBytes(info); bw.Write(bytes); bw.Flush(); return ms.ToArray(); } } }
补充说明
- 生成Win32资源的代码是简化实现,若需要更完整的资源结构,可以参考Windows SDK中的版本资源规范。
- 确保添加了
System.Reflection.dll的引用,避免属性识别问题。 - 编译完成后删除临时资源文件,避免冗余文件。
内容的提问来源于stack exchange,提问作者Santiago
相关产品推荐
相关产品推荐

