异步方法上的 FatalExecutionEngineError
FatalExecutionEngineError on async method
好的。因此,根据我被指出的文章,我继续重写了大部分代码。
看起来像这样:
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('[=10=]')[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('[=11=]')[0];
}
或者要求用户按任意键
public void PerguntaSimOuNao(byte[] pergunta)
{
estadoTEF = StateTEF.AguardaSimNao;
mensagemJanela = "(S)im / (N)ão";
tituloJanela = Encoding.ASCII.GetString(pergunta).Split('[=12=]')[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);
}
}
现在,获取新信息。当我 运行 它使用任务时,没有 async/await,只返回一个任务,它的结果同步触发 FatalExecutionError。如果 ComunicaComTef
为 int,并删除 Task.Run
(只是 运行 同步代码),则不会触发错误,并且循环 运行 是完美的。
问题的上一个版本,如果需要的话:
I've been learning async programming for the last few months, and I've
found an error I don't know how to debug/handle:
Here's the setup. I have a window ShowTEF
, which calls two methods,
IniciaFuncaoSitef
and async ComunicaComTEF
. Both of them call
external dll methods, which return integer values and a byte[] by ref.
IniciaFuncaoSitef
simply starts an operation, by providing some
parameters to the external dll. ComunicaComTEF
has a while
loop,
that, for each sync call for the external method calls a
this.Dispatcher.Invoke()
to refresh the UI. Here's the simplified
code:
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
(
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('[=14=]'); //<------ Error thrown here------
lbl_Title.Text = item.titulo.TrimEnd('[=14=]'); } ```
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 @
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('[=15=]');
string b = item.titulo.TrimEnd('[=15=]');
tbl_Body.Text = a;
lbl_Title.Text = b;
} ```
And once again, I triggered the previous FatalExecutionError
. Here's
the stack strace:
AmbiPDV.exe!PDV_WPF.Telas.SiTEFBox.AtualizaUI(System.ValueTuple
item = {System.ValueTuple}) Line 536 + 0xc bytes C#
mscorlib.dll!System.Progress>.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 =
4458568, 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 = 4458568, 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
顺便说一下,我想感谢你指点我那篇文章
关于 IProgress。它比大量等待和异步更有意义
空洞!
最初,该问题实际上与异步编程无关,而是与非托管代码和内存处理有关。感谢 Alois,他为我指明了正确的方向,我设法找到了问题所在。
外部库需要一个固定大小的字节数组(准确地说是 22000 字节),并且在库和我的软件交互时它必须位于同一地址。犯了两个大错误。首先,每次我需要清除缓冲区以便将信息发送到外部库时,我都使用 bufferTEF = new buffer[22000]
。因此,我没有清除它,而是实例化了一个新的,因此更改了它的地址,因此外部库再次找到它时遇到问题。其次,每当我向外部库发送数据时,我都使用bufferTEF = Encoding.ASCII.GetBytes("information"))
,这意味着我发回了一个与我发送的信息长度相同的数组。由于外部库要求最少20000字节,炸了不知道怎么办。
好的。因此,根据我被指出的文章,我继续重写了大部分代码。
看起来像这样:
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('[=10=]')[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('[=11=]')[0];
}
或者要求用户按任意键
public void PerguntaSimOuNao(byte[] pergunta)
{
estadoTEF = StateTEF.AguardaSimNao;
mensagemJanela = "(S)im / (N)ão";
tituloJanela = Encoding.ASCII.GetString(pergunta).Split('[=12=]')[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);
}
}
现在,获取新信息。当我 运行 它使用任务时,没有 async/await,只返回一个任务,它的结果同步触发 FatalExecutionError。如果 ComunicaComTef
为 int,并删除 Task.Run
(只是 运行 同步代码),则不会触发错误,并且循环 运行 是完美的。
问题的上一个版本,如果需要的话:
I've been learning async programming for the last few months, and I've found an error I don't know how to debug/handle:
Here's the setup. I have a window
ShowTEF
, which calls two methods,IniciaFuncaoSitef
andasync ComunicaComTEF
. Both of them call external dll methods, which return integer values and a byte[] by ref.
IniciaFuncaoSitef
simply starts an operation, by providing some parameters to the external dll.ComunicaComTEF
has awhile
loop, that, for each sync call for the external method calls athis.Dispatcher.Invoke()
to refresh the UI. Here's the simplified code: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 ( 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('[=14=]'); //<------ Error thrown here------ lbl_Title.Text = item.titulo.TrimEnd('[=14=]'); } ``` 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 @ 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('[=15=]'); string b = item.titulo.TrimEnd('[=15=]'); tbl_Body.Text = a; lbl_Title.Text = b; } ```
And once again, I triggered the previous
FatalExecutionError
. Here's the stack strace:AmbiPDV.exe!PDV_WPF.Telas.SiTEFBox.AtualizaUI(System.ValueTuple item = {System.ValueTuple}) Line 536 + 0xc bytes C# mscorlib.dll!System.Progress>.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 = 4458568, 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 = 4458568, 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顺便说一下,我想感谢你指点我那篇文章 关于 IProgress。它比大量等待和异步更有意义 空洞!
最初,该问题实际上与异步编程无关,而是与非托管代码和内存处理有关。感谢 Alois,他为我指明了正确的方向,我设法找到了问题所在。
外部库需要一个固定大小的字节数组(准确地说是 22000 字节),并且在库和我的软件交互时它必须位于同一地址。犯了两个大错误。首先,每次我需要清除缓冲区以便将信息发送到外部库时,我都使用 bufferTEF = new buffer[22000]
。因此,我没有清除它,而是实例化了一个新的,因此更改了它的地址,因此外部库再次找到它时遇到问题。其次,每当我向外部库发送数据时,我都使用bufferTEF = Encoding.ASCII.GetBytes("information"))
,这意味着我发回了一个与我发送的信息长度相同的数组。由于外部库要求最少20000字节,炸了不知道怎么办。