You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 12:57:36