Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations TouchToneTommy on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Can't get GetASyncKeyState to work in console application.

Status
Not open for further replies.

chrknudsen

Programmer
Sep 25, 2006
8
DK
I'm fairly new to Delphi (though I've been programming in Pascal for some time) and to get to know it better, I've decided to make a small console game. The game should basically move a character across the screen according to keypressed up, down, left and right. You have to be able to press multiple keys, so that you can also move diagonally. I've found that I need to use the GetASyncKeyState for this. To test it I wrote the below test program, but I can't get it to work. I can compile it without errors, but when I run it, my console window just freezes, ignoring all keypresses. After a while my keypresses will result in a beep from the computer and I have to forcefully close the console window. Why doesn't the below code work?

Code:
PROGRAM Test;

{$APPTYPE CONSOLE}

USES SysUtils, Windows;

VAR I : Integer;

BEGIN
   I := 0;
   REPEAT
      IF GetAsyncKeyState(VK_RETURN) <> 0 THEN BEGIN
         Inc(I);
         Write(I);
      END;
   UNTIL GetAsyncKeyState(VK_ESCAPE) <> 0;
END.
 
What OS version are you using (Win9x, 2K, XP)?

In 2K your code works as expected to work and it can be used to check the arrow keys. The problem is I don't know what *you* are expecting for it :).

Change it a little, use Write(I, #13#10); instead of Write(I); and recompile.

Run it and do a very quick press in the ENTER key; you'll see something like

1
2
3
4
5
6
...
nnn

This is due to the fact that the code detectes the keypress while the key is actually down (and what is quick for you are ages for the CPU).

Press ENTER and keep it down: you'll see an endless stream of numbers in your console screen.

Press any other key but ESCAPE; of course you'll see nothing; now keep ESCAPE and the console is done.

Now change the code a little:

Code:
...
   REPEAT
      IF GetAsyncKeyState(VK_RETURN) <> 0 THEN BEGIN
         Inc(I);
         Write(I, #13#10);
      [b]WHILE GetAsyncKeyState(VK_RETURN) AND $8000 <> 0 DO {Nothing};[/b]
      END;
   UNTIL GetAsyncKeyState(VK_ESCAPE) <> 0;
...

Now you can press ENTER and have only one count per press.

HTH.
buho (A).
 
If you are trying to get a hang on Delphi, go straigth to Windows applications.

Console applications have not use except in some very rare market niches, and after mastering Windows applications you'll be better prepared to tackle console ones.

buho (A).
 
Thanks for the replies! :eek:)

I'm using Windows 98.

As to what I expect of the code, I expect it to do exactly what it did when you ran it in 2K. However, as I wrote in my initial post, when I compile and run the program, my console window freezes and doesn't react to keypresses. I've also made similar tests with KeyState, but with the same result (the console window freezes).

My plan is to make an ASCII game, which is why I'm making a console and not a Windows application.
 
I of course meant to say that I've made similar tests with GetKeyState... :)

Also, I'm using Delphi 7 Second Edition v7.2
 
Well, I've now tested the compiled code on a WindowsXP machine and it works as intended. So now I know that it's my computer and not my copy of Delphi that's malfunctioning. I wonder if it's because I'm using Win98?
 
That was what I was thinking. But I never wrote a console for Win9x, so I can't say.

buho (A).
 
I just think it's odd that such basic functions as GetASyncKeyState and GetKeyState can't work in Windows98. If anybody else on these boards have a chance to test my code in Win98, please do so! :)
 
Those functions works in Win9x normal apps. The problem is mostly the console app.

buho (A).
 
I've decided to try to use ReadConsoleInput() instead, so that my game will also run in Win98. I want to read the InputEvents into a buffer array that will contain 128 events, as in the below C code snippet I found on the web:

Code:
INPUT_RECORD irInBuf[128]; 

if (! ReadConsoleInput( 
                hStdin,      /* input buffer handle    */ 

                irInBuf,     /* buffer to read into    */ 
                128,         /* size of read buffer    */ 
                &cNumRead) ) /* number of records read */ 
            MyErrorExit("ReadConsoleInput"); 

for (i = 0; i < cNumRead; i++) 
            switch(irInBuf[i].EventType) { 
 
                case KEY_EVENT: /* keyboard input */ 
                    KeyEventProc(irInBuf[i].Event.KeyEvent); 

                    break; 

(and so on...)

I've come up with this code snippet:

Code:
   VAR InputBuffer : Array [1..128] OF TInputRecord;

   GetNumberOfConsoleInputEvents(InputHandle, InputEventsRead);
   IF InputEventsRead > 0 THEN BEGIN
      ReadConsoleInput(InputHandle, InputBuffer, 128, InputEventsRead);
      FOR I := 1 TO InputEventsRead DO BEGIN
         IF (InputBuffer[I].EventType = KEY_EVENT) AND (InputBuffer[I].KeyEvent.bKeyDown = True) THEN BEGIN
            CASE InputBuffer[I].KeyEvent.AsciiChar OF
               'W', 'w' : MoveUp := True;
            END;
         END;
      END:
   END;

When I try to compile it, I get this error: "Types of actual and formal var parameters must be identical" referring to InputBuffer in "ReadConsoleInput(InputHandle, InputBuffer, 128, InputEventsRead)". Am I declaring my InputBuffer array incorrectly?
 
The function declararation expects a single record and not an array/pointer :(

To add insult to the injury, you can't typecast an array as a record.

----------------
Trick 1:

UNCHECK "Typed @ operator" in the compiler options and use:

var
p : PInputRecord;
...
begin
p := @InputBuffer;
ReadConsoleInput(InputHandle, p^, 128, InputEventsRead);

----------------
Trick 2:

var
InputBuffer : array [1..128] of TInputRecord;
r : TInputRecord absolute InputBuffer;
begin
ReadConsoleInput(InputHandle, r, 128, InputEventsRead);


buho (A).
 
I was "dumbified" last night :(

The better way to pass the buffer is the simplest one:

ReadConsoleInput(InputHandle, InputBuffer[1], 128, InputEventsRead);

Defining the parameter as a TInputRecord like Borland does permits calls like:

ReadConsoleInput(InputHandle, InputBuffer[n], m - n, InputEventsRead);

In this way, you can "fill up" the array in several calls with a simple syntax.

Borland is right, I'm the one wrong here.

buho (A).
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top