【问题标题】:Cross-thread operation not valid: Control 'Form1' accessed from a thread other than the thread it was created on跨线程操作无效:控件“Form1”从创建它的线程以外的线程访问
【发布时间】:2013-04-16 15:34:02
【问题描述】:

我对 C# 很陌生,但我一直在玩它来学习。到目前为止,我有一个不错的应用程序,它可以执行诸如启动屏幕保护程序和控制 Windows 系统音量之类的操作。

但是我遇到了一些麻烦,我不确定出了什么问题。我已经在网站上解决了一堆类似的问题,但我认为其中任何一个都不适用于我的具体案例。

所以我想从网络控制应用程序。我的计划是让应用程序每隔几秒钟检查一次我网站上的网页是否有命令。根据网站返回的内容,应用程序将执行不同的操作(静音、音量增大等)并将网站上的命令重置为空白。网站命令部分全部用PHP完成,非常简单,这部分没有问题。

我创建了一个按钮,该按钮调用一个检查页面并执行操作的函数。它工作得很好。但是当我试图让它自动检查网站时,我遇到了错误。我很确定这是因为我将检查和执行操作移到了一个新线程。所以让我们继续看代码。这些不是完整的文件,但我认为您需要知道以提供帮助。如果您需要更多信息,请告诉我。

Form1.cs

public Form1(Boolean init = true)
    {
        if (init)
        {
            InitializeComponent(); //initialize UI
            startWebMonitor(); //starts web monitor thread for remote web commands
        }
    }

private void startWebMonitor()
    {
        Thread t = new Thread(WebMonitor.doWork);
        t.Start();
    }

public IntPtr getWindowHandle()
    {
        return this.Handle;
    }

WebMonitor.cs

public static void doWork()
    {
        while(true)
        {
            checkForUpdate();
            Thread.Sleep(1000);
        }
    }

private static void checkForUpdate()
    {
        lastCommand = getLastCommand();

        if (lastCommand.Equals(""))
        {
            //No Update
        }
        else
        {
            processCommand(lastCommand);
        }
    }

public static void processCommand(String command)
    {
        if(command.Equals("mute"))
        {
            VolumeCtrl.mute(Program.form1.getWindowHandle());
        }

        HTTPGet req2 = new HTTPGet();
        req2.Request("http://mywebsite.com/commands.php?do=clearcommand");
    }

音量控制.cs

private static IntPtr getWindowHandle()
    {
        Form1 form1 = new Form1(false); //false = do not initialize UI again
        return form1.getWindowHandle();
    }

    public static void mute(IntPtr handle)
    {
        SendMessageW(getWindowHandle(), WM_APPCOMMAND, getWindowHandle(), (IntPtr)APPCOMMAND_VOLUME_MUTE);
    }

好吧,基本上静音功能需要窗口句柄才能工作。但是当我尝试从新线程获取窗口句柄时,它会抛出错误:

跨线程操作无效:从 线程不是创建它的线程。

那么我该如何解决这个问题?我在这里阅读了其他答案,说您需要使用 Invoke 或 Delegate,但我不确定它们是如何工作的或在哪里使用它们。我试图遵循其中一个答案,但我遇到了更多错误。

【问题讨论】:

  • 请不要直接使用Threads。对于长时间运行的 I/O 绑定操作,请使用 async/await。对于长时间运行的 CPU 密集型操作,请使用 Tasks。
  • 感谢您教我如何使用谷歌...。您认为我是如何找到这个网站的?我经历了大约 7 个其他类似的问题,但都没有奏效。所以我问了我自己的问题,就像它告诉我的那样。
  • 所有其他问题都是关于从另一个线程更新 UI。但我不想更新它我只想获取一点信息(窗口句柄)并返回它。它的返回部分与其他答案一起使用。
  • 您接受的答案与@DavutGürbüz 在他的第一条评论中链接到的问题中接受的答案几乎相同。

标签: c# multithreading winforms


【解决方案1】:

在这种情况下,在方法内部编写调用操作。然后您可以从控件的线程和其他线程访问。

delegate IntPtr GetWindowHandleDelegate();

private IntPtr GetWindowHandle() {

    if (this.InvokeRequired) {

        return (IntPtr)this.Invoke((GetWindowHandleDelegate)delegate() {

            return GetWindowHandle();

        });
    }

    return this.Handle;
}

或者,如果您想避免委托地狱,您可以使用内置委托和 lambda 编写更多代码。

private IntPtr GetWindowHandle() {
    if (this.InvokeRequired)
        return (IntPtr)this.Invoke((Func<IntPtr>)(GetWindowHandle));

    return this.Handle;
}

【讨论】:

    【解决方案2】:

    试试这段代码,它一定会对你有所帮助

    BackgroundWorker bw = new BackgroundWorker();
    bw.DoWork += (s, e) => { };
    bw.RunWorkerCompleted += (s, e) => 
     {
      //Call you windowsform method or anything which you want to do
     };
    bw.RunWorkerAsync();
    

    【讨论】:

      猜你喜欢
      • 2011-01-15
      • 2016-05-21
      • 2016-06-11
      • 2015-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多