_judith: Tastatur und Maus Kontrollieren

Hallo,

ich möchte mit VC++ und DirectInput eine Anwendung erstellen die es mir erlaubt das der User mein Programm erst in den Hintergrund/beenden (schieben) kann wenn ich es will. Das dies möglich ist dessen bin ich mir sicher weil ich es selbst schon öfters gesehen habe z. B. bei diversen Installationen.

Ist das möglich alle Tastatureingaben bzw. Mauseingaben abzufangen so das Windows davon nichts mitbekommt?

_judith

PS: Ich habe ein Fenster

  1. Hallo Judith,

    ich möchte mit VC++ und DirectInput eine Anwendung erstellen die es mir erlaubt das der User mein Programm erst in den Hintergrund/beenden (schieben) kann wenn ich es will.

    okay, wir reden also von einer "selbstgestrickten" Anwendung unter Windows, ja?

    Das dies möglich ist dessen bin ich mir sicher weil ich es selbst schon öfters gesehen habe z. B. bei diversen Installationen.

    Es ist eingeschränkt möglich, ja. Und es hat eigentlich nichts damit zu tun, dass du Tastatur- oder Mausereignisse kontrollieren musst, sondern bloß dein Anwendungsfenster "modal" machen. Modal heißt in diesem Fall, dass der Rest drumherum erst wieder Eingaben bekommt, wenn das modale Fenster geschlossen ist.
    Windows kennt zwei Stufen von modalen Fenstern: Anwendungsmodal und systemmodal. Ein modales Fenster auf Anwendungsebene "blockiert" zwar den Rest der Anwendung, solange es geöffnet ist (das sind die meisten Dialogfenster), man kann aber zu anderen Anwendungen wechseln und mit denen weiter arbeiten. Ein systemmodales Fenster erlaubt auch das Wechseln zu einer anderen offenen Anwendung nicht mehr.

    Ist das möglich alle Tastatureingaben bzw. Mauseingaben abzufangen so das Windows davon nichts mitbekommt?

    Wie gesagt: Mit den Tastatur- und Mausereignissen brauchst du dich gar nicht abzugeben. Das macht Windows für dich, wenn du das Fenster mit den entsprechenden Flags erzeugst.

    Aber mich interessiert brennend, wozu du so eine radikale Maßnahme ergreifen möchtest. Systemmodale Fenster werden üblicherweise nur bei schwerwiegenden Systemfehlern verwendet, bei denen auch die Interaktion mit anderen Fenstern kritisch sein kann, solange die Fehlersituation nicht geklärt ist.
    Die Installationsprogramme, die du als Beispiel genannt hast, fallen alle in die Kategorie der anwendungsmodalen Fenster - du kannst während der laufenden Installation jederzeit zu anderen offenen Anwendungen wechseln.

    So long,
     Martin

    PS: Ich habe ein Fenster

    Ich auch. Sogar mehrere. ;-)
    Oder meintest du "Ich habe Windows"? Dann hätte meine Antwort lauten müssen:
    Ich auch. Sogar mehrere. ;-)

    --
    Wer im Glashaus sitzt, sollte Spaß am Fensterputzen haben.
    1. Aber mich interessiert brennend, wozu du so eine radikale Maßnahme ergreifen möchtest. Systemmodale Fenster werden üblicherweise nur bei schwerwiegenden Systemfehlern verwendet, bei denen auch die Interaktion mit anderen Fenstern kritisch sein kann, solange die Fehlersituation nicht geklärt ist.
      Die Installationsprogramme, die du als Beispiel genannt hast, fallen alle in die Kategorie der anwendungsmodalen Fenster - du kannst während der laufenden Installation jederzeit zu anderen offenen Anwendungen wechseln.

      Wie bekomme ich dann mein Fenster modal oder wie bekomme ich es so das es so lange im Vordergrund bleibt bis ich es beende?

      Kann man seiner eigenen Anwendung eine Nachricht schicken das es nun den Fokus erhalten hat und anderen Anwendung das sie ihn verloren haben? Wäre das ein Ansatz?

        
      ///////////////////////////////////////////////////////////////  
       ShowCursor(FALSE);  
       Hinstance = hinstance;  
        
       // Get the Systemresolution  
       ScreenWidth  = GetSystemMetrics(SM_CXSCREEN);  
       ScreenHeight = GetSystemMetrics(SM_CYSCREEN);  
        
       WNDCLASSEX wndclass;  
        
       // Fill the WNDCLASSES structure  
       wndclass.cbSize      = sizeof(WNDCLASSEX);  
       wndclass.hIcon       = 0;  
       wndclass.hIconSm     = 0;  
       wndclass.hCursor      = 0;  
       wndclass.hInstance     = Hinstance;  
       wndclass.lpfnWndProc   = Handler;  
       wndclass.hbrBackground  = (HBRUSH)GetStockObject(BLACK_BRUSH);  
       wndclass.style       =  CS_OWNDC | CS_NOCLOSE | CS_HREDRAW | CS_VREDRAW;  
       wndclass.lpszClassName  = "WINDOW_CLASS";  
       wndclass.lpszMenuName  = 0;  
       wndclass.cbClsExtra    = 0;  
       wndclass.cbWndExtra   = 0;  
        
       // Register the Window  
       if(!RegisterClassEx(&wndclass))  {  
        MessageBox(0, "Die Fensterklasse konnte nicht registriert werden.", "Fehler", MB_OK | MB_ICONERROR);  
        return EXIT_FAILURE;  
       }  
        
       // Create the Window  
       if((MainWindowHandle = CreateWindowEx(0,"JV_WINDOW_CLASS", "HOAX", WS_VISIBLE | WS_POPUP,  
        0, 0, ScreenWidth, ScreenHeight, 0, 0, Hinstance, 0)) == 0) {  
        MessageBox(0, "Das Fenster konnte nicht erstellt werden.", "Fehler", MB_OK | MB_ICONERROR);  
        return EXIT_FAILURE;  
       }  
      ///////////////////////////////////////////////////////////////  
      
      

      Was muss ich hier ändern?
      Meine DirectInput initialisierung sieht momentan so aus:

      ///////////////////////////////////////////////////////////////  
      void InitDirectInput(void)  
      {  
       // DirectInput-Objekt erzeugen  
          if(FAILED(DirectInput8Create(Hinstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&g_pDI, NULL))) {  
       MessageBox(0, "DirectInput-Objekt konnte nicht erzeugt werden.", "Fehler", MB_OK | MB_ICONERROR);  
        PostQuitMessage(0);  
        return;  
       }  
        
          // Keyboard initialisieren  
       if(InitKeyboard() == S_FALSE) {  
       MessageBox(0, "Tastatur konnte nicht initialisiert werden.", "Fehler", MB_OK  | MB_ICONERROR);  
        PostQuitMessage(0);  
        return;  
       }  
      }  
        
      HRESULT InitKeyboard(void)  
      {  
       // Keyboard-Objekt erzeugen  
          g_pDI->CreateDevice(GUID_SysKeyboard, &g_pKeyboard, NULL);  
        
          if(NULL == g_pKeyboard) {  
              MessageBox(0, "Keine Tastatur gefunden.", "Fehler", MB_ICONERROR | MB_OK);  
          PostQuitMessage(0);  
              return S_FALSE;  
          }  
        
          // Datenformat festlegen  
          g_pKeyboard->SetDataFormat(&c_dfDIKeyboard);  
        
          // festlegen, wie DirectInput Zugriff auf das Gerät erhält  
       g_pKeyboard->SetCooperativeLevel(MainWindowHandle, DISCL_FOREGROUND | DISCL_EXCLUSIVE);  
        
       // akquirieren des Gerätes  
          g_pKeyboard->Acquire();  
        
       return S_OK;  
      }  
      ///////////////////////////////////////////////////////////////  
      
      
      1. Hallo,

        Die Installationsprogramme, die du als Beispiel genannt hast, fallen alle in die Kategorie der anwendungsmodalen Fenster - du kannst während der laufenden Installation jederzeit zu anderen offenen Anwendungen wechseln.
        Wie bekomme ich dann mein Fenster modal oder wie bekomme ich es so das es so lange im Vordergrund bleibt bis ich es beende?

        laut Win32 API-Referenz kannst du beim Erzeugen des Fensters das Flag WS_EX_TOPMOST im ersten Parameter dwExStyle von CreateWindowEx() angeben. Das hat zur Folge, dass das Fenster immer zuoberst bleibt, ähnlich wie der Windows-Taskmanager mit der Option "Always on top".
        Allerdings kannst du dann immer noch andere Fenster aktivieren; mit diesem Flag steuerst du also nur das Verhalten der Fenster_anzeige_, nicht die Verarbeitung von Eingaben.

        "Echte" systemmodale Fenster, wie ich sie von früher in Erinnerung habe, werden anscheinend von aktuellen Windows-Versionen nicht mehr unterstützt.

        Kann man seiner eigenen Anwendung eine Nachricht schicken das es nun den Fokus erhalten hat und anderen Anwendung das sie ihn verloren haben?

        Das macht Windows sogar von sich aus. :-)
        Die Fensterprozedur deiner Anwendung bekommt automatisch von Windows die Message WM_KILLFOCUS übermittelt, _bevor_ Windows ihm den Focus entziehen will.

        Wäre das ein Ansatz?

        Ja, gute Idee. Dein Programm könnte auf die WM_KILLFOCUS-Message reagieren und entweder über den Rückgabewert die weitere Bearbeitung des Ereignisses unterbinden (weiß aber nicht, ob das funktioniert), oder aber rotzfrech SetFocus() mit dem eigenen Fenster-Handle aufrufen und sich so automatisch selbst wieder aktivieren (Holzhammermethode).

        if((MainWindowHandle = CreateWindowEx(0,"JV_WINDOW_CLASS", "HOAX", WS_VISIBLE | WS_POPUP,
          0, 0, ScreenWidth, ScreenHeight, 0, 0, Hinstance, 0)) == 0)

        Dumme Frage: Wenn du sowieso ein Fenster erstellst, das den ganzen Bildschirm ausfüllt - warum dann nicht gleich als maximiertes Fenster (WS_MAXIMIZE)? Dann bräuchtest du vorher nicht einmal die Bildschirmgröße abzufragen.

        Meine DirectInput initialisierung sieht momentan so aus:

        An der Stelle muss ich passen, da ich mich mit den DirectX-Techniken bisher noch nicht befasst habe.

        Schönes Wochenende,
         Martin

        --
        Niemand lebt allein von seinen Träumen.
        Aber wer träumt, lebt noch.
        1. Ja, gute Idee. Dein Programm könnte auf die WM_KILLFOCUS-Message reagieren und entweder über den Rückgabewert die weitere Bearbeitung des Ereignisses unterbinden (weiß aber nicht, ob das funktioniert), oder aber rotzfrech SetFocus() mit dem eigenen Fenster-Handle aufrufen und sich so automatisch selbst wieder aktivieren (Holzhammermethode).

          funktioniert bei mir nicht:

            
          LRESULT CALLBACK Handler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)  
          {  
           switch(msg) {  
            case WM_KILLFOCUS:  
             SetFocus(hwnd);  
             break;  
           }  
           return DefWindowProc(hwnd, msg, wparam, lparam);  
          }  
          
          

          Habe ich etwas falsch verstanden?

          Dumme Frage: Wenn du sowieso ein Fenster erstellst, das den ganzen Bildschirm ausfüllt - warum dann nicht gleich als maximiertes Fenster (WS_MAXIMIZE)? Dann bräuchtest du vorher nicht einmal die Bildschirmgröße abzufragen.

          Die Systemauflösung brauche ich für DirectX ich mache keine Abfrage was für Auflösungen unterstützt werden sondern verwende die eingestellte Auflösung in der Annahme diese wird unterstützt;)
          Und ich habe ein paar weniger anzeigefehler ...
          Ich hatte in meinem Code-posting übrigens einen Fehler ...
          wer findet ihn? *g*
          Die WindowClass-Name stimmt in meinem Posting nicht überein normal verwende ich hier eine Konstante(#define)
          WS_MAXIMIZE wäre eine tolle Idee ... hab ich sofort ergänzt :)

          1. Hi,

            funktioniert bei mir nicht:

            LRESULT CALLBACK Handler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)

            {
            switch(msg) {
              case WM_KILLFOCUS:
               SetFocus(hwnd);
               break;
            }
            return DefWindowProc(hwnd, msg, wparam, lparam);
            }

            
            >   
            > Habe ich etwas falsch verstanden?  
              
            nee, aber ich. ;-)  
            Das Thema wurde inzwischen so interessant, dass ich selbst mal den Compiler angeheizt und ein bisschen probiert habe. Ergebnis meiner Tests:  
            Der WM\_SETFOCUS/KILLFOCUS-Mechanismus reagiert anscheinend nur auf Focus-Wechsel \_innerhalb\_ der Applikation. Wird aber die gesamte Anwendung (das Programmfenster) deaktiviert, kommt gar kein WM\_KILLFOCUS, denn intern behält das zuletzt focussierte Child Window den Focus.  
            Aber beim Aktivieren und Deaktivieren des Programmfensters kommt eine andere Message, nämlich WM\_ACTIVATE. Damit kann man die Geschichte auswerten:  
              
               case WM\_ACTIVATE:  
                  if (LoWord(wparam)==WA\_INACTIVE)  
                   { MessageBeep(-1);  
                     SetForegroundWindow(hwnd);  
                   }  
                  break;  
              
            Ich habe hier der Einfachheit halber ein MessageBeep() drin, damit ich sofort höre, ob die Stelle im Programm überhaupt durchlaufen wird.  
            Die Sache hat nur einen Haken: Es gibt eine Windows-Einstellung, die das programmgesteuerte Setzen des aktiven Fensters verhindert (z.B. einstellbar mit TweakUI: "General" Tab, "Prevent applications from stealing the focus"). Hatte ich diese Option an (default), dann blinkt nur der Button in der Taskleiste, der das Programm repräsentiert, dreimal kurz auf, sonst passiert nichts. Schaltet man die Option aus, funktioniert's wie erwartet. Allerdings nur beim ersten Klicken; klicke ich ein anderes Fenster dann ein zweites Mal an, wechselt der Progrmmfocus dann doch. Vielleicht habe ich die Methode auch nicht "sauber" umgesetzt.  
              
            
            > WS\_MAXIMIZE wäre eine tolle Idee ... hab ich sofort ergänzt :)  
              
            Naja, dann hab ich ja immerhin ein Kleinigkeit beisteuern können.  
            So long,  
             Martin  
            
            -- 
            Faulheit ist, mit dem Cocktailshaker in der Hand auf das nächste Erdbeben zu warten.
            
            1. case WM_ACTIVATE:

              if (LOWORD(wparam)==WA_INACTIVE)
                     { //MessageBeep(-1);
                       SetForegroundWindow(hwnd);
                     }
                    break;

                
              sorry, aber das funktioniert bei mir auch nicht ...  
                
              Anscheinend ist das ein sehr umfassendes Problem dies zu realisieren oder ich bin einfach nur zu dumm dafür ...