// 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; }