8

私はMacOSX 10.8の問題を調査しており、私は機知に富んでいます。次に何をすべきかわかりません。

アプリケーションは32ビットで、いくつかのCarbon呼び出しが含まれています。

問題は次のとおりです。ドックのアプリケーションアイコンを右クリックしてメニュー項目[非表示]を選択し、アプリケーションが非表示になったら、ドックから[表示]メニュー項目を選択すると問題が発生します。メインドキュメントウィンドウは表示されません(パレットとメニューは表示されます)。

この時点で、パレットが表示されていても、[表示]メニュー項目は[非表示]に変更されません。

アプリケーションドックメニューから「表示」を選択すると、メインドキュメントウィンドウが表示されると思います。他のMacアプリケーションと同じように。

失敗した場合、トラックパッドでAppExposéジェスチャを使用してドキュメントウィンドウを表示し、メインドキュメントウィンドウを選択すると、メインドキュメントウィンドウを再び表示できます。

ターミナルまたはXcodeからアプリケーションを起動すると正常に動作します。ドキュメントウィンドウが表示され、アプリケーションのドックメニュー項目が期待どおりに「非表示」に変わります。* .appの親ディレクトリに移動し、。と入力して、ターミナルからアプリを起動します./MyApp.app/Contents/MacOS/MyApp

Finderでアプリケーションアイコンをダブルクリックして起動すると失敗します。

アプリケーションデリゲートの再表示機能からのログメッセージは、アプリケーションがターミナルとXcodeから起動されたときに表示されますが、Finderから起動されたときには表示されません。

– applicationWillUnhide:
– applicationDidUnhide:

Console.appで、スローされた例外(またはその他のメッセージ)を確認しました。ありません。


更新

これを試してデバッグするために、Finderから起動し、Xcodeを使用してプロセスに接続しました。

例外がスローされているのではないかと思っていたので、Xcodeの「ExceptionBreakpoint」を使用してテストし、objc_exception_throwにブレークポイントを設定すると(念のため)、アプリケーションを非表示または「表示」しても壊れません。

NSApplicationWillUnhideNotificationそれから、とNSApplicationDidUnhideNotificationが発送されていることを証明する必要があると思いました。Xcodeまたはターミナルから起動したときですが、Finderから起動した場合はそうではありません。

Xcodeをアプリケーションにアタッチした後、「シンボリックブレークポイントの追加」を介してブレークポイントを設定することでこれを確認しました。

-[NSNotificationCenter postNotificationName:object:userInfo] 

次に、デバッガーコマンド「po *(id *)($ esp + 12)」を追加して、最初のパラメーターをそのセレクター(通知名)に出力しました。

StackOverflowのここに投稿された回答で見つけました。

それを使って、「表示」メニュー項目を選択した後に投稿された通知を見ることができます。Xcode / Terminalから起動すると、次の通知が投稿されます。

NSApplicationWillUpdateNotification, NSWindowDidUpdateNotification, NSApplicationDidUpdateNotification, ** NSApplicationWillUnhideNotification **, ..., ** NSApplicationDidUnhideNotification **, ..., NSApplicationWillBecomeActiveNotification, ...

NSApplicationWillUnhideNotificationこの状況で投稿されます。

Finderから起動すると、次の通知が投稿されているのがわかります。

NSApplicationWillUpdateNotification, NSWindowDidUpdateNotification, NSApplicationDidUpdateNotification, NSApplicationWillBecomeActiveNotification, ...

を送信しませんNSApplicationWillUnhideNotification。また、Xcodeで起動したバージョンから[表示]を選択すると-[NSApplication _doUnhideWithoutActivation]、バックトレースに表示されます。Finderで起動したバージョンに接続するときにその関数にブレークポイントを設定しても、[表示]を選択してもブレークは発生しません。

それから、私は自分自身に思いました、おそらくアプリケーションはそれが隠されていないと考えています。

アイドル状態のイベントハンドラーがあるので、そこから[[NSApplication sharedApplicaton] isHidden]、アプリケーションを非表示にして「表示」している間の値を出力しました。

問題のある状況では、アプリケーションが非表示になっていない場合は、に対して印刷さNOisHiddenます。アプリケーションが非表示になると、のために印刷さYESisHiddenます。ドックメニューから「表示」を選択すると、印刷が続行されNOますisHidden。非表示になっていることはわかっていますが、アプリケーションの一部がアクティブ化されています。NSPanelsとがNSMenuBar表示されます。

アプリケーションのExposéモードに入るとドキュメントウィンドウが表示されます。ドキュメントウィンドウをクリックするとウィンドウが表示されますが、ドックメニュー項目は「表示」のisHiddenままYESです。

再表示メカニズムはサンプルアプリケーションで正常に機能するため、コードがこれを遮断するために何かを実行していると確信しています。


ターミナルから起動したアプリケーションとファインダーから起動したアプリケーションの違いは何でしょうか。

アプリケーションに環境変数を使用してログを記録させましたが[[NSProcessInfo processInfo] environment]、実際に確認できる唯一の違いは、ターミナルアプリケーションの変数にPWDが存在することです。これを使用するコードには何も表示されません。

アプリケーションにを介してコマンドライン引数をログに記録[[NSProcessInfo processInfo] arguments]させましたが、Finderで起動したバージョンでは何か違うことがわかります。ターミナルとFinderで起動されたバージョンはどちらも、最初の引数としてバイナリのパスをリストします。Finderには、2番目のパラメータ「-psn_0_89445704」も表示されます。Mac OS XがGUIアプリケーションのコマンドライン引数に追加するものであり、Dockメニューから適切に非表示および表示する他のアプリケーションのコマンドライン引数に追加されることをオンラインで読みました。

この謎を解くために私をさらに導くかもしれない他の考えはありますか?ヘルプや提案をありがとう!

4

2 に答える 2

3

AppKitでAppleエンジニアと協力した後、解決策が見つかりました。

このアプリケーションでは、さまざまな理由で次の方法でイベントキューを「フラッシュ」します。

NSEvent* lastEvent = [NSEvent otherEventWithType:NSPeriodic
                                            location:NSMakePoint(0.0, 0.0)
                                            modifierFlags:0
                                           timestamp:[NSDate timeIntervalSinceReferenceDate]
                                        windowNumber:1
                                             context:NULL
                                             subtype:0
                                               data1:0
                                               data2:0];

    [[NSApplication sharedApplication] discardEventsMatchingMask:NSAnyEventMask beforeEvent:lastEvent];

Mac OS Xシステムは、起動時に「Show」イベントをアプリケーションに送信します。起動時に呼び出されるフラッシュ関数は、そのイベントをキューから効果的に削除しますが、Mac OS Xのコアプロセス部分には、表示と非表示、およびその他のタイプのイベントタイプを追跡する独自の内部キューがあります。 t繰り返しメッセージを送信します。(このフラッシュが本当に必要かどうかを調査します)

問題は、がすべてdiscardEventsMatchingMask:NSAnyEventMaskのイベントで呼び出されると、アプリケーションのイベントをクリアしますが、コアプロセスのshowイベントに応答しないため、コアプロセスはshowイベントを再度送信する必要がないと判断することです。

この特定の問題の解決策は、イベントがクリアされる場所をより選択的にすることです。私の新しい実装では、コアプロセスによって送信されるイベントをクリアしません。

/* a bug in Apple's Core Process group forces me to isolate which events should be cleared as
        show|hide|activate|deactivate messages get sent by Core Process, but are not _marked_ as
     handled and so Core Process thinks that the "Show" event is still pending and will not send
     another */

    NSEvent* lastEvent = [NSEvent otherEventWithType:NSPeriodic
                                            location:NSMakePoint(0.0, 0.0)
                                            modifierFlags:0
                                           timestamp:[NSDate timeIntervalSinceReferenceDate]
                                        windowNumber:1
                                             context:NULL
                                             subtype:0
                                               data1:0
                                               data2:0];


    NSEventMask maskForEventsToDiscard = (NSPeriodic |
                                          NSLeftMouseDown |
                                          NSLeftMouseUp |
                                          NSMouseMoved |
                                          NSLeftMouseDragged |
                                          NSRightMouseDragged |
                                          NSMouseEntered |
                                          NSMouseExited |
                                          NSKeyDown |
                                          NSOtherMouseDown |
                                          NSOtherMouseUp |
                                          NSOtherMouseDragged);

    [[NSApplication sharedApplication] discardEventsMatchingMask:maskForEventsToDiscard
                                                     beforeEvent:lastEvent];

「show」イベントは起動時にクリアされないため、今すぐ作業を表示および非表示にしてください。

AppleのKFに感謝します!

于 2013-06-14T16:33:37.843 に答える
0

この手法はコメントには当てはまりませんが、DTraceを身近に感じることをお勧めします。上記のコメントで、NSWindowをサブクラス化し、NSLogステートメントを-orderOut:などのメソッドに配置することを提案しました。ただし、これにDTraceを使用すると、はるかに効果的である可能性があります。ただし、後でわかるように、監視するオブジェクトのアドレスを知ることは依然として有用です。利点は、コードにコードを散らかさないことです。 NSLogステートメントの束。

最も単純なスクリプトは次のとおりです。

#pragma D option quiet

objc$target:NSWindow:-orderOut?:entry
{
    printf( "%30s %10s %x %x\n", probemod, probefunc, arg0, arg1 );
}

そして、次のようなことを行うことにより、アプリケーションのプロセスIDで呼び出されます。

sudo dtrace -s dtrace_window.d -p9434

この特定のケースでは、arg0にウィンドウのアドレスが含まれます。残念ながら、DTrace内からウィンドウのタイトルやNSStringのコンテンツを取得することは明らかに簡単ではありませんが、努力する価値があるかもしれません。私はここここで、誰かがこれらのことのいずれかを行うことを知っているかどうかを確認するための質問があります。(ウィンドウのタイトルを取得できる場合は、ウィンドウのアドレスから文字列へのマップを設定できます。)

関与していると思われるすべてのメソッド、関数などにプローブを簡単に接続できるため、この問題を解決するために「イベントの追跡」を試みることができます。

したがって、最終的には、必要なヒントがこの問題を解決するまで、DTraceプローブを追加し続けることをお勧めします。

于 2013-03-22T02:31:36.277 に答える