首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用PInvoke更改窗口大小不会导致工作区更新

使用PInvoke更改窗口大小不会导致工作区更新
EN

Stack Overflow用户
提问于 2021-01-13 17:21:03
回答 2查看 333关注 0票数 1

我试图写一个简单的程序,调整一个在800x600年代创建的没有可调整大小的窗体边框的桌面显示器的旧游戏。到目前为止,我已经能够成功地使用MoveWindowSetWindowPos更改窗口大小,但问题是客户端区域(即实际游戏)没有更新;如果窗口大小增加,则额外的区域被填充黑色,如果图像被缩小,则会被裁剪。但是,我发现资源管理器中的“显示桌面”按钮将强制客户区域正确更新。因此,我的问题是,当资源管理器显示桌面时,如何复制资源管理器所做的工作?

我已经尝试过RedrawWindowUpdateWindow,甚至直接发送WM_PAINT消息,但是似乎没有什么效果。据spy++称,游戏窗口似乎也没有子窗口。

更新

我刚刚尝试了最小化,然后重新显示窗口,它也确实修复了客户端区域。这可能解释了为什么显示桌面以前工作过。虽然这是一个潜在的解决方案,但理想情况下,我希望能够调整原地的大小,而不是快速最小化,然后重新显示。

UPDATE2

我已经按照要求包括了用于测试的代码,以及上面描述的症状的屏幕截图。

测试代码:

代码语言:javascript
运行
复制
foreach (var process in Process.GetProcessesByName("magic string"))
{
    var h = process.MainWindowHandle;

    if (h == IntPtr.Zero)
        continue;

    GetWindowRect(h, out RECT rect);

    //define new window size
    int width = 646;
    int height = 509;

    MoveWindow(h, rect.Left, rect.Top, width, height, true);
    
    //working solution, but not visually ideal
    ShowWindow(h, ShowWindowCommands.Minimize);
    ShowWindow(h, ShowWindowCommands.Restore);

    //note: none of the below methods that attempt to force update work
    //(i.e. does not visibly scale the client area)

    //RedrawWindow attempts
    RedrawWindow(h, IntPtr.Zero, IntPtr.Zero, RedrawWindowFlags.EraseNow);
    RedrawWindow(h, IntPtr.Zero, IntPtr.Zero, RedrawWindowFlags.UpdateNow);
    RedrawWindow(h, IntPtr.Zero, IntPtr.Zero, RedrawWindowFlags.Invalidate);

    //SetWindowPos attempts
    SetWindowPos(h, IntPtr.Zero, rect.Left, rect.Top, width, height, SetWindowPosFlags.SWP_SHOWWINDOW);
    SetWindowPos(h, IntPtr.Zero, rect.Left, rect.Top, width, height, SetWindowPosFlags.SWP_DRAWFRAME);

    //send WM_PAINT
    SendMessage(h, WM_PAINT, IntPtr.Zero, IntPtr.Zero);

    //@dxiv's suggestion
    RedrawWindow(h, IntPtr.Zero, IntPtr.Zero, RedrawWindowFlags.Invalidate);
    RedrawWindow(h, IntPtr.Zero, IntPtr.Zero, RedrawWindowFlags.Erase);

    //@NetMage's suggestion
    UInt32 lParam = 0;
    lParam = (UInt32)((UInt16)height << 16 | (UInt16)width);
    SendMessage(h, WM_SIZE, IntPtr.Zero, new IntPtr(lParam));

    //@Drake Wu - MSFT's suggestion
    SetWindowPos(h, IntPtr.Zero, rect.Left, rect.Top, width, height, SetWindowPosFlags.SWP_FRAMECHANGED);
}

PInvoke定义:

代码语言:javascript
运行
复制
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
    public int Left;
    public int Top;
    public int Right;
    public int Bottom;
}

[DllImport("user32.dll", SetLastError = true)]
static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);

[DllImport("user32.dll", SetLastError = true)]
static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int Width, int Height, bool Repaint);

private enum ShowWindowCommands : int
{
    Hide = 0,
    Normal = 1,
    ShowMinimized = 2,
    Maximize = 3, 
    ShowMaximized = 3,
    ShowNoActivate = 4,
    Show = 5,
    Minimize = 6,
    ShowMinNoActive = 7,
    ShowNA = 8,
    Restore = 9,
    ShowDefault = 10,
    ForceMinimize = 11
}

[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow);

[Flags]
private enum RedrawWindowFlags : uint
{
    Invalidate = 0x1,
    InternalPaint = 0x2,
    Erase = 0x4,
    Validate = 0x8,
    NoInternalPaint = 0x10,
    NoErase = 0x20,
    NoChildren = 0x40,
    AllChildren = 0x80,
    UpdateNow = 0x100,
    EraseNow = 0x200,
    Frame = 0x400,
    NoFrame = 0x800
}

[DllImport("user32.dll")]
static extern bool RedrawWindow(IntPtr hWnd, IntPtr lprcUpdate, IntPtr hrgnUpdate, RedrawWindowFlags flags);

[Flags]
private enum SetWindowPosFlags : uint
{
    SWP_ASYNCWINDOWPOS = 0x4000,
    SWP_DEFERERASE = 0x2000,
    SWP_DRAWFRAME = 0x0020,
    SWP_FRAMECHANGED = 0x0020,
    SWP_HIDEWINDOW = 0x0080,
    SWP_NOACTIVATE = 0x0010,
    SWP_NOCOPYBITS = 0x0100,
    SWP_NOMOVE = 0x0002,
    SWP_NOOWNERZORDER = 0x0200,
    SWP_NOREDRAW = 0x0008,
    SWP_NOREPOSITION = 0x0200,
    SWP_NOSENDCHANGING = 0x0400,
    SWP_NOSIZE = 0x0001,
    SWP_NOZORDER = 0x0004,
    SWP_SHOWWINDOW = 0x0040,
}

[DllImport("user32.dll", SetLastError = true)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);

private const int WM_PAINT = 0x000F;
private const int WM_SIZE = 0x0005;

[DllImport("user32.dll")]
public static extern Int64 SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-01-15 07:32:07

我使用Spy++来监视这个游戏窗口的消息,发现它似乎通过处理WM_MOVE重新绘制窗口内容。只需调用PostMessage(hwnd,WM_MOVE,0,0)就可以更新窗口(注意到:此解决方案仅适用于此游戏窗口)。

通常的方法是,您可以使用Spy++来监视窗口消息,并在行为不同时比较接收到的消息之间的差异。

另一种方法是向窗口中添加大小大小的边框样式:

代码语言:javascript
运行
复制
LONG_PTR stlyle = GetWindowLongPtr(hwnd, GWL_STYLE);
LONG_PTR ret = SetWindowLongPtr(hwnd, GWL_STYLE, stlyle | WS_SIZEBOX);

然后你可以手动拖动边框来改变大小,当然你仍然需要手动移动窗口来更新内容。

票数 2
EN

Stack Overflow用户

发布于 2021-01-13 18:00:48

也许在游戏的主窗口中有一个客户端窗口,当游戏的主窗口这样做时,它不会改变它的大小。毕竟,它根本不是为调整大小而设计的。您可以使用Visual附带的Spy++或类似工具评估应用程序的窗口等级。如果主窗口中有子窗口,则可以尝试向其发送类似的消息,使其调整大小。

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65706755

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档