Warum würde meine Anwendung nicht "einblenden" (anzeigen)

8

Ich untersuche ein Problem auf Mac OS X 10.8, und ich bin am Ende meiner Weisheit. Ich bin mir nicht sicher, was ich als nächstes tun soll.

Die Anwendung ist 32 Bit und enthält einige Carbon-Aufrufe.

Hier ist das Problem: Wenn ich mit der rechten Maustaste auf das Anwendungssymbol im Dock klicke, wähle den Menüpunkt "Verstecken", dann, nachdem die Anwendung ausgeblendet wurde, wähle ich den Menüpunkt "Zeigen" aus dem Dock, und die Problem tritt auf: das Hauptdokumentfenster erscheint nicht (die Paletten und das Menü erscheinen).

An dieser Stelle ändert sich der Menüpunkt "Zeigen" nicht zu "Ausblenden", obwohl die Paletten sichtbar geworden sind.

Ich erwarte, dass das Hauptdokumentfenster sichtbar wird, wenn ich "Zeige" aus dem Anwendungsdockmenü wähle. Genau wie andere Mac-Anwendungen.

Wenn es fehlschlägt, kann ich das Hauptdokumentfenster wieder sichtbar machen, wenn ich die App Exposé-Geste auf dem Trackpad verwende, um die Dokumentfenster anzuzeigen und das Hauptdokumentfenster auszuwählen.

Es funktioniert einwandfrei, wenn ich die Anwendung vom Terminal oder von Xcode aus starte. Das Dokumentfenster wird angezeigt und das Dockmenüelement für meine Anwendung ändert sich wie erwartet in "Ausblenden". Ich starte die App vom Terminal, indem ich zum übergeordneten Verzeichnis der * .app navigiere und ./MyApp.app/Contents/MacOS/MyApp eintippe.

Es schlägt fehl, wenn ich durch Doppelklicken auf das Anwendungssymbol im Finder gestartet habe.

Meine Protokollnachrichten von den Funktionen zum Einblenden des Anwendungsdelegaten werden angezeigt, wenn die Anwendung über das Terminal und Xcode gestartet wird, nicht jedoch über den Finder.

%Vor%

Ich habe in der Console.app nach allen ausgelösten Ausnahmen (oder anderen Nachrichten) gesucht. Da sind keine.

Aktualisieren :

Um zu versuchen, dies zu debuggen, startete ich vom Finder und verwendete Xcode, um an den Prozess anzuhängen.

Ich hatte vermutet, dass eine Ausnahme ausgelöst wurde, und als ich es mit dem "Exception Breakpoint" von Xcode getestet und einen Haltepunkt auf objc_exception_throw gesetzt habe (nur für den Fall), brach es nicht, wenn ich die Anwendung verstecke oder "zeige" .

Ich dachte dann, dass ich beweisen müsste, dass die NSApplicationWillUnhideNotification und NSApplicationDidUnhideNotification gesendet wurden. Sie sind, wenn ich von Xcode oder vom Terminal starte, aber wenn ich vom Finder starte, sind sie nicht.

Ich habe das verifiziert, indem ich, nachdem ich Xcode an die Anwendung angehängt habe, einen Haltepunkt über "Add Symbolic Breakpoint" für:

gesetzt habe %Vor%

Und dann fügte ich einen Debugger-Befehl hinzu: "po * (id *) ($ esp + 12)", um den ersten Parameter für diesen Selektor (den Benachrichtigungsnamen) auszugeben.

Was ich in einer Antwort hier in StackOverflow gefunden habe .

Damit kann ich die Benachrichtigungen sehen, die gepostet werden, nachdem ich den Menüpunkt "Zeigen" gewählt habe. Wenn ich von Xcode / Terminal starte, sehe ich folgende Benachrichtigungen:

%Vor%

NSApplicationWillUnhideNotification wird in dieser Situation gepostet.

Wenn ich vom Finder aus starte, sehe ich folgende Benachrichtigungen:

%Vor%

Es wird nicht NSApplicationWillUnhideNotification gesendet. Wenn ich "Show" von der Xcode-gestarteten Version wähle, sehe ich -[NSApplication _doUnhideWithoutActivation] im Backtrace. Das Setzen eines Haltepunkts für diese Funktion beim Anhängen an die vom Finder gestartete Version führt nicht zu einer Unterbrechung, wenn ich "Zeigen" auswähle.

Dann dachte ich mir, vielleicht denkt die Anwendung, dass es nicht versteckt ist.

Ich habe einen leeren Event-Handler, also habe ich den Wert von [[NSApplication sharedApplicaton] isHidden] ausgedruckt, während ich die Anwendung ausgeblendet habe und "zeige".

Für die Problemsituation, wenn die Anwendung nicht ausgeblendet ist, wird NO für isHidden ausgegeben. Wenn die Anwendung ausgeblendet wird, wird YES für isHidden ausgegeben. Wenn ich "Zeigen" aus dem Dock-Menü wähle, wird weiterhin NO für isHidden ausgedruckt. Es weiß, dass es ausgeblendet ist, aber ein Teil der Anwendung wurde aktiviert: die NSPanels und die NSMenuBar erscheinen.

Ich kann das Dokumentfenster sehen, indem ich den Exposé-Modus der Anwendung betrete, und wenn ich auf das Dokumentfenster klicke, erscheint das Fenster, aber der Menüpunkt "Dock" ist immer noch "Show" und isHidden ist immer noch YES .

Der Einblenden-Mechanismus funktioniert gut für eine Beispielanwendung, also bin ich mir ziemlich sicher, dass unser Code etwas unternimmt, um dies zu deaktivieren.

Ich frage mich, was würde sich zwischen einer Anwendung unterscheiden, die vom Terminal gestartet wird, und einer Anwendung, die vom Finder gestartet wird?

Ich hatte die Anwendungsprotokollierung der Umgebungsvariablen mit [[NSProcessInfo processInfo] environment] und der einzige wirkliche Unterschied, den ich sehen konnte, ist, dass PWD in den Variablen für die Terminal-Anwendung existiert: Ich kann nichts in unserem Code sehen macht davon Gebrauch.

Ich habe die Anwendung die Befehlszeilenargumente über [[NSProcessInfo processInfo] arguments] protokollieren lassen, und ich sehe etwas anderes in der vom Finder gestarteten Version. Sowohl die von Terminal als auch Finder gestartete Version listet den Pfad der Binärdatei als erstes Argument auf. Der Finder listet auch einen zweiten Parameter auf, "-psn_0_89445704".Ich habe online gelesen, dass Mac OS X den Befehlszeilenargumenten für GUI-Anwendungen etwas hinzufügt, und ich sehe, dass es den Befehlszeilenargumenten für andere Anwendungen hinzugefügt wird, die im Menü "Dock" ordnungsgemäß ausgeblendet und angezeigt werden.

Hast du irgendwelche anderen Gedanken, die mich weiter zur Lösung dieses Geheimnisses führen? Danke für jede Hilfe oder Vorschläge!

    
Lyndsey Ferguson 23.05.2017, 11:57
quelle

2 Antworten

2

Nach der Arbeit mit Apple Engineers in AppKit wurde eine Lösung gefunden.

In unserer Anwendung "spülen" wir die Ereigniswarteschlange aus verschiedenen Gründen über diese Methode:

%Vor%

Die Mac OS X-Systeme senden beim Start ein "Show" -Ereignis an die Anwendung. Unsere Funktion flush , die beim Start aufgerufen wird, entfernt dieses Ereignis effektiv aus der Warteschlange, aber der Hauptprozessteil von Mac OS X hat seine eigene interne Warteschlange, die das Ein- und Ausblenden und andere Arten von Ereignissen verfolgt Ereignistypen, sodass keine wiederholten Nachrichten gesendet werden. (Ich werde untersuchen, ob diese Flush wirklich notwendig ist)

Das Problem ist, dass, wenn discardEventsMatchingMask:NSAnyEventMask auf jedes Ereignis aufgerufen wird, es die Ereignisse für die Anwendung löscht, aber nicht auf die show des Kernprozesses reagiert Ereignis und so Kernprozess denkt, dass es das Show-Ereignis nicht erneut senden muss.

Die Lösung für dieses spezielle Problem besteht darin, selektiver zu sein, wenn Ereignisse gelöscht werden. Mit meiner neuen Implementierung lösche ich keine Ereignisse, die vom Kernprozess gesendet werden.

%Vor%

Da das Ereignis "show" beim Start nicht gelöscht wird, können Sie jetzt die Arbeit ein- und ausblenden!

Ein besonderer Dank an KF von Apple!

    
Lyndsey Ferguson 26.06.2013, 18:17
quelle
0

Diese Technik wird nicht in einen Kommentar passen, aber ich würde vorschlagen, DTrace mit dem Thema vertraut zu machen. Ich habe in den obigen Kommentaren vorgeschlagen, NSWindow abzulagern und NSLog-Anweisungen in die Methoden -orderOut: usw. zu schreiben. Die Verwendung von DTrace wäre jedoch wahrscheinlich weitaus effektiver - obwohl Sie, wie Sie sehen werden, die Adresse der Objekte, die Sie beobachten werden, immer noch nützlich finden werden - das Positive ist, dass Sie Ihren Code nicht mit einem Bündel von NSLog-Anweisungen.

Das einfachste Skript könnte sein:

%Vor%

und würde mit der Prozess-ID der Anwendung aufgerufen werden, indem Sie Folgendes tun:

%Vor%

In diesem speziellen Fall enthält arg0 die Adresse des Fensters. Leider ist es scheinbar nicht trivial, den Titel des Fensters oder sogar den Inhalt eines NSString innerhalb von DTrace zu erhalten, aber die Mühe lohnt sich. Ich habe eine Frage hier und hier , um zu sehen, ob jemand eines dieser Dinge zu tun weiß. (Wenn Sie den Titel eines Fensters erhalten, können Sie eine Karte von der Adresse des Fensters zu einer Zeichenkette erstellen.)

Es wäre leicht, Sonden an alle Methoden, Funktionen usw. anzuhängen, von denen Sie denken, dass sie beteiligt sein könnten, so dass Sie versuchen können, "dem Ereignis zu folgen", um dieses Problem zu lösen.

Letztendlich würde ich vorschlagen, einfach DTrace-Sonden hinzuzufügen, bis etwas den erforderlichen Hinweis gibt, um dieses Problem zu lösen.

    
ericgorr 23.05.2017 11:57
quelle

Tags und Links