【问题标题】:Sending WM_CLOSE to C# application works unexpected将 WM_CLOSE 发送到 C# 应用程序意外工作
【发布时间】:2013-01-18 15:11:27
【问题描述】:

我在使用 WM_CLOSE 消息关闭 C# 应用程序时遇到了意外行为。在我的场景中,我需要确保 C# 应用程序在其 MSI 安装包运行时关闭。 MSI 对此没有适当的操作,因此我正在编写一个自定义操作 DLL,该 DLL 嵌入到 MSI 包中并公开 EnsureApplicationClosed 函数。 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# 应用程序。当收到此 WM_CLOSE 消息时,需要检查 TaskManagerClosing 关闭原因,因为我发现 SendMessage(hWnd, WM_CLOSE, (LPARAM)0, (WPARAM)0) 在 C# 应用程序中产生 TaskManagerClosing 关闭原因。 Console.Beep() 用于测试以产生声音信号,因此我可以听到输入了处理程序代码。

这个问题只有在我第二次运行 DLL 测试应用程序时才有效。当我第一次运行测试应用程序时,我没有听到声音信号并且 C# 进程保留在任务管理器中。所以不会触发 C# 应用程序的 FormClosing 事件。我试图用 PostMessage 替换 SendMessage 但没有成功。 GetLastError() 在这两种情况下总是返回 18。有没有人遇到过同样的问题并且知道如何解决?

提前致谢。

【问题讨论】:

  • Windows Installer 检测应用程序是否正在运行(XP 和更高版本),并通过发送WM_CLOSE 提示关闭它(在 Win7 上看到,可能是 Vista 和更高版本)。此行为可能取决于 Windows Installer 的版本,而不是 Windows。我猜您可以编写 MSI 脚本以静默方式执行此操作,而不是自定义 DLL。当像这样发送WM_CLOSE 时,接收应用程序总是得到CloseReason.TaskManagerClosing。
  • 我在 Windows Installer 方面不是很有经验,但在谷歌搜索我的任务时,大多数建议是创建一个自定义操作作为此类事情的纯粹方式。它适用于具有可见窗口的普通应用程序。

标签: c# c++ winapi sendmessage


【解决方案1】:

玩了一整天后,我找到了出现此问题的原因。自定义动作 DLL 没问题。问题来自 .NET ListView 控件。如果表单的 Visible 属性在 Shown 事件处理程序中设置为 false,并且如果表单上有 ListView 控件,则它对第一次从另一个应用程序发送的 WM_CLOSE 消息免疫。 WM_CLOSE 只是没有出现在 WndProc 方法中。我已经通过覆盖表单的 SetVisibleCore 来修复它,但无论如何这个问题看起来很奇怪,并且似乎是 .NET 中的一个错误。

【讨论】:

    猜你喜欢
    • 2011-07-21
    • 2021-07-28
    • 2013-06-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多