In a non-UI application, how do I handle "The calling thread cannot access this object because a different thread owns it"

0

I've read through the discussion at The calling thread cannot access this object because a different thread owns it but it's all about UI.

I have two C# EXEs that are purely command-line, but are throwing this error as well.

Perhaps the full exception message will help:

System.InvalidOperationException
  HResult=0x80131509
  Message=The calling thread cannot access this object because a different thread owns it.
  Source=WindowsBase
  StackTrace:
   at System.Windows.Threading.Dispatcher.VerifyAccess()
   at Microsoft.ClearScript.Windows.WindowsScriptEngine.Dispose(Boolean disposing)
   at Microsoft.ClearScript.ScriptEngine.Finalize()

  This exception was originally thrown at this call stack:
    [External Code]

This would seem to imply, to my un-educated eye, that the problem is not in my code but in the ClearScript finalization code. Yes, these are ClearScript-enabled applications which attach to Windows JScript interpreter to provide run-time extensions.

Suggestions welcome.

LATER

Code was requested. Here it is, slightly redacted.

using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Security.Permissions;
using System.Text;
using Microsoft.ClearScript.Windows;

namespace NormanEmailHandler
{
  internal static class Program
  {
    private static JScriptEngine JSengine;

    private static Dictionary<string, object>
      Settings = new Dictionary<string, object>();

    public static string ProcessName = Process.GetCurrentProcess().ProcessName;

    public static string
      UserDomain = Environment.GetEnvironmentVariable("USERDOMAIN");

    public static string DomainProcess = "[" + UserDomain + "] " + ProcessName;

    private static void JSSetup(JScriptEngine jse)
    {
      jse.AddHostType("CSString", typeof (String));
      jse.AddHostType("CSConsole", typeof (Console));
      jse.AddHostType("CSFile", typeof (File));
      jse.AddHostType("CSFileInfo", typeof (FileInfo));
      jse.AddHostType("CSDirectory", typeof (Directory));
      jse.AddHostType("CSPath", typeof (Path));
      jse.AddHostType("CSSearchOption", typeof (SearchOption));
      jse.AddHostType("CSEncoding", typeof (Encoding));
      jse.AddHostType("CSMemoryStream", typeof (MemoryStream));
      jse.AddHostObject("CSSettings", Settings);
      jse.AddHostType("CSTimeSpan", typeof (TimeSpan));
      jse.AddHostType("CSConfig", typeof (Config));
      jse.AddHostType("CSThread", typeof (System.Threading.Thread));
    }

    private static void MyHandler(
      object sender,
      UnhandledExceptionEventArgs args
    )
    {
      Exception e = (Exception) args.ExceptionObject;
      var msgBlock = new StringBuilder();
      msgBlock
        .Append("[")
        .Append(Environment.GetEnvironmentVariable("USERDOMAIN"))
        .Append("] ")
        .Append(Process.GetCurrentProcess().ProcessName)
        .Append(" ")
        .AppendLine(e.Message);
      msgBlock.Append(sender).AppendLine();
      msgBlock.AppendLine(e.StackTrace);
      msgBlock
        .AppendLine(e.InnerException == null ? e.InnerException.Message : "");
      msgBlock
        .Append("Runtime terminating: ")
        .Append(args.IsTerminating)
        .AppendLine();

      T.Inform(msgBlock.ToString()); // T is a logging tool
    }

    [
      SecurityPermission(
        SecurityAction.Demand,
        Flags = SecurityPermissionFlag.ControlAppDomain)
    ]
    private static void Main(string[] args)
    {
      var currentDomain = AppDomain.CurrentDomain;
      currentDomain.UnhandledException += MyHandler;

      config = new Config();
      config.SetErrorOnNull(true);
      config
        .Reload(Path
          .Combine(".\\Configuration", Path.GetFileName(config.Path)));

      Dictionary<string, string> Params =
        CommandLineArguments.Arguments(keepSlash: true);
      if (Params.ContainsKey("/DEBUG"))
      {
        Debugger.Launch();
      }

      string connectionString = config.Retrieve("connection.string");
      string MarshallerEmailHandler = config.Retrieve("marshaller.sproc");
      string MarshallerEmailHandler_01_StartUpdate =
        config.Retrieve("start.sproc");
      string MarshallerEmailHandler_03_FinishUpdate =
        config.Retrieve("finish.sproc");

      T.Inform("Attempting to instantiate JScriptEngine");
      try
      {
        JSengine =
          new JScriptEngine(WindowsScriptEngineFlags.EnableDebugging |
            WindowsScriptEngineFlags.EnableJITDebugging);
        T.Inform("Succesfully instantiated JScriptEngine");
      }
      catch (Exception exc)
      {
        T
          .Fail("Failed to instantiate JScriptEngine",
          exc.Message,
          exc.Source,
          exc.StackTrace,
          exc.InnerException.Message);

        return;
      }
      JSSetup (JSengine); // JSengine);

      JSengine.AddHostObject("T", T);

      SQL sqlServer = new SQL(connectionString);
      Settings["CSDatabase"] = sqlServer;

      string sql;
      while (true)
      {
        sql = "EXEC " + MarshallerEmailHandler;
        T.Inform($"Attempting to execute {sql}");
        DataTable dt = sqlServer.DTEval(sql, 60, 3);
        if (sqlServer.LastError == null)
        {
          T.Inform($"Successfully executed {sql}");
        }
        else
        {
          T.Fail($"Failed to execute {sql} : {sqlServer.LastError.Message}");

          break;
        }

        if (dt.Rows == null || dt.Rows.Count == 0)
        {
          T.Inform("No records marshalled");
          break;
        }

        foreach (DataRow dr in dt.Rows)
        {
          SQL.UpdateDictionaryFromDataRow(ref Settings, dr);

          int RetrievedEmailID = (int) dr["RetrievedEmailID"];
          int EmailRetrievalControllerFK =
            (int) dr["EmailRetrievalControllerFK"];
          int database_UserFK = (int) dr["database_UserFK"];
          int EmailRetrievalUserID = (int) dr["EmailRetrievalUserID"];

          string RetrievedEmailMessageID =
            (string) dr["RetrievedEmailMessageID"];
          string RetrievedEmailFrom = (string) dr["RetrievedEmailFrom"];
          string RetrievedEmailTOs = (string) dr["RetrievedEmailTOs"];
          string RetrievedEmailCCs = (string) dr["RetrievedEmailCCs"];
          string RetrievedEmailSubject = (string) dr["RetrievedEmailSubject"];
          string RetrievedEmailBody = (string) dr["RetrievedEmailBody"];
          string ThisName = (string) dr["ThisName"];
          string EmailHandlingRuleSetJson =
            (string) dr["EmailHandlingRuleSetJson"];
          string EmailHandlingSourceName =
            (string) dr["EmailHandlingSourceName"];

          //start the job
          sql =
            "EXEC " +
            MarshallerEmailHandler_01_StartUpdate +
            " " +
            RetrievedEmailID;
          T.Inform($"Attempting to execute {sql}");
          sqlServer.Exec(sql, 60, 3);
          if (sqlServer.LastError == null)
          {
            T.Inform($"Successfully executed {sql}");
          }
          else
          {
            T.Fail($"Failed to execute {sql} : {sqlServer.LastError.Message}");
            break;
          }

          string script =
            Path
              .Combine(@"C:\Web\Data\RetrievedEmails\RuleSets",
              "EmailHandler.js");
          T.Inform("Attempting to execute ruleset script:", script);

          string errorMsg = string.Empty;
          string scriptText = File.ReadAllText(script);

          object answer = JSengine.Evaluate(scriptText);

          if (answer.ToString() == "OK")
          {
            T.Inform("Script OK.");
            sql =
              "EXEC " +
              MarshallerEmailHandler_03_FinishUpdate +
              " " +
              RetrievedEmailID;
            T.Inform($"Attempting to execute {sql}");
            sqlServer.Exec(sql, 60, 3);
            if (sqlServer.LastError != null)
            {
              T
                .Fail($"Failed to execute {sql} : {
                  sqlServer.LastError.Message}");
            }
            else
            {
              T.Inform($"Successfully executed {sql}");
            }
          }
          else
          {
            string msg =
              string
                .Format("Handler: ruleset {0} failure: RetrievedEmailID={2}\nEmailRetrievalControllerFK={3}\ndatabase_UserFK={4}\nEmailRetrievalUserID={5}",
                script,
                string.Empty,
                RetrievedEmailID,
                EmailRetrievalControllerFK,
                database_UserFK,
                EmailRetrievalUserID);
            T.Fail (msg);
          }
        }
      }
      sqlServer.Close();
    }
  }
}

Given some of the comments, I expect that object answer = JSengine.Evaluate(scriptText); is the offending line and that extra bits need to be added to that invocation.

c#
multithreading
clearscript
asked on Stack Overflow Jul 15, 2020 by bugmagnet • edited Jul 15, 2020 by bugmagnet

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0