2

WM_CLOSEメッセージを使用してC#アプリケーションを閉じると、予期しない動作が発生します。私のシナリオでは、MSIインストールパッケージの実行時にC#アプリケーションが閉じていることを確認する必要があります。MSIにはこれに対する適切なアクションがないため、MSIパッケージに埋め込まれ、EnsureApplicationClosed関数を公開するカスタムアクションDLLを作成していました。以下のDLLの関連コード:

#include "stdafx.h"
#include <iostream>
#include <vector>
#include <string>
#include "TlHelp32.h"

std::vector<DWORD> processesList;

//This method is the main point of interest
BOOL CALLBACK enumWindowsProc(__in HWND hWnd, __in LPARAM lParam)
{
    if(processesList.size()==0) return FALSE;
    DWORD processId;
    GetWindowThreadProcessId(hWnd, &processId);
    int index=0;
    while(index<processesList.size())
    {
        if(processesList.at(index)==processId)
        {
            //Remove process id from the list
            processesList.erase(processesList.begin()+index);
            //Should close main windows of the process found.
            SendMessage(hWnd, WM_CLOSE, (LPARAM)0, (WPARAM)0);
        }
        else
        {
            index++;
        }
    }
    return TRUE;
}
std::vector<DWORD> FindProcessesId(const LPCWSTR processName)
{
    std::vector<DWORD> result;

    PROCESSENTRY32 processInfo;
    processInfo.dwSize = sizeof(processInfo);

    HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
    if ( processesSnapshot == INVALID_HANDLE_VALUE ) return result;

    Process32First(processesSnapshot, &processInfo);
    if (wcscmp(processName, processInfo.szExeFile)==0)
    {
        result.push_back(processInfo.th32ProcessID);
    }

    while ( Process32Next(processesSnapshot, &processInfo) )
    {
        if ( wcscmp(processName, processInfo.szExeFile)==0 )
        {
            result.push_back(processInfo.th32ProcessID);
        }
    }

    CloseHandle(processesSnapshot);
    return result;
}
void InnerEnsureApplicationClosed(const LPCWSTR processName)
{
    processesList=FindProcessesId(processName);
    BOOL enumeratingWindowsSucceeded = ::EnumWindows( enumWindowsProc, NULL );
}
//Function exported by the DLL
UINT __stdcall EnsureApplicationClosed ( MSIHANDLE hModule )
{
    InnerEnsureApplicationClosed(L"SomeApplication.exe");
    return ERROR_SUCCESS;
}

そして、私はこのdllをテストするために単純なコンソールアプリを使用しています:

#include "stdafx.h"
#include <SomeApplicationCustomActions.h>

int _tmain(int argc, _TCHAR* argv[])
{
    EnsureApplicationClosed(NULL);
    return 0;
}

C#アプリケーションは、サードパーティのアプリからの通知のみを表示する必要があるため、ほとんどの場合、ユーザーには表示されません。これが、カスタムFormClosingハンドラーがある理由です。

private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
   Console.Beep();
   if (e.CloseReason != CloseReason.WindowsShutDown && e.CloseReason!= CloseReason.TaskManagerClosing)
   {
       e.Cancel = true;
       this.Hide();
   }
}

SendMessage(hWnd、WM_CLOSE、(LPARAM)0、(WPARAM)0)を使用してC#アプリケーションを閉じています。SendMessage(hWnd、WM_CLOSE、(LPARAM)0、(WPARAM)0)がC#アプリケーションでTaskManagerClosing close reasonを生成することがわかったため、このWM_CLOSEメッセージを受信したときに、TaskManagerClosingclosereasonをチェックする必要があります。Console.Beep()は、ハンドラーコードが入力されていることを聞くことができるように、音声信号を生成するためのテストに使用されます。

これが動作するのは、DLLテストアプリケーションを2回実行した場合のみであるという問題。テストアプリケーションを初めて実行したときに、音声信号が聞こえず、C#プロセスがタスクマネージャーに残っています。そのため、C#アプリケーションのFormClosingイベントは発生しません。SendMessageをPostMessageに置き換えようとしましたが、成功しませんでした。GetLastError()は、どちらの場合も常に18を返します。誰かが同じ問題に直面し、それを解決する方法を知っていますか?

前もって感謝します。

4

1 に答える 1

2

一日中遊んだ後、この問題が発生する理由を見つけました。カスタムアクションDLLは問題ありません。問題は.NETListViewコントロールにあります。ShownイベントハンドラーでフォームのVisibleプロパティがfalseに設定されていて、フォームにListViewコントロールがある場合、別のアプリケーションから最初に送信されるWM_CLOSEメッセージの影響を受けなくなります。WM_CLOSEは、WndProcメソッドには表示されません。フォームのSetVisibleCoreをオーバーライドすることで修正しましたが、とにかくこの問題は非常に奇妙に見え、.NETのバグであるように見えます。

于 2013-02-05T15:22:56.040 に答える