捕获块中的Powershell异常导致意外的执行流
我试图理解为什么下面的代码会打印“我不应该被打印!!” try块中的代码抛出错误,当其errorAction变量设置为stop时,Ppowershell将执行相应的catch块 在catch测试中,函数调用将失败(因为测试函数param属性validateSet不包括“fasdfsd”),并抛出明显未终止的异常:ParameterArgumentValidationError 我现在希望在测试函数调用后,执行会在第行继续,打印“捕获的异常”并退出,但它会跳出catch块并继续执行打印“我不应该被打印!!” 我错过了什么 谢谢, 戴维德 @beatcracker如果在非 终止异常执行应继续执行下一个异常 写入输出为“捕获异常”的指令,并且不久之后 应该退出脚本的命令“exit” -戴维德·塔莱斯科 我同意,这一点我不清楚。我的意思是,它总是以这种方式工作——必须将catch块中的所有内容包装到另一个try/catch中,否则它只会在出现非终止错误时悄悄退出catch scriptblock,并继续执行接下来的任何代码捕获块中的Powershell异常导致意外的执行流,powershell,error-handling,try-catch,throw,Powershell,Error Handling,Try Catch,Throw,我试图理解为什么下面的代码会打印“我不应该被打印!!” try块中的代码抛出错误,当其errorAction变量设置为stop时,Ppowershell将执行相应的catch块 在catch测试中,函数调用将失败(因为测试函数param属性validateSet不包括“fasdfsd”),并抛出明显未终止的异常:ParameterArgumentValidationError 我现在希望在测试函数调用后,执行会在第行继续,打印“捕获的异常”并退出,但它会跳出catch块并继续执行打印“我不应该被
下面不是一个真正的答案,只是一个想法,希望更熟练的人能从中有所收获。
虽然我不能很好地理解它为什么这样做,但我想我在
System.Management.Automation
assembly(public class ScriptBlock
)中将它本地化为这段代码:
catch的ScriptBlockforExceptionHandler设置为true
时,由内部密封类ExceptionHandlerNode
的方法Invoke
创建脚本块,该方法从上述公共类ScriptBlock
调用CreateExceptionHandler
:
internal void InvokeWithPipe(bool useLocalScope, bool writeErrors, object dollarUnder, object input, object scriptThis, Pipe outputPipe, ref ArrayList resultList, params object[] args)
{
ExecutionContext contextFromTLS = this.GetContextFromTLS();
if (contextFromTLS.CurrentPipelineStopping)
{
throw new PipelineStoppedException();
}
ParseTreeNode codeToInvoke = this.GetCodeToInvoke();
if (codeToInvoke != null)
{
InvocationInfo invocationInfo = new InvocationInfo(null, codeToInvoke.NodeToken, contextFromTLS);
contextFromTLS.Debugger.PushMethodCall(invocationInfo, this);
bool flag = false;
ScriptInvocationContext oldScriptContext = null;
Pipe shellFunctionErrorOutputPipe = null;
CommandOrigin scopeOrigin = contextFromTLS.EngineSessionState.currentScope.ScopeOrigin;
Exception exception = null;
SessionStateInternal engineSessionState = contextFromTLS.EngineSessionState;
ActivationRecord oldActivationRecord = null;
try
{
ScriptInvocationContext scriptContext = new ScriptInvocationContext(useLocalScope, scriptThis, dollarUnder, input, args);
this.EnterScope(contextFromTLS, scriptContext, out oldScriptContext, out oldActivationRecord);
shellFunctionErrorOutputPipe = contextFromTLS.ShellFunctionErrorOutputPipe;
if (!writeErrors)
{
contextFromTLS.ShellFunctionErrorOutputPipe = null;
}
contextFromTLS.EngineSessionState.currentScope.ScopeOrigin = CommandOrigin.Internal;
if (!string.IsNullOrEmpty(this.File))
{
contextFromTLS.Debugger.PushRunning(this.File, this, false);
flag = true;
}
codeToInvoke.Execute(null, outputPipe, ref resultList, contextFromTLS);
}
catch (ReturnException exception2)
{
if (!this._isScriptBlockForExceptionHandler)
{
ParseTreeNode.AppendResult(contextFromTLS, exception2.Argument, null, ref resultList);
}
else
{
exception = exception2;
}
}
finally
{
if (flag)
{
contextFromTLS.Debugger.PopRunning();
}
contextFromTLS.ShellFunctionErrorOutputPipe = shellFunctionErrorOutputPipe;
contextFromTLS.EngineSessionState.currentScope.ScopeOrigin = scopeOrigin;
try
{
this.LeaveScope(contextFromTLS, oldScriptContext, engineSessionState, oldActivationRecord);
}
finally
{
contextFromTLS.Debugger.PopMethodCall();
}
}
if (exception != null)
{
throw exception;
}
}
}
internal static ScriptBlock CreateExceptionHandler(ParseTreeNode body, Token token, int pipelineSlots, int variableSlots)
{
return new ScriptBlock(token, null, null, null, null, body, null, false, null, null, null, pipelineSlots, variableSlots) { _isScriptBlockForExceptionHandler = true };
}
请注意,当\u isScriptBlockForExceptionHandler
设置为true
时,当在上面的InvokeWithPipe
方法中执行catch块时,如果发生异常,则不会引发异常:
catch (ReturnException exception2)
{
if (!this._isScriptBlockForExceptionHandler)
{
ParseTreeNode.AppendResult(contextFromTLS, exception2.Argument, null, ref resultList);
}
else
{
exception = exception2;
}
}
我不清楚AppendResult
方法的作用,但我发现:
调用AppendResult方法主要是调用
返回对象的公共属性以检索
将被写入输出控制台
@beatcracker如果在非
终止异常执行应继续执行下一个异常
写入输出为“捕获异常”的指令,并且不久之后
应该退出脚本的命令“exit”
-戴维德·塔莱斯科
我同意,这一点我不清楚。我的意思是,它总是以这种方式工作——必须将catch块中的所有内容包装到另一个try/catch中,否则它只会在出现非终止错误时悄悄退出catch scriptblock,并继续执行接下来的任何代码
下面不是一个真正的答案,只是一个想法,希望更熟练的人能从中有所收获。
虽然我不能很好地理解它为什么这样做,但我想我在
System.Management.Automation
assembly(public class ScriptBlock
)中将它本地化为这段代码:
catch的ScriptBlockforExceptionHandler设置为true
时,由内部密封类ExceptionHandlerNode
的方法Invoke
创建脚本块,该方法从上述公共类ScriptBlock
调用CreateExceptionHandler
:
internal void InvokeWithPipe(bool useLocalScope, bool writeErrors, object dollarUnder, object input, object scriptThis, Pipe outputPipe, ref ArrayList resultList, params object[] args)
{
ExecutionContext contextFromTLS = this.GetContextFromTLS();
if (contextFromTLS.CurrentPipelineStopping)
{
throw new PipelineStoppedException();
}
ParseTreeNode codeToInvoke = this.GetCodeToInvoke();
if (codeToInvoke != null)
{
InvocationInfo invocationInfo = new InvocationInfo(null, codeToInvoke.NodeToken, contextFromTLS);
contextFromTLS.Debugger.PushMethodCall(invocationInfo, this);
bool flag = false;
ScriptInvocationContext oldScriptContext = null;
Pipe shellFunctionErrorOutputPipe = null;
CommandOrigin scopeOrigin = contextFromTLS.EngineSessionState.currentScope.ScopeOrigin;
Exception exception = null;
SessionStateInternal engineSessionState = contextFromTLS.EngineSessionState;
ActivationRecord oldActivationRecord = null;
try
{
ScriptInvocationContext scriptContext = new ScriptInvocationContext(useLocalScope, scriptThis, dollarUnder, input, args);
this.EnterScope(contextFromTLS, scriptContext, out oldScriptContext, out oldActivationRecord);
shellFunctionErrorOutputPipe = contextFromTLS.ShellFunctionErrorOutputPipe;
if (!writeErrors)
{
contextFromTLS.ShellFunctionErrorOutputPipe = null;
}
contextFromTLS.EngineSessionState.currentScope.ScopeOrigin = CommandOrigin.Internal;
if (!string.IsNullOrEmpty(this.File))
{
contextFromTLS.Debugger.PushRunning(this.File, this, false);
flag = true;
}
codeToInvoke.Execute(null, outputPipe, ref resultList, contextFromTLS);
}
catch (ReturnException exception2)
{
if (!this._isScriptBlockForExceptionHandler)
{
ParseTreeNode.AppendResult(contextFromTLS, exception2.Argument, null, ref resultList);
}
else
{
exception = exception2;
}
}
finally
{
if (flag)
{
contextFromTLS.Debugger.PopRunning();
}
contextFromTLS.ShellFunctionErrorOutputPipe = shellFunctionErrorOutputPipe;
contextFromTLS.EngineSessionState.currentScope.ScopeOrigin = scopeOrigin;
try
{
this.LeaveScope(contextFromTLS, oldScriptContext, engineSessionState, oldActivationRecord);
}
finally
{
contextFromTLS.Debugger.PopMethodCall();
}
}
if (exception != null)
{
throw exception;
}
}
}
internal static ScriptBlock CreateExceptionHandler(ParseTreeNode body, Token token, int pipelineSlots, int variableSlots)
{
return new ScriptBlock(token, null, null, null, null, body, null, false, null, null, null, pipelineSlots, variableSlots) { _isScriptBlockForExceptionHandler = true };
}
请注意,当\u isScriptBlockForExceptionHandler
设置为true
时,当在上面的InvokeWithPipe
方法中执行catch块时,如果发生异常,则不会引发异常:
catch (ReturnException exception2)
{
if (!this._isScriptBlockForExceptionHandler)
{
ParseTreeNode.AppendResult(contextFromTLS, exception2.Argument, null, ref resultList);
}
else
{
exception = exception2;
}
}
我不清楚AppendResult
方法的作用,但我发现:
调用AppendResult方法主要是调用
返回对象的公共属性以检索
将被写入输出控制台
如果
ParameterArgumentValidationError
是一个非终止异常,那么一切都会正常工作。您必须将其转换为终止异常,例如:测试函数-param“fasdfsd”-ErrorAction Stop
以获得所需的结果。IIRC使用-ErrorAction
参数设置错误操作不包括参数错误,因此您必须在此处设置$ErrorActionPreference='Stop'
。@AnsgarWiechers,是的,刚刚检查过,您是对的,因此$ErrorActionPreference='Stop'
是唯一的方法。@beatcracker如果在非终止异常后一切正常,则执行应继续执行下一条指令,即写入输出“捕获的异常”,并在应退出脚本的命令“exit”后立即执行。相反,它会写入输出“我不应该被打印”@DavideTalesco是的,它不符合逻辑,但它总是这样工作,请参阅我的“答案”了解详细信息。如果ParameterArgumentValidationError
是一个非终止异常,那么一切都会正常工作。您必须将其转换为终止异常,例如:测试函数-param“fasdfsd”-ErrorAction Stop
以获得所需的结果。IIRC使用-ErrorAction
参数设置错误操作不包括参数错误,因此您必须在此处设置$ErrorActionPreference='Stop'
。@AnsgarWiechers,是的,刚刚检查过,您是对的,因此$ErrorActionPreference='Stop'
是唯一的方法。@beatcracker如果在非终止异常后一切正常,则执行应继续执行下一条指令,即写入输出“捕获的异常”,并在命令“exit”后立即退出scrip