找出从后台线程访问哪些winforms控件

For*_*ega 8 c# devexpress winforms

我们已经建立了一个巨大的winforms项目,已经进行了多年.

有时,我们的用户会得到一个看起来像这样的例外.

这个问题的解决方案似乎是:

不要从后台线程访问UI组件

.

但由于我们的项目是一个非常大的项目,有很多不同的线程,我们没有成功找到所有这些.

有没有办法检查(使用某些工具或调试选项)从后台线程调用哪些组件?

澄清:

我创建了一个带有单个的winforms项目示例Form,其中包含两个Button

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        button1.Text = "Clicked!";
    }

    private void button2_Click(object sender, EventArgs e)
    {

        Task.Run(() =>
        {
            button2.BackColor = Color.Red; //this does not throw an exception
            //button2.Text = "Clicked"; //this throws an exception when uncommented
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

button2单击按钮时,背景颜色设置为红色.这发生在后台线程中(被认为是不良行为).但是,它不会(立即)抛出异常.我想用一种方法将其视为"不良行为".最好是通过扫描我的代码,但如果只能通过调试,(一旦从后台线程访问UI组件就暂停),它也没关系.

Zor*_*ind 2

我这样做是为了寻找特定的情况,但当然,需要根据您的需求进行调整,但这样做的目的是至少给您一种可能性。

我调用了这个方法SearchForThreads,但由于它只是一个示例,您可以随意命名它。

这里的主要思想可能是将此方法调用添加到基类并在构造函数上调用它,使其更加灵活。

然后使用反射在从此基类派生的所有类上调用此方法,如果在任何类中发现这种情况,则抛出异常或其他内容。

有一个先决条件,那就是 Framework 4.5 的使用。此版本的框架添加了一个CompilerServices属性,该属性为我们提供有关方法调用者的详细信息。

这方面的文档在这里

有了它,我们可以打开源文件并深入研究它。

我所做的只是使用基本的文本搜索来搜索您在问题中指定的情况。

但它可以让您了解如何在您的解决方案上执行此操作,因为我对您的解决方案知之甚少,所以我只能使用您在帖子上放置的代码。

public static void SearchForThreads(
        [System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
        [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
        [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
        {
            var startKey = "this.Controls.Add(";
            var endKey = ")";

            List<string> components = new List<string>();

            var designerPath = sourceFilePath.Replace(".cs", ".Designer.cs");
            if (File.Exists(designerPath))
            {
                var designerText = File.ReadAllText(designerPath);
                var initSearchPos = designerText.IndexOf(startKey) + startKey.Length;

                do
                {
                    var endSearchPos = designerText.IndexOf(endKey, initSearchPos);
                    var componentName = designerText.Substring(initSearchPos, (endSearchPos - initSearchPos));
                    componentName = componentName.Replace("this.", "");
                    if (!components.Contains(componentName))
                        components.Add(componentName);

                } while ((initSearchPos = designerText.IndexOf(startKey, initSearchPos) + startKey.Length) > startKey.Length);
            }

            if (components.Any())
            {
                var classText = File.ReadAllText(sourceFilePath);
                var ThreadPos = classText.IndexOf("Task.Run");
                if (ThreadPos > -1)
                {
                    do
                    {
                        var endThreadPos = classText.IndexOf("}", ThreadPos);

                        if (endThreadPos > -1)
                        {
                            foreach (var component in components)
                            {
                                var search = classText.IndexOf(component, ThreadPos);
                                if (search > -1 && search < endThreadPos)
                                {
                                    Console.WriteLine($"Found a call to UI thread component at pos: {search}");
                                }
                            }
                        }
                    }
                    while ((ThreadPos = classText.IndexOf("Task.Run", ++ThreadPos)) < classText.Length && ThreadPos > 0);
                }
            }
        }
Run Code Online (Sandbox Code Playgroud)

我希望它能帮助你。

如果你分割文本,你可以得到行号,这样你就可以输出它,但我不想经历这个麻烦,因为我不知道什么对你有用。

string[] lines = classText.Replace("\r","").Split('\n');
Run Code Online (Sandbox Code Playgroud)