WPFで開発したアプリがリモートデスクトップで正しく動作しなかったことってありませんか。
「リモートデスクトップなんで、ごめんなさいねー」でサポートしないことがあったりなかったり。
一昔前の私はあったり。
しかしながらコロナ禍の今、そうもいっていられなくなりました。
そこで、出会ったリモートデスクトップ問題とその解決についてまとめます。
※今回はグラフィックス関係についてのみ扱います。
検証環境の用意
リモートデスクトップ接続中ではハードウェア描画に制限がかかります。
ローカル開発環境にハードウェア描画に制限をかけ検証環境をデバッグのしやすい環境を手元に作ります。
グラフィックスの問題の追及はこれで大方OKでしょう。
以下のドキュメントを参考にレジストリキー HKEY_CURRENT_USER\SOFTWARE\Microsoft\Avalon.Graphics\DisableHWAcceleration
に値を設定します。
https://docs.microsoft.com/dotnet/framework/wpf/graphics-multimedia/graphics-rendering-registry-settings
この環境下であらかた確認対処します。
あらかた確認対処後、実際にリモートデスクトップ接続を作り試します。
パソコンを複数台用意する必要がありますが、そうもいかない方はスマホアプリの利用を検討してください。
- Remote Desktop 8 (Android)
- Microsoft リモート デスクトップ (iOS)
(症状1)D3DImage が描画されない
原因はドキュメント書いてありました。
リモートデスクトップ接続中やソフトウェア描画中は表示なしとあります。
https://docs.microsoft.com/archive/blogs/wpf3d/d3dimage-and-software-rendering
対処法は オーバーライドが2つある SetBuckBuffer メソッドの内enableSoftwareFallback
が指定できる方を使い、ソフトウェア描画時であれば true
、ハードウェア描画時であれば false
を指定します。
このソフトウェア描画時かどうか情報は毎度判断するのは高くつくので、然るべきタイミングで作ってキャッシュするのがよいでしょう。
以下に実装の断片を用意しました。
private static bool MakeIsSoftwareRenderingMode()
{
// Rendering tier
var renderingTier = RenderCapability.Tier >> 16;
if (renderingTier == 0)
return true;
// Remote desktop
if (GetSystemMetrics(SM_REMOTESESSION) != 0)
return true;
// DisableHWAcceleration
try
{
var subKey = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Avalon.Graphics");
if (subKey != null)
{
var d = (int) subKey.GetValue("DisableHWAcceleration");
if (d != 0)
return true;
}
}
catch
{
// ignored
}
return false;
}
private const int SM_REMOTESESSION = 0x1000;
[DllImport("user32")]
private static extern int GetSystemMetrics(int index);
// --------------------------------------------------------
// 初期化時
SystemEvents.SessionSwitch += SystemEventsOnSessionSwitch;
isSoftwareRenderingMode = MakeIsSoftwareRenderingMode():
// 後処理時
SystemEvents.SessionSwitch -= SystemEventsOnSessionSwitch
private void SystemEventsOnSessionSwitch(object sender, SessionSwitchEventArgs e)
{
// セッションのロックが解除されたとき、リモート接続されたとき
if (e.Reason == SessionSwitchReason.SessionUnlock)
isSoftwareRenderingMode = MakeIsSoftwareRenderingMode():
}
bool isSoftwareRenderingMode;
(症状2)ShaderEffect が動作しない
原因はドキュメント書いてありました。
ShaderEffect
PS 2.0 シェーダーは、ソフトウェアでレンダリングするときに実行されます。
ただし、PS 3.0 がシステムのハードウェアでサポートされている場合でも、
ソフトウェアのレンダリング中に PS 3.0 シェーダーは実行されません。
とあります。
私の場合の原因はPS3.0の利用していたから、対処法はPS2.0で再実装しました。そもそもPS2.0では命令数が足りなくてPS3.0にしたのですが、これを何とかなる範囲で何とかしました。
一般的には PS2.0内で収まるのであれば、PS2.0でシェーダーをコンパイルしなおします。