WinAPI中如何优雅地关闭一个没有窗口的应用程序?

3
我正在编写一个小工具应用程序,可以通过热键控制系统主音量,为我的女友的笔记本电脑提供此类功能键。我很快就编写了代码,并且主要功能完美地运行起来;然而,由于我没有创建任何窗口(只是处理WM_HOTKEY消息的消息循环),所以我无法以更加优雅的方式终止应用程序,而只能不雅地终止进程(还有,当系统关闭时,它会显示“我应该等待进程结束还是现在杀死它”的窗口,在窗口标题通常所在的位置上显示一些垃圾)。
有没有任何方法可以做到这一点,而不涉及创建一个假窗口,仅仅是为了拦截WM_CLOSE消息?
以下是代码(我故意省略了混音控制函数,因为它们与问题无关):
int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow) {
    MSG msg;
    int step;
    MixerInfo_t mi;
    HANDLE mutex;

    mutex = CreateMutex(NULL, TRUE, "volhotkey");
    if (mutex == NULL)
        return 1;
    if (GetLastError() == ERROR_ALREADY_EXISTS)
        return 0;

    RegisterHotKey(NULL, 1, MOD_ALT | MOD_CONTROL, VK_F5);
    RegisterHotKey(NULL, 2, MOD_ALT | MOD_CONTROL, VK_F6);
    RegisterHotKey(NULL, 3, MOD_ALT | MOD_CONTROL, VK_F7);

    mi = GetMixerControls();
    step = (mi.maxVolume - mi.minVolume) / 20;

    while (GetMessage(&msg, NULL, 0, 0)) {
        switch (msg.message) {
            case WM_HOTKEY:
                switch (msg.wParam) {
                    case 1:
                        AdjustVolume(&mi, -step);
                        break;
                    case 2:
                        AdjustVolume(&mi, step);
                        break;
                    case 3:
                        SetMute(&mi, !IsMuted(&mi));
                        break;
                }
                MessageBeep(MB_ICONASTERISK);
                break;
            case WM_DESTROY:
                PostQuitMessage(0);
                break;
            default:
                break;
        }
    }

    UnregisterHotKey(NULL, 1);
    UnregisterHotKey(NULL, 2);

    return msg.wParam;
}

提前感谢!

顺便说一下,就记录而言,WM_DESTROY也从未发布过。


你是否已经检查过你的笔记本电脑制造商的支持网站,看看他们是否已经提供了这样的实用工具? - Ferruccio
我有,但他们没有。这是一台HP nx6110,它甚至没有相应的按钮,就像我在问题中已经说明的那样。 - IneQuation
4个回答

4
你可以使用SetConsoleCtrlHandler()函数来监听关闭事件。
SetConsoleCtrlHandler( ShutdownHandler, TRUE );

你的处理器应该长这样:

BOOL WINAPI ShutdownHandler( DWORD dwCtrlType )
{
    if( dwCtrlType == CTRL_SHUTDOWN_EVENT || dwCtrlType == CTRL_LOGOFF_EVENT )
    {
        ExitProcess( 0 );
        return TRUE; // just to keep the compiler happy
    }

    return FALSE;
}

尽管名称为SetConsoleCtrlHandler(),但它可以在应用程序是否为控制台应用程序的情况下工作。


1
谢谢,伙计,这正是我一直在寻找的! :) 而且你可能是唯一一个真正仔细阅读问题的人。 - IneQuation

1

查看ExitProcess API调用以优雅地关闭进程。要检测Windows关机,请在消息处理中包含WM_ENDSESSION。如果您的应用程序比发布的更复杂,您还可以查看ExitThread函数。


感谢您将此消息带给我的注意,这会很有用! - IneQuation

1

你没有创建任何窗口,甚至没有隐藏的窗口,所以没有办法通过向窗口发送消息使循环退出。这也是WM_DESTROY从未被触发的原因。

剩下的就只有使用PostThreadMessage()来发布WM_QUIT了。你必须通过某种方式找到线程ID。使用Shell_NotifyIcon()是明智的选择。


是的,但是a)我特别要求一个非虚假窗口的解决方案,b)发布退出消息没有问题。问题在于如何处理外部请求以优雅地关闭应用程序。 - IneQuation
嗯,不是假的,是真的。只是不可见。发现 Windows 正在关闭也非常有用,这样你就可以终止应用程序而不会因此卡住或被 Windows 强制关闭。如果你只是不想使用窗口,那么可以使用命名管道。 - Hans Passant

0
你可以很容易地在系统托盘中显示某些内容,从而优雅地关闭它。随着应用程序的发展,这可能是可取的,因为用户最终可能希望能够配置或更改热键,或者在它们干扰其他应用程序时暂时关闭它们等。
或者设置另一个热键,显示一个带有配置选项的小窗口?

该应用程序不会扩展,它只是一个微小的、私人的应用程序,并且我特别要求一个无窗口的解决方案。 - IneQuation

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