You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Win32 API应用窗口失焦后运行时永久消失问题排查

Windows窗口失焦/最小化后无法恢复的问题修复

问题描述

使用C语言开发Windows窗口应用,需求是窗口未获焦点时暂停更新循环,获焦时恢复。当前窗口关闭、最大化事件处理正常,但点击最小化按钮或窗口失焦后,窗口会永久消失,无法恢复或重新获取焦点,只能通过命令行终止应用;键盘、鼠标等其他事件功能正常。

问题原因

主循环中当suspended为true时,直接执行continue跳过了所有逻辑,包括Windows消息队列的处理。Windows系统依赖消息队列处理窗口激活、恢复等操作,比如点击任务栏恢复窗口的事件需要发送WM_ACTIVATE消息,但暂停状态下消息完全得不到处理,导致窗口无法响应恢复指令。

另外,WM_ACTIVATE的触发时机包含窗口失焦场景,若希望仅在窗口最小化时暂停,可结合WM_SIZE消息的SIZE_MINIMIZED状态做更精准控制。

修复方案

  1. 调整主循环逻辑:暂停状态下仍需处理系统消息,只跳过业务更新逻辑(即原代码中Sleep(16)模拟的部分)。
  2. (可选)优化暂停触发条件:结合WM_SIZE的最小化事件,区分窗口失焦和最小化两种场景,按需控制暂停状态。

修改后的完整代码

#include <stdio.h>
#include <string.h>
#include <windows.h>

typedef _Bool bool;
#define true    ( ( bool ) 1 )
#define false   ( ( bool ) 0 )

bool suspended;
bool running;

void
platform_update
( void )
{
    MSG message;
    while ( PeekMessageA ( &message , 0 , 0 , 0 , PM_REMOVE ) )
    {
        TranslateMessage ( &message );
        DispatchMessageA ( &message );
    }
}

LRESULT CALLBACK
platform_process_message
(   HWND    window
,   UINT    message
,   WPARAM  w_param
,   LPARAM  l_param
)
{
    switch ( message )
    {
        case WM_CLOSE:
        {
            printf ( "Quitting application.\n" );
            running = false;
            return 0;
        }

        case WM_DESTROY:
        {
            PostQuitMessage ( 0 );
            return 0;
        }

        case WM_ACTIVATE:
        {
            if ( running )
            {
                if ( LOWORD ( w_param ) == WA_INACTIVE )
                {
                    printf ( "Suspending application.\n" );
                    suspended = true;
                }
                else
                {
                    printf ( "Resuming application.\n" );
                    suspended = false;
                }
            }
        }
        break;

        case WM_SIZE:
        {
            printf ( "Resized application window.\n" );
            // 可选:仅在窗口最小化时暂停,恢复时取消暂停
            if (w_param == SIZE_MINIMIZED) {
                printf("Window minimized, suspending.\n");
                suspended = true;
            } else if (w_param == SIZE_RESTORED || w_param == SIZE_MAXIMIZED) {
                printf("Window restored, resuming.\n");
                suspended = false;
            }
        }
        break;
    }
    return DefWindowProcA ( window , message , w_param , l_param );
}

int
main
( void )
{
    HINSTANCE h_instance = GetModuleHandleA ( 0 );

    // Register window class.
    WNDCLASSA class;
    memset ( &class , 0 , sizeof ( class ) );
    class.style = CS_DBLCLKS;
    class.lpfnWndProc = platform_process_message;
    class.cbClsExtra = 0;
    class.cbWndExtra = 0;
    class.hInstance = h_instance;
    class.hIcon = LoadIcon ( h_instance, IDI_APPLICATION );
    class.hCursor = LoadCursor ( 0 , IDC_ARROW );
    class.hbrBackground = 0;
    class.lpszClassName = "test_window";
    if ( !RegisterClassA ( &class ) )
    {
        printf ( "Error: RegisterClassA failed.\n" );
        return 1;
    }

    // Initialize window dimensions.
    int window_x = 100;
    int window_y = 100;
    int window_width = 200;
    int window_height = 200;

    // Initialize window style.
    int window_style = WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION
                     | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_THICKFRAME
                     ;
    int window_ex_style = WS_EX_APPWINDOW;
    RECT border = { 0 , 0 , 0 , 0 };
    AdjustWindowRectEx ( &border , window_style , 0 , window_ex_style );
    window_x += border.left;
    window_y += border.top;
    window_width += border.right - border.left;
    window_height += border.bottom - border.top;

    // Create the window.
    HWND window = CreateWindowExA ( window_ex_style
                                  , class.lpszClassName
                                  , "Test window"
                                  , window_style
                                  , window_x
                                  , window_y
                                  , window_width
                                  , window_height
                                  , 0
                                  , 0
                                  , h_instance
                                  , 0
                                  );
    if ( !window )
    {
        printf ( "Error: CreateWindowExA failed.\n" );
        return 2;
    }

    // Display the window.
    ShowWindow ( window , SW_SHOW );

    // Update loop.
    running = true;
    suspended = false;
    while ( running )
    {
        // 始终处理系统消息,不管是否暂停
        platform_update ();
        
        if ( suspended )
        {
            // 暂停时让出CPU资源,比Sleep更高效
            WaitMessage();
            continue;
        }
        
        // 模拟业务更新逻辑
        Sleep ( 16 );
    }
    running = false;

    // Destroy window.
    if ( !DestroyWindow ( window ) )
    {
        printf ( "Error: DestroyWindow failed." );
        return 3;
    }

    return 0;
}

关键修改点说明

  • 主循环调整:将platform_update()移到suspended判断之前,确保无论是否暂停都处理消息队列。
  • 暂停时优化:用WaitMessage()替代Sleep(16),让线程在无消息时休眠,节省CPU资源。
  • 可选的最小化逻辑:在WM_SIZE中处理最小化和恢复事件,精准控制暂停状态(若需仅在最小化时暂停,可注释掉WM_ACTIVATE中的暂停逻辑)。

内容的提问来源于stack exchange,提问作者Oh Fiveight

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 12:44:50