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

.NET 7调用SqlPackage重定向StdError遇无效句柄问题排查

在C# .NET 7中调用SqlPackage时StdError重定向的问题与解决

问题现象

在.NET 7应用中通过System.Diagnostics.Process调用SqlPackage.exe时,出现以下异常情况:

  • 执行无错误的有效命令(如/help)时,若同时开启RedirectStandardError,会抛出“发生意外故障:句柄无效”的错误;
  • 执行存在错误的命令时,却能正常捕获StdError中的错误信息;
  • 若关闭StdError重定向,可正常获取有效命令的StdOutput,但无法捕获潜在的错误信息。

原因分析

这个问题的根源是SqlPackage.exe的特殊行为:当命令执行成功且无错误输出时,它会主动关闭标准错误流(StdError)的句柄,但此时.NET的Process组件仍通过BeginErrorReadLine异步监听该流,导致句柄状态不匹配,触发“无效句柄”异常。而当命令有错误时,SqlPackage会正常向StdError写入数据并保持句柄有效,因此不会触发错误。

解决方法

要同时捕获StdOutput和StdError且避免异常,推荐以下两种可靠方案:

方案1:后台同步读取错误流(替代异步事件监听)

放弃BeginErrorReadLine的异步监听,改用后台线程同步读取StdError流,避免异步监听的句柄冲突:

using System.Diagnostics;

// 注意:若为控制台应用,需将Main方法改为async Task Main()
async Task Main()
{
    var processStartInfo = new ProcessStartInfo
    {
        FileName = @"sqlpackage.exe",
        Arguments = "/help",
        RedirectStandardOutput = true,
        RedirectStandardError = true,
        UseShellExecute = false,
        CreateNoWindow = true
    };

    using var process = new Process
    {
        StartInfo = processStartInfo,
        EnableRaisingEvents = true
    };

    // 异步监听StdOutput
    process.OutputDataReceived += (sender, eventArgs) =>
    {
        if (!string.IsNullOrEmpty(eventArgs.Data))
        {
            Console.WriteLine($"输出:{eventArgs.Data}");
        }
    };

    process.Start();
    process.BeginOutputReadLine();

    // 在后台线程同步读取StdError,避免句柄冲突
    var errorReadTask = Task.Run(() =>
    {
        while (!process.StandardError.EndOfStream)
        {
            var errorLine = process.StandardError.ReadLine();
            if (!string.IsNullOrEmpty(errorLine))
            {
                Console.WriteLine($"错误:{errorLine}");
            }
        }
    });

    // 等待进程结束和错误流读取完成
    process.WaitForExit();
    await errorReadTask;

    Console.WriteLine($"Process completed with exit code: {process.ExitCode}");
}

方案2:禁用异步错误监听,仅在需要时读取

如果你的场景中错误输出较少,也可以先不开启BeginErrorReadLine,而是在进程退出后检查ExitCode,若不为0再读取StdError内容:

using System.Diagnostics;

var processStartInfo = new ProcessStartInfo
{
    FileName = @"sqlpackage.exe",
    Arguments = "/help",
    RedirectStandardOutput = true,
    RedirectStandardError = true,
    UseShellExecute = false,
    CreateNoWindow = true
};

using var process = new Process
{
    StartInfo = processStartInfo
};

process.Start();

// 同步读取StdOutput
string output = process.StandardOutput.ReadToEnd();
Console.WriteLine($"输出:{output}");

process.WaitForExit();

// 仅当进程异常退出时读取StdError
if (process.ExitCode != 0)
{
    string error = process.StandardError.ReadToEnd();
    Console.WriteLine($"错误:{error}");
}

Console.WriteLine($"Process completed with exit code: {process.ExitCode}");

注意事项

  • 若使用同步读取流,需确保读取操作在WaitForExit前执行,避免进程退出后流被关闭;
  • 异步读取方案更适合长时间运行的进程,不会阻塞主线程;
  • 需确保SqlPackage.exe的路径正确,或已添加到系统环境变量中。

内容的提问来源于stack exchange,提问作者Frederik Madsen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 18:45:31