// Event handler to handle a keyboard event.
        // We use this function to accumulate characters typed into the console and build
        // up the password this way.
        // All event handler functions must return a boolean value indicating whether
        // the password processing function should continue to read in another console
        // input record (via ReadConsoleInput() API).
        // Returning a true indicates continue.
        // Returning a false indicates don't continue.
        private bool KeyEventProc(INPUT_RECORD input_record, ref string strBuildup)
        {
            // From the INPUT_RECORD, extract the KEY_EVENT_RECORD structure.
            KEY_EVENT_RECORD ker = input_record.Event.KeyEvent;

            // We process only during the keydown event.
            if (ker.bKeyDown != 0)
            {
                IntPtr intptr = new IntPtr(0);  // This is to simulate a NULL handle value.
                char ch = (char)(ker.uchar.UnicodeChar);  // Get the current character pressed.
                uint dwNumberOfCharsWritten = 0;
                string strOutput = "*";  // The character string that will be displayed on the console screen.

                // If we have received a Carriage Return character, we exit.
                if (ch == (char)'\r')
                {
                    return false;
                }
                else
                {
                    if (ch > 0)  // The typed in key must represent a character and must not be a control ley (e.g. SHIFT, ALT, CTRL, etc)
                    {
                        // A regular (non Carriage-Return character) is typed in...

                        // We first display a '*' on the screen...
                        WriteConsole
                            (
                            hStdout,           // handle to screen buffer
                            strOutput,            // write buffer
                            1,     // number of characters to write
                            ref dwNumberOfCharsWritten,  // number of characters written
                            intptr                // reserved
                            );

                        // We build up our password string...
                        string strConcat = new string(ch, 1);

                        // by appending each typed in character at the end of strBuildup.
                        strBuildup += strConcat;

                        if (++iCounter < MaxNumberOfCharacters)
                        {
                            // Adding 1 to iCounter still makes iCounter less than MaxNumberOfCharacters.
                            // This means that the total number of characters collected so far (this is
                            // equal to iCounter, by the way) is less than MaxNumberOfCharacters.
                            // We can carry on.
                            return true;
                        }
                        else
                        {
                            // If, by adding 1 to iCounter makes iCounter greater than MaxNumberOfCharacters,
                            // it means that we have already collected MaxNumberOfCharacters number of characters
                            // inside strBuildup. We must exit now.
                            return false;
                        }
                    }
                }
            }

            // The keydown state is false, we allow further characters to be typed in...
            return true;
        }
 // All event handler functions must return a boolean value indicating whether
 // the password processing function should continue to read in another console
 // input record (via ReadConsoleInput() API).
 // Returning a true indicates continue.
 // Returning a false indicates don't continue.
 private bool WindowBufferSizeEventProc(INPUT_RECORD input_record, ref string strBuildup)
 {
     // Since our Window Buffer Size Event Handler does not intend to do anything,
     // we simply return a true to indicate to the password processing
     // function to readin another console input record.
     return true;
 }
        // The main function of this class.
        public void PasswordInput(ref string refPasswordToBuild, int iMaxNumberOfCharactersSet)
        {
            INPUT_RECORD[] irInBuf = new INPUT_RECORD[128]; // Define an array of 128 INPUT_RECORD structs.
            uint cNumRead = 0;
            bool bContinueLoop = true;  // Used to indicate whether to continue our ReadConsoleInput() loop.

            // Reset character counter.
            iCounter = 0;

            // Initialize hStdin.
            if (hStdin == (IntPtr)0)
            {
                hStdin = GetStdHandle(Constants.STD_INPUT_HANDLE);
                if (hStdin == Constants.INVALID_HANDLE_VALUE)
                {
                    return;
                }
            }

            // Initialize hStdout.
            if (hStdout == (IntPtr)0)
            {
                hStdout = GetStdHandle(Constants.STD_OUTPUT_HANDLE);
                if (hStdout == Constants.INVALID_HANDLE_VALUE)
                {
                    return;
                }
            }

            // Retrieve the current console mode.
            if (GetConsoleMode(hStdin, ref dwSaveOldMode) == false)
            {
                return;
            }

            // Set the current console mode to enable window input and mouse input.
            // This is not necessary for our password processing application.
            // This is set only for demonstration purposes.
            //
            // By setting ENABLE_WINDOW_INPUT into the console mode, user interactions
            // that change the size of the console screen buffer are reported in the
            // console's input buffer. Information about this event can be read from
            // the input buffer by our application using the ReadConsoleInput function.
            //
            // By setting ENABLE_MOUSE_INPUT into the console mode, if the mouse pointer
            // is within the borders of the console window and the window has the
            // keyboard focus, mouse events generated by mouse movement and button presses
            // are placed in the input buffer. Information about this event can be read from
            // the input buffer by our application using the ReadConsoleInput function.
            dwMode = Constants.ENABLE_WINDOW_INPUT | Constants.ENABLE_MOUSE_INPUT;
            if (SetConsoleMode(hStdin, dwMode) == false)
            {
                return;
            }

            // To safeguard against invalid values, we stipulate that only if iMaxNumberOfCharactersSet
            // is greater than zero do we set MaxNumberOfCharacters equal to it.
            // Otherwise, MaxNumberOfCharacters is set to 20 by default.
            // An alternative to setting MaxNumberOfCharacters to a default value is to throw an exception.
            if (iMaxNumberOfCharactersSet > 0)
            {
                MaxNumberOfCharacters = iMaxNumberOfCharactersSet;
            }
            else
            {
                // We could throw an exception here if we want to.
                MaxNumberOfCharacters = 20;
            }

            // Main loop to collect characters typed into the console.
            while (bContinueLoop == true)
            {
                if
                (
                    ReadConsoleInput
                    (
                        hStdin,      // input buffer handle
                        irInBuf,     // buffer to read into
                        128,         // size of read buffer
                        out cNumRead // number of records read
                    ) == true
                )
                {
                    // Dispatch the events to the appropriate handler.
                    for (uint i = 0; i < cNumRead; i++)
                    {
                        // Lookup the hashtable for the appropriate handler function... courtesy of Derek Kiong !
                        ConsoleInputEvent cie_handler = (ConsoleInputEvent)htCodeLookup[(object)(irInBuf[i].EventType)];

                        // Note well that htCodeLookup may not have the handler for the current event,
                        // so check first for a null value in cie_handler.
                        if (cie_handler != null)
                        {
                            // Invoke the handler.
                            bContinueLoop = cie_handler(irInBuf[i], ref refPasswordToBuild);
                        }
                    }
                }
            }

            // Restore the previous mode before we exit.
            SetConsoleMode(hStdin, dwSaveOldMode);

            return;
        }