短篇故事
游戏是全屏启动的,但它是一个全屏的 directX 窗口。游戏支持窗口化,但我必须手动操作。我在 Delphi 中制作了一个程序,可以拉伸/调整任何窗口的大小以适应屏幕(使用系统范围的热键),没有边框和标题,因此它看起来像全屏,但不会触发直接硬件访问。这很重要,因为我使用的 DisplayLink 适配器不支持直接硬件访问所使用的技巧,但希望全屏播放而没有丑陋的边框。我可以调整除全屏 DirectX 窗口之外的任何窗口的大小,我必须手动将全屏模式更改为窗口化,这就是我想要自动化的。
长篇大论(如果您想知道问题,请向下滚动)
我有一个 DisplayLink 适配器,它的电视屏幕距离我的电脑 5 米。我想用它来运行游戏,这样我就可以在沙发上玩游戏了。然而,DisplayLink 驱动程序无法全屏播放大多数游戏,因为大多数游戏在全屏时会绕过桌面窗口管理器 (DWM) 直接访问图形硬件。
这是 DisplayLink 的常见/已知问题。在窗口模式下,适配器性能非常好,所以我想在 Delphi 中编写一个小程序,将窗口化的 Directx 屏幕最大化到全屏,而不是通过最大化它来全屏,而是将窗口拉伸到全屏。
我制作的程序运行得很好,但是只有当directx屏幕已经窗口化时(游戏开始全屏,所以我必须单击窗口图标使其窗口化)。该游戏已经有一个选项可以在启动时使其窗口化,但固定分辨率的可能性较小。我想在全屏启动时自动化这个过程。我想将directx屏幕更改为窗口化,然后将其调整大小/拉伸到全屏而不最大化它。
程序如何运作
该程序定义了一个系统范围的键盘热键。当按下热键时,程序getForeGroundWindow()
通过将其拉伸到全屏来最大化任何活动的前台窗口(windows API)并使其成为无边框窗口,因此它看起来像全屏。这使您还可以在您喜欢的任何屏幕上运行游戏,而不仅仅是在系统的主屏幕上。再次按下热键时,窗口将返回到之前的状态(切换)。在应用“补丁”之前,它还会检查窗口的类型,因此它不能用于不可调整大小的窗口。
问题
我知道必须将窗口的句柄拉伸/调整为全屏。当它是全屏 DirectX 窗口时,除非它是窗口化的,否则我无法做任何事情。如何通过向此窗口句柄 ( sendMessage()
) 发送消息将其状态更改为窗口化。这可能吗?
一些代码(让您了解幕后发生的事情)
function TWinSpread.setWindowStyleBounds( h : hWnd; lStyle : LongInt = 0; pR : PRect = nil ) : LongInt;
var
bRestore : Boolean;
r : TRect;
pMouse : TPoint;
rStyle : TStyleStruct;
begin
Result:=0;
if( h <= 0 ) then
Exit;
bRestore:=( lStyle > 0 );
if( NOT bRestore ) then
begin
lStyle:=getWindowLong( h, GWL_STYLE );
Result:=lStyle;
lStyle:=lStyle and not WS_BORDER and not WS_SIZEBOX and not WS_DLGFRAME;
if( Assigned( pR )) then
begin
r:=pR^;
end
else begin
getWindowRect( h, r );
r:=getDisplayRect( getDisplayNumFromPoint( Point( r.left+2, r.top+2 ) ) );
end;
end
else begin
Result:=lStyle;
end;
rStyle.styleOld:=Result;
rStyle.styleNew:=lStyle;
if( Result = lStyle ) then
begin
rStyle.styleOld:=getWindowLong( h, GWL_STYLE );
end;
sendMessage( h, WM_ENTERSIZEMOVE, 0, 0 );
__restoreWindow( h );
setWindowLong( h, GWL_STYLE, lStyle );
if( NOT bRestore ) then
begin
setWindowPos( h, HWND_TOP, r.left,r.top,r.right-r.left,r.bottom-r.top, SWP_FRAMECHANGED and WS_SIZEBOX );
moveWindow( h, r.left,r.top,r.right-r.left,r.bottom-r.top, TRUE );
sendMessage( h, WM_SIZE, SIZE_RESTORED, makeLong( r.right-r.left,r.bottom-r.top ));
end
else begin
// updateWindowFrame( h );
setWindowPos( h, 0, 0,0,0,0, SWP_FRAMECHANGED or SWP_NOMOVE or SWP_NOSIZE or SWP_NOZORDER or SWP_NOOWNERZORDER );
getWindowRect( h, r );
end;
sendMessage( h, WM_EXITSIZEMOVE, 0, 0 );
sendMessage( h, WM_STYLECHANGED, GWL_STYLE, longInt( Pointer( @rStyle )));
activateWindow( h );
windows.setFocus( h );
if( __mousePresent() ) then
begin
pMouse:=__getMousePos();
// Simulate click to focus window
__setMousePos( Point( r.left+2, r.top+2 ));
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
// restore mouse
__setMousePos( pMouse );
end;
end;
procedure TWinSpread.shHotKeyHotKey(Sender: TObject; Index: Integer);
var
h : hWnd;
sHandle : string;
lStyle : LongInt;
bNoBorder : Boolean;
begin
h:=getForeGroundWindow();
if( h <= 0 ) then
exit;
if( h = handle ) then
begin
showMessage( 'It works!' );
exit;
end;
sHandle:=ItoA( h );
if( scWinStyles.Count > 0 ) then
begin
lStyle:=AtoI( scWinStyles.list.Values[sHandle] );
if( lStyle > 0 ) then
begin
scWinStyles.list.delete( scWinStyles.list.IndexOfName(sHandle));
setWindowStyleBounds( h, lStyle );
Exit;
end;
end;
bNoBorder:=windowIsBorderless( h );
if( isMaximized( h ) or bNoBorder ) then
begin
// does not work for ActiveX fullscreen window :-(
//if( bNoBorder ) then
// setWindowSizeable( h, TRUE );
__maximizeWindow( h );
__restoreWindow( h );
showWindow( h, SW_SHOWNORMAL );
end;
if( windowIsSizeable( h ) ) then
begin
lStyle:=setWindowStyleBounds( h );
scWinStyles.list.Values[sHandle]:=ItoA( lStyle );
end
else Windows.Beep( 600, 200 );
end;
一些截图
注意:图中有错字,ActiveX 必须是 DirectX ;-)
有关 DisplayLink 问题的更多信息:http: //support.displaylink.com/knowledgebase/articles/543922-games-do-not-work-on-windows-with-displaylink-soft