What's new
  • Visit Rebornbuddy
  • Visit Panda Profiles
  • Visit LLamamMagic
  • Visit Resources
  • Visit Downloads
  • Visit Portal

LF a pro programmer to help me catch a runaway thread

highvoltz

Well-Known Member
Joined
Mar 22, 2010
Messages
1,729
Reaction score
141
It looks like the Problem is that every time I hit the 'Recompile All' button in HB all my variables get cleared and get assigned to new values on the stack/heap and a new thread gets created while I lose track of the old thread that keeps on running. I tried to run thread.abort(); in my plugin's finalizer but it doesn't look like it gets executed.
I tried different things, nothing worked, ran out of ideas.
Trying to make my plugin fool proof.

If anyone wants to look at my code and help me out I would appreciate it. I'm pretty new to C# and never ever played with threads before.



keyPollThread is the name of the thread and it executes ThreadLoop()
Plugin_Thread is the main plugin thread. I use it only to check if HB is running and exit the loop in ThreadLoop() if it isn't
Code:
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Forms;
using System.Diagnostics;
//using Microsoft.Aspnet.Snapin;
using System.Threading;

using Styx.Plugins.PluginClass;
using Styx.Logic.BehaviorTree;
using Styx.Helpers;
using Styx.WoWInternals;

namespace HighVoltz
{
    public partial class FormHB_Toggle : Form
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// 
true if managed resources should be disposed; otherwise, false.
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.KeyBindButton = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // KeyBindButton
            // 
            this.KeyBindButton.BackColor = System.Drawing.SystemColors.Control;
            this.KeyBindButton.Location = new System.Drawing.Point(7, 12);
            this.KeyBindButton.Name = "KeyBindButton";
            this.KeyBindButton.Size = new System.Drawing.Size(175, 25);
            this.KeyBindButton.TabIndex = 0;
            this.KeyBindButton.Text = "Click to set Keybind";
            this.KeyBindButton.UseVisualStyleBackColor = false;
            this.KeyBindButton.Click += new System.EventHandler(this.KeyBindButton_Click);
            this.KeyBindButton.KeyDown += new System.Windows.Forms.KeyEventHandler(this.KeyBindButton_KeyDown);
            // 
            // FormHB_Toggle
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(194, 47);
            this.Controls.Add(this.KeyBindButton);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.Name = "FormHB_Toggle";
            this.Text = "Key Binding";
            this.Load += new System.EventHandler(this.FormHB_Toggle_Load);
            this.Shown += new System.EventHandler(this.FormHB_Toggle_Shown);
            this.ResumeLayout(false);

        }

        #endregion
        private bool keyBindMode = false;
        private System.Windows.Forms.Button KeyBindButton;

        public FormHB_Toggle()
        {
            InitializeComponent();
        }

        private void FormHB_Toggle_Load(object sender, EventArgs e)
        {

        }

        private void KeyBindButton_Click(object sender, EventArgs e)
        {
            if (!keyBindMode)
            {
                KeyBindButton.BackColor = System.Drawing.SystemColors.GradientInactiveCaption;
                KeyBindButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
                KeyBindButton.Text = "Press key combination";
                keyBindMode = true;
            }
        }

        private void KeyBindButton_KeyDown(object sender, KeyEventArgs e)
        {
            // return if not changing keybinds.
            if (!keyBindMode) return;
            // convert modifier keys to string to be displayed on button
            string keybindstring = (e.Shift ? "Shift " : "") + (e.Control ? "Ctrl " : "") + (e.Alt ? "Alt " : "");
            KeyBindButton.Text = keybindstring;
            if (e.KeyCode == Keys.ShiftKey || e.KeyCode == Keys.ControlKey || e.KeyCode == Keys.Menu)
            {
                return;
            }
            // display numbers in button text correctly.
            if (e.KeyCode >= Keys.D0 && e.KeyCode <= Keys.D9)
            {
                keybindstring += e.KeyValue - 48;
            }
            else
            {
                keybindstring += e.KeyCode.ToString();
            }
            KeyBindButton.Text = keybindstring;
            KeyBindButton.FlatStyle = System.Windows.Forms.FlatStyle.Standard;
            KeyBindButton.BackColor = System.Drawing.SystemColors.Control;
            keyBindMode = false;
            HB_Toggle.SetKeyBind(e.KeyData);
        }

        private void FormHB_Toggle_Shown(object sender, EventArgs e)
        {
            // everything here has to do with displaying the current keybind on the button.
            System.Windows.Forms.Keys vKey = HighVoltz.Properties.Settings.Default.Keybind;
            if (vKey == 0)
            {
                KeyBindButton.Text = "Click to set Keybind";
            }
            else
            {
                // extract modifier keys                 
                string keybindstring = ((vKey & Keys.Shift) != 0 ? "Shift " : "") + ((vKey & Keys.Control) != 0 ? "Ctrl " : "") + ((vKey & Keys.Alt) != 0 ? "Alt " : "");
                // Shift = 0x00010000, Ctrl= 0x00020000, Alt = 0x00040000
                // remove modifer bits
                vKey ^= (Keys.Shift & vKey) | (Keys.Control & vKey) | (Keys.Alt & vKey);
                // display numbers in button text correctly.
                switch (vKey)
                {
                    case Keys.D0:
                        keybindstring += "0";
                        break;
                    case Keys.D1:
                        keybindstring += "1";
                        break;
                    case Keys.D2:
                        keybindstring += "2";
                        break;
                    case Keys.D3:
                        keybindstring += "3";
                        break;
                    case Keys.D4:
                        keybindstring += "4";
                        break;
                    case Keys.D5:
                        keybindstring += "5";
                        break;
                    case Keys.D6:
                        keybindstring += "6";
                        break;
                    case Keys.D7:
                        keybindstring += "7";
                        break;
                    case Keys.D8:
                        keybindstring += "8";
                        break;
                    case Keys.D9:
                        keybindstring += "9";
                        break;
                    default:
                        keybindstring += vKey.ToString();
                        break;
                }
                KeyBindButton.Text = keybindstring;
            }
        }
    }

    public class KeyBindClass
    {
        [DllImport("user32.dll")]
        static extern short GetAsyncKeyState(System.Windows.Forms.Keys vKey);
        private System.Windows.Forms.Keys _keyBind;
        // these modifer bools are true if our keybind uses them. otherwise false.
        private bool _shiftkey = false;
        private bool _ctrlkey = false;
        private bool _altkey = false;
        public KeyBindClass()
        {
            Logging.Write("KeyBindClass(): ", "");
            _keyBind = new System.Windows.Forms.Keys();
            Logging.Write("-KeyBindClass(): ", "");
        }
        ~KeyBindClass()
        {
            // never gets called?!?!?
            Logging.Write("-finalizer(): ", "");
        }
        public KeyBindClass(System.Windows.Forms.Keys k)
        {  // k holds both modifer and  key so we need to extract the modifer
            _shiftkey = (k & Keys.Shift) != 0 ? true : false;
            _ctrlkey = (k & Keys.Control) != 0 ? true : false;
            _altkey = (k & Keys.Alt) != 0 ? true : false;
            // now we remove the modifer flags from k and assign it to _keybind.
            _keyBind = (Keys.Shift & k) | (Keys.Control & k) | (Keys.Alt & k);
        }

        public System.Windows.Forms.Keys KeyBind
        {
            get { return _keyBind; }
            set
            {   // check for modifer flags 
                _shiftkey = (value & Keys.Shift) != 0 ? true : false;
                _ctrlkey = (value & Keys.Control) != 0 ? true : false;
                _altkey = (value & Keys.Alt) != 0 ? true : false;
                // remove modifer bits
                value ^= (Keys.Shift & value) | (Keys.Control & value) | (Keys.Alt & value);
                _keyBind = value;
            }
        }

        public static bool IsKeyDown(System.Windows.Forms.Keys vKey)
        {

            return (GetAsyncKeyState(vKey) != 0);
        }

        public bool PollKeys()
        {   // checks button status and returns true if our beybind is pressed.
            if (IsKeyDown(_keyBind) && (!(_shiftkey ^ IsKeyDown(Keys.ShiftKey))) &&
                (!(_ctrlkey ^ IsKeyDown(Keys.ControlKey))) && (!(_altkey ^ IsKeyDown(Keys.Menu))))
            {
                return true;
            }
            return false;
        }
    }


    public class HB_Toggle : HBPlugin
    {
        private readonly FormHB_Toggle _configForm = new FormHB_Toggle();
        public override string Name { get { return "HB Toggle"; } }
        public override string Author { get { return "HighVoltz"; } }
        private readonly Version _version = new Version(1, 0);
        public override Version Version { get { return _version; } }
        public override string ButtonText { get { return "KeyBinding"; } }
        public override bool WantButton { get { return true; } }
        private static KeyBindClass _keyBind = new KeyBindClass();
        private static bool _initalized = false;
        // making a new thread because pulse() doesn't get called when bot is paused and it doesn't get called frequently 
        // enough to get acurrate key readings. thread terminates when HB terminates.
        Thread keyPollThread = new Thread(new ThreadStart(ThreadLoop));
        private static Thread Plugin_Thread = Thread.CurrentThread;
        [DllImport("user32.dll")]
        static extern IntPtr GetForegroundWindow();
        private static IntPtr _wowHandle = ObjectManager.WoWProcess.MainWindowHandle;
        // contains info about this plugin, used to determine if this plugin is enabled from my thread loop.
        static private Styx.Plugins.PluginContainer myPlugin;
        
          public HB_Toggle()
          {
              Logging.Write("_initalized:{0} ", _initalized);
          }

          ~HB_Toggle()
          {
             // this never gets called?!?!?
              // Logging.Write("in finalizer: _initalized:{0} ", _initalized);
              Logging.Write("Aborting previous key poll thread : ", "");
              keyPollThread.Abort();
        //      Logging.Write("Aborted previous key thread : ", "");
          }

        public override void OnButtonPress()
        {
            _configForm.ShowDialog();
        }
        public override void Pulse()
        {
            // run this once the program starts.
            if (!_initalized)
            {
                Logging.Write("loadSettings: ", "");
                loadSettings();
                 _initalized = true;
                 Logging.Write("Getting Plugin: ", "");
                 for (int i = 0; i < Styx.Plugins.PluginManager.Plugins.Count(); i++)
                 {
                     myPlugin = Styx.Plugins.PluginManager.Plugins[i];
                     if (myPlugin.Plugin.PluginName == "HB Toggle")
                         break;
                 }

                Logging.Write("Starting Thread: ", "");
                if (keyPollThread.IsAlive)
                {
                    Logging.Write("Thread is still alive!: ", "");
                }
                keyPollThread.Start();
                Logging.Write("Thread Started: ", "");

            }
        }

        // this is being run in a new thread
        public static void ThreadLoop()
        {
            //PluginContainer plug = Styx.Plugins.PluginManager.Plugins[1];
            bool toggleControl = false;
            // exit loop when main thread terminates.
            while (Plugin_Thread.IsAlive && _initalized)
            {
                // if keybind is pressed and our wow window is the currently active window then toggle HB
                IntPtr fgWinHwnd = GetForegroundWindow();
                if (_keyBind.PollKeys() && !toggleControl && (_wowHandle == fgWinHwnd) && myPlugin.Enabled)
                {
                    toggleControl = true;
                    Toggle();
                }
                else
                {
                    toggleControl = false;
                }
                // Yield the rest of the time slice.
                Thread.Sleep(75);
            }
            Logging.Write("Thread Terminated","");
        }

        private static void Toggle()
        {
            if (TreeRoot.IsRunning)
            {
                Logging.Write("Honorbuddy: {0}", "Off");
                // MoveStop not working.
                // WoWMovement.MoveStop();
                TreeRoot.Stop();
                Styx.Lua.DoString("UIErrorsFrame:AddMessage(\"HonorBuddy: OFF\", 1.0, 0.0, 0.0)");
                //run UIErrorsFrame:AddMessage("HonorBuddy: ON")

            }
            else
            {
                Logging.Write("Honorbuddy: {0}", "On");
                Styx.Lua.DoString("UIErrorsFrame:AddMessage(\"HonorBuddy: ON\", 0.1, 1.0, 0.1)");
                TreeRoot.Start();
            }
        }

        public static void SetKeyBind(System.Windows.Forms.Keys vKey)
        {
            _keyBind.KeyBind = vKey;
            SaveKeybind(vKey);
        }

        private static void SaveKeybind(System.Windows.Forms.Keys vKey)
        {
            HighVoltz.Properties.Settings.Default.Keybind = vKey;
            HighVoltz.Properties.Settings.Default.Save();
        }

        private static void loadSettings()
        {
            _keyBind.KeyBind = HighVoltz.Properties.Settings.Default.Keybind;
        }

    }
}
 
Last edited:
Set the IsBackground property of your thread to true. It will clean itself up for you.

Or, the logical thing to do, would be to set a name for the thread, and just abort any threads that have the name, before creating a new one.
 
Set the IsBackground property of your thread to true. It will clean itself up for you.

Or, the logical thing to do, would be to set a name for the thread, and just abort any threads that have the name, before creating a new one.
I have set my thread IsBackground=true. it'll cleanup after HB exits but not after a recompile .

I have searched google and could not find anyway to enumerate through a process's managed threads.
System.Diagnostics.Process.Threads returns a list of ProcessThreads which is a list of the OS's native threads and is different from .nets managed threads Thread class
ProcessThreads does not have any name properties.

simple solution would be to have HB call the Plugin's finalizer before doing a recompile.
 
Last edited:
I have set my thread IsBackground=true. it'll cleanup after HB exits but not after a recompile .

I have searched google and could not find anyway to enumerate through a process's managed threads.
System.Diagnostics.Process.Threads returns a list of ProcessThreads which is a list of the OS's native threads and is different from .nets managed threads Thread class
ProcessThreads does not have any name properties.

simple solution would be to have HB call the Plugin's finalizer before doing a recompile.

Couldn't you make it static so any instance will see the same thread?
 
Easiest would be to implement IDisposable; and tell the GC to add memory pressure on your class instance.
 
Back
Top