C++ 是什么导致两个相等的后续SetWindowPos()调用设置不同的窗口大小?

C++ 是什么导致两个相等的后续SetWindowPos()调用设置不同的窗口大小?,c++,winapi,C++,Winapi,作为我正在进行的项目的一部分,我想突出显示桌面的各个区域。现在我使用一个半透明的红色顶层窗口来实现这一点,即 使用红色背景笔刷创建自定义窗口类 为我的自定义类创建一个窗口,该窗口具有WS\u EX\u LAYERED样式集 调用以使窗口50%半透明 它基本上工作得很好,但我注意到覆盖窗口和函数之间有一个相当奇特的交互:当将宽度或高度传递给SetWindowPos时,它们分别小于32。39像素,第一次调用SetWindowPos实际上会使窗口比请求的窗口大,但后续调用会按预期工作。下面是一个小示例

作为我正在进行的项目的一部分,我想突出显示桌面的各个区域。现在我使用一个半透明的红色顶层窗口来实现这一点,即

  • 使用红色背景笔刷创建自定义窗口类
  • 为我的自定义类创建一个窗口,该窗口具有
    WS\u EX\u LAYERED
    样式集
  • 调用以使窗口50%半透明
  • 它基本上工作得很好,但我注意到覆盖窗口和函数之间有一个相当奇特的交互:当将宽度或高度传递给
    SetWindowPos
    时,它们分别小于32。39像素,第一次调用
    SetWindowPos
    实际上会使窗口比请求的窗口大,但后续调用会按预期工作。下面是一个小示例程序,它演示了这个问题-它在桌面的左上角创建覆盖窗口,然后以1秒的延迟调用两次
    SetWindowPos
    。请注意,红色矩形首先是方形,然后垂直收缩

    #include <windows.h>
    
    int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
    {
        // Register custom class for overlay window, forcing red background
        WNDCLASSW overlayClassDef = {
            0,
            DefWindowProcW,
            0,
            0,
            hInstance,
            NULL,
            NULL,
            ::CreateSolidBrush( RGB( 255, 0, 0 ) ),
            NULL,
            L"Overlay_Window"
        };
    
        ATOM overlayClass = ::RegisterClassW( &overlayClassDef );
    
        // Create overlay window using 'layered' flag to enable making it
        // translucent
        HWND m_overlay = ::CreateWindowExW(
            WS_EX_LAYERED | WS_EX_NOACTIVATE,
            (LPCWSTR)overlayClass,
            NULL,
            0,
            0,
            0,
            0,
            0,
            NULL,
            NULL,
            hInstance,
            NULL
        );
    
        // Strip WS_BORDER and WS_DLGFRAME styles to get perfectly flat window; these
        // styles appear to get added by default for toplevel windows.
        int windowStyle = ::GetWindowLongPtr( m_overlay, GWL_STYLE );
        windowStyle &= ~WS_BORDER;
        windowStyle &= ~WS_DLGFRAME;
        ::SetWindowLongPtr( m_overlay, GWL_STYLE, windowStyle );
    
        // Show the window and make it 50% translucent
        ::ShowWindow( m_overlay, SW_SHOWNA );
        ::SetLayeredWindowAttributes( m_overlay, 0, 127, LWA_ALPHA );
    
        // Set the position to 100/100 (50x20 pixels); the window on
        // screen becomes higher than 20 pixels though!
        ::SetWindowPos( m_overlay, HWND_TOPMOST, 100, 100, 50, 20, SWP_NOACTIVATE);
        ::Sleep( 1000 );
    
        // Set the position once more -- this time, the window shrinks
        // to 20 pixels vertically.
        ::SetWindowPos( m_overlay, HWND_TOPMOST, 100, 100, 50, 20, SWP_NOACTIVATE);
        ::Sleep( 1000 );
    
        // Releasing resources omitted for brevity
        return 0;
    }
    
    #包括
    int WINAPI WinMain(HINSTANCE HINSTANCE、HINSTANCE HPPreInstance、LPSTR lpCmdLine、int nCmdShow)
    {
    //为覆盖窗口注册自定义类,强制使用红色背景
    WNDCLASSW OVERYCLASSDEF={
    0,
    除窗程序,
    0,
    0,
    hInstance,
    无效的
    无效的
    ::CreateSolidBrush(RGB(255,0,0)),
    无效的
    L“叠加窗口”
    };
    ATOM OverlyClass=::RegisterClassW(&OverlyClassDef);
    //使用“分层”标志创建覆盖窗口,使其能够
    //半透明的
    HWND m_overlay=::CreateWindowExW(
    WS_EX_分层| WS_EX_未激活,
    (LPCWSTR)超循环类,
    无效的
    0,
    0,
    0,
    0,
    0,
    无效的
    无效的
    hInstance,
    无效的
    );
    //剥离WS_边框和WS_DLGFRAME样式,以获得完全平坦的窗口;这些
    //默认情况下,顶级窗口会添加样式。
    int windowStyle=::GetWindowLongPtr(m_覆盖,GWL_样式);
    windowStyle&=~WS\u边框;
    windowStyle&=~WS\u DLGFRAME;
    ::SetWindowLongPtr(m_叠加、GWL_样式、WindowsStyle);
    //显示窗口并使其50%半透明
    ::显示窗口(m_叠加,SW_显示);
    ::SetLayeredWindowAttributes(m_overlay,0,127,LWA_ALPHA);
    //将位置设置为100/100(50x20像素);打开窗口
    //屏幕变得高于20像素!
    ::设置窗口位置(m_覆盖,HWND_最顶部,100,100,50,20,SWP_未激活);
    ::睡眠(1000);
    //再次设置位置--这一次,窗口缩小
    //垂直方向为20像素。
    ::设置窗口位置(m_覆盖,HWND_最顶部,100,100,50,20,SWP_未激活);
    ::睡眠(1000);
    //为了简洁起见,省略了释放资源
    返回0;
    }
    
    关于这种行为的一些观察:

  • 如果在窗口标志中设置了
    WS_BORDER
    WS_DLGFRAME
    ,则不会出现此效果;不过,我想清除这两个标志,以获得一个完全平坦的窗口
  • 使用宽度>=32像素时,效果似乎不会水平出现
  • 使用高度>=39像素时,效果似乎不会垂直出现
  • 如果不使用自定义窗口类,而是实例化内置的
    静态类
    时,也可以复制此属性

  • 是否有一个接近32像素的魔法最小窗口大小需要特别处理?

    更新窗口样式后,您需要确保重新计算窗口大小和帧度量。为此,您需要在更新视觉样式后立即使用
    SWP\u FRAMECHANGED
    标志手动调用
    SetWindowPos

    ::SetWindowLongPtr( m_overlay, GWL_STYLE, windowStyle );
    ::SetWindowPos(m_overlay, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOREPOSITION | SWP_NOREDRAW);
    

    如果为
    dwStyle
    传递了
    0
    ,则出现
    CreateWindow
    指定默认窗口样式;在此处传递
    WS\u弹出窗口
    可以避免触发奇怪的
    SetWindowPos
    行为,还可以减少清除
    WS\u边框
    WS\u DLGFRAME
    的需要。
    WS\u重叠
    被定义为
    0x0
    。“重叠窗口有一个标题栏和一个边框。”这可能解释了你的部分观察结果。@IInspectable啊,这很有趣-这让我想知道
    WS\u overlapped
    是否适合在我的案例中使用。。也许我应该选择
    WS\u POPUP
    WS\u POPUP
    看起来确实是个更好的选择。毕竟,您不需要标题栏或边框,只需要一个窗口,它只包含一个客户端区域。这看起来不错!值得一提的是,文档中指出,
    SWP\u NOOWNERZORDER
    SWP\u NOREPOSITION
    是同义词,因此可以删除其中一个。支持这是正确答案的进一步证据:文档在备注部分中说明:“某些窗口数据是缓存的,因此在调用SetWindowPos函数之前,您使用SetWindowLong所做的更改不会生效。具体来说,如果更改任何帧样式,必须使用SWP_FRAMECHANGED标志调用SetWindowPos,才能正确更新缓存。”