title: FatalExecutionEngineError in C# / WSC (COM) interop
body: <p>I'm about to start a migration project at work for a legacy system written in VBScript. It has an interesting structure in that much of it was segregated by writing various components as "WSC" files, which are effectively a way of exposing VBScript code in a COM-like manner. The boundary interface from the "core" to these components is fairly tight and well known so I was hoping that I would be able to tackle writing a new core and reuse the WSCs, postponing their rewriting.</p>
  
  <p>It's possible to load a WSC by adding a reference to "Microsoft.VisualBasic" and calling</p>
  
  <pre><code>var component = (dynamic)Microsoft.VisualBasic.Interaction.GetObject("script:" + controlFilename, null);
  </code></pre>
  
  <p>where "controlFilename" is the full file path. GetObject returns a reference of type "System.__ComObject" but the properties and methods can be accessed using .net's "dynamic" type.</p>
  
  <p>This appeared to work fine initially, but I've encountered problems when quite a specific set of circumstances are combined - my worry is that this may happen in other cases or, even worse, that bad things are happening much of the time and being masked, just waiting to blow up when I least expect it.</p>
  
  <p>The raised exception is of type "System.ExecutionEngineException", which sounds particularly scary (and vague)!</p>
  
  <p>I've cobbled together what I believe to be the minimum reproduce case and was hoping that someone could cast a little light on what the problem could be. I've also identified some tweaks that can be made that seem to prevent it, though I can't explain why.</p>
  
  <ol>
  <li><p>Create a new empty "ASP.NET Web Application" called "WSCErrorExample" (I've done this in VS 2013 / .net 4.5 and VS 2010 / .net 4.0, it makes no difference)</p></li>
  <li><p>Add a reference to "Microsoft.VisualBasic" to the project</p></li>
  <li><p>Add a new "Web Form" called "Default.aspx" and paste the following over the top of "Default.aspx.cs"</p>
  
  
  
  <pre class="lang-cs prettyprint-override"><code>using System;
  using System.IO;
  using System.Reflection;
  using System.Runtime.InteropServices;
  using Microsoft.VisualBasic;
  
  namespace WSCErrorExample
  {
      public partial class Default : System.Web.UI.Page
      {
          protected void Page_Load(object sender, EventArgs e)
          {
              var currentFolder = GetCurrentDirectory();
              var logFile = new FileInfo(Path.Combine(currentFolder, "Log.txt"));
              Action&lt;string&gt; logger = message =&gt;
              {
                  // The try..catch is to avoid IO exceptions when reproducing by requesting the page many times
                  try { File.AppendAllText(logFile.FullName, message + Environment.NewLine); }
                  catch { }
              };
  
              var controlFilename = Path.Combine(currentFolder, "TestComponent.wsc");
              var control = (dynamic)Interaction.GetObject("script:" + controlFilename, null);
  
              logger("About to call Go");
              control.Go(new DataProvider(logger));
              logger("Completed");
          }
          private static string GetCurrentDirectory()
          {
              // This is a way to get the working path that works within ASP.Net web projects as well as Console apps
              var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
              if (path.StartsWith(@"file:\", StringComparison.InvariantCultureIgnoreCase))
                  path = path.Substring(6);
              return path;
          }
  
          [ComVisible(true)]
          public class DataProvider
          {
              private readonly Action&lt;string&gt; _logger;
              public DataProvider(Action&lt;string&gt; logger)
              {
                  _logger = logger;
              }
  
              public DataContainer GetDataContainer()
              {
                  return new DataContainer();
              }
  
              public void Log(string content)
              {
                  _logger(content);
              }
          }
  
          [ComVisible(true)]
          public class DataContainer
          {
              public object this[string fieldName]
              {
                  get { return "Item:" + fieldName; }
              }
          }
      }
  }
  </code></pre></li>
  <li><p>Add a new "Text File" called "TestComponent.wsc", open its properties window and change "Copy to Output Directory" to "Copy if newer" then paste the following in as its content</p>
  
  
  
  <pre class="lang-none prettyprint-override"><code>&lt;?xml version="1.0" ?&gt;
  &lt;?component error="false" debug="false" ?&gt;
  &lt;package&gt;
      &lt;component id="TestComponent"&gt;
          &lt;registration progid="TestComponent" description="TestComponent" version="1" /&gt;
          &lt;public&gt;
              &lt;method name="Go" /&gt;
          &lt;/public&gt;
          &lt;script language="VBScript"&gt;
              &lt;![CDATA[
                  Function Go(objDataProvider)
                      Dim objDataContainer: Set objDataContainer = objDataProvider.GetDataContainer()
                      If IsEmpty(objDataContainer) Then
                          mDataProvider.Log "No data provided"
                      End If
                  End Function
          ]]&gt;
          &lt;/script&gt;
      &lt;/component&gt;
  &lt;/package&gt;
  </code></pre></li>
  </ol>
  
  <p>Running this once should cause no apparent issue, the "Log.txt" file will be written to the "bin" folder. Refreshing the page, however, normally results in an exception</p>
  
  <blockquote>
    <p>Managed Debugging Assistant 'FatalExecutionEngineError' has detected a problem in 'C:\Program Files (x86)\IIS Express\iisexpress.exe'.</p>
    
    <p>Additional information: The runtime has encountered a fatal error. The address of the error was at 0x733c3512, on thread 0x1e10. 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.</p>
  </blockquote>
  
  <p>Occasionally, the second request does not result in this exception, but holding down F5 in the browser window for a couple of seconds will ensure that it rears its ugly head. The exception, so far as I can tell, happens at the "If IsEmpty" check (other versions of this reproduce case had more logging calls, which pointed to that line being the source of the problem).</p>
  
  <p>I've tried various things to try to get to the bottom of this, I've tried to recreate in a console app and the problem does not occur, even if I spin up hundreds of threads and get them to process the work above. I've tried an ASP.Net MVC web application, rather than using a Web Form and the same issue DOES occur. I've tried changing the apartment state from the default MTA to STA (I was clutching at straws a bit at that point!) and it made no change to the behavour. I've tried building a web project that uses Microsoft's <a href="http://www.asp.net/aspnet/overview/owin-and-katana" rel="nofollow">OWIN implementation</a> and the issue occurs in that scenario as well.</p>
  
  <p>Two interesting things that I have noticed - if the "DataContainer" class does not have an indexed property (or a default method / property, decorated with a [DispId(0)] attribute - not illustrated in this example) then the error does not occur. If the "logger" closure does not contain a "FileInfo" reference (if a string "logFilePath" was maintained, rather than the FileInfo instance "logFile") then the error does not occur. I suppose it sounds like one approach would be to avoid doing these things! But I would be concerned that there could be other ways to trigger this scenario that I don't currently know about and trying to enforce the rule of not-doing-these-things could get complicated as the code base grows, I can imagine this error creeping back in without it being immediately obvious why.</p>
  
  <p>On one run (through Katana), I got additional call stack information:</p>
  
  <blockquote>
    <p>This thread is stopped with only external code frames on the call stack. External code frames are typically from framework code but can also include other optimized modules which are loaded in the target process.</p>
    
    <p>Call stack with external code</p>
    
    <p>mscorlib.dll!System.Variant.Variant(object obj)
    mscorlib.dll!System.OleAutBinder.ChangeType(object value, System.Type type, System.Globalization.CultureInfo cultureInfo)
    mscorlib.dll!System.RuntimeType.TryChangeType(object value, System.Reflection.Binder binder, System.Globalization.CultureInfo culture, bool needsSpecialCast)
    mscorlib.dll!System.RuntimeType.CheckValue(object value, System.Reflection.Binder binder, System.Globalization.CultureInfo culture, System.Reflection.BindingFlags invokeAttr)
    mscorlib.dll!System.Reflection.MethodBase.CheckArguments(object[] parameters, System.Reflection.Binder binder, System.Reflection.BindingFlags invokeAttr, System.Globalization.CultureInfo culture, System.Signature sig)
    [Native to Managed Transition]</p>
  </blockquote>
  
  <p>One final note: if I create a wrapper for the "DataProvider" class, using <a href="http://msdn.microsoft.com/en-us/library/system.reflection.ireflect(v=vs.110).aspx" rel="nofollow">IReflect</a> and map the calls over IDispatch on to calls to the underlying "DataProvider" instance then the issue goes away. But again, deciding that this is somehow the answer seems dangerous to me - if I have to be meticulous about ensuring that any reference passed to the components has such a wrapper then errors could creep in that could be difficult to track down. What if a reference that IS encased in an IReflect-implementing wrapper returns a reference from a method or property call that isn't wrapped up in the same way? I suppose the wrapper could try to do something like ensuring it only returns "safe" reference (ie. those without indexed properties or DispId=0 methods or properties) without wrapping <em>them</em> in a further IReflect wrapper.. but it all seems a bit hacky.</p>
  
  <p>I really have no clue where next to go with this problem, does anyone have any idea? </p>