power-poler
Goto Top

Prüfen ob das aktuelle Fenster den Focus hat

Abend

Versuche michgerade daran, das Spiel SNAKE in C zu Programmiern.
Soweit funktioniert auch alles, die Schlange bewegt sich, frist kleine '#' und wächst dabei.
Auch kann sie sich selbst beisen und man verliert.

Nun zu meinem Problem.
Die gedrükte Pfeiltaste frage ich mit:
GetAsyncKeyState(Links oder rechts oder rauf oder runter, jenachdem welche taste ich abfragen möchte ob sie gedrückt wurde)
Also zum Beispiel:
GetAsyncKeyState(VK_LEFT);

nun zu meinem Problem.
Wenn ich das CMD fenster, in dem sich die Schnlage bewegt aus dem Focus nehme, werden die Pfeiltasten trotzdem noch eingelsen.

Also wenn ich in einem Ordner Pfeil hoch oder runter drücke, bewegt sich im Hintergrund die Schlange.

Gibt es hier eine Möglichgeit sowas in den Programmcode einzuarbeiten:
if(Ja ich habe den Focus)
um eine Weitere richtingsänderung der Schlange zu unterbinden, wenn das CMD Fenster nicht den Focus hat?

Schonmal Danke für eine Antwort

Mfg
Power-Poler

Content-Key: 221687

Url: https://administrator.de/contentid/221687

Printed on: April 18, 2024 at 16:04 o'clock

Member: rubberman
rubberman Nov 11, 2013 at 18:04:42 (UTC)
Goto Top
Hallo Power-Poler,

rein empirisch würde ich ja ein getch() in eine Threadfunktion packen, dann hat sich dein Problem von selbst erledigt. Wer braucht schon GetAsyncKeyState() face-wink
Ansonsten, schau mal ins MSDN, da findest du WinAPIs, wie GetActiveWindow() oder GetFocus().

Grüße
rubberman
Member: Power-Poler
Power-Poler Nov 11, 2013 at 18:18:13 (UTC)
Goto Top
Danke für deine Antwort.
GetFocus(), habe ich bereits probiert, da steht im Debug immer "Speicher kann nicht gelesen werden"
GetActiveWindow() schau ich mir mal an.

Naja getch() würde zwar gehen, aber dan würde die Schlange auf Input warten, was ja nicht sin der Sache ist.
Sie soll sich ja bei keinen Tastendruck von sich aus bewegen.
Member: rubberman
rubberman Nov 11, 2013 at 20:19:54 (UTC)
Goto Top
Zitat von @Power-Poler:
Naja getch() würde zwar gehen, aber dan würde die Schlange auf Input warten, was ja nicht sin der Sache ist.

Darum habe ich von einer Threadfunktion gesprochen, die asynchron auf Input wartet face-wink

Grüße
rubberman
Member: Power-Poler
Power-Poler Nov 20, 2013 at 15:40:30 (UTC)
Goto Top
Habe es jetzt mit GetAktiveWindow versucht, bekomme immer noch den Fehler "Speicher kann nicht gelesen werden".

Wie genau könnte ich einen solche asynchronen Threadfunktion erzeugen?
Member: rubberman
rubberman Nov 20, 2013 updated at 17:33:30 (UTC)
Goto Top
Naja, das ist nicht sooo schwer. Für Standard-C gibt es da _beginthread() und _beginthreadex(), Die WinAPI hätte da auch noch was zu bieten, aber für deine Zwecke reicht die erstgenannte Funktion völlig aus. Im MSDN findet sich wie immer die entsprechende Referenz. Wie du siehst wird die entsprechende Funktion übergeben, die als Thread laufen soll. Ebenso ist es möglich einen Parameter für die Threadfunktion als Voidpointer zu übergeben. Das bedeutet für die Threadfunktion, dass sie immer vom Typ void ist (also keinen Wert zurück geben kann) und einen Parameter vom Typ void* akzeptiert. Dieser zeigt dann auf den Speicherbereich der vorher deklarierten Variable aus der aufrufenden Funktion.

Mal ein Beispiel mit entsprechenden Kommentaren.
#include <windows.h>
#include <process.h>
#include <stdio.h>
#include <conio.h>

// vordefinierte Werteliste
#define ESC    27 {{comment_single_line_double_slash:1}}
#define RIGHT 333 {{comment_single_line_double_slash:2}}
#define LEFT  331 {{comment_single_line_double_slash:3}}
#define UP    328 {{comment_single_line_double_slash:4}}
#define DOWN  336 {{comment_single_line_double_slash:5}}

void __cdecl Fn_GetKey(void *const pInt); // Prototyp der Threadfunktion

int main(void)
{
  int iInput = 0; // Wert wird von der Threadfunktion geändert
  char chOutput = '\x0f'; // Startzeichen  

  // Threadfunktion aufrufen, Zeiger auf iInput wird übergeben
  if (_beginthread((void(*)(void*))Fn_GetKey, 0, (void*)&iInput) == ((uintptr_t)(intptr_t)-1))
    return EXIT_FAILURE; // wenn Thread nicht erstellt werden konnte, raus hier

  while (iInput != ESC) // Endlosschleife bis Escape
  {
    switch(iInput)
    {
      case RIGHT:
        chOutput = '\x10';  
        break;
      case LEFT:
        chOutput = '\x11';  
        break;
      case UP:
        chOutput = '\x1e';  
        break;
      case DOWN:
        chOutput = '\x1f';  
    }

    printf("%c\n", chOutput); // Ausgabe  
    Sleep(200);
  }

  return EXIT_SUCCESS;
}

// Threadfunktion, Parameter *pInt ist Zeiger auf Speicherbereich von iInput
void __cdecl Fn_GetKey(void *const pInt)
{
  int iKey = 0;

  do // Endlosschleife bis Escape
  {
    iKey = _getch(); // Warte auf Tastatureingabe
    // Bei F- oder Pfeiltasten, gibt der erste Aufruf 0 oder 224 zurück
    // Führe _getch() erneut aus um den Scancode aus dem Puffer zu lesen
    // http://msdn.microsoft.com/en-us/library/aa299374(v=vs.60).aspx
    if (iKey == 0 || iKey == 224)
      iKey = 256 + _getch(); // 256 wird zwecks Unterscheidung zu ASCII Werten addiert

    *((int*)pInt) = iKey; // Schreibe neuen Wert in den adressierten Speicherbereich

  } while (iKey != ESC);

  _endthread(); // Thread beenden
}
Wie du siehst, reagiert das Programm auf Pfeiltasten und Esc zum Beenden. Die Schleife in der Mainfunktion läuft unabhängig davon weiter, während der Wert für iInput in der Threadfunktion verändert wird.
Soweit ziemlich simpel, oder?

Grüße
rubberman
Member: Power-Poler
Power-Poler Nov 21, 2013 at 16:02:46 (UTC)
Goto Top
Vielen Dank, funktioniert einwandfrei.
Glaube auch das die Eingabe direkter ist als vorher.
(Versuche gerade die Funktion zu Hintersteigen, weshalb dort eigendlich nur void drinsteht.
Laut der libary scheint es ein start_address zu sein. Weshalb die Funktion aber auch ohne auskommt, weiß ich ja gerade nicht)
Achso, kann man damit auch gleichzeitigen Tastendruck abfragen?
Also zum Beispiel die Leertaste und eine Richtungstaste, oder links und hoch für diagonal nach oben?
Kann dir gerne meinen Quellcode zuschicken, kann ihn natürlich auch hier veröffentlichen, würde ich aber nur sehr ungern machen.
Wahrscheinlich wird man am code auch erkennen das ich noch Anfänger bin, behelfe mir einiger (wie ich finde) unschöner "Tricks" für bestimmte Bedingungen.

Nochmal vielen Dank für deine Hilfe
Member: rubberman
rubberman Nov 21, 2013 updated at 22:48:10 (UTC)
Goto Top
Zitat von @Power-Poler:

Laut der libary scheint es ein start_address zu sein.
Gemeint ist die Startadresse der Funktion. Das ist lediglich ein Name und steht dort eigentlich zum besseren Verständnis und nicht um dich zu verwirren face-wink

Zitat von @Power-Poler:

Achso, kann man damit auch gleichzeitigen Tastendruck abfragen?
Dazu solltest du dir vielleicht GetKeyboardState() ansehen. Diese Funktion füllt ein Bytearray mit dem Status aller Tasten, bei dem du mit bitweisem UND prüfen kannst ob eine bestimmte Taste gedrückt wurde. Mit logischem UND kannst du mehrere solcher Tests zusammenfassen, um herauszufinden ob bestimmte Tastenkombinationen gedrückt wurden. (Ist aber nur Theorie, hatte heute keine Zeit da was zu testen.)

Grüße
rubberman

EDIT:
Kurz skizziert für Kombinationen der Pfeiltasten ...
#include <windows.h>
#include <process.h>
#include <stdio.h>
#include <conio.h>

// vordefinierte Werteliste
#define ESC         27 // Escape       (ASCII 27)
#define RIGHT      333 // Pfeil rechts (256 + Scancode 77)
#define LEFT       331 // Pfeil links  (256 + Scancode 75)
#define UP         328 // Pfeil oben   (256 + Scancode 72)
#define DOWN       336 // Pfeil unten  (256 + Scancode 80)
#define UPRIGHT   1000 // 4 frei definierte Werte > 511
#define UPLEFT    1001
#define DOWNRIGHT 1002
#define DOWNLEFT  1003

void __cdecl Fn_GetKey(void *const pInt); // Prototyp der Threadfunktion

int main(void)
{
  int iInput = 0; // Wert wird von der Threadfunktion geändert
  char szOutput[3] = "\x0f"; // Startzeichen  

  // Threadfunktion aufrufen, Zeiger auf iInput wird übergeben
  if (_beginthread((void(*)(void*))Fn_GetKey, 0, (void*)&iInput) == ((uintptr_t)(intptr_t)-1))
    return EXIT_FAILURE; // wenn Thread nicht erstellt werden konnte, raus hier

  while (iInput != ESC) // Endlosschleife bis Escape
  {
    switch(iInput)
    {
      case RIGHT:
        strcpy(szOutput, "\x10");  
        break;
      case LEFT:
        strcpy(szOutput, "\x11");  
        break;
      case UP:
        strcpy(szOutput, "\x1e");  
        break;
      case DOWN:
        strcpy(szOutput, "\x1f");  
        break;
      case UPRIGHT:
        strcpy(szOutput, "\x1e\x10");  
        break;
      case UPLEFT:
        strcpy(szOutput, "\x1e\x11");  
        break;
      case DOWNRIGHT:
        strcpy(szOutput, "\x1f\x10");  
        break;
      case DOWNLEFT:
        strcpy(szOutput, "\x1f\x11");  
    }

    printf("%s\n", szOutput); // Ausgabe  
    Sleep(200);
  }

  return EXIT_SUCCESS;
}

// Threadfunktion, Parameter *pInt ist Zeiger auf Speicherbereich von iInput
void __cdecl Fn_GetKey(void *const pInt)
{
  int iKey = 0;
  BYTE rgKeys[256];

  do // Endlosschleife bis Escape
  {
    iKey = _getch(); // Warte auf Tastatureingabe
    // Bei F- oder Pfeiltasten, gibt der erste Aufruf 0 oder 224 zurück
    // Führe _getch() erneut aus um den Scancode aus dem Puffer zu lesen
    // http://msdn.microsoft.com/en-us/library/aa299374(v=vs.60).aspx
    if (iKey == 0 || iKey == 224)
      iKey = 256 + _getch(); // 256 wird zwecks Unterscheidung zu ASCII Werten addiert

    GetKeyState(0); // Diesen Aufruf verstehe ich selbst nicht, funktioniert aber nicht ohne.
    GetKeyboardState(rgKeys); // Den Status aller Tasten in rgKeys speichern.

    // Tastenkombinationen abfragen
    // http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
    if (rgKeys[VK_UP] & 0x80 && rgKeys[VK_RIGHT] & 0x80)
      iKey = UPRIGHT;
    else if (rgKeys[VK_UP] & 0x80 && rgKeys[VK_LEFT] & 0x80)
        iKey = UPLEFT;
      else if (rgKeys[VK_DOWN] & 0x80 && rgKeys[VK_RIGHT] & 0x80)
          iKey = DOWNRIGHT;
        else if (rgKeys[VK_DOWN] & 0x80 && rgKeys[VK_LEFT] & 0x80)
            iKey = DOWNLEFT;

    *((int*)pInt) = iKey; // Schreibe neuen Wert in den adressierten Speicherbereich

  } while (iKey != ESC);

  _endthread(); // Thread beenden
}
Member: rubberman
rubberman Nov 24, 2013 at 17:51:29 (UTC)
Goto Top
Ich hab mich noch mal kurz mit der ursprünglichen Frage beschäftigt.
Bei mir funktioniert es letztlich mit der Funktion GetGUIThreadInfo().
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
  GUITHREADINFO gti; // http://msdn.microsoft.com/en-us/library/windows/desktop/ms632604(v=vs.85).aspx
  HWND hwndMe = GetConsoleWindow(); // eigenes Fensterhandle

  memset(&gti, 0, sizeof(gti)); // Speicherbereich der Struktur von Datenmüll befreien
  gti.cbSize = sizeof(gti); // Größe der Struktur muss laut Doku festgelegt werden

  for (;;) // Endlosschleife
  {
    GetGUIThreadInfo(0, &gti); // Struktur mit Daten füllen

    // Wenn die Member hwndActive und hwndFocus dem eigenen Fensterhandle entsprechen,
    // solltest du davon ausgehen können, dass du eingehende Tastatureingaben auf dein
    // Fenster anwenden kannst.
    if (gti.hwndActive == hwndMe && gti.hwndFocus == hwndMe)
      puts("JA");  
    else
      puts("NEIN");  

    Sleep(200);
  }
  return 0;
}
Grüße
rubberman
Member: Power-Poler
Power-Poler Feb 17, 2014 at 18:37:35 (UTC)
Goto Top
Abend, sorry das ich mich schon wider melde.

Versuche mich gerade wider an einem kleinen Spiel
Dieses mal will ich das ganze etwas übersichtlicher und aufgeräumter im Quellcode haben.
Gerade versuche ich die main von der Funktion Fn_GetKey zu trennen.
Habe jetzt eine Quelle.cpp mit der main.
Eine input.cpp mit der Funktion Fn_GetKey
und eine input.h die das ganze vereinen soll.
Folgendes Problem.
Folgede if Abrfage in der main schlägt fehl:
if (_beginthread((void(*)(void*))Fn_GetKey, 0, (void*)&iInput) == ((uintptr_t)(intptr_t)-1)) 
Wenn ich diese Bedingung weglasse gehts.
Meine eingaben werden ausgegeben (die int werte)
die Funktion "_beginthread" liefert 48 zurück, der rechte teil mit dem der wert verglichen wird -1.
Ich schätze mal das es nicht gerade sinnvoll ist, die Bedingung einfach wegzulassen.
Mache ich da irgendwas falsch, bzw mache ich einen Denkfehler?
Kann natürlich sein, das mein Denkfehler dort liegt, das ich überhaupt versuche das ganze voneinander zu trennen.

Schonmal danke für deine Antwort.
Member: rubberman
rubberman Feb 17, 2014 updated at 19:55:04 (UTC)
Goto Top
Hallo Power-Poler.

Die Bedingung macht durchaus Sinn. Wie du in der Referenz zu _beginthread nachlesen kannst, gibt die Funktion den Wert -1L zurück, wenn sie fehlschlägt. Darum lasse ich oben im Code das Programm per return EXIT_FAILURE; enden, sobald dieser Fall eintritt. Wenn die Funktion in deinem Fall 48 zurückgibt, ist also alles in Ordnung. Was folgt denn bei dir auf diese Bedingung?

Zitat von @Power-Poler:

Kann natürlich sein, das mein Denkfehler dort liegt, das ich überhaupt versuche das ganze voneinander zu trennen.
Geschmackssache.
Du musst natürlich bedenken, was beim Kompilieren passiert. Jede *.cpp Datei (wieso eigentlich cpp, wenn du C Code schreibst?) wird separat zu einer Objektdatei kompiliert. Erst der Linker macht aus den einzelnen Objektdateien die *.exe. Im Normalfall hat der Compiler also immer nur eine Quelldatei im Auge und versucht den Code darin zu optimieren.
Ergo: Wenn du die Mainfunktion von den darin aufgerufenen Funktionen trennst, mag das für dich übersichtlicher werden, den Compiler beschneidest du aber damit in seiner Fähigkeit den Gesamtcode zu optimieren. Der Zugewinn an Übersichtlichkeit ist marginal, der Performanceverlust deines Programms kann dagegen oft überwiegen. Einfach alle unterschiedlichsten Funktionen in ein separates Paar Quell- und Headerdateien zu packen macht im Zweifelsfall keinen Sinn. Sinnvoll wäre allerdings die Präprozessoranweisungen (wie #define ...) und die Funktionsprototypen in eine Headerdatei zu bringen. Das Includieren dieses Headers lässt den Compiler den Code des Headers an dieser Stelle einlesen (so, als würde er dort geschrieben stehen). Das erhöht die Übersichtlichkeit und behindert den Compiler in keinem Fall.

Bei großen Projekten solltest du schon trennen, um den Überblick nicht zu verlieren. Dann aber so, dass es sinnvoll wird. Bspw. alle Funktionen und Strukturen, die einem bestimmten Themenzweck dienen, zusammen mit denen, die evtl. von diesen aufgerufen werden, zusammenfassen.

Grüße
rubberman
Member: Power-Poler
Power-Poler Feb 18, 2014 updated at 08:50:50 (UTC)
Goto Top
#include <stdlib.h>
#include <stdio.h>
#include "input.h" 

int main(void){
	int input = 0;
	_beginthread((void(*)(void*))Fn_GetKey, 0, (void*)&input);
	//if (_beginthread((void(*)(void*))Fn_GetKey, 0, (void*)&iInput) == ((uintptr_t)(intptr_t)-1)) {
		do{		
			printf("%d\n",input);  
			input=0;
			
			_sleep(50);
		}while(input!=ESC);
	//}
	return 0;
}
Wenn ich es so mache gehts, wenn ich die if Bedingung einbinde, schlägt die Überprüfung fehl, und er überspringt die Schleife.
if (_beginthread((void(*)(void*))Fn_GetKey, 0, (void*)&iInput) ==(test= ((uintptr_t)(intptr_t)-1))) 
Habe mal testeshalber das gemacht und einen Breakpoint gesetzt.
beginthread liefert wie gesagt 48 zurück. test hat den Wert -1.


Ein großes Projekt wird das sicher nicht. Aber du schreibst das man bestimmte Funktionen zusammenfassen sollte (sobald es größer wird).
Da ich damit etwas Üben möchte, versuche ich das ganze mal so aufzutrennen, Das es für mich sinnvoll wäre.
Also eine Datei die die Tastatur Abfragt. Eine die nachher den Inhalt des Fensters neu aufbaut, sobald sich was ändert. Und dan mal schauen was ich sonst noch brauche.
cpp Dateien deshalb weil VS 2012 sie Standarthaft so benennt. Ich könnte sie von Hand .c nennen, aber warum sollte ich das tun? Ich hoffe ja noch (keine Ahnung ob es klappt) später sobald das ganze doch größer wird, und ich c++ Funktionen brauche, einfach den Compiler anzuweisen in c++ zu kompilieren.


Danke für deine Antwort

EDIT:
Ahh, da liegt mein Problem.
Man sollte auch dan ganze vollständig lesen.
Da ist keine geschweifte Klammer mehr nach der if Anweisung, es folgt direkt eine return Anweisung.
Werde es dran dementsprechend umbauen.
Member: rubberman
rubberman Feb 18, 2014 at 18:07:26 (UTC)
Goto Top
Zitat von @Power-Poler:

Ich hoffe ja noch (keine Ahnung ob es klappt) später sobald das ganze doch größer wird, und ich c++ Funktionen brauche, einfach den Compiler anzuweisen in c++ zu kompilieren.
Umgekehrt wird ein Schuh draus. VS kompiliert standardmäßig als C++, wenn du nicht explizit in den Projekteigenschaften einstellst, dass als C kompiliert werden soll. In dem Fall zieht VS aber den Uralt - C89 - Standard an. Ergo: VS ist nicht besonders gut geeignet um C zu lernen.
BTW Wenn du C++ Code schreiben willst, gewöhne dir C besser gar nicht erst an. Das versaut den Stil. Da spreche ich aus eigener Erfahrung. Ich bin so auf C und die prozedurale Schreibweise geprägt, dass ich wohl nie behaupten werde, einigermaßen vernünftiges C++ zu schreiben. Die beiden Sprachen sind unterschiedlicher als du denkst, auch wenn C aus Kompatibilitätsgründen fast vollständig in C++ aufgeht.

EDIT:
Ahh, da liegt mein Problem.
Hehe. Noch deutlicher hätte ich es auch wirklich nicht schreiben können face-wink

Grüße
rubberman