Пример #1
0
        /// <summary>
        /// Initializes the script domain inside its application domain.
        /// </summary>
        /// <param name="apiBasePath">The path to the root directory containing the scripting API assemblies.</param>
        private ScriptDomain(string apiBasePath)
        {
            // Each application domain has its own copy of this static variable, so only need to set it once
            CurrentDomain = this;

            // Attach resolve handler to new domain
            AppDomain.AssemblyResolve    += HandleResolve;
            AppDomain.UnhandledException += HandleUnhandledException;

            // Load API assemblies into this script domain
            foreach (string apiPath in Directory.EnumerateFiles(apiBasePath, "ScriptHookRDRNetAPI.dll", SearchOption.TopDirectoryOnly))
            {
                Log.Message(Log.Level.Debug, "Loading API from ", apiPath, " ...");

                try
                {
                    scriptApis.Add(Assembly.LoadFrom(apiPath));
                    Log.Message(Log.Level.Debug, "API from ", apiPath, " loaded", " ...");
                }
                catch (Exception ex)
                {
                    Log.Message(Log.Level.Error, "Unable to load ", Path.GetFileName(apiPath), ": ", ex.ToString());
                }
            }
        }
Пример #2
0
        /// <summary>
        /// The main execution logic of all scripts.
        /// </summary>
        void MainLoop()
        {
            IsRunning = true;

            // Wait for script domain to continue this script
            continueEvent.Wait();

            while (IsRunning)
            {
                // Process keyboard events
                while (keyboardEvents.TryDequeue(out Tuple <bool, KeyEventArgs> ev))
                {
                    try
                    {
                        if (!ev.Item1)
                        {
                            KeyUp?.Invoke(this, ev.Item2);
                        }
                        else
                        {
                            KeyDown?.Invoke(this, ev.Item2);
                        }
                    }
                    catch (ThreadAbortException)
                    {
                        // Stop main loop immediately on a thread abort exception
                        return;
                    }
                    catch (Exception ex)
                    {
                        ScriptDomain.HandleUnhandledException(this, new UnhandledExceptionEventArgs(ex, false));
                        break;                         // Break out of key event loop, but continue to run script
                    }
                }

                try
                {
                    Tick?.Invoke(this, EventArgs.Empty);
                }
                catch (ThreadAbortException)
                {
                    // Stop main loop immediately on a thread abort exception
                    return;
                }
                catch (Exception ex)
                {
                    ScriptDomain.HandleUnhandledException(this, new UnhandledExceptionEventArgs(ex, true));

                    // An exception during tick is fatal, so abort the script and stop main loop
                    Abort(); return;
                }

                // Yield execution to next tick
                Wait(Interval);
            }
        }
Пример #3
0
        /// <summary>
        /// Unloads scripts and destroys an existing script domain.
        /// </summary>
        /// <param name="domain">The script domain to unload.</param>
        public static void Unload(ScriptDomain domain)
        {
            Log.Message(Log.Level.Info, "Unloading script domain ...");

            domain.Abort();
            domain.Dispose();

            try
            {
                AppDomain.Unload(domain.AppDomain);
            }
            catch (Exception ex)
            {
                Log.Message(Log.Level.Error, "Failed to unload script domain: ", ex.ToString());
            }
        }
Пример #4
0
        /// <summary>
        /// Creates a new script domain.
        /// </summary>
        /// <param name="basePath">The path to the application root directory.</param>
        /// <param name="scriptPath">The path to the directory containing scripts.</param>
        /// <returns>The script domain or <c>null</c> in case of failure.</returns>
        public static ScriptDomain Load(string basePath, string scriptPath)
        {
            string _scriptPath;

            // Make absolute path to scrips location
            //if (!Path.IsPathRooted(scriptPath))
            _scriptPath = Path.GetFullPath(Path.Combine(basePath, scriptPath));

            // Create application and script domain for all the scripts to reside in
            var name  = "ScriptDomain_" + (_scriptPath.GetHashCode() ^ Environment.TickCount).ToString("X");
            var setup = new AppDomainSetup();

            setup.ApplicationBase       = _scriptPath;
            setup.ShadowCopyFiles       = "true";      // Copy assemblies into memory rather than locking the file, so they can be updated while the domain is still loaded
            setup.ShadowCopyDirectories = _scriptPath; // Only shadow copy files in the scripts directory

            var appdomain = AppDomain.CreateDomain(name, null, setup, new System.Security.PermissionSet(System.Security.Permissions.PermissionState.Unrestricted));

            appdomain.SetCachePath(Path.GetTempPath());
            appdomain.SetShadowCopyFiles();
            appdomain.SetShadowCopyPath(_scriptPath);
            appdomain.InitializeLifetimeService(); // Give the application domain an infinite lifetime

            // Need to attach the resolve handler to the current domain too, so that the .NET framework finds this assembly in the ASI file
            AppDomain.CurrentDomain.AssemblyResolve += HandleResolve;

            ScriptDomain scriptdomain = null;

            try
            {
                scriptdomain = (ScriptDomain)appdomain.CreateInstanceFromAndUnwrap(typeof(ScriptDomain).Assembly.Location, typeof(ScriptDomain).FullName, false, BindingFlags.NonPublic | BindingFlags.Instance, null, new object[] { basePath }, null, null);

                //Log.Message(Log.Level.Debug, "Script domain created: ", "Name: ", name, " FullName: ", typeof(ScriptDomain).FullName, " Assembly Location: ", typeof(ScriptDomain).Assembly.Location);
            }
            catch (Exception ex)
            {
                Log.Message(Log.Level.Error, "Failed to create script domain: ", ex.ToString(), " ", name, " ", typeof(ScriptDomain).FullName, " ", typeof(ScriptDomain).Assembly.Location);
                AppDomain.Unload(appdomain);
            }

            // Remove resolve handler again
            AppDomain.CurrentDomain.AssemblyResolve -= HandleResolve;
            Log.Message(Log.Level.Debug, "Resolve handler removed");

            return(scriptdomain);
        }
Пример #5
0
        /// <summary>
        /// Aborts execution of this script.
        /// </summary>
        public void Abort()
        {
            IsRunning = false;

            try
            {
                Aborted?.Invoke(this, EventArgs.Empty);
            }
            catch (Exception ex)
            {
                ScriptDomain.HandleUnhandledException(this, new UnhandledExceptionEventArgs(ex, true));
            }

            waitEvent.Release();

            if (thread != null)
            {
                Log.Message(Log.Level.Warning, "Aborted script ", Name, ".");

                thread.Abort(); thread = null;
            }
        }