6

サンプル プログラムに全画面表示のサポートを追加しているときに、このちょっと厄介な動作に出くわしました。

全画面ウィンドウの作成は機能しますが、全画面ウィンドウを含む出力で (別のアプリケーションから) ウィンドウを移動するとすぐに、自動的にウィンドウ表示に戻ります。

この動作を防ぐ方法はありますか (全画面ウィンドウがウィンドウ表示に戻らないようにするため)。

参考までに、これは小さなスタンドアロンの例です (問題は簡単に再現できます)。

また、それが役立つ場合、私は Windows 8.1 で実行しています。

私はすでに WindowAssociationFlags と SwapChainFlags を変更しようとしましたが、Discard の代わりに FlipSequential を使用するのと同じように、どちらも成功しませんでした

SharpDX.DXGI.Factory2 factory = new SharpDX.DXGI.Factory2();
SharpDX.DXGI.Adapter adapter = factory.GetAdapter(0);

var renderForm1 = new RenderForm("Form 1");
factory.MakeWindowAssociation(renderForm1.Handle, SharpDX.DXGI.WindowAssociationFlags.IgnoreAll);

Device device = new Device(adapter, DeviceCreationFlags.BgraSupport);

SharpDX.DXGI.SwapChainDescription sd = new SharpDX.DXGI.SwapChainDescription()
{
    BufferCount = 2,
    ModeDescription = new SharpDX.DXGI.ModeDescription(0, 0, new SharpDX.DXGI.Rational(50, 1),  SharpDX.DXGI.Format.R8G8B8A8_UNorm),
    IsWindowed = true,
    OutputHandle = renderForm1.Handle,
    SampleDescription = new SharpDX.DXGI.SampleDescription(1,0),
    SwapEffect = SharpDX.DXGI.SwapEffect.Discard,
    Usage = SharpDX.DXGI.Usage.RenderTargetOutput,
    Flags = SharpDX.DXGI.SwapChainFlags.None
};

var swapChain1 = new SharpDX.DXGI.SwapChain(factory, device, sd);

renderForm1.Left = 1922; //Just hardcoded here to move window to second screen
renderForm1.Width = 1920;
renderForm1.Height = 1080;
renderForm1.FormBorderStyle = FormBorderStyle.None;

swapChain1.SetFullscreenState(true, null);
swapChain1.ResizeBuffers(2, 1920, 1080, SharpDX.DXGI.Format.R8G8B8A8_UNorm, SharpDX.DXGI.SwapChainFlags.AllowModeSwitch);

var resource = Texture2D.FromSwapChain<Texture2D>(swapChain1, 0);
var renderView = new RenderTargetView(device, resource);

RenderLoop.Run(renderForm1, () =>
{
    device.ImmediateContext.ClearRenderTargetView(renderView, new SharpDX.Color4(1, 0, 0, 1));
    swapChain1.Present(1, SharpDX.DXGI.PresentFlags.None);
});

編集: C++ サンプルも試しました (Microsoft から DirectX11 の基本的なチュートリアルを取得し、フルスクリーン スイッチを追加しました)。これは同じ動作につながるため、SharpDX 固有の問題ではありません。

メッセージ ループを調べたところ、これが発生すると、最初のフルスクリーン モードがウィンドウ モードに戻り、WM_DISPLAYCHANGE メッセージが表示されます)。

4

3 に答える 3

3

いくつかの試行と試行の後、ここに私が使用したさまざまな回避策があります。どれも理想的ではありませんが、モードを変更するよりも何らかの方法で優れています。

1/全画面ウィンドウの中央にカーソルを置き、キーボード ショートカットを使用して再び制御できるようにします。パーツの実行中は実際には何もできないため、これは理想的ではありませんが、少なくとも偶発的な「災害クリック」を防ぐことができます。キーボードの操作も妨げません。

2/共有テクスチャを持つ DX9 レンダラーを使用します。DX9 Swapchain は親ウィンドウをデスクトップに設定できるため、別のウィンドウに移動してもフォーカスが失われません。フォーカスされたウィンドウを一番上に置くと、移動中に境界線がほとんど表示されなくなりますが、すべてを失うよりは許容範囲内です。将来の証明ではありませんが、推測はしばらくの間実際のままです.

3/Windows 7 のままにして、DWM サービスを無効にします。

Windows 8 ではもう動作しませんが、私の使用例では、私が働いているほとんどのメディア企業がまだ Windows 7 を使用しているため、少なくとも 5 ~ 10 年間は有効なソリューションであり続けます。

4/DX11 ウィンドウをフォアグラウンドに強制する

基本的にSetForegroundWindowを継続的に呼び出して、別のウィンドウがフォーカスを取得しないようにします。

5/プレゼンテーション レベルでのモード切り替えを防止します。

私のアプリケーションでは、プレゼンテーションが発生したときにアクセスできるようになったので、次のルーチンを使用します (Present を呼び出す前に)。

-フォアグラウンド ウィンドウ ハンドルを取得します ( GetForegroundWindowを使用)。フォアグラウンド ハンドルがフルスクリーン ウィンドウの場合は、いつものように Present を呼び出します。

Foreground ハンドルがフルスクリーン ウィンドウでない場合は、次の手順を実行します。可視性のチェックは不要であることに注意してください。重ねて表示されていないウィンドウでも全画面表示が失われるからです。(まじでこれはまずい…)

-フォアグラウンドウィンドウがモニターとオーバーラップしているかどうかを確認します。GetWindowRect を呼び出して境界を取得し、モニターの位置との交差を実行します。

または、DXGI_PRESENT_TEST フラグを使用して、スワップチェーンで Present を呼び出します。ウィンドウが重なっている場合、Present 呼び出しは DXGI_STATUS_OCCLUDED を返します。

ウィンドウがオーバーラップする場合は、非表示にするか、別のモニターに移動します (オーバーラップしないように任意の場所に移動します) 。ShowWindowSetWindowPosは、このタスクに最適です。

閉塞状態が返されなくなるまで、Test present 呼び出しをループで繰り返します (Windows がメッセージをすぐに処理していない可能性があるため、これは重要です)。occluded フラグがなくなったら、Present を通常どおり呼び出します。

于 2016-09-21T10:12:44.307 に答える
0

プロセスがフォーカスを失ったときに DXGI が自動的にフルスクリーン モードを終了しないようにする方法があります。基本的に、DXGI は GetForegroundWindow() を呼び出し、返されたウィンドウが自分のものかどうかを確認します。そうでない場合は、フルスクリーン モードをオフにします。したがって、この関数を独自の置換にフック/リダイレクトすると、(フォーカスがあるかどうかに関係なく) 常にウィンドウが返されます。これで仕事が完了します。

これを行う簡単なコードを次に示します。これは 64 ビット モード用であり、実際の関数を呼び出す必要がないことを前提としているため、代わりの関数へのジャンプ命令で先頭を上書きするだけです。

HWND WINAPI get_our_window()
{
    return our_window;
}
void disable_automatic_leaving_fullscreen_on_lost_focus()
{
    // get the address of GetForegroundWindow
    char *p = (char *)GetProcAddress(GetModuleHandleA("user32.dll"), "GetForegroundWindow");

    // make the function code writable
    DWORD old;
    VirtualProtect(p, 12, PAGE_EXECUTE_WRITECOPY, &old);

    // overwrite the function start:
    // mov rax, <address_of_GetOurWindow>
    p[0] = 0x48, p[1] = 0xB8, *(void **)(p + 2) = (void *)get_our_window;
    // jmp rax
    p[10] = 0xFF, p[11] = 0xE0;
}

このコードはデモンストレーション専用です。真の関数を呼び出す機能を保持する必要がある場合は、別のより複雑な方法でフックする必要がありますが、これは別の主題です

于 2020-02-05T14:00:11.343 に答える