Click here to Skip to main content
15,897,718 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I am coding an application and this codepart is the simplified version of the not working part...
The app will be a Windows Form application manipulating UI using UIAutomation.
Mainthread and eventhandling thread manipulates the UI as well so during event handling i want to suspend the mainthread not to have concurrent usage of the UI.
Maybe the used method is not the best...but i have no idea how to do this in different way.
My problem would be that for event handling i am using a thread and this thread hangs when i want to reach mainthread's static variables in the for statement.
This simplified version manages to access it but hangs during the iteration, my real application hangs for the first iteration...
Any help would be appreciated...
Thanks in advanced,
B.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Automation;
using System.Diagnostics;
using System.Threading;
using Microsoft.Win32;
using System.IO;
using System.Windows;
using System.Windows.Forms;
using System.Net.Mail;
using System.Net;
using System.Net.Mime;
using System.Runtime.InteropServices;
using System.Media;

namespace TestEHWin
{
    internal static class Worker
    {
        internal static AutomationElement eventSenderElement = null;
        internal static List<AutomationElement> dialogs = new List<AutomationElement>();

        internal static Thread mainThread;
        internal static string debugFile = @"C:\Test_logging.txt";
        internal static string debugFileEvent = @"C:\Test_logging_Event.txt";

        internal static void Start()
        {
            StreamWriter sw = new StreamWriter(Worker.debugFileEvent);
            sw.Close();

            int _counter = 0;
            mainThread = Thread.CurrentThread;
            DebugWrite("M - Mainthread id: " + mainThread.ManagedThreadId);

            dialogs = GetChildDialogs(AutomationElement.RootElement);

            foreach (AutomationElement _item in dialogs)
            {
                DebugWrite("M - Dialog: " + _item.Current.Name);
            }

            Automation.AddStructureChangedEventHandler(AutomationElement.RootElement, TreeScope.Children, new StructureChangedEventHandler(strucChangedHandler));

            DateTime stop = DateTime.Now.AddSeconds(20);
            do
            {
                _counter++;
                DebugWrite("M - Iteration number #" + _counter.ToString());
                Sleep(10);
            } while (DateTime.Now < stop);

            Automation.RemoveStructureChangedEventHandler(AutomationElement.RootElement, strucChangedHandler);

            MessageBox.Show("Finished....");
        }

        private static void strucChangedHandler(object _sender, StructureChangedEventArgs _e)
        {
            AutomationElement _senderElement = _sender as AutomationElement;
            Object _windowPattern;

            if (_e.StructureChangeType == StructureChangeType.ChildAdded)
            {
                if (false == _senderElement.TryGetCurrentPattern(WindowPattern.Pattern, out _windowPattern))
                {
                    return;
                }

                eventSenderElement = _senderElement;
                System.Threading.Thread _thread = new Thread(new ThreadStart(testHandler));
                _thread.Priority = ThreadPriority.Highest;
                _thread.Start();
            }
        }

        private static void testHandler()
        {
            mainThread.Suspend();
            DebugWriteEvent("EHT - Thread #" + mainThread.ManagedThreadId.ToString() + " will be suspended.");
            DebugWriteEvent("EHT - ThreadID: " + Thread.CurrentThread.ManagedThreadId);

            DebugWriteEvent("EHT - New dialog name: " + eventSenderElement.Current.Name);

            dialogs = GetChildDialogs(AutomationElement.RootElement);
            DebugWriteEvent("EHT - Dialogs number: #" + dialogs.Count.ToString());

            for (int i = 0; i < dialogs.Count; i++)
            {
                DebugWriteEvent("EHT - Dialog#" + (i + 1).ToString() + " in root: " + dialogs[i].Current.Name);
                Thread.Sleep(100);
            }

            DebugWriteEvent("EHT - Thread #" + mainThread.ManagedThreadId.ToString() + " will be alive again.");
            mainThread.Resume();
        }

        internal static void Sleep(int timeoutInMs)
        {
            TimeSpan wait = DateTime.Now.TimeOfDay;
            wait = wait.Add(new TimeSpan(0, 0, 0, 0, timeoutInMs));

            while (DateTime.Now.TimeOfDay < wait)
            {
                Application.DoEvents();
            }
        }

        internal static List<AutomationElement> GetChildDialogs(AutomationElement _basedialog)
        {
            List<AutomationElement> _dialoglist = new List<AutomationElement>();
            TreeWalker _walker = new TreeWalker(new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window));
            AutomationElement _node = _walker.GetFirstChild(_basedialog);
            while (_node != null)
            {
                _dialoglist.Add(_node);
                _node = _walker.GetNextSibling(_node);
            }
            return _dialoglist;
        }

        internal static void DebugWrite(string _text)
        {
            StreamWriter sw = File.AppendText(Worker.debugFile);
            sw.WriteLine(DateTime.Now.ToString("HHmmss_fffffff: ") + _text + " Thread ID: " + Thread.CurrentThread.ManagedThreadId.ToString());
            sw.Flush();
            sw.Close();
            return;
        }

        internal static void DebugWriteEvent(string _text)
        {

            StreamWriter sw = File.AppendText(Worker.debugFileEvent);
            sw.WriteLine(DateTime.Now.ToString("HHmmss_fffffff: ") + _text + " Thread ID: " + Thread.CurrentThread.ManagedThreadId.ToString());
            sw.Flush();
            sw.Close();
            return;
        }
    }
}

EDIT: Fixed code formatting
Posted
Updated 27-Jun-11 2:46am
v3

I find it difficult to answer this question to make the answer helpful.
I'll try to put it in this way: this attempt to design multithreading code failed. Please, do your favor and don't try to improve it. You will need abandon it and start writing correct code from scratch, but to help you I would need to know what should be accomplished and why.

For now, I can only explain what's wrong and give you some references which could give your better ides.

The main problem is your attempt to synchronize thread using home-baked method based on spin wait. If you think that you can use non-threading techniques and apply them to thread, think again. You fail to use thread synchronization primitives which cannot be implemented not using OS calls, by combination of "if" "while" and the like. When you wait for system events, OS switches your thread off and keeps it in a special wait state and never schedule it back to execution until waken up, so the thread uses strictly zero CPU time. You simply keep the thread running waiting for a regular condition. This is not multithreading. The closest multithreading analog of such wait is EventWaitHandle.

One such bad thing is Application.DoEvents. By some reason, this is a common fallacy. You should never use it, as a matter of thumb rule. As to waiting for DoEvents in another thread, this is a way to wait forever, as you can keep pumping messages the way they never end.

Each and every Sleep call makes no sense. You sleep 10 ms; why? Do you hope something will change.

One more bad sign is identification of threads by ManagedThreadId. Why? There is no need to do this. Another bad sign is changing thread priority. You really never need it. If your logic is wrong, priority cannot improve thing. Think about it. By raising priority you don't make anything faster, you only make other threads yielding to you thread more often. Why do you think it improves throughput?

You code looks too complex for the task. This is one reason I cannot understand the purpose of all this. If would be possible if it was almost working code with just one or two problems. In this case, nearly everything is wrong. So, I cannot help you with your code. Again, I am sure you should start it over.

Just few ideas.

To work with UI, you never call any UI methods or properties from non-UI thread. You cannot call anything related to UI from non-UI thread. Instead, you need to use the method Invoke or BeginInvoke of System.Windows.Threading.Dispatcher (for both Forms or WPF) or System.Windows.Forms.Control (Forms only).

You will find detailed explanation of how it works and code samples in my past answers:
Control.Invoke() vs. Control.BeginInvoke()[^],
Problem with Treeview Scanner And MD5[^].

See also more references on threading:
How to get a keydown event to operate on a different thread in vb.net[^],
Control events not firing after enable disable + multithreading[^].

—SA
 
Share this answer
 
v2
Thanks for your answer!
The "environment" of the code was not too detailed as you mentioned so I would give some further information about it. I agree your point of view that this code is not too good, I am not an expert of C#, just an enthusiastic amateur... :-)

So this simplyfied code should do the following:
- simple form comes up with a single button
- pushing the button causes Worker.Start() method invoking
- the mainthread starts and runs till 20 seconds, structure changes event handling added for newly created dialogs in the root of automation tree.
- so when a new program started (eg: a browser) the event handling should run meanwhile mainthread should not do anything (that is why I used this simple logging to check what is happening where...). But if it is possible without thread usage should be good as well.
- sleep is used beacuse in my app there is UI manipulation and then sleeps are required because of some results in the UI. I used Application.DoEvents because of sleeps, if a thread is sleeped too long then an incoming event goes to trash can not be handled (at least i have not managed to)
- so if this code (or some other one) should do that the mainpart "freezes" till eventhandling finishes...

And a little bit from my application (.net 3.5 sp1):
- WinForm application - form is used because of user input handling for the rest of the code
- Mainthread should do something (in this simplification logging in every 10ms). In the app it is doing some other programs handling via UIAutomation (open dialogs of a program, handle its controls, eg... so basically emulating the program usage via C# code). Let's say mainthread handles "A" program. For sake of correctness UIVerifier tool (UIAutomation helper) can not see each control of "A" program so mouse positioning and usage is needed for handling controls (some image recognision is added to have controls position...)
- Event handling part should do the handling of the new dialogs in UIAutomation root (eg: handling of newly started program). Let's say this part handles "B" program. Again some problems with not recognised controls...
- Mainthread should be suspended because "A" program and "B" program can not be used parallel. Parallel usage can cause misclicks and some other problems.

Meanwhile I am checking the resources you linked.
Maybe EventWaitHandle will do the work I would need...
Thanks in advanced,
B.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900