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

.NET MAUI Android发布版调用Roslyn EvaluateAsync遇CoreLib.dll缺失异常

.NET MAUI Android Release模式下Roslyn脚本执行抛出System.IO.FileNotFoundException

问题详情

构建面向Android的.NET MAUI应用,使用Roslyn的EvaluateAsync方法执行字符串代码时,调试模式运行正常,但Release构建后点击执行按钮立即抛出异常,堆栈跟踪如下:

System.IO.FileNotFoundException: Could not find file '/System.Private.CoreLib.dll'.
File name: '/System.Private.CoreLib.dll'
   at Interop.ThrowExceptionForIoErrno(ErrorInfo errorInfo, String path, Boolean isDirError)
   at Interop.CheckIo(Error error, String path, Boolean isDirError)
   at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String path, OpenFlags flags, Int32 mode, Boolean failForSymlink, Boolean& wasSymlink, Func`4 createOpenException)
   at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, UnixFileMode openPermissions, Int64& fileLength, UnixFileMode& filePermissions, Boolean failForSymlink, Boolean& wasSymlink, Func`4 createOpenException)
   at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode, Func`4 createOpenException)
   at System.IO.Strategies.OSFileStreamStrategy..ctor(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
   at System.IO.Strategies.UnixFileStreamStrategy..ctor(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
   at System.IO.Strategies.FileStreamHelpers.ChooseStrategyCore(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
   at System.IO.Strategies.FileStreamHelpers.ChooseStrategy(FileStream fileStream, String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, Int64 preallocationSize)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, Boolean useAsync)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)
   at Roslyn.Utilities.StandardFileSystem.OpenFile(String filePath, FileMode mode, FileAccess access, FileShare share)
   at Roslyn.Utilities.CommonCompilerFileSystemExtensions.OpenFileWithNormalizedException(ICommonCompilerFileSystem fileSystem, String filePath, FileMode fileMode, FileAccess fileAccess, FileShare fileShare)
   at Microsoft.CodeAnalysis.MetadataReference.CreateFromAssemblyInternal(Assembly assembly, MetadataReferenceProperties properties, DocumentationProvider documentation)
   at Microsoft.CodeAnalysis.MetadataReference.CreateFromAssemblyInternal(Assembly assembly)
   at Microsoft.CodeAnalysis.Scripting.Script.GetReferencesForCompilation(CommonMessageProvider messageProvider, DiagnosticBag diagnostics, MetadataReference languageRuntimeReferenceOpt)
   at Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScriptCompiler.CreateSubmission(Script script)
   at Microsoft.CodeAnalysis.Scripting.Script.GetCompilation()
   at Microsoft.CodeAnalysis.Scripting.Script`1[[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].GetExecutor(CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.Scripting.Script`1[[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].RunAsync(Object globals, Func`2 catchException, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.Scripting.Script`1[[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].RunAsync(Object globals, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.RunAsync[Object](String code, ScriptOptions options, Object globals, Type globalsType, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.EvaluateAsync[Object](String code, ScriptOptions options, Object globals, Type globalsType, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.EvaluateAsync(String code, ScriptOptions options, Object globals, Type globalsType, CancellationToken cancellationToken)
   at MauiScriptingTest.MainPage.EvaluateScript()

复现步骤

  • 创建基于.NET 8的新.NET MAUI项目
  • 添加Microsoft.CodeAnalysis.CSharp.Scripting 4.8.0版本的NuGet引用
  • 在MainPage.xaml.cs中添加如下代码:
public MainPage()
{
    InitializeComponent();
    this.BindingContext = this;
}

private string? evaluationResult = "Not evaluated yet";
public string? EvaluationResult
{
    get => evaluationResult;
    set
    {
        evaluationResult = value;
        OnPropertyChanged();
    }   
}

private async void OnCounterClicked(object sender, EventArgs e)
{
    await EvaluateScript();
}

private async Task EvaluateScript()
{
    try
    {
        var result = await CSharpScript.EvaluateAsync("return 1+2;");
        EvaluationResult = result.ToString();
    }
    catch (Exception ex)
    {
        EvaluationResult = ex.ToString();
    }
}
  • 在MainPage.xaml中添加如下内容:
<Button
    x:Name="CounterBtn"
    Text="Click me"
    SemanticProperties.Hint="Counts the number of times you click"
    Clicked="OnCounterClicked"
    HorizontalOptions="Center" />

<Label Text="{Binding EvaluationResult}"></Label>
  • 在Android模拟器(Pixel 5,Android 14.0,API 34)中以RELEASE模式运行应用(调试模式无此问题)
  • 点击按钮触发脚本执行

解决方案

1. 显式指定脚本的程序集引用

在Release模式下,Android的.NET程序集并非以独立DLL文件存在,而是嵌入到APK中。Roslyn默认逻辑会尝试从文件系统加载System.Private.CoreLib.dll,导致找不到文件。需要手动添加内存中的程序集引用:

修改EvaluateScript方法:

private async Task EvaluateScript()
{
    try
    {
        var options = ScriptOptions.Default
            // 添加必要的程序集引用,直接从内存中获取元数据
            .AddReferences(
                typeof(object).Assembly, // 对应System.Private.CoreLib
                typeof(Task).Assembly,
                Assembly.GetExecutingAssembly()
            )
            .AddImports("System"); // 添加常用命名空间,避免脚本中写全限定名

        var result = await CSharpScript.EvaluateAsync("return 1+2;", options);
        EvaluationResult = result.ToString();
    }
    catch (Exception ex)
    {
        EvaluationResult = ex.ToString();
    }
}

2. 配置项目确保必要程序集被保留

在项目文件(.csproj)中添加Release模式的专属配置,确保Roslyn相关组件和核心.NET程序集不被修剪:

<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)'=='Release|net8.0-android'">
    <PublishTrimmed>false</PublishTrimmed>
    <AotCompile>false</AotCompile>
    <DebugType>portable</DebugType>
    <DebugSymbols>true</DebugSymbols>
</PropertyGroup>

<ItemGroup Condition="'$(Configuration)|$(TargetFramework)'=='Release|net8.0-android'">
    <!-- 标记这些程序集为修剪根,确保不被移除 -->
    <TrimmerRootAssembly Include="Microsoft.CodeAnalysis.CSharp.Scripting" />
    <TrimmerRootAssembly Include="Microsoft.CodeAnalysis.Scripting" />
    <TrimmerRootAssembly Include="System.Private.CoreLib" />
</ItemGroup>

原因说明

Android Release模式下,.NET MAUI会将所有程序集打包合并到APK/AAB中,不会在设备文件系统中生成独立的.dll文件。Roslyn的默认脚本初始化逻辑会尝试通过文件路径加载核心程序集,而该路径在Android设备上不存在,因此抛出FileNotFoundException。通过显式添加内存中的程序集引用,让Roslyn直接使用程序集的元数据,绕过文件加载逻辑,即可解决问题。

内容的提问来源于stack exchange,提问作者Marc Miller

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 01:51:05