跨线程操作无效:Control '从创建它的线程以外的线程访问

本文关键字:线程 创建 访问 无效 操作 Control | 更新日期: 2023-09-27 18:09:24

我对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");
    }

VolumeCtrl.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);
    }

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

跨线程操作无效:控制从a访问的Form1非创建该线程的线程。

那么我如何绕过这个呢?我在这里读过其他的答案,说你需要使用调用或委托,但我不确定这些是如何工作的,或者在哪里使用它们。我试着去理解其中一个答案,但是我得到了更多的错误。

跨线程操作无效:Control '从创建它的线程以外的线程访问

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

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;
}

试试这段代码,它一定会帮到你的

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();