从C#关闭最小化/图标化的进程

3
这是我的问题:我需要从一个C#程序中关闭一个已经运行的进程。问题在于,该进程现在作为一个图标运行(最小化到任务栏),除非用户至少打开它一次(这在无人值守的机器上永远不会发生),否则它将永远没有一个主窗口。
我还有另一个要求,即应用程序应该被“关闭”,而不是“终止”。我需要将其内存缓冲区写入磁盘 - 终止它会导致数据丢失。
以下是我尝试过的方法:
        foreach (Process proc in Process.GetProcesses())
        {
            if (proc.ProcessName.ToLower().StartsWith("myapp"))
            {
                if (proc.MainWindowHandle.ToInt32() != 0)
                {
                    proc.CloseMainWindow();
                    proc.Close();
                    //proc.Kill();  <--- not good!
                }
            }
        }

我添加了一个 if 条件句,发现当窗口最小化时,MainWindowHandle == 0。删除 if 不起作用。无论是 CloseMainWindow() 还是 Close() 都不起作用。只有 Kill() 起作用,但正如上面提到的 - 这不是我需要的。
任何想法都可以接受,包括使用神秘的 Win32 API 函数 :)
4个回答

2
这应该有效:
[DllImport("user32.dll", CharSet=CharSet.Auto)]
private static extern IntPtr FindWindow(string className, string windowName);
[DllImport("user32.dll", CharSet=CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

private const int WM_CLOSE = 0x10;
private const int WM_QUIT = 0x12;

public void SearchAndDestroy(string windowName) 
{
    IntPtr hWnd = FindWindow(null, windowName);
    if (hWnd == IntPtr.Zero)
        throw new Exception("Couldn't find window!");
    SendMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}

由于一些窗口不响应 WM_CLOSE,可能需要发送 WM_QUIT。这些声明应该在32位和64位上都有效。


1

如果它在任务栏上,它将有一个窗口。或者你是指它在任务栏通知区域(也称为系统托盘)吗?如果是这样,它仍然会有一个窗口。

Win32应用程序实际上没有一个“主窗口”,除了约定俗成的(主窗口是调用PostQuitMessage响应WM_DESTROY的窗口,导致消息循环退出)。

运行程序后,运行Spy++。要查找进程拥有的所有窗口,您应该从主菜单中选择Spy->Processes。这将显示进程树。从那里,您可以深入到线程,然后到窗口。这将告诉您进程拥有哪些窗口。记下窗口类和标题。有了这些,您可以使用FindWindow(或EnumWindows)在未来找到窗口句柄。

有了窗口句柄,您可以发送WM_CLOSE或WM_SYSCOMMAND / SC_CLOSE(相当于单击窗口标题上的“X”)消息。这应该会使程序正常关闭。

请注意,我是从Win32的角度来谈论这个问题的。您可能需要使用P/Invoke或其他技巧才能从.NET程序中使其工作。


0

这里有一些答案和澄清:

rpetrich: 我之前尝试过你的方法,问题是我不知道窗口名称,因为它因用户和版本而异,只有exe名称保持不变。我所拥有的仅是进程名称。正如你在上面的代码中看到的那样,该进程的MainWindowHandle为0。

Roger: 是的,我确实是指任务栏通知区域 - 感谢澄清。 我需要调用PostQuitMessage。 我只是不知道如何,在仅给定进程而不是窗口的情况下。

Craig: 我很高兴解释这种情况:该应用程序具有命令行界面,允许您指定参数来控制其操作和结果保存位置。但是一旦它运行起来,唯一停止它并获取结果的方式就是右键单击托盘通知区域并选择“退出”。

现在我的用户想要脚本/批处理这个应用程序。他们从批处理中启动它没有任何问题(只需指定exe名称和一堆标志),但是在运行过程中遇到了困难。假设没有人会更改进程以提供API来停止它的运行(它相当古老),我需要一种人为关闭它的方法。

同样,在无人值守的计算机上,启动进程的脚本可以由任务调度或操作控制程序启动,但没有办法关闭该进程。

希望这澄清了我的情况,再次感谢所有试图帮助我的人!


我知道这个问题已经超过六年了,但是你有没有找到C#的解决方案?我最终使用了一个叫做AutoIt的脚本语言,它有一个ProcessClose函数,可以不“杀死”它,但是在C#中没有成功。 - kayleeFrye_onDeck
不,@kayleeFrye_onDeck,我已经好几年没碰过这个了 :) - Traveling Tech Guy
好的!感谢你这位代码考古学家的回答!:P 我正在自己解决一个关闭进程常量exe名称的问题。如果我找到了解决方法,我会告诉你的! - kayleeFrye_onDeck
kayleeFrye_onDeck<== 你是否已经制作出一个可行的解决方案?如果是,你会考虑在这个帖子中分享吗? - oakman

-1

为了澄清你尝试这个的原因:如果进程上唯一的用户界面是系统托盘图标,为什么要关闭它但保持进程运行?用户如何访问该进程?如果机器是“无人值守”的,为什么还要关注托盘图标?


这不是一个答案。这应该是在问题下面的评论。 - rory.ap
知道了。感谢建议。虽然也许在2008年我提问时还没有这样的东西。/耸肩 - Craig Eddy
1
即使在2008年,问题和答案也从未混淆过。 - rory.ap
好的。再次感谢。 - Craig Eddy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接