Window, welches über ein Spiel im Vollbild-Modus gelegt ist, interagierbar machen

  • .NET (FX) 4.5–4.8
  • WPF

Es gibt 2 Antworten in diesem Thema. Der letzte Beitrag () ist von ClonkAndre.

    Window, welches über ein Spiel im Vollbild-Modus gelegt ist, interagierbar machen

    Hallo liebe Community!

    Folgenes Szenario: Ich habe mittles WPF ein Overlay erstellt, welches sich über das Spiel GTA IV legt. Das ganze funktioniert soweit auch wunderbar! Nur ich habe da ein Problem bei welchem ich Unterstützung brauche, undzwar kann ich mit keinem Control auf diesem Window interagieren, da GTA IV den Cursor versteckt und stattdessen seinen eigenen Cursor im Spiel malt.

    Nun hatte ich folgende Idee: Wenn man die Windows Taste drückt um das Startmenü aufzurufen, dann kommt der standard Windows Cursor zurück mit dem ich wieder mit Controls auf diesem Window interagieren kann. Doch sobald ich wieder auf GTA IV klicke um es in den Vordergrund zubringen verschwindet dieser wieder.

    Meine Frage: Kann ich das WPF Window vielleicht mittels pinvoke manuell den Fokus geben so das Windows wieder den standard Cursor wiederherstellt und so das GTA IV kein Maus/Tastertur Input mehr erhält?
    SetFocus habe ich schon getestet, leider ohne Erfolg.


    Das ganze wird übrigens nicht zum cheaten oder ähnlichen benutzt sondern soll ein cooles neues Feature für version 1.6 von meinem GTA IV Launcher werden.

    Falls sich jemand fragt warum ich nicht gleich mit DirectX ein Overlay erstelle welches sich in GTA IV injected: Ich würde super gerne alle Features von WPF verwenden also ist dies keine Option für mich.


    Wie es im Moment aussieht:


    Diese Klasse verwende ich um das WPF Window über GTA IV anzeigen zu können
    Spoiler anzeigen

    C#-Quellcode

    1. public class WindowSinker {
    2. #region DLLImports
    3. [DllImport("user32.dll")]
    4. static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
    5. [DllImport("user32.dll")]
    6. static extern IntPtr SetFocus(IntPtr hWnd);
    7. [DllImport("user32.dll")]
    8. static extern IntPtr DeferWindowPos(IntPtr hWinPosInfo, IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
    9. [DllImport("user32.dll")]
    10. static extern IntPtr BeginDeferWindowPos(int nNumWindows);
    11. [DllImport("user32.dll")]
    12. static extern bool EndDeferWindowPos(IntPtr hWinPosInfo);
    13. #endregion
    14. #region Variables
    15. private static readonly IntPtr HWND_TOP = new IntPtr(0);
    16. private const UInt32 SWP_NOSIZE = 0x0001;
    17. private const UInt32 SWP_NOMOVE = 0x0002;
    18. private const UInt32 SWP_NOACTIVATE = 0x0010;
    19. private const UInt32 SWP_NOZORDER = 0x0004;
    20. private const int WM_ACTIVATEAPP = 0x001C;
    21. private const int WM_ACTIVATE = 0x0006;
    22. private const int WM_SETFOCUS = 0x0007;
    23. private const int WM_WINDOWPOSCHANGING = 0x0046;
    24. private WindowInteropHelper windowInteropHelper;
    25. private Window targetWindow;
    26. #endregion
    27. #region Events
    28. private void Window_Loaded(object sender, RoutedEventArgs e)
    29. {
    30. if (windowInteropHelper == null)
    31. return;
    32. if (windowInteropHelper.Handle == IntPtr.Zero)
    33. return;
    34. SetWindowPos(windowInteropHelper.Handle, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
    35. HwndSource Source = HwndSource.FromHwnd(windowInteropHelper.Handle);
    36. Source.AddHook(new HwndSourceHook(WndProc));
    37. }
    38. private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    39. {
    40. if (windowInteropHelper == null)
    41. return;
    42. if (windowInteropHelper.Handle == IntPtr.Zero)
    43. return;
    44. HwndSource Source = HwndSource.FromHwnd(windowInteropHelper.Handle);
    45. Source.RemoveHook(new HwndSourceHook(WndProc));
    46. }
    47. #endregion
    48. #region Constructor
    49. public WindowSinker(Window window)
    50. {
    51. targetWindow = window;
    52. windowInteropHelper = new WindowInteropHelper(targetWindow);
    53. }
    54. #endregion
    55. #region Methods
    56. public void Sink()
    57. {
    58. targetWindow.Loaded += Window_Loaded;
    59. targetWindow.Closing += Window_Closing;
    60. }
    61. public void Unsink()
    62. {
    63. targetWindow.Loaded -= Window_Loaded;
    64. targetWindow.Closing -= Window_Closing;
    65. }
    66. #endregion
    67. #region Functions
    68. public IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    69. {
    70. switch (msg) {
    71. case WM_SETFOCUS:
    72. hWnd = windowInteropHelper.Handle;
    73. SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
    74. handled = true;
    75. break;
    76. }
    77. return IntPtr.Zero;
    78. }
    79. #endregion
    80. }



    Dies sind die XAML Settings für dieses Window falls notwending

    HTML-Quellcode

    1. WindowStartupLocation="CenterScreen"
    2. WindowStyle="None"
    3. WindowState="Maximized"
    4. Topmost="True"
    5. AllowsTransparency="True"
    6. Background="Transparent"
    7. Loaded="Window_Loaded"
    8. Title="GTA IV Launcher Overlay" Width="1366" Height="768">



    Falls weitere Fragen bestehen einfach ein Kommentar schreiben!
    Ich würde mich sehr über Vorschläge freuen!
    Wenn ich dir auf irgendeiner Art und Weise helfen konnte, drück doch bitte den "Hilfreich" Button :thumbup:

    Für VB.NET Entwickler: Option Strict On nicht vergessen!
    Hey, ich glaube so wie du das vorhast, wird das entweder immer wieder Probleme geben, oder erst gar nicht richtig funktionieren. Der Weg mit einer Dll-Injection wäre der bessere. Wofür brauchst du denn die WPF Funktionalität bei einem einem Spiele Overlay? Da braucht man doch nicht viel, nur ein paar klickbare Rechtecke mit einem Text drauf. Alternartiv zum selbst rendern bei einer Dll-Injection, nimm ImGui, damit kannst du leicht eine GUI basteln.


    Was du noch versuchen könntest, aber wird vermutlich auch damit scheitern, denn entweder hat das Spiel-Fenster den Focus, oder deins(bzw. dein Control).
    Das Spiel-Fenster ist ein normales Fenster, das halt nur anders gerendert wird, da es nicht als MDI eingestellt ist, kannst du versuchen einfach ein Control anstatt eines ganzen Fensters zu nehmen, und dieses mit SetParent als Child des Spiele-Fenster setzen. Aber ob das geht ist pure spekulation.

    BitBrösel schrieb:

    Wofür brauchst du denn die WPF Funktionalität bei einem einem Spiele Overlay?

    Ich möchte das Overlay gerne mit schönen Animationen schmücken und das geht mit WPF sehr einfach. Und ich habe halt den Vorteil das ich alle bereits erstelle Controls in diesem Overlay wiederverwenden kann.
    Außerdem soll das Overlay die aktuellsten Twitter Tweets anzeigen via der Twitter API die ich bereits integriert habe und der User soll in der Lage sein seine eigene Plugins zu schreiben die später im Overlay angezeigt werden und das geht halt alles sehr einfach mit C#. Das Overlay kann halt direkt mit meinem Launcher kommunizieren was bei C++ (denke ich) nicht der Fall wäre.

    BitBrösel schrieb:

    kannst du versuchen einfach ein Control anstatt eines ganzen Fensters zu nehmen, und dieses mit SetParent als Child des Spiele-Fenster setzen. Aber ob das geht ist pure spekulation.

    Interessante Idee, hat aber leider nicht funktioniert.


    Ich konnte es aber letztendlich mit SetActiveWindow und SetForegroundWindow lösen :thumbup:
    Wenn ich dir auf irgendeiner Art und Weise helfen konnte, drück doch bitte den "Hilfreich" Button :thumbup:

    Für VB.NET Entwickler: Option Strict On nicht vergessen!