2013-04-16 47 views
-2

对于C#来说,我很新,但我一直在玩它来学习。到目前为止,我有一个很好的应用程序,可以启动屏幕保护程序并控制Windows系统的音量。跨线程操作无效:从其创建的线程以外的线程访问控制'Form1'

但是我有一些麻烦,我不知道什么是错的。我已经在网站上看到了一些类似的问题,但我认为它们中的任何一个都不适用于我的具体情况。

所以我想从网络上控制应用程序。我的计划是让应用程序每隔几秒钟在我的网站上检查一个网页。根据网站返回的内容,应用会执行不同的操作(静音,启动等),并将网站上的命令重置为空白。网站命令部分都是用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); 
    } 

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

Cross-thread operation not valid: Control 'Form1' accessed from a thread other than the thread it was created on.

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

+3

http://stackoverflow.com/q/142003/413032你怎么会这么上搜索? –

+1

请不要直接使用'Thread's。对于长时间运行的I/O绑定操作,请使用async/await。对于长时间运行的CPU绑定操作,请使用“任务”。 –

+0

感谢教我如何谷歌....你认为我发现这个网站?我经历了大约7个其他类似的问题,但都没有成功。所以我问我自己的问题就像它告诉我要做的一样。 – mitchelltrout

回答

2

在这种情况下,在方法内部写入invoke操作。然后,您可以从控件的线程和其他线程访问这两个线程。或者,如果你想避免委托地狱,你可以使用内置委托和lambda编写更少的代码。

private IntPtr GetWindowHandle() { 

    if (this.InvokeRequired == true) { 

     return (IntPtr)this.Invoke((Func<IntPtr>)(() => { 

      return GetWindowHandle(); 

     })); 
    } 

    return this.Handle; 
} 
+0

非常感谢你!这正是我需要的。 – mitchelltrout

0

试试这个代码,它一定会帮助你

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(); 
相关问题