1

C++ アプリケーションから C# .NET 4 WPF アプリケーションを閉じようとしています。C++ アプリは、ウィンドウを列挙し、特定のプロセス ID に対応するウィンドウを見つけ、PostMessage を介してウィンドウに WM_CLOSE を送信してから、WaitForSingleObject(pid, 5000) を送信するという標準的な手法を使用します。ただし、私の WPF アプリは決して閉じません。つまり、WaitForSingleObject がタイムアウトします。

私の WPF アプリは Window::OnClosed() をオーバーライドします。

  • ウィンドウの X をクリックして WPF アプリを手動で閉じると、このメソッドが呼び出されます。
  • 同様に、Windows の [アプリケーション] タブの [タスク マネージャー] で WPF プロセスに対して [タスクの終了] を実行すると、このメソッドが呼び出されます (明らかに、そのタブでは WM_CLOSE メッセージが使用されますが、[プロセス] タブでは、タスクの終了は WM_QUIT メッセージを使用します)。 )。
  • 私の C++ アプリが WM_CLOSE を送信すると、このメソッドは呼び出されません
  • 私の C++ アプリが代わりに WM_QUIT を送信すると (したがって、送信されたメッセージを除いてすべての C++ ソース コードは変更されません)、私の WPF アプリは終了します。
  • WPF アプリで独自の WndProc() ハンドラーを作成しようとしましたが、WPF GUI にマウスを合わせるとメソッドが呼び出されますが、C++ アプリが WM_CLOSE を送信すると、このメソッドは呼び出されず、WPF アプリとほとんど同じです。 WM_CLOSE メッセージを受け取りません。
  • Process.GetProcessById(pid) と proc.CloseMainWindow() を使用できる C# アプリを作成しました。これは WM_CLOSE と同じことを行うはずです。これは機能します: OnClosed() メソッドが呼び出されます。

PostMessage(hwnd, WM_CLOSE) は、C++ アプリから WPF アプリを適切に閉じる正しい方法ですか?

4

2 に答える 2

2

最終的に答えを見つけましたが、それは非常に単純でした。問題は、「特定のプロセス ID に対応するものを見つける」ことに関連するコードでした。問題は、私が低レベルの win32 をあまり行っていないことです。そのため、重要な詳細を見逃していました。誰かを助ける場合に備えて、何が起こっていたかと解決策は次のとおりです。

C++ 関数 closeProc() は、閉じる必要がある既存のプロセスへのハンドルを開き、win32 関数 EnumWindows によって検出された各ウィンドウに対してコールバック関数 requestMainWindowClose() が呼び出されるようにし、requestMainWindowClose() がクローズ メッセージを送信したと想定します。対象のプロセスに対して、プロセスが終了するのを待ちます。プロセスが一定時間内に終了しない場合、TerminateProcess() を介して強制的に終了しようとします。それでもダメなら諦めます。closeProc() は次のようになります。

void closeProc()
{
    HANDLE ps = OpenProcess( SYNCHRONIZE | PROCESS_TERMINATE, FALSE, dwProcessId );
    if (ps == NULL)
        throw std::runtime_error(...);

    EnumWindows( requestMainWindowClose, dwProcessId );

    static const int MAX_WAIT_MILLISEC = 5000; 
    const DWORD result = WaitForSingleObject(ps, MAX_WAIT_MILLISEC);
    if (result != WAIT_OBJECT_0)
    {
        if (result == WAIT_TIMEOUT)
        {
            LOGF_ERROR("Could not clcose proc (PID %s): did not exit within %s ms",
                dwProcessId << MAX_WAIT_MILLISEC);
        }
        else
        {
            LOGF_ERROR("Could not close proc (PID %s): %s", 
                dwProcessId << getLastWin32Error());
        }

        LOGF_ERROR("Trying to *terminate* proc (PID %s)", dwProcessId);
        if (TerminateProcess(ps, 0))
            exited = true;
        }
    }

    CloseHandle( ps ) ;
}

問題は requestMainWindowClose にあり、元のコードは次のとおりです。

BOOL CALLBACK 
requestMainWindowClose( HWND nextWindow, LPARAM closePid )
{
    DWORD windowPid;
    GetWindowThreadProcessId(nextWindow, &windowPid);
    if ( windowPid==(DWORD)closePid )
    {
        ::PostMessage( nextWindow, WM_CLOSE, 0, 0 );
        return false;
    }

    return true;
}

上で定義したように、コールバック関数は、EnumWindows() によって渡されたウィンドウ ハンドル (nextWindow) のプロセス ID を決定し、閉じたいプロセス (closePid) と比較します。一致する場合、関数は CLOSE メッセージを送信して戻ります。

これまでのところ、すべて順調です。問題は、それが false を返すことです。そのため、EnumWindows() はプロセスの 1 つのウィンドウにのみメッセージを送信し、WPF アプリケーションには複数のウィンドウがあるように見えます。コードが 1 つのウィンドウしか作成しない場合でも、隠れたウィンドウが背後で作成されます。 WPFによって。それらはすべて EnumWindows によって検出されます。ただし、最初のウィンドウがメイン アプリケーション ウィンドウになることはめったにありません。したがって、 requestMainWindowClose() は、私の WPF アプリのメイン ウィンドウに CLOSE を送信することはなく、チャンスもありませんでした。

実際、修正は簡単でした。つまり、false を返さないでください。

BOOL CALLBACK 
requestMainWindowClose( HWND nextWindow, LPARAM closePid )
{
    DWORD windowPid;
    GetWindowThreadProcessId( nextWindow, &windowPid );
    if ( windowPid==(DWORD)closePid )  
        ::PostMessage( nextWindow, WM_CLOSE, 0, 0 );

    return true; 
}

WPF のトップ アプリ ウィンドウのみが CLOSE メッセージに応答します。

于 2013-04-17T03:07:46.463 に答える
1

システム メニューの [閉じる] コマンドに直接相当するものは次のとおりです。

PostMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);

代わりにそれを試すことができます。

于 2013-03-06T00:44:13.900 に答える