Click here to Skip to main content
15,886,919 members
Articles / Programming Languages / C++

An All-Purpose Keyboard Hooker

Rate me:
Please Sign up or sign in to vote.
4.61/5 (62 votes)
30 May 2004CPOL10 min read 366.6K   8.2K   160   60
An easy to use keyboard hooking DLL that is suitable for most applications.

Sample Image - Keyhook_Demo.gif

The Problem

Although installing and maintaining keyboard hooks are said to be simple because all we need to look into is the ::SetWindowsHookEx API, there are a couple of things that are worth at least some thinking. One issue is caused by the fact that if we need to hook all running threads, which is most likely the case, then the callback process-function must be located in a DLL. This rule by itself isn't the problem but the attenuation on the DLL reusability caused by which definitely is. Every application will process the captured keyboard events in its own ways, so why should we build a DLL that is only suitable for one program?

The Solution

The rule is not to be changed, so the function will have to be located in a DLL no matter what, however, we can try to maximize the reusability of the DLL so that it can be suitable for almost all kinds of applications, that is, make it an all-purpose keyboard hooking DLL.

To achieve the goal, some important points have been taken into account:

  • Synchronization

    The DLL is to be used by multiple processes, among which some data are shared, it is crucial that we assure synchronized access to those shared pieces of data. In this project, a mutex is used for this purpose.

  • Specific Keyboard Events Definition

    What kinds of keyboard events are to be captured? Under what conditions? Whom should be notified and how they should be notified when an appropriate keyboard event has been captured? All these specified information will help the applications to hook the right events. The KEYENTRY struct defined in this project wraps all the details, it is to be described later.

  • Notifying the Applications Effectively

    When a keyboard event is determined to match all criteria and conditions that are defined by one application, it could also match all criteria and conditions defined by other application(s), too, so multiple applications may need to be notified upon one event. Usually, we notify an application by either using a callback function, or send a message to the application's window via the Windows message system. Using callback functions is not safe here because we must wait until the function returns. If a poor-written application has an infinite loop inside the function, then we get messed up altogether. Using the Windows message system is an alternative and, obviously ::SendMessage should be avoided because of the same problem described above, so finally, ::PostMessage seemed to be the choice.

    There are some down-sides on using ::PostMessage, though. Firstly, it is not guaranteed to be up-to-time, a message sent today using ::PostMessage could be received tomorrow, a little exaggerated, I admit, but by the time the applications receive the notification, system information such as keyboard states, foreground windows, etc. could have changed already, so the DLL must try to send all those valuable information at the moment it captured the events. Secondly, an even worse limit, on Win32 platform, we can only send up to 64 bits of data to the application a time, 32 bits by the wParam and 32 bits by the lParam. I wouldn't even think of allocating data in the heap memory and sending pointers to the applications, the message may never be received and the memory will probably leak. In this project, the wParam is occupied by the handle to the window in which the captured keyboard event occurred, so we need to find a way to pack as much data as possible, into the lParam, while insuring the important portions (such as scan code, previous key state, up-down flag, etc.) of the original lParam remain intact.

  • Comprehensive Keyboard Event Information Retrieval

    As an all-purpose DLL, it is essential that as much detailed information of the captured keyboard events as possible, should be able to be retrieved by the applications. The KEYRESULT struct defined in this project wraps all the detailed information, it is to be described later.

Implementation

Defining the Keyboard Event

The KEYENTRY struct defines what kinds of events should be captured and how should the captured events be processed. It has the following members:

Type Name Comments
UINT nMessage The notification message that will be sent when appropriate key events are captured, must not be zero.
HWND hCallWnd Handle to the window to which the notification messages will be sent, must not be null.
HWND hHookWnd Handle to the window in which key events occur. If set to null, key events occurred in any window are to be captured.
BYTE iKeyEvent Types of the key events (key-down, key-up, key-repeat) to capture. If set to 0, all kinds of events are to be captured.
BYTE iMinVKCode The minimum virtual key code to capture, must not be greater than iMaxVKCode.
BYTE iMaxVKCode The maximum virtual key code to capture, must not be smaller than iMinVKCode.
BYTE iCombKeys Combination Key Flags (alt, ctrl, shift). If set to 0, combination key states do not matter.
BYTE iIndicators On/off states of keyboard indicators (caps-lock, num-lock, scroll-lock). If set to 0, indicator states do not matter.
DWORD dwReserved Reserved, do not use.

Values of iKeyEvent can be a combination of the following:

  • KH_KEY_DOWN,
  • KH_KEY_UP,
  • KH_KEY_REPEAT.

Values of iCombKeys can be a combination of the following:

  • KH_ALT_PRESSED,
  • KH_ALT_NOT_PRESSED,
  • KH_CTRL_PRESSED,
  • KH_CTRL_NOT_PRESSED,
  • KH_SHIFT_PRESSED,
  • KH_SHIFT_NOT_PRESSED,

Values of iIndicators can be a combination of the following:

  • KH_CAPSLOCK_ON,
  • KH_CAPSLOCK_OFF,
  • KH_NUMLOCK_ON,
  • KH_NUMLOCK_OFF,
  • KH_SCRLOCK_ON,
  • KH_SCRLOCK_OFF,

Retrieving the Captured Keyboard Event Information

The KEYRESULT struct is used to retrieve detailed information of the captured keyboard events. It has the following members:

Type Name Comment
HWND hOccurredInWnd Handle to the window in which the key event occurred.
BYTE iKeyEvent The captured key event type (key-down, key-up, or key-repeat).
BYTE iVKCode The virtual key code produced by the captured key-stroke.
BYTE iCombKeys Combination Key Flags (alt, ctrl, shift) when the key event was captured.
BYTE iIndicators On/off states of keyboard indicators (caps-lock, num-lock, scroll-lock) when the key event was captured.
TCHAR chPrintableChar The printable character produced by the captured key-stroke, 0 if not printable.
TCHAR szKeyName[32] Name of the key. E.g.: "Shift".

Note: KEYRESULT is a typedef of either tagKeyResultW or tagKeyResultA, depending on whether UNICODE is defined. chPrintableChar and szKeyName[32] are either wchar_t or char.

Values of iKeyEvent, iVKCode, iCombKeys and iIndicators are similar with those members of the KEYENTRY struct except that this struct is used to retrieve data only, so those values will represent actual event data. For example, in a KEYENTRY struct, its iKeyEvent member could be KH_KEY_DOWN | KH_KEY_REPEAT to indicate that the user wants to capture key-down and key-repeat events, whereas in a KEYRESULT struct, the iKeyEvent will never be such a combination because if a captured event was a key-down, then it was not a key-repeat.

DLL Exported Functions

Functions exported by the DLL are few in number and easy to use, below are description of some essential functions. For a complete list of functions and return codes, please download and take a look at KeyHook.h.


LRESULT __declspec(dllexport) InstallKeyHook();

Return Values

The function returns KH_OK if succeeded, an error code otherwise.

Remarks

Registers current process to the hook. Applications must call this function before using the hook.


LRESULT __declspec(dllexport) UninstallKeyHook();

Return Values

The function returns KH_OK if succeeded, an error code otherwise.

Remarks

Un-registers current process from the hook. Applications should call this function as soon as they finish using the hook, to release all occupied global resources.


LRESULT __declspec(dllexport) AddKeyEntry(LPCKEYENTRY lpEntry);

Parameters

  • lpEntry

    [in] Address of a KEYENTRY struct which contains information of the key event to be captured.

Return Values

The function returns KH_OK if succeeded, an error code otherwise.

Remarks

Adds a new key entry to the hook entry list, o that key events match the conditions defined in lpEntry will be captured.


LRESULT __declspec(dllexport) RemoveKeyEntry(LPCKEYENTRY lpEntry);

Parameters

  • lpEntry

    [in] Address of a KEYENTRY struct which contains information of the key entry to be removed from the hook entry list.

Return Values

The function returns KH_OK if succeeded, an error code otherwise.

Remarks

Removes a specified key entry from the hook entry list, so that the specified key event will no longer be captured. Key entries that were added by other processes are not accessible and will not be affected.


LRESULT __declspec(dllexport) RemoveAllKeyEntries();

Return Values

The function returns KH_OK if succeeded, an error code otherwise.

Remarks

Removes all key entries that were added by current process from the hook entry list. Key entries that were added by other processes are not accessible and will not be affected.


LRESULT __declspec(dllexport) GetKeyEventResult(WPARAM wParam, LPARAM lParam, LPKEYRESULT lpKeyResult, UINT nMask = KH_MASK_ALL);

Parameters

  • wParam

    [in] The WPARAM that was received with the notification message.

  • lParam

    [in] The LPARAM that was received with the notification message.

  • lpKeyResult

    [out] Address of a KEYRESULT struct which will receive detailed information of the captured key event.

  • nMask

    [in] Specifies which member(s) of the KEYRESULT struct must be filled in.

Return Values

The function returns KH_OK if succeeded, an error code otherwise.

Remarks

When a predefined key event is captured, the message defined in KEYENTRY::nMessage will be sent to the target window defined in KEYENTRY::hCallWnd, along with a wParam and a lParam, which contain detailed information of the captured key event. This function parses those parameters and extracts the information.

Values of nMask can be a combination of the following. If you do not need all the information, then specify as few flags as possible to reduce computing time.

Symbol Meaning
KH_MASK_ALL All members must be filled in.
KH_MASK_OCCURREDWND The hOccurredInWnd member must be filled in.
KH_MASK_EVENTTYPE The iKeyEvent member must be filled in.
KH_MASK_VKCODE The iVKCode member must be filled in.
KH_MASK_COMBKEYS The iCombKeys member must be filled in.
KH_MASK_INDICATORS The iIndicators member must be filled in.
KH_MASK_PRINTABLECHAR The chPrintableChar member must be filled in.
KH_MASK_KEYNAME The szKeyName member must be filled in.

Todo

To use the DLL, you may follow these easy steps:

  1. Copy KeyHook.h, KeyHook.dll, Keyhook.lib into your project directory, add KeyHook.lib into your workspace by "Project - Add to Project - Files", and include KeyHook.h where needed.
  2. Call InstallKeyHook to install the hook.
  3. Call AddKeyEntry to register key entries which consist of information on what kinds of keyboard events should be captured and whom should be notified.
  4. Call GetKeyEventResult to retrieve information of the captured key events after your application window receives the notification messages, with values of the wParam and lParam unaltered.
  5. Call UninstallKeyHook to release resources as soon as you've finished using the hook.

That's it! Really simple, isn't it? Now, it's time for some code sample.

Code Sample

In this sample, we will be writing a key logger which records key-strokes occurring in any window, and no, I am by no means abetting password stealing.

///////////////////////////////////////////////////////
// MyDlg.h
///////////////////////////////////////////////////////

class CMyDlg : public CDialog
{
    // ... ...

protected:

    // Add a function to handle the notification message
    LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);
}


///////////////////////////////////////////////////////
// MyDlg.cpp
///////////////////////////////////////////////////////

// ID of the notification message that will be sent to us
#define WM_MY_MESSAGE (WM_APP + 100)

BEGIN_MESSAGE_MAP(CMyDlg, CDialog)
 //{{AFX_MSG_MAP(CMyDlg)
 // ... ... 
 //}}AFX_MSG_MAP
 ON_MESSAGE(WM_MY_MESSAGE, OnMyMessage) // Maps our message
END_MESSAGE_MAP()

BOOL CMyDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    // ... ...

    // TODO: Add extra initialization here

    LRESULT lRes = InstallKeyHook(); // Install the hook
    ASSERT(lRes == KH_OK);

    // Prepare the KEYENTRY struct
    KEYENTRY ke;
    
    ke.nMessage = WM_MY_MESSAGE; // Our message ID
    ke.hCallWnd = m_hWnd; // Send message to this window
    ke.hHookWnd = 0; // Capture key-strokes occurred in any windows
    ke.iCombKeys = 0; // Combination key states do not matter
    ke.iIndicators = 0; // Caps-lock, Num-lock,
                       // Scroll-lock on/off states do not matter
    ke.iKeyEvent = 
      KH_KEY_DOWN | KH_KEY_REPEAT; // Capture key-down and key-repeat events
    ke.iMinVKCode = 0; // Capture all keys regardless of their virtual key codes
    ke.iMaxVKCode = 255;

    // Add the entry to the hook
    lRes = AddKeyEntry(&ke);
    ASSERT(lRes == KH_OK);

    return TRUE;  // return TRUE  unless you set the focus to a control
}

void CMyDlg::OnDestroy() 
{
    CDialog::OnDestroy();

    // TODO: Add your message handler code here
    UninstallKeyHook(); // Uninstall the hook to cleanup.
}

// Handler of WM_MY_MESSAGE notification
LRESULT CMyDlg::OnMyMessage(WPARAM wParam, LPARAM lParam)
{
    // In this sample we will display the event types and
    // characters that the captured key events produced.

    KEYRESULT kr; // The struct to receive information

    // Information that we need to extract are event type and printable character
    UINT nMask = KH_MASK_EVENTTYPE | KH_MASK_PRINTABLECHAR;
 
    // This function extracts event details
    LRESULT lRes = GetKeyEventResult(wParam, lParam, &kr, nMask);
    ASSERT(lRes == KH_OK);

    // We only display key-strokes that produce printable characters
    if (kr.chPrintableChar != 0)
    {
        CString sEvent;
        if (kr.iKeyEvent == KH_KEY_DOWN)
            sEvent = _T("Key Down");
        else if (kr.iKeyEvent == KH_KEY_UP)
            sEvent = _T("Key Up");
        else if (kr.iKeyEvent == KH_KEY_REPEAT)
            sEvent = _T("Key Repeat");
        else
            ASSERT(FALSE); // will never happen

        CString sMsg;
        sMsg.Format(_T("Event: %s\nCharacter:%c"), sEvent, kr.chPrintableChar);
        MessageBox(sMsg); // display the result
    }

    return (LRESULT)0;
}

Limitations

There are limits on maximum number of processes that can use the hook simultaneously and maximum number of entries that can be registered by all processes. If those limits are reached, subsequent operations may fail. Current version supports up to 256 unique processes to add up to 1024 key entries in total.

This DLL is not a low-level keyboard hooker so it does not intercept low-level system key events, such as Alt-Tab, Ctrl-Alt-Del, etc. To capture those events, you will need to specify WH_KEYBOARD_LL instead of WH_KEYBOARD, as the 2nd parameter of ::SetWindowsHookEx. Please check SetWindowsHookEx on MSDN for details of low-level keyboard hooking.

History

v1.00 -- May 06, 2004

  • Initial release.

v1.01 -- May 14, 2004

  • Fixed an error on verifying combination keys and keyboard indicators.
  • Updated the demo project, so now it demonstrates the use of the hook more clearly.

v1.02 -- May 26, 2004

  • Fixed some minor problems on events translation.
  • Removed unnecessary combo-key flags and indicator flags.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
China China
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
AnswerRe: Great Software! Great Article! Licence? Pin
Zeno of Elea3-May-06 6:37
Zeno of Elea3-May-06 6:37 
Generalif you use delphi... help me. Pin
sunghan23-May-05 23:40
sunghan23-May-05 23:40 
QuestionHow can Used You are dll.. for Delphi Pin
sunghan23-May-05 18:43
sunghan23-May-05 18:43 
GeneralUsing Low Level Hook Pin
asdrubael12-May-05 2:26
asdrubael12-May-05 2:26 
GeneralRe: Using Low Level Hook Pin
#realJSOP13-Nov-05 2:20
mve#realJSOP13-Nov-05 2:20 
Generalcode for dll Pin
dressman198126-Apr-05 22:40
dressman198126-Apr-05 22:40 
QuestionWhere is the input focus? Pin
Free to Go21-Mar-05 9:36
Free to Go21-Mar-05 9:36 
GeneralVB6 Usage of this C++ DLL Pin
razor_sparks14-Mar-05 13:57
razor_sparks14-Mar-05 13:57 
Yes Smile | :) the DLL will work in VisualBasic 6 OMG | :OMG: , but only after some API touchups.;P

You will need to hook the WindowProc function of your application (look up how to do it online, I'm assuming you already know how to do that Hmmm | :| ). You will also need to specify a unique Windows message number and listen for that message to get notified from the DLL that an event is ready to be retrieved. Use RegisterWindowsMessage for best results. Smile | :)

I have listed below the equivalent VB6 declarations needed to use the DLL within VB. I haven't fully tested this Sigh | :sigh: , so PLEASE point out my mistakes with REASONS why they are mistakes and I will post an ERRATA with all the changes made. Unsure | :~

KEYBOARD HOOK GLOBALS MODULE (modGlobals.bas)=========================================
'START COPY HERE
Option Explicit

'KEYENTRY, For what or where is KeyHook.dll going to be looking, and who should it tell?
Public Type KEYENTRY
    Message As Integer      'NEVER set to zero! (see note at bottom of function declerations)
                            '   0xC000 through 0xFFFF, use RegisterWindowsMessage for best results
    hCallWindow As Long     'send messages to this Window handle
    hHookWindow As Long     'listen for keys in this Window, set to 0 for ALL Window processes
    KeyEvent As Byte        'up/down/repeat?    0=all, 1 = down, 2 = up, 4=repeat
    MinKeyVirtualCode As Byte   ' must be <= Max!
    MaxKeyVirtualCode As Byte   ' must be >= Min!
    CombKeysFlags As Byte   '0 = doesn't matter, else (see definitions below)
    CNSIndicators As Byte   'CAPS/NUM/SCROLL Indicator lights, 0 = doesn't matter else (see below)
    Reserved As Long        'DO NOT USE, set to 0
End Type

'KEYRESULT, retrieve detailed info on captured events
Public Type KEYRESULT
    hOccuredInWindow As Long    'Which window saw the keystroke
    KeyEvent As Byte            'UP/DOWN/REPEAT
    VirtualKeyCode As Byte
    CombKeysFlags As Byte       'CTRL/ALT/SHIFT bit flags
    PrintableChar As Byte       'code of printable character, else 0
    KeyName(32) As Byte         'Key Name (in byte array)
End Type

'KEYRESULT Masks -- Used by the "GetKeyEventResult" Function, Specifies
'                   Which Members of the KEYRESULT struct must be filled in.
Public Const KH_MASK_ALL = &HFFFFFFFF     ' All members must be filled in
Public Const KH_MASK_OCCURREDWND = &H1    ' The "hOccurredInWnd" member must be filled in
Public Const KH_MASK_EVENTTYPE = &H2      ' The "iKeyEvent" member must be filled in
Public Const KH_MASK_VKCODE = &H4         ' The "iVKCode" member must be filled in
Public Const KH_MASK_COMBKEYS = &H8       ' The "iCombKeys" member must be filled in
Public Const KH_MASK_INDICATORS = &H10    ' The "iIndicators" member must be filled in
Public Const KH_MASK_PRINTABLECHAR = &H20 ' The "chPrintableChar" member must be filled in
Public Const KH_MASK_KEYNAME = &H40       ' The "szKeyName" member must be filled in

' Key Event Types -- Key-Down, Key-Up, Key-Repeat.
Public Const KH_KEY_DOWN = &H1            ' Key-down event
Public Const KH_KEY_UP = &H2              ' Key-up event
Public Const KH_KEY_REPEAT = &H4          ' Key-repeat event, the key is held down for long enough

' Combination Key Flags -- States of the Alt, Ctrl, and Shift Keys.
Public Const KH_ALT_PRESSED = &H1         ' The "Alt" key is(or must be) pressed
Public Const KH_ALT_NOT_PRESSED = &H2     ' The "Alt" key must not be pressed
Public Const KH_CTRL_PRESSED = &H4        ' The "Ctrl" key is(or must be) pressed
Public Const KH_CTRL_NOT_PRESSED = &H8    ' The "Ctrl" key must not be pressed
Public Const KH_SHIFT_PRESSED = &H10      ' The "Shift" key is(or must be) pressed
Public Const KH_SHIFT_NOT_PRESSED = &H20  ' The "Shift" key must not be pressed

' Keyboard Indicator Flags -- On/Off States of the Caps-Lock, Num-Lock, and
'                             Scroll-Lock Indicators.
Public Const KH_CAPSLOCK_ON = &H1         ' The "Caps-Lock" indicator is(or must be) on
Public Const KH_CAPSLOCK_OFF = &H2        ' The "Caps-Lock" indicator must be off
Public Const KH_NUMLOCK_ON = &H4          ' The "Num-Lock" indicator is(or must be) on
Public Const KH_NUMLOCK_OFF = &H8         ' The "Num-Lock" indicator must be off
Public Const KH_SCRLOCK_ON = &H10         ' The "Scroll-Lock" indicator is(or must be) on
Public Const KH_SCRLOCK_OFF = &H20        ' The "Scroll-Lock" indicator must be off

' Function Return Values & Error Codes
' Constants work better than an ENUM as if you mis-capitalize an ENUM member, the member is
'  changed instead of what was typed in code.  A constant will capitalize for you, letting you
'  know you got it right. :-D
Public Const KH_OK = 0                          ' Operation completed successfully.
Public Const KH_ERR_LOCK_FAIL = 1               ' Failed to obtain ownership of the mutex.
Public Const KH_ERR_ALREADY_INSTALLED = 2       ' Hook is already installed by current process.
Public Const KH_ERR_NOT_INSTALLED = 3           ' Hook is not installed by current process.
Public Const KH_ERR_INSTALL_FAIL = 4            ' ::SetWindowsHookEx API failed.
Public Const KH_ERR_UNINSTALL_FAIL = 5          ' ::UnhookWindowsHookEx API failed.
Public Const KH_ERR_MAX_ENTRY_REACHED = 6       ' Maxumum entry number is reached.
Public Const KH_ERR_ALREADY_REGISTERED = 7      ' Entry already registered.
Public Const KH_ERR_NOT_REGISTERED = 8          ' Entry is not registered.
Public Const KH_ERR_PROCESS_IN_USE = 9          ' The specified process has already installed the hook.
Public Const KH_ERR_MAX_PROCESS_REACHED = 10    ' Maximum process number is reached
Public Const KH_ERR_INVALID_PARAM = 11          ' Invalid parameter(s).
Public Const KH_ERR_INVALID_CALLWND = 12        ' Invalid call window handle.
Public Const KH_ERR_INVALID_USERMESSAGE = 13    ' Invalid user message ID
Public Const KH_ERR_INVALID_VKCODE_SEQUENCE = 14    ' Invalid virtual key code sequence.
Public Const KH_ERR_INVALID_COMBKEY_FLAGS = 15      ' Invalid combination key flags.
Public Const KH_ERR_INVALID_INDICATOR_FLAGS = 16    ' Invalid keyboard indicator flags.

'All these funtions return one of 16 above values on error or 0 if successful
Public Declare Function InstallKeyHook Lib "KeyHook.dll" () As Long
Public Declare Function UninstallKeyHook Lib "KeyHook.dll" () As Long
Public Declare Function AddKeyEntry Lib "KeyHook.dll" (ByRef ke As KEYENTRY) As Long
Public Declare Function RemoveKeyEntry Lib "KeyHook.dll" (ByRef ke As KEYENTRY) As Long
Public Declare Function RemoveAllKeyEntries Lib "KeyHook.dll" () As Long
'For GetKeyEventResult, set "mask" with some combination of KEYRESULT Masks (above)
'   Use as few masks as possible to speed up processing time, or 0xFFFFFFFF to return all info
'   Return value is same as other functions
Public Declare Function GetKeyEventResult Lib "KeyHook.dll" _
    (ByRef wParam As Long, ByRef lParam As Long, ByRef kr As KEYRESULT, _
    ByVal mask As Long) As Long

'NOTE: To use this functionality in VB, you will have to hook the window process and listen
'   for a user defined message!!
'   Use the API function RegisterWindowsMessage with a string value unique to your application
'   to have Windows assign a unique message number for this purpose.  This number will change
'   between sessions, so DO NOT ASSUME the one it assigns will be valid after the user has
'   rebooted or logged off.
'   (in the range of 0xC000 through 0xFFFF)

'====END OF LISTING=================================================================================


Dusty :->
dustin_AT_novi-sparks_DOT_info
GeneralRe: VB6 Usage of this C++ DLL Pin
razor_sparks18-Mar-05 14:58
razor_sparks18-Mar-05 14:58 
GeneralRe: VB6 Usage of this C++ DLL Pin
duane206425-Jun-07 17:12
duane206425-Jun-07 17:12 
GeneralForeign language characters don't display Pin
Kim Major20-Oct-04 23:30
Kim Major20-Oct-04 23:30 
GeneralRe: Foreign language characters don't display Pin
Kim Major21-Oct-04 1:48
Kim Major21-Oct-04 1:48 
GeneralBCB / Win2k / XP compatibility Pin
Jim67829-Sep-04 6:26
Jim67829-Sep-04 6:26 
GeneralRe: BCB / Win2k / XP compatibility Pin
Circum1-Nov-04 12:06
Circum1-Nov-04 12:06 
Generalblock keyboard Pin
vpappas9-Aug-04 2:04
vpappas9-Aug-04 2:04 
GeneralRe: block keyboard Pin
navi20002-Sep-04 6:19
navi20002-Sep-04 6:19 
GeneralRe: block keyboard Pin
Vatara2-Sep-04 8:36
Vatara2-Sep-04 8:36 
GeneralRe: block keyboard Pin
Abin2-Sep-04 15:47
Abin2-Sep-04 15:47 
GeneralStatically linking Pin
Khaari5-Aug-04 3:19
professionalKhaari5-Aug-04 3:19 
GeneralRe: Statically linking Pin
jocool31-Aug-04 10:31
jocool31-Aug-04 10:31 
GeneralRe: Statically linking Pin
Khaari9-Sep-04 17:12
professionalKhaari9-Sep-04 17:12 
GeneralEXCELLENT ARTICLE!! Pin
pyrokins19-Jul-04 8:22
pyrokins19-Jul-04 8:22 
GeneralVB.NET Pin
Virgil Reboton18-Jun-04 0:04
Virgil Reboton18-Jun-04 0:04 
QuestionHave you though about notifying the keyboard event by synchronization objects EVENT? Pin
Member 5608549-Jun-04 16:07
Member 5608549-Jun-04 16:07 
AnswerRe: Have you though about notifying the keyboard event by synchronization objects EVENT? Pin
helen200411-Nov-04 15:11
helen200411-Nov-04 15:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.