优雅地关闭WinAPI中的无窗口应用程序

时间:2011-02-01 18:48:18

标签: c winapi

我正在编写一个小实用程序应用程序,通过我的GF的热键控制系统主音量,其笔记本电脑由于某些原因被剥夺了这些功能键。我立刻掀起了代码,我的主要功能完美无缺;但是,由于我没有创建任何窗口(只是处理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也从未发布过。

4 个答案:

答案 0 :(得分: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 :(得分:1)

查看ExitProcess API调用,以便正常关闭进程。要检测Windows关闭,请在邮件处理中包含WM_ENDSESSION。如果您的应用程序比发布的内容更复杂,您可能还需要查看ExitThread函数。

答案 2 :(得分:1)

您没有创建任何窗口,甚至没有隐藏窗口,因此无法通过向窗口发送消息来退出循环。也是WM_DESTROY永远不会触发的原因。

剩下的就是PostThreadMessage()来发布WM_QUIT。你必须能够以某种方式找到线程ID。使用Shell_NotifyIcon()是明智的。

答案 3 :(得分:0)

您可以随时轻松地在系统托盘中显示某些内容,从而可以优雅地关闭它。随着应用程序的增长,这可能是可取的,因为用户最终可能希望能够配置或更改热键,或暂时将其关闭,以免干扰其他应用程序等。

或者有另一个热键显示带有配置选项的小窗口?