How do I get the GUI thread of winform?
I have a winforms app with multiple GUI threads. I want them to be able to access each other's thread objects without having to keep track of that information separately.
Is there a function in .NET that I can feed a winforms control or window object, and get back the thread? Or a function in the API I can pinvoke for the threadID?
(please no comments saying I should do it another way... also this is not about cross-thread window operations.)
Thanks!
Edit
For those of you who for some reason believed my italicized text, congratualations, you're hired!! Here is the problem:
"App is crashing in the wild by locking up totally, that is, it stop responding. Very intermittent, and trying to debug it, it seems to never happen."
So what do do? Install an option in the program that the user can activate under our direction, whereby from another GUI thread in the same app, do a thread.abort on the main GUI thread, then we can look at the call stack in the error log. Viola, found an impossible to debug error in less than a day. (Stop now, it had nothing to do with abusing multithreading:-)
I'll admit I almost didn't ask this, the reason I did was I could see an object reference to the main form, but there wasn't any for its thread. I'm giving Chris Shain the answer a/c it is a quick way, unfortunately when the thread is hanging, I wouldn't be able to do an invoke (it would hang too). A little more digging revealed the GetWindowThreadProcessId API call. But it's an unmanaged thread ID, apparently there are complications turning that into a managed thread ID.
So I bit the bullet and put in a global reference to the main UI thread. Would have posted it to begin with, but hadn't written it yet.
Now if you'll pardon the VB...
In main public module/static class:
Public GUIThread As Threading.Thread
Sub Main()
'' // Create app main window
ShellForm = New frmShell
'' // Save main GUI Thread for abort routine
GUIT开发者_Python百科hread = Threading.Thread.CurrentThread
If GetSetting("MyApp", "Testing", "CrashDebug", "False") = "True" Then
'' // DO NOT run the pgm. like this normally - with try/catch around
'' // Application.Run - or uncaught errors will kill the whole app!!!
Try
'' // This is the other of the ‘Multiple GUI threads’ I talked
'' // about in the Orig Post.
Dim t As New Threading.Thread(AddressOf StartCrashDebug)
t.Start()
Application.Run(ShellForm)
Catch ex As Exception
'' // This error routine passes errors off to another thread which
'' // logs them (and also shows messages)
MyLogError(ex, "CrashDebug - Main Window blew up")
End Try
Else
'' // Normal mode - uncaught errors will get caught by UnhandledException,
'' // logged, and Winforms will keep the GUI alive (since we _do_ care
'' // more about users than computers right ;-)
Application.Run(ShellForm)
End If
End Sub
Sub StartCrashDebug()
Dim f As New frmCrashFinder
'' // Starting a window like this on a separate thread makes it ‘Another
'' // GUI thread’ for winforms, by design
Application.Run(f)
End Sub
In ‘aborter’ WinForm:
Public Class frmCrashFinder
Inherits Windows.Form
Private Sub Abort_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Abort.Click
GUIThread.Abort()
End Sub
End Class
All GUI elements in Windows Forms are typically done on a single thread. I strongly recommend avoiding trying to do this any other way.
You can always marshal code to that thread by using Control.Invoke or Control.BeginInvoke with any Control.
If you really want to get the thread's ID (not sure what use this will be..?), you could use:
int GetControlThreadId(Control control)
{
int threadId;
control.Invoke( new Action( () =>
{
threadId = Thread.CurrentThread.ManagedThreadId;
}));
return threadId;
}
If your code is not in a form or control, you can use
if (System.Windows.Forms.Form.ActiveForm.InvokeRequired)
{
System.Windows.Forms.Form.ActiveForm.Invoke(...);
}
This should do it, however I agree with other posters that this is probably the wrong thing to do for other reasons...
var thatWindowsThread = (Thread)(WhateverWindow.Invoke(()=>Thread.CurrentThread);
WindowsFormsSynchronizationContext.Current has Post and Send methods from which you can delegate command to UI thread
If you don't have access to any forms or windows or controls you can pull the thread or SynchronizationContext
from, you can use
System.Windows.Forms.Application.OpenForms
This worked for me. System.Windows.Forms.Form.ActiveForm
was null in my case, but Metro's answer made me look closer at static classes in System.Windows.Forms
.
Use
System.Windows.Forms.Application.OpenForms.Invoke(...) or BeginInvoke.
You can get the rest from other answers.
Better answer:
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsGUIThread([MarshalAs(UnmanagedType.Bool)] bool bConvert);
http://pinvoke.net/default.aspx/Constants.IsGUIThread
精彩评论