【问题标题】:FatalExecutionEngineError on async method异步方法上的 FatalExecutionEngineError
【发布时间】:2020-05-17 22:13:12
【问题描述】:

好的。因此,根据我被指出的文章,我继续重写了大部分代码。

看起来像这样:

Progress<string, string> progressIndicator;
public void ShowTEF()
{
            progressIndicator = new Progress<(string body, string title)>(AtualizaUI);
            ComunicaComTEF(progressIndicator);
}

private async Task<int> ComunicaComTEF(IProgress<(string body, string title)> progress)
        {
            int retorno = 10000;
            return await Task.Run<int>(() =>
            {
                while (retorno == 10000)
                {
                    if (estadoTEF != StateTEF.OperacaoPadrao && estadoTEF != StateTEF.RetornaMenuAnterior)
                    {
                        Debug.WriteLine("estadoTEF != OperacaoPadrao. Awaiting response");
                        return 0;
                    }
                    else
                    {
                        Debug.WriteLine("estadoTEF == OperacaoPadrao");
                        retorno = ContinuaVendaTEF();
                    }
                    if (progress != null)
                        progress.Report((mensagemJanela, tituloJanela));
                }
                if (retorno < 0) this.Dispatcher.Invoke(() => DialogBox.Show("ERRO DE TEF", DialogBox.DialogBoxButtons.No, DialogBox.DialogBoxIcons.Error, true, "Erro!"));
                if (statusAtual != StatusTEF.Confirmado) statusAtual = StatusTEF.Erro;
                Debug.WriteLine("Closing window due to loop ending");
                this.Dispatcher.Invoke(() => this.Close());
                StatusChanged?.Invoke(this, new TEFEventArgs() { TipoDoTEF = _tipoTEF, Valor = valor, idMetodo = _idMetodo, status = statusAtual });
                return 0;
            });
        }

        private int ContinuaVendaTEF()
        {
            Debug.WriteLine(Encoding.ASCII.GetString(bufferTEF).Split('\0')[0], 0);
            var retorno = ContinuaFuncaoSiTefInterativo(ref Comando, ref TipoCampo, ref TamMinimo, ref TamMaximo, bufferTEF, bufferTEF.Length, 0);
            ProcessaComando(Comando, bufferTEF);
            LimpaBuffer();
            return retorno;
        }

ProcessaComando 是一个开关,根据comando 执行某些操作,例如显示消息

private void ExibeMensagemOperador(byte[] buffer)
{
    tituloJanela = "OPERAÇÃO NO TEF";
    mensagemJanela = Encoding.ASCII.GetString(buffer).Split('\0')[0];
}

或者要求用户按任意键

public void PerguntaSimOuNao(byte[] pergunta)
{
    estadoTEF = StateTEF.AguardaSimNao;
    mensagemJanela = "(S)im / (N)ão";
    tituloJanela = Encoding.ASCII.GetString(pergunta).Split('\0')[0];
}

然后被PreviewTextInput捕获

private void Window_PreviewTextInput(object sender, TextCompositionEventArgs e)
        if (estadoTEF == StateTEF.AguardaSimNao && (e.Text.ToUpper() == "S" || e.Text.ToUpper() == "N"))
        {
            LimpaBuffer();
            if (e.Text.ToUpper() == "S")
            {
                bufferTEF = Encoding.ASCII.GetBytes("0");
                estadoTEF = StateTEF.OperacaoPadrao;
                ComunicaComTEF(progressIndicator);
            }
            else if (e.Text.ToUpper() == "N")
            {
                bufferTEF = Encoding.ASCII.GetBytes("1");
                estadoTEF = StateTEF.OperacaoPadrao;
                ComunicaComTEF(progressIndicator);
            }
        }

现在,获取新信息。当我使用任务运行它时,没有异步/等待,只返回一个任务,其结果同步触发 FatalExecutionError。如果ComunicaComTef为int,去掉Task.Run(只是同步运行代码),不会触发错误,循环运行完美。


问题的先前版本(如果需要):

过去几个月我一直在学习异步编程,而且我已经 发现一个我不知道如何调试/处理的错误:

这是设置。我有一个窗口ShowTEF,它调用了两个方法, IniciaFuncaoSitefasync ComunicaComTEF。两人打电话 外部 dll 方法,返回整数值和一个 byte[] by ref.

IniciaFuncaoSitef 简单地启动一个操作,通过提供一些 外部dll的参数。 ComunicaComTEF 有一个 while 循环, 也就是说,对于外部方法的每个同步调用调用一个 this.Dispatcher.Invoke() 刷新用户界面。这是简化的 代码:


        public void ShowTEF(TipoTEF tipoTEF, decimal vlrTEF)
        {
            Topmost = true;
            InitializeComponent();
            Show();
            IniciaFuncaoSiTefInterativo((int)tipoTEF, (vlrTEF*100).ToString("0.00")); //Starts a new interation with the
external DLL.
            stateTEF=StateTEF.OperacaoPadrao; //Allows the while loop on ComunicaComTEF to run
            statusTEF = StatusTEF.EmAndamento; //This will be used by ShowTEF's caller to know what was the outcome of the operation.
            ComunicaComTEF();
        }

        private async void ComunicaComTEF()
        {
            int retorno = 10000;
            await Task.Run(() =>
            {
                while (retorno == 10000) //The external DLL returns 10000 as long as it needs my software to keep communicating with it.
                {
                    if (stateTEF != StateTEF.CancelamentoRequisitado) //If there still stuff to do, and the user hasn't cancelled, the loop
falls here.
                    {
                        if (stateTEF != StateTEF.OperacaoPadrao) //If the DLL asked some user interaction, the loop falls here.
                        {
                            this.Dispatcher.Invoke(() => AtualizaUI());
                            return;
                        }
                        else //If the DLL is still "chatting" with my software, the loop goes on.
                        {
                            retorno = ContinuaVendaTEF().intRetorno;
                            this.Dispatcher.Invoke(() => AtualizaUI());
                        }
                    }
                    else //If the user presses Escape at any time, it will fall here at the next loop.
                    {
                        statusTEF = StatusTEF.Cancelado;
                        retorno = CancelaOperacaoAtual();
                        this.Dispatcher.Invoke(() => this.Close());
                        return;
                    }
                }
                string msgErro = retorno switch //These are actual error messages I've shortened here to save space
                {
                    -1 => "ERRMESS1",
                    -3 => "ERRMESS3",
                    -4 => "ERRMESS4",
                    -5 => "ERRMESS5",
                    -8 => "ERRMESS8",
                    -9 => "ERRMESS9",
                    -10 => "ERRMESS10",
                    -12 => "ERRMESS12",
                    -20 => "ERRMESS20",
                    -40 => "ERRMESS40",
                    _ => "NAE" //Not an Error
                };
                if (msgErro != "NAE") this.Dispatcher.Invoke(() => DialogBox.Show((msgErro)); //DialogBox inherits Window but has some
custom parameters, like custom icons and custom buttons.
                if (statusTEF != StatusTEF.Confirmado) statusTEF = StatusTEF.Erro; //If, when the loop ends when return != 10000, the
status is not confirmed, it understands there has been an error.
                this.Dispatcher.Invoke(() => this.Close()); //Closes the current window.
                StatusChanged?.Invoke(this, new TEFEventArgs() { TipoDoTEF = _tipoTEF, Valor = valor, idMetodo = _idMetodo, status =
statusTEF }); //Alerts whoever called ShowTEF about the new status.
                return;
            });
        }

        private (int intRetorno, string msgRetorno) ContinuaVendaTEF()
        {
            int retorno = ContinuaFuncaoSiTefInterativo(ref Comando, ref TipoCampo, bufferTEF, bufferTEF.Length);
            ProcessaComando(bufferTEF, bufferTEF.Length);
            ClearBuffer();
            return (retorno, "NORETURN");
        }
              private void Window_PreviewTextInput(object sender, TextCompositionEventArgs e)
        {
            if (stateTEF == StateTEF.AguardaMenu && e.Text.IsNumbersOnly())
            {
                    int opcaoEscolhida = int.Parse(e.Text);
                    ClearBuffer();
                    bufferTEF = Encoding.UTF8.GetBytes(opcaoEscolhida.ToString());
                    stateTEF = StateTEF.OperacaoPadrao;
                    ComunicaComTEF();
            }
            else if (stateTEF == StateTEF.AguardaSimNao && (e.Text.ToUpper() == "S" || e.Text.ToUpper() == "N"))
            {
                ClearBuffer();
                if (e.Text.ToUpper() == "S")
                {
                    bufferTEF = Encoding.UTF8.GetBytes("0");
                }
                else if (e.Text.ToUpper() == "N")
                {
                    bufferTEF = Encoding.UTF8.GetBytes("1");
                }
              stateTEF = StateTEF.OperacaoPadrao;
              ComunicaComTEF();
            } ```

`IniciaFuncaoSiTefInterativo` and `ContinuaFuncaoSiTefInterativo` are
the external methods imported using a DllImport with StdCall
convention. `ProcessaComando` reads `Comando`, `TipoCampo` and
`bufferTEF` and changes `stateTEF` to a different state from
`OperacaoPadrao` so that the loop is broken and the user has to
interact with the software. There is a `Window_KeyDown` and
`Window_PreviewTextInput` that captures keystrokes as long as stateTEF
is not OperacaoPadrao, processes it (storing the appropriate result in
bufferTEF) and calls `ComunicaComTEF` back again.

----------

So that's it for the code. Now the issue. Sometimes the process runs
flawlessly, but sometimes I get the following error:

> Managed Debugging Assistant 'FatalExecutionEngineError' has detected a problem in
'M:\TrilhaWorkSpace\AmbiPDV-NFVenda\PDV_PRINCIPAL\bin\AnyCPU\Debug\AmbiPDV.exe'.
Additional Information: The runtime has encountered a fatal error. The
address of the error was at 0xf5b029e1, on thread 0x72bc. The error
code is 0xc0000005. This error may be a bug in the CLR or in the
unsafe or non-verifiable portions of user code. Common sources of this
bug include user marshaling errors for COM-interop or PInvoke, which
may corrupt the stack.

I've tried enabling Managed Compatibility Mode
(https://stackoverflow.com/questions/56846918/keyvaultclient-throws-fatalexecutionengineerror-on-debug-randomly),
but I still get the same error. I've also tried disabling Diagnostics
Tools when debugging. 

Any hints on how should I tackle this issue? I can provide any further
info required, of course.


----------

EDIT.: Here's the Call Stack

>     [Managed to Native Transition]      WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame
frame = {System.Windows.Threading.DispatcherFrame}) + 0xbb bytes   
  WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame
frame) + 0x4d bytes    
  PresentationFramework.dll!System.Windows.Application.RunDispatcher(object
ignore) + 0x60 bytes   
  PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window
window) + 0x7a bytes   
  PresentationFramework.dll!System.Windows.Application.Run(System.Windows.Window
window) + 0x2e bytes   
  PresentationFramework.dll!System.Windows.Application.Run() + 0x1e
bytes     AmbiPDV.exe!PDV_WPF.App.Main() + 0x5a bytes 


----------

EDIT 04/02/2020

As per @PanagiotisKanavos, I've adopted IProgress to better update my
interface to show information (and request it) from the user. 

``` public async Task ShowTEF(TipoTEF tipoTEF, decimal vlrTEF) { ...
//ComunicaComTEF(); var progressIndicator = new Progress<(string,
string)>(AtualizaUI); await ComunicaComTEF(progressIndicator); }

private async Task ComunicaComTEF(IProgress<(string, string)>
progress) { await Task.Run(() =>
    {
        while (retorno == 10000)
        {
            progress.Report((message, title));
            if (estadoTEF != StateTEF.CancelamentoRequisitado)
            {
                if (estadoTEF != StateTEF.OperacaoPadrao)
                {
                    return;//Not sure if this should be return or break...
                }
                else
                {
                    retorno = ContinuaVendaTEFAsync().Result;
                }
            }
            else
            {
                statusAtual = StatusTEF.Cancelado;
                retorno = CancelaOperacaoAtual().Result;
                this.Dispatcher.Invoke(() => this.Close());
                return;
            }
        } ... } private void AtualizaUI((string body, string titulo) item) {
    tbl_Body.Text = item.body.TrimEnd('\0'); //<------ Error thrown here------
    lbl_Title.Text = item.titulo.TrimEnd('\0'); } ```

Now I'm getting a different error. Right at the "tbl_Body.Text" line,
I got a `System.AccessViolationException` error. Here's the stack
trace:

> AmbiPDV.exe!PDV_WPF.Telas.SiTEFBox.AtualizaUI(System.ValueTuple<string,string>
item = {System.ValueTuple<string,string>}) Line 533 + 0x3 bytes   C# 
  mscorlib.dll!System.Progress<System.ValueTuple<string,string>>.InvokeHandlers(object
state) + 0x5e bytes    
  WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate
callback, object args, int numArgs) + 0xae bytes   
  WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object
source = {System.Windows.Threading.Dispatcher}, System.Delegate
callback, object args, int numArgs, System.Delegate catchHandler =
null) + 0x35 bytes     
  WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeImpl()
+ 0xdd bytes      WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(object
state) + 0x3f bytes    
  WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(object
obj) + 0x42 bytes  
  mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext
executionContext, System.Threading.ContextCallback callback, object
state, bool preserveSyncCtx) + 0xc4 bytes  
  mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext
executionContext, System.Threading.ContextCallback callback, object
state, bool preserveSyncCtx) + 0x17 bytes  
  mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext
executionContext, System.Threading.ContextCallback callback, object
state) + 0x44 bytes    
  WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.Run(MS.Internal.CulturePreservingExecutionContext
executionContext = {MS.Internal.CulturePreservingExecutionContext},
System.Threading.ContextCallback callback, object state) + 0x9a bytes 
  WindowsBase.dll!System.Windows.Threading.DispatcherOperation.Invoke()
+ 0x50 bytes      WindowsBase.dll!System.Windows.Threading.Dispatcher.ProcessQueue() +
0x176 bytes    
  WindowsBase.dll!System.Windows.Threading.Dispatcher.WndProcHook(System.IntPtr
hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool
handled) + 0x5c bytes  
  WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd =
5967824, int msg = 49656, System.IntPtr wParam = 0, System.IntPtr
lParam = 0, ref bool handled = false) + 0xa1 bytes     
  WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object
o) + 0x6c bytes    
  WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate
callback, object args, int numArgs) + 0x52 bytes   
  WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object
source = {System.Windows.Threading.Dispatcher}, System.Delegate
callback, object args, int numArgs, System.Delegate catchHandler =
null) + 0x35 bytes     
  WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority
priority, System.TimeSpan timeout, System.Delegate method, object
args, int numArgs) + 0x142 bytes   
  WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr
hwnd = 5967824, int msg = 49656, System.IntPtr wParam = 0,
System.IntPtr lParam = 0) + 0xf4 bytes        [Native to Managed
Transition]       [Managed to Native Transition]   
  WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame
frame = {System.Windows.Threading.DispatcherFrame}) + 0xbb bytes   
  WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame
frame) + 0x4d bytes    
  PresentationFramework.dll!System.Windows.Application.RunDispatcher(object
ignore) + 0x60 bytes   
  PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window
window) + 0x7a bytes   
  PresentationFramework.dll!System.Windows.Application.Run(System.Windows.Window
window) + 0x2e bytes   
  PresentationFramework.dll!System.Windows.Application.Run() + 0x1e
bytes     AmbiPDV.exe!PDV_WPF.App.Main() + 0x5a bytes 

I read @
https://stackoverflow.com/questions/48417900/app-crashes-with-attempted-to-read-or-write-protected-memory
that this could be caused by passing string literals to functions that
expected them to be mutable. However, I believe this is not the case,
as I rewrote `AtualizaUI()` as follows:

    private void AtualizaUI((string body, string titulo) item)
    {
        string a = item.body.TrimEnd('\0');
        string b = item.titulo.TrimEnd('\0');
        tbl_Body.Text = a;
        lbl_Title.Text = b;
    } ```

再一次,我触发了之前的FatalExecutionError。这里是 堆栈跟踪:

AmbiPDV.exe!PDV_WPF.Telas.SiTEFBox.AtualizaUI(System.ValueTuple item = {System.ValueTuple}) 第 536 行 + 0xc 字节 C# mscorlib.dll!System.Progress>.InvokeHandlers(对象 状态)+ 0x5e 字节
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate 回调、对象 args、int numArgs) + 0xae 字节
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(对象 源 = {System.Windows.Threading.Dispatcher},System.Delegate 回调,对象参数,int numArgs,System.Delegate catchHandler = null) + 0x35 字节
WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeImpl() + 0xdd 字节 WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(对象 状态)+ 0x3f 字节
WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(对象 obj) + 0x42 字节
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext、System.Threading.ContextCallback 回调、对象 state, bool preserveSyncCtx) + 0xc4 字节
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext、System.Threading.ContextCallback 回调、对象 状态,bool preserveSyncCtx) + 0x17 字节
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext、System.Threading.ContextCallback 回调、对象 状态)+ 0x44 字节
WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.Run(MS.Internal.CulturePreservingExecutionContext executionContext = {MS.Internal.CulturePreservingExecutionContext}, System.Threading.ContextCallback 回调,对象状态)+ 0x9a 字节 WindowsBase.dll!System.Windows.Threading.DispatcherOperation.Invoke() + 0x50 字节 WindowsBase.dll!System.Windows.Threading.Dispatcher.ProcessQueue() + 0x176 字节
WindowsBase.dll!System.Windows.Threading.Dispatcher.WndProcHook(System.IntPtr hwnd,int msg,System.IntPtr wParam,System.IntPtr lParam,ref bool 已处理)+ 0x5c 字节
WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd = 4458568,int msg = 49656,System.IntPtr wParam = 0,System.IntPtr lParam = 0, ref bool 处理 = false) + 0xa1 字节
WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(对象 o) + 0x6c 字节
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate 回调,对象 args,int numArgs) + 0x52 字节
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(对象 源 = {System.Windows.Threading.Dispatcher},System.Delegate 回调,对象参数,int numArgs,System.Delegate catchHandler = null) + 0x35 字节
WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority 优先级、System.TimeSpan 超时、System.Delegate 方法、对象 args, int numArgs) + 0x142 字节
WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr hwnd = 4458568,int msg = 49656,System.IntPtr wParam = 0, System.IntPtr lParam = 0) + 0xf4 bytes [Native to Managed 过渡] [托管到本机过渡]
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame 帧 = {System.Windows.Threading.DispatcherFrame}) + 0xbb 字节
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame 帧)+ 0x4d 字节
PresentationFramework.dll!System.Windows.Application.RunDispatcher(对象 忽略)+ 0x60 字节
PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window 窗口)+ 0x7a 字节
PresentationFramework.dll!System.Windows.Application.Run(System.Windows.Window 窗口)+ 0x2e 字节
PresentationFramework.dll!System.Windows.Application.Run() + 0x1e 字节 AmbiPDV.exe!PDV_WPF.App.Main() + 0x5a 字节

顺便说一句,我要感谢您将我指向那篇文章 关于 IProgress。它比大量的 await 和 async 更有意义 空虚!

【问题讨论】:

  • 请分享调用堆栈
  • async void 用于事件处理程序。至少将您的方法语法更改为async Task。另一个问题是尝试从任务内部修改 UI。这就是 await 的用途 - 将执行返回到 UI 上下文,因此您不需要需要使用 Invoke
  • 这意味着ShowEF本身应该是async Task,所以它可以使用await ComunicaComTEF()。该方法应该是async Task ComunicaComTEF()。而不是主要将 UI 代码包装在 Task.Run 中,只有真正需要在后台运行的部分应该以这种方式运行。这段代码似乎没有包含任何需要Task.Run 的东西,它主要是 UI 更新
  • ContinuaVendaTEF() 是阻塞的方法吗?这就是应该在Task.Run 内部调用的方法。如果你想在一个紧密的循环中进行轮询调用,也许更好的选择是使用IProgress&lt;T&gt;,如Enabling progress and cancellation in Async APIs 所示,并将 UI 更新与轮询循环解耦
  • ExecutionError 意味着经常出现非托管内存损坏。这通常是由错误的 PInvoke 签名引起的。尝试在 VS 中启用 MDA 以捕获那里的常见错误。另一个来源是您的缓冲区。您将它们放在通过引用传递的托管代码中,这些代码可以在您调用它们时进行 GC、移动。这可能会导致有趣的竞争条件,GC 认为没有人在使用该数组并移动数据。这可能导致托管堆损坏,您可以在托管内存中写入任意位置。您是否正确固定数据?

标签: c# .net wpf async-await c#-8.0


【解决方案1】:

这个问题最初实际上与异步编程无关,而是因为非托管代码和内存处理。感谢 Alois,他为我指明了正确的方向,我设法找到了问题所在。

外部库需要一个固定大小的字节数组(准确地说是 22000 字节),并且在库和我的软件交互时,它必须位于同一地址。犯了两个大错误。首先,每次我需要清除缓冲区以便向外部库发送信息时,我都使用bufferTEF = new buffer[22000]。因此,我没有清除它,而是实例化了一个新的,因此更改了它的地址,因此外部库再次找到它时遇到了问题。其次,每当我向外部库发送数据时,我使用bufferTEF = Encoding.ASCII.GetBytes("information")),这意味着我发送回一个与我发送的信息长度相同的数组。由于外部库预计最少 20000 字节,所以它爆炸了,不知道该怎么办。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多