Quantcast
Channel: ClearScript
Viewing all articles
Browse latest Browse all 2297

Commented Unassigned: Cannot access a disposed object Error [114]

0
0
I get a weird "Cannot access a disposed object." error which I cant track down for its evil root.

Im stuck in not finding the cause and out of ideas.
its going to be difficult to explain, but basically I have 2 VBScript Engines instances running the same code in a sequential, non-parallel order. like:

```
var engine1 = new VBScriptEngine();
engine1.Execute(..);
engine1.Execute(..);
try {
engine1.Execute("engine1.Interrupt()");
} catch (Exception e) {
if (!(e is ScriptInterruptedException || e?.InnerException is ScriptInterruptedException))
throw e;
}
engine1.Dispose();
engine1 = null;

var engine2 = new VBScriptEngine();
engine2.Execute(..);
engine2.Execute(..);
try {
engine2.Execute("engine2.Interrupt()");
} catch (Exception e) {
if (!(e is ScriptInterruptedException || e?.InnerException is ScriptInterruptedException))
throw e;
}
engine2.Dispose();
engine2 = null;

```

If I dont call the Dispose() method after an engine finishes its job with a final Interrupt() call inside a loaded Script Document, everything is doing fine.

but on calling the Dispose() method on the 1st VBScript Engine after it finishes all tasks and stops with an Interrupt() call, the 2nd VBScript Engine still makes it to end of all its user script including the final Interrupt() call from within a Script document, but than somehow hits an "Cannot access a disposed object" Error after the engine successfully catches the Interrupt Exception.

I ve attached some screenshots where I think they might be better to further analyze the issue.

in addition some short descriptions to the attached images:

(se_all_intrptex.PNG)
1st script engine runs all script code, finishes and finally calls Interrupt() inside a script document, hitting the breakpoint in WindowsScriptEngine.Site.cs:231.

(se1_1oninvokeex.PNG)
hits InvokeMethod(...):177
and continues with Marshal.GetObjectForNativeVariant(...)

(se1_ondisposeex.PNG)
step-in next stops at CoTaskMemBlock.cs - Dispose():85
NOTICE the exception in the Locals Window writes: Exception HRESULT 0x80020101

(se1_errout.PNG + se1_errout_det.PNG)
last significant break seems to be here, where the exception catched before is being thrown.


when the 2nd script engine finishes all user script and receives the Interrupt() call, things go the same except that the Exception in the Locals Window (se2_2ondisposeex.PNG) now writes "Cannot access a disposed object..."

further it continues and throws a TargetInvocationException with the "Cannot access a disposed object" inside as inner exception.

the only difference I could catch here is that omitting the Dispose() call on the script engines produces all times a HRESULT 0x80020101 Exception as soon as CoTaskMemBlock.cs - Dispose():86 is hit.

but by having the Dispose() calls, all scripts engines created afterwards will produce a "Cannot access a disposed object".

one more to note: the object which is being noted as disposed is a: Microsoft.ClearScript.Windows.VBScriptEngine' object. the screenshots: se2_1oninvokeex.PNG, se2_2ondisposeex.PNG just suck in showing it.

I tried to step trough into any line inside the clearscript implementation where the scriptengine.IsDisposed prop was set to true, but no luck. I could not find any context where the scriptengine was marked as IsDisposed=true before the exception "Cannot access a disposed object" was thrown.

anyways, my quick fix so far is a simple exception check similar to:
```
...
catch (Exception e) {
if !(myFlag.IsScriptFinished && e.InnerException != null && e.InnerException.Message.Contains("Cannot access a disposed object."))
throw e;
}
...
```

much appreciate any help. thanks





Comments: turns out the issue is occurring in recursive ManagedContext <> ScriptHostContext calls. and my code has a lot of it. but the evil doer was actually my helper function to suppress the, for me in most cases useless, ScriptInterruptedException. ``` public static void Stop(this WindowsScriptEngine engine) { try { engine.Interrupt(); } catch (Exception e) { if (!e.IsScriptInterruptedException()) throw; } } public static bool IsScriptInterruptedException(this Exception e) { do { if (e is ScriptInterruptedException) return true; e = e.InnerException; } while (e != null); return false; } ``` what seems to have happened in case of recursive calls was, that inner level calls in the ScriptContext couldnt catch up with the ScriptInterrupted exceptions thrown by child calls. so had other nestest exceptions(TargetInvocation in most cases) which the Engine tried to package into new exceptions. but then, if the engine was disposed already by a deeper occured invokation down the ladder, it tried to work on a dead engine instance. well, in short the fix for my problem would be either to check for a ScriptInterrupted Exception explicitly on each line of invocation whether in ManagedContext or ScriptContext or to go with this to have more simple project level code for cases where I don't care of a ScriptInterruptedException's: WindowsScriptEngine.cs:108 ``` private bool suppressInterruptedException = false; ``` WindowsScriptEngine.cs:384 ``` /// <summary> /// Interrupts script execution and causes the script engine to throw an exception. /// </summary> /// <remarks> /// This method can be called safely from any thread. /// </remarks> public override void Interrupt() { Interrupt(false); } /// <summary> /// Interrupts script execution and causes the script engine to throw an exception. /// </summary> /// <remarks> /// This method can be called safely from any thread. /// </remarks> /// <param name="suppressException">Suppresses the ScriptInterruptException</param> public void Interrupt(bool suppressException) { VerifyNotDisposed(); suppressInterruptedException = suppressException; var excepInfo = new EXCEPINFO { scode = RawCOMHelpers.HResult.E_ABORT }; activeScript.InterruptScriptThread(ScriptThreadID.Base, ref excepInfo, ScriptInterruptFlags.None); } ``` WindowsScriptEngine.cs:769 ``` internal override T ScriptInvoke<T>(Func<T> func) { VerifyAccess(); return base.ScriptInvoke(() => { try { return func(); } catch (Exception exception) { try { ThrowScriptError(exception); } catch (ScriptInterruptedException) { if (suppressInterruptedException) { suppressInterruptedException = false; return default(T); } throw; } throw; } }); } ``` Does this going to break something else? For now it does well in my project.

Viewing all articles
Browse latest Browse all 2297