Наверное лучше обрабатывать сообщения WM_KEYDOWN\WM_KEYUP, цикл будет ненужон, а если через GetKeyState, тогда необходимо "глушить" повторы, вот пример на таймере, вспоминать, как дербанить проект нету времени
Код:
var
i: smallint;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
if i <> GetKeyState(VK_Shift) then
begin
if GetKeyState(VK_Shift) and $8000 <> 0 then
Memo1.Lines.Add('Shift Down') else
Memo1.Lines.Add('Shift Up');
i:= GetKeyState(VK_Shift);
end else exit;
end;