问题
I'm trying to make a crossplatform C# application using C#, mono/GTK# on Linux and .NET/GTK# on Windows, however the startup sequence seems to need to be slightly different under the two platforms:
Under Linux:
public static void Main (string[] args)
{
Gdk.Threads.Init ();
// etc...
Under Windows:
public static void Main (string[] args)
{
Glib.Thread.Init ();
Gdk.Threads.Init ();
// etc...
Both require it to be done that way: Windows complains about g_thread_init() not being called with the linux code, and linux complains about it already being called with the Windows code. Other than this, it all works wonderfully.
My first attempt at a "solution" looked like:
public static void Main (string[] args)
{
try {
Gdk.Threads.Init ();
} catch (Exception) {
GLib.Thread.Init ();
Gdk.Threads.Init ();
}
// etc...
But even this messy solution doesn't work; the error is coming from GTK+, unmanaged code, so it can't be caught. Does anyone have any bright ideas about the cause of the problem, and how to fix it? Or, failing that, have bright ideas on how to detect which one should be called at runtime?
回答1:
Gtk+ on Win32 does not properly support threading. You need to do all your GUI calls from the same thread as you did called Gtk.Main().
It's not actually as bad as it sounds. There's a trick you can use to dispatch functions to the main thread. Just use GLib.Idle.Add() from any thread and the function will be called shortly in the same thread as the main loop is running in. Just remember to return false in the idle handler function, or it will go on running forever.
If you follow and use the above techniques you don't even need to call Gdk.Threads.Init() at all.
回答2:
I'm not sure if this works on Mono, but you can use the Environment class to determine which OS the program is running on.
public static void Main (string[] args)
{
PlatformID platform = Environment.OSVersion.Platform;
if (platform == PlatformID.Win32NT ||
platform == PlatformID.Win32S ||
platform == PlatformID.Win32Windows)
Glib.Thread.Init();
else if (platform != PlatformID.Unix)
throw new NotSupportedException("The program is not supported on this platform");
Gdk.Threads.Init();
// etc...
The PlatformID enum contains more than just Windows and Unix however, so you should check for other values.
回答3:
Actually, this might be a bug either in the C# bindings for GDK or your version of GDK. According to the documentation for gdk\_threads\_init(), g\_thread\_init()
has to be called first, and the GTK# documentation says the same:
GLib.Thread.Init()
has to be called beforeGdk.Threads.Init()
.
On my Linux machine (with GDK 2.14.4), a C program that calls gdk\_threads\_init()
without having called g\_thread\_init()
prints an error message and terminates with an error. Have you made sure that you have the same GDK version on both Linux and Windows, and that the version on Linux (if it's different from the Windows version) also requires the call to g\_thread\_init()
, or if it's something that has been changed between the two versions. Finally, check that this program terminates with an error:
#include <gdk/gdk.h>
int main(int argc, char **argv) {
gdk_threads_init();
return 0;
}
Compile it with gcc -o test `pkg-config --cflags --libs gdk-2.0` test.c
(assuming you've saved it as test.c.)
If that program terminates with an error, it's a bug in your GDK# library.
If it doesn't, it's a bug in your GDK version.
回答4:
Umm, have you tried decorating your Main method with the [STAThread] attribute?
e.g.
#if !Mono //or whatever
[STAThread]
#endif
public static void Main (string[] args)
{
Gdk.Threads.Init ();
...
}
Failing that you could use conditional compilation like so ...
public static void Main (string[] args)
{
Gdk.Threads.Init ();
#if !Mono //or whatever
Gdk.Threads.Init ();
#endif
...
}
来源:https://stackoverflow.com/questions/505759/crossplatform-threading-and-gtk-not-working-properly