我试图写一个简单的程序,调整一个在800x600年代创建的没有可调整大小的窗体边框的桌面显示器的旧游戏。到目前为止,我已经能够成功地使用MoveWindow
或SetWindowPos
更改窗口大小,但问题是客户端区域(即实际游戏)没有更新;如果窗口大小增加,则额外的区域被填充黑色,如果图像被缩小,则会被裁剪。但是,我发现资源管理器中的“显示桌面”按钮将强制客户区域正确更新。因此,我的问题是,当资源管理器显示桌面时,如何复制资源管理器所做的工作?
我已经尝试过RedrawWindow
、UpdateWindow
,甚至直接发送WM_PAINT
消息,但是似乎没有什么效果。据spy++称,游戏窗口似乎也没有子窗口。
更新
我刚刚尝试了最小化,然后重新显示窗口,它也确实修复了客户端区域。这可能解释了为什么显示桌面以前工作过。虽然这是一个潜在的解决方案,但理想情况下,我希望能够调整原地的大小,而不是快速最小化,然后重新显示。
UPDATE2
我已经按照要求包括了用于测试的代码,以及上面描述的症状的屏幕截图。
测试代码:
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定义:
[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);
发布于 2021-01-15 07:32:07
我使用Spy++来监视这个游戏窗口的消息,发现它似乎通过处理WM_MOVE
重新绘制窗口内容。只需调用PostMessage(hwnd,WM_MOVE,0,0)
就可以更新窗口(注意到:此解决方案仅适用于此游戏窗口)。
通常的方法是,您可以使用Spy++来监视窗口消息,并在行为不同时比较接收到的消息之间的差异。
另一种方法是向窗口中添加大小大小的边框样式:
LONG_PTR stlyle = GetWindowLongPtr(hwnd, GWL_STYLE);
LONG_PTR ret = SetWindowLongPtr(hwnd, GWL_STYLE, stlyle | WS_SIZEBOX);
然后你可以手动拖动边框来改变大小,当然你仍然需要手动移动窗口来更新内容。
发布于 2021-01-13 18:00:48
也许在游戏的主窗口中有一个客户端窗口,当游戏的主窗口这样做时,它不会改变它的大小。毕竟,它根本不是为调整大小而设计的。您可以使用Visual附带的Spy++或类似工具评估应用程序的窗口等级。如果主窗口中有子窗口,则可以尝试向其发送类似的消息,使其调整大小。
https://stackoverflow.com/questions/65706755
复制相似问题