2009-02-03 7 views
5

Ich versuche, eine plattformübergreifende C# .NET-Anwendung mit C#, Mono/GTK # auf Linux und .NET/GTK # auf Windows zu machen, aber die Startreihenfolge unter den beiden Plattformen etwas anders sein zu müssen scheint:Crossplatform Threading und GTK #, funktioniert nicht (richtig)?

unter Linux:

public static void Main (string[] args) 
{ 
    Gdk.Threads.Init(); 
    // etc... 

unter Windows:

public static void Main (string[] args) 
{ 
    Glib.Thread.Init(); 
    Gdk.Threads.Init(); 
    // etc... 

Beide erfordern es, dass die Art und Weise getan werden: Windows über g_thread_init beschwert() nicht mit dem Linux-Code genannt wird, und Linux klagt darüber bereits sein namens mit dem Windows-Code. Ansonsten funktioniert alles wunderbar.

Mein erster Versuch einer „Lösung“ sah aus wie:

public static void Main (string[] args) 
{ 
    try { 
     Gdk.Threads.Init(); 
    } catch (Exception) { 
     GLib.Thread.Init(); 
     Gdk.Threads.Init(); 
    } 
    // etc... 

Aber auch diese unordentlich Lösung funktioniert nicht; Der Fehler kommt von GTK +, nicht verwaltetem Code, daher kann er nicht abgefangen werden. Hat jemand gute Ideen über die Ursache des Problems und wie kann man es beheben? Oder, wenn das nicht klappt, haben Sie gute Ideen, wie Sie erkennen können, welche zur Laufzeit aufgerufen werden sollten?

Antwort

8

Gtk + auf Win32 unterstützt Threading nicht ordnungsgemäß. Sie müssen alle Ihre GUI-Aufrufe von demselben Thread ausführen wie Sie Gtk.Main() aufgerufen haben.

Es ist nicht wirklich so schlimm wie es klingt. Es gibt einen Trick, mit dem Sie Funktionen an den Hauptthread senden können. Verwenden Sie einfach GLib.Idle.Add() aus einem beliebigen Thread, und die Funktion wird in Kürze in demselben Thread aufgerufen, in dem die Hauptschleife ausgeführt wird. Denken Sie daran, in der Leerlaufbehandlungsfunktion false zurückzugeben, oder sie wird für immer ausgeführt.

Wenn Sie folgen und die oben genannten Techniken verwenden, müssen Sie überhaupt nicht Gdk.Threads.Init() aufrufen.

+0

Johan hat Recht - Sie müssen GDK-Funktionen nicht wirklich aus mehreren Threads aufrufen, und es ist eine schlechte Idee, dies trotzdem zu tun (Winforms und WPF lassen Sie das gar nicht erst tun) –

+0

Vielen Dank für die extra Meile und Suche nach den GTK # ruft :) –

+0

Woho! Danke für die 300 Punkte! –

0

Umm, haben Sie versucht, Ihre Hauptmethode mit dem Attribut [STAThread] zu dekorieren?

z.B.

#if !Mono //or whatever 
[STAThread] 
#endif 
public static void Main (string[] args) 
{ 
    Gdk.Threads.Init(); 
    ... 
} 

Failing, dass Sie wie so bedingte Kompilierung verwenden könnte ...

public static void Main (string[] args) 
{ 
    Gdk.Threads.Init(); 
#if !Mono //or whatever 
    Gdk.Threads.Init(); 
#endif 
    ... 
} 
+0

Ich habe den Eindruck, dass STAThread nur COM beeinflussen, so dass dies in diesem Fall nichts bedeutet. –

2

Ich bin mir nicht sicher, ob dies auf Mono funktioniert, aber Sie können die Umgebungsklasse verwenden, um festzustellen, welche das Programm OS läuft weiter.

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... 

Die PlatformID Enum enthält mehr als nur Windows und Unix jedoch so sollten Sie für andere Werte überprüfen.

1

Eigentlich könnte dies ein Fehler entweder in den C# -Bindungen für GDK oder Ihre Version von GDK sein. Gemäß der Dokumentation für gdk\_threads\_init(), g\_thread\_init() muss zuerst, und die GTK # Dokumentation sagt das gleiche aufgerufen werden:

GLib.Thread.Init() has to be called before Gdk.Threads.Init() .

Auf meinem Linux-Rechner (mit GDK 2.14.4), einem C-Programm, das gdk\_threads\_init() Anrufe ohne g\_thread\_init() genannt hat druckt eine Fehlermeldung und endet mit einem Fehler. Haben Sie sichergestellt, dass Sie sowohl unter Linux als auch unter Windows dieselbe GDK-Version haben und dass die Version unter Linux (wenn sie von der Windows-Version abweicht) auch den Aufruf g\_thread\_init() erfordert oder wenn etwas zwischen beiden geändert wurde Versionen. Schließlich überprüft, dass dieses Programm mit einem Fehler beendet:

#include <gdk/gdk.h> 

int main(int argc, char **argv) { 
    gdk_threads_init(); 

    return 0; 
}

es Kompilieren mit gcc -o test `pkg-config --cflags --libs gdk-2.0` test.c
(vorausgesetzt, Sie es als test.c. gespeichert haben)

Wenn das Programm mit einem Fehler endet, dann ist es ein Fehler in Ihrer GDK # -Bibliothek.
Wenn nicht, ist es ein Fehler in Ihrer GDK-Version.

+0

Es ist nicht so, dass es nicht aufgerufen wird, es wird aufgerufen, es wird nur als Teil des Wrappers in Linux aufgerufen und nicht als Teil des Wrappers in Windows aufgerufen. Ich teste mit den gleichen Versionen von GTK # auf beiden, aber ich bin mir nicht sicher über die GTK + Versionen. –

+0

Bestätigt von Ihrem kleinen Test, ist der Unterschied definitiv in GTK #, aber ich verwende die gleiche Version zwischen Linux und Windows. –

+0

Ich habe versucht, die GTK # Quellen zu suchen, aber ich kann keine offensichtlichen Orte finden, an denen Gdk.Threads.Init() g \ _thread \ _init() aufruft, also denke ich, dass Ihr nächster Halt die GTK # -Entwickler sein sollten Mailingliste oder Forum, aber das sieht irgendwie komisch aus. – arnsholt