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

发布于 2024-07-06 11:57:36 字数 938 浏览 8 评论 0原文

这是我的问题:我需要从 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!
                }
            }
        }

在发现窗口最小化时 MainWindowHandle == 0 后,我添加了 if 子句。 删除 if 没有帮助。 CloseMainWindow()Close() 都不起作用。 Kill() 可以,但如上所述 - 这不是我需要的。

任何想法都会被接受,包括使用神秘的 Win32 API 函数:)

Here's my issue: I need to close a process, already running, from a C# program.
The problem is that the process now runs as an icon (minimized to taskbar), and unless the user opens it at least once (which will never happen on unattended machines), it'll never
have a main window.

The other requirement that I have is that the application be closed not killed. I need it to write it's memory buffers to disk - and killing it causes data loss.

Here's what I tried so far:

        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!
                }
            }
        }

I've added the if clause, after discovering that MainWindowHandle == 0 when the window was minimized. Removing the if doesn't help. Neither the CloseMainWindow() nor the Close() work. The Kill() does, but as mentioned above - it's not what I need.

Any idea would be accepted, including the use of arcane Win32 API functions :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

埋葬我深情 2024-07-13 11:57:36

这应该可行:

[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 位。

This should work:

[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);
}

Since some windows don't respond to WM_CLOSE, WM_QUIT might have to be sent instead. These declarations should work on both 32bit and 64bit.

空袭的梦i 2024-07-13 11:57:36

如果它位于任务栏上,它将有一个窗口。 或者您的意思是它位于任务栏通知区域(又名 SysTray)? 在这种情况下,它仍然会有一个窗口。

Win32 应用程序实际上并不具有“主窗口”,除非按照惯例(主窗口是调用 PostQuitMessage 来响应 WM_DESTROY 的窗口,导致消息循环退出)。

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

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

请注意,我在这里是从 Win32 的角度进行讨论的。 您可能需要使用 P/Invoke 或其他技巧才能使其在 .NET 程序中工作。

If it's on the taskbar, it'll have a window. Or did you mean that it's in the taskbar notification area (aka the SysTray)? In which case, it'll still have a window.

Win32 applications don't really have a "main window", except by convention (the main window is the one that calls PostQuitMessage in response to WM_DESTROY, causing the message loop to exit).

With the program running, run Spy++. To find all of the windows owned by a process, you should select Spy -> Processes from the main menu. This will display a tree of processes. From there, you can drill down to threads, and then to windows. This will tell you which windows the process has. Note down the window class and caption. With these, you can use FindWindow (or EnumWindows) to find the window handle in future.

With the window handle, you can send a WM_CLOSE or WM_SYSCOMMAND/SC_CLOSE (equivalent to clicking on the 'X' on the window caption) message. This ought to cause the program to shut down nicely.

Note that I'm talking from a Win32 point-of-view here. You might need to use P/Invoke or other tricks to get this to work from a .NET program.

自由如风 2024-07-13 11:57:36

以下是一些答案和说明:

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

Roger
是的,我的意思是任务栏通知区域 - 感谢您的澄清。
需要调用PostQuitMessage。 我只是不知道如何,仅给出一个进程,而不是一个窗口。

克雷格
我很乐意解释这种情况:该应用程序有一个命令行界面,允许您指定参数来指示它将做什么以及将结果保存在哪里。 但是一旦它运行,停止它并获取结果的唯一方法是在托盘通知中右键单击它并选择“退出”。

现在我的用户想要编写/批处理应用程序。 他们从批处理启动它绝对没有问题(只需指定 exe 名称和一堆标志),但随后陷入了正在运行的进程。 假设没有人会更改进程以提供 API 来在运行时停止它(它很旧),我需要一种方法来人为地关闭它。

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

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

Here are some answers and clarifications:

rpetrich:
Tried your method before and the problem is, I don't know the window name, it differs from user to user, version to version - just the exe name remains constant. All I have is the process name. And as you can see in the code above the MainWindowHandle of the process is 0.

Roger:
Yes, I did mean the taskbar notification area - thanks for the clarification.
I NEED to call PostQuitMessage. I just don't know how, given a processs only, and not a Window.

Craig:
I'd be glad to explain the situation: the application has a command line interface, allowing you to specify parameters that dictate what it would do and where will it save the results. But once it's running, the only way to stop it and get the results is right-click it in the tray notification are and select 'exit'.

Now my users want to script/batch the app. They had absolutely no problem starting it from a batch (just specify the exe name and and a bunch of flags) but then got stuck with a running process. Assuming no one will change the process to provide an API to stop it while running (it's quite old), I need a way to artificially close it.

Similarly, on unattended computers, the script to start the process can be started by a task scheduling or operations control program, but there's no way to shut the process down.

Hope that clarifies my situation, and again, thanks everyone who's trying to help!

最后的乘客 2024-07-13 11:57:36

澄清为什么要尝试这样做的问题:如果进程上唯一的用户界面是系统托盘图标,那么为什么要杀死它并保持进程运行? 用户如何访问该流程? 如果机器是“无人值守”的,为什么还要关心托盘图标呢?

Question to clarify why you're attempting this: If the only user interface on the process is the system tray icon, why would you want to kill that and but leave the process running? How would the user access the process? And if the machine is "unattended", why concern yourself with the tray icon?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文