Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 258
  • Last Modified:

Trap keystrokes and log to text file

Hey,
This one should be simple enough.

I want to log everything I type on my computer to a text file.
I will have the program open and in the taskbar, but it will not have focus.
I want to log all characters to a text file and start a new line when the enter key is pressed.

Can someone show me sample code for doing this please?

Thanks
John
0
BlackStorm
Asked:
BlackStorm
  • 5
  • 4
  • 2
  • +1
1 Solution
 
shaneholmesCommented:
Actually it is not simple!

If your program does not have focus, then you are trapping keystrokes at the OS level and not your application.....

sholmes
0
 
shaneholmesCommented:
basically, for:

Non-Windows NT: you will need to learn to use the SetWindowsHookEx.
Windows NT: you will need to write your own keyboard driver (VxD).

sholmes
0
 
BlackStormAuthor Commented:
Ah right...
I remember I found code to do this around a year ago.
I think it might have been from about.com but not positive.

I will have a look around and see what I can find, I did a lot of searching but everything I foudn was unrelated.

Thanks Shane
John
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
mikelittlewoodCommented:
This code should handle pretty much most of what you want.
I just made an application to do this and put it in my system tray password protected

I used a listview to hold the current information, and saved text to a database, but you could replace the code and put the current text in a stringlist and do
StringList1.SaveToFile('c:\log.txt') or something

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  // initialise the hook
  Hooked := False;
end;

procedure TfrmMain.mnuOnClick(Sender: TObject);
begin
  {check to see if the Hook is running and don't allow it to be started again}
  if Hooked then
    begin
    Messagebox(frmMain.Handle,'Journal Hook has already been started',
    'No can Restart', MB_OK or MB_ICONQUESTION);
    Exit;
    end;

  JHook := SetWindowsHookEx(WH_JOURNALRECORD , @JHookFunc, hInstance, 0);
  if JHook > 0 then
    begin
      Hooked := True;
      frmMain.Caption := cFormCaption + ' [Hook On]';
    end
    else
      ShowMessage('No Journal Hook availible');
end;

procedure TfrmMain.mnuOffClick(Sender: TObject);
begin
  // turn off the hook
  Hooked := False;
  UnhookWindowsHookEx(JHook);
  JHook := 0;
  frmMain.Caption := cFormCaption + ' [Hook Off]';
end;

procedure TfrmMain.ApplicationEventsMessage(var Msg: tagMSG;
  var Handled: Boolean);
begin
  Handled := False;
  if (Msg.message = WM_CANCELJOURNAL) and Hooked then
    JHook := SetWindowsHookEx(WH_JOURNALRECORD , @JHookFunc, hInstance, 0);
  {IMPORTANT, , whenever the Ctrl+Esc keys are pressed the Journal Hook is canceled
  and a WM_CANCELJOURNAL is sent, so you need to restart the hook}
end;

function JHookFunc(Code:integer; wParam: Longint; var EventStrut: TEVENTMSG): Longint; stdcall;
var
  KeyState1: TKeyBoardState;
  AryChar: Array[0..1] of Char;
  Count: Integer;
  VirtKey, ScanCode: Cardinal;
  S:      string;
begin
  {this is the Hook function that gets ALL of the Keyboard events}
  Result := CallNextHookEx(JHook, Code, wParam, Integer(@EventStrut));
  {go ahead and Call nextHook, although it's not really needed in a Journal Hook}
  if Code < 0 then Exit;
  if Code = HC_ACTION then
    begin
  {HC_ACTION means that an event has occured}
    if (EventStrut.paramL = 17923) then {17923 is for Ctrl+Break}
      begin
      {It is recomended to end hooks with Ctrl+Break}
      frmMain.mnuOffClick(frmMain);  {Ctrl+Break will End the Journal record}
      Exit;
      end;

    if EventStrut.message = WM_KEYDOWN then
    begin
      {the LOBYTE LOWORD of papamL has the Virtual Key Code}
      VirtKey := LOBYTE(LOWORD(EventStrut.paramL));
      {the HIBYTE LOWORD of paramL has the Scan Code}
      ScanCode := HIBYTE(LOWORD(EventStrut.paramL));
      GetKeyboardState(KeyState1);
      {to find out if the Shift key is down we get the KeyboardState}
      Count := ToAscii(VirtKey,ScanCode, KeyState1, AryChar, 0);
      {ToAscii( )  function is like TranslateMessage( ) and converts the Virtual Key to a Char}

      if Count = 1 then
      begin
        // get the current focused application title
        S := GetActiveWindowTitle;
        // add the keypressed to the listview
        frmMain.VisualizeKey(AryChar[0], S);
      end;

    end;
  end;
end;

procedure TfrmMain.VisualizeKey(Key: Char; AppName: string);
var
  B:Boolean;
  I: Integer;
  ListItm: TListITem;
begin
  with lvLog do
  begin
    B := False;
    // loop through the listview
    for I := 0 to (Items.Count-1) do
    begin
      // see if the current application exists in our view
      if (AppName=Items[I].Caption) then
      begin
        B := True;
        Break; // for I
      end;
    end;
    if B then
    begin
      Items[I].SubItems[0] := Items[I].SubItems[0] + Key;

      // now check the current length of the selected application line and
      // see if we need to save a log to the database
      if Length( Trim(Items[I].SubItems[0]) ) > 200 then
      begin

        // DO YOU SAVING TO FILE HERE

        // clear the value in the selected item
        Items[I].SubItems[0] := '';
      end;
    end
    else
    begin
      ListItm := Items.Add;
      ListItm.Caption := AppName;
      ListItm.SubItems.Add(Key);
    end;
  end;
end;
0
 
BlackStormAuthor Commented:
Hey mike,
Thanks very much for doing that!
I will give you more points than I have put here.

Did you test that code?

I am getting a lot of errors when I try run it.
Mostly Undeclared identifier and a few others:

[Warning] Unit1.pas(134): Comparing signed and unsigned types - widened both operands
if (AppName=Items[I].Caption) then

[Error] Unit1.pas(146): There is no overloaded version of 'Trim' that can be called with these arguments
if Length( Trim(Items[I].SubItems[0]) ) > 200 then


Thanks
John
0
 
mikelittlewoodCommented:
I have only taken out a few procedures from the application I wrote, but they are the most useful ones. I havent given you code for an entire application up there.
Do you think you will be able to insert the code I have given you into an application you can write. Are you able to modify it enough or would you like me to send you the entire application I wrote minus all the database stuff it is doing. If you want me to send it you will have to give me time to re-code it a little and email it to you.
0
 
BlackStormAuthor Commented:
Hey Mike,
I would really appreciate if you could send the program.
My email is {email address removed - ai, cs admin}

Thanks very much!
John
0
 
mikelittlewoodCommented:
Ok gimme till tomorrow to send it to you
0
 
Slick812Commented:
hello BlackStorm , Here is some code for a library program that uses the WH_KEYBOARD  hook to get the keyboard messages. I feel like the key hook is better for this sort of thing, than the journal hook. The Library DLL will get called into every process running in the windows message system, so there will be several instances of this DLL running after the hook is set, so i placed a memory mapped file in it to keep the Forms handle in so each library can send it a WM_USER message with the Charater that was typed. When the program gets the message, it will write a text character to the log file or a line break.

Here is the code for a key logger in the library program -




library LogKey;

uses
  Windows, Messages;

{$R *.RES}


const
mapFileName = 'yu7bx(4+jHg3z';

var
Hooked: Boolean;
pMsgForm: PDWORD;
hKeyHook, hMemFile: Cardinal;



function KeyHookFunc(Code, VirtualKey, KeyStroke: Integer): Integer; stdcall;
var
KeyState1: TKeyBoardState;
AryChar: Array[0..1] of Char;
Count: Integer;
begin
Result := 0;
if Code = HC_NOREMOVE then Exit;
Result := CallNextHookEx(0, Code, VirtualKey, KeyStroke);
if (pMsgForm <> nil) and  ((KeyStroke and (1 shl 30)) <> 0) then
  begin
  GetKeyboardState(KeyState1);
  Count := ToAscii(VirtualKey,KeyStroke, KeyState1, AryChar, 0);
  if Count = 1 then
  if Ord(AryChar[0]) = 13 then
    PostMessage(pMsgForm^, WM_USER+5432, 10, $0A0D)
    else
    PostMessage(pMsgForm^, WM_USER+5432, 20, Ord(AryChar[0]));
  end;
end;

function StartHook(hMsgWnd: Cardinal): Integer; export;
begin
Result := 1;
if Hooked then Exit;
if not IsWindow(hMsgWnd) then
  begin
  Result := 4;
  Exit;
  end;
 
hMemFile := CreateFileMapping(MAXDWORD, nil,PAGE_READWRITE,0,SizeOf(pMsgForm), mapFileName);
pMsgForm := MapViewOfFile(hMemFile, FILE_MAP_WRITE, 0, 0, 0);
if pMsgForm = nil then
  begin
  CloseHandle(hMemFile);
  Result := 3;
  Exit;
  end;
hKeyHook := SetWindowsHookEx(WH_KEYBOARD, KeyHookFunc, hInstance, 0);
if hKeyHook > 0 then
  begin
  pMsgForm^ := hMsgWnd;
  Hooked := True;
  Result := 0;
  end else
  begin
  UnmapViewOfFile(pMsgForm);
  CloseHandle(hMemFile);
  pMsgForm := nil;
  Result := 2;
  end;
end;

function StopHook: Boolean; export;
begin
if pMsgForm <> nil then
  begin
{be sure to release your MemMap file}
  UnmapViewOfFile(pMsgForm);
  CloseHandle(hMemFile);
  pMsgForm := nil;
  end;
if Hooked then
  begin
  Result := UnhookWindowsHookEx(hKeyHook);
  end else
  Result := True;
if Result then
Hooked := False;
end;


procedure EntryProc(Reason : Cardinal);
begin
if (Reason = Dll_Process_Attach) then
  begin
{get your memory file at startup, hMemFile will be Zero if not there}
  hMemFile := OpenFileMapping(FILE_MAP_WRITE, False, mapFileName);
  if hMemFile > 0 then
    begin
    pMsgForm := MapViewOfFile(hMemFile, FILE_MAP_WRITE, 0, 0, 0);
    end;
  end;

 if (Reason = Dll_Process_Detach) then
 begin
 if pMsgForm <> nil then
  begin
  {be sure to release your MemMap Pointer}
  UnmapViewOfFile(pMsgForm);
  CloseHandle(hMemFile);
  end;
 if hKeyHook <> 0 then
   UnhookWindowsHookEx(hKeyHook);
 end;
end;

exports
  StartHook,
  StopHook;

begin
Hooked := False;
hKeyHook := 0;
hMemFile := 0;
pMsgForm := nil;
DLLProc := @EntryProc;
EntryProc(Dll_Process_Attach);
end.


 = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

here is some code for your program to start and stop the Key hook, there is a Form1 OnCreate event, a window message procedure and 2 button click events -




private
    { Private declarations }
    hKLogLib: THandle;
    LogFile: TextFile;
    procedure KeyLogMess(var Message: TMessage); message WM_USER+5432;



procedure TForm1.FormCreate(Sender: TObject);
begin
hKLogLib := 0;
end;


procedure TForm1.KeyLogMess(var Message: TMessage);
begin
// this gets the message form one of the DLL's and writes Char to LogFile
if Message.WParam = 10 then // wParam as 10 is charage return
  WriteLn(LogFile)
  else // wParam as 20 is text character
  if (Message.WParam = 20) and (Message.LParam < 256) then
    Write(LogFile, Char(Message.LParam));
end;


procedure TForm1.sbut_StartHookClick(Sender: TObject);
var
StartHook: function(hMsgWnd: Cardinal): Integer;
begin
// button click to start Key Hook
if hKLogLib = 0 then
  hKLogLib := LoadLibrary('LogKey.dll');
if hKLogLib = 0 then
  begin
  ShowMessage('ERROR - Could not load library');
  Exit;
  end;

@StartHook := GetProcAddress(hKLogLib, 'StartHook');
if @StartHook = nil then
  begin
  ShowMessage('StartHook was not located');
  FreeLibrary(hKLogLib);
  hKLogLib := 0;
  Exit;
  end;

if StartHook(Handle) <> 0 then
  begin
  ShowMessage('StartHook was not Successful');
  FreeLibrary(hKLogLib);
  hKLogLib := 0;
  Exit;
  end;
AssignFile(LogFile, 'E:\Log1.log');
Rewrite(LogFile);
//WriteLn(LogFile, 'New Log');
end;


procedure TForm1.but_StopHookClick(Sender: TObject);
var
StopHook: function: Boolean;
begin
// button click to stop hook
if hKLogLib = 0 then Exit;
CloseFile(LogFile);
@StopHook := GetProcAddress(hKLogLib, 'StopHook');
if @StopHook = nil then
  ShowMessage('StopHook was not located')
  else
  if not StopHook then
  ShowMessage('StopHook was not successful')
  else

FreeLibrary(hKLogLib);
hKLogLib := 0;
end;


 - - - - - - - - -- - - - - - - - - - - - - - - - - - - - - - - -

maybe this can help you out
ask questions if you need more
0
 
mikelittlewoodCommented:
Hi John, Im trying to email you this code.
What is the format of your email address?

john @ [nospam] hostingrefuge.com
is [nospam] meant to be nospam. ?
0
 
BlackStormAuthor Commented:
Hey Mike,
That is exactly what I was looking for, thank you very much.
 
I really appreciate your help!

John

PS. Sorry Slick, I didn't get a chance to try your code because Mike emailed me his program.
0
 
Slick812Commented:
thats OK John,

I beleive that the rules here at EE  say something about  Not  using E-mail to do responces or answers to questions. .
The purpose here at EE is to have searchable code solutions for people to look at again and again. . if you E-Mail your solutions then others who are searching for a soution will not be able to see the code for the EE question that might help them. . .

you may consider posting some form of code you used for the solution here in this question so others may be helped by it; ;

Good Luck !
0
 
mikelittlewoodCommented:
I did post the code that I sent BlackStorm back near the beginning of the question.
All I did was send him it again with the form as well.
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 5
  • 4
  • 2
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now