Bug when reading password with confirmation and retries in a loop - c#

EDIT: The problem I am facing is that I enter an incorrect password to confirm previous password and it works since it is the first try. The max is 3 tries. During 2nd and 3rd try even if password is correct it will say invalid password.
I do not want that
I know I am missing something very trivial but I cannot manage to find it. This is the code:
class Program
{
static void Main(string[] args)
{
string username = "";
string pass = "";
string confirmPass = "";
Console.Write("Enter username: ");
username = Console.ReadLine();
Console.WriteLine();
Console.Write("Enter your password: ");
ConsoleKeyInfo key;
do
{
key = Console.ReadKey(true);
// Backspace Should Not Work
if (key.Key != ConsoleKey.Backspace && key.Key != ConsoleKey.Enter)
{
pass += key.KeyChar;
Console.Write("*");
}
else if (key.Key == ConsoleKey.Backspace && pass.Length > 0)
{
pass = pass.Substring(0, (pass.Length - 1));
Console.Write("\b \b");
}
// Stops Receving Keys Once Enter is Pressed
} while (key.Key != ConsoleKey.Enter);
Console.WriteLine("");
Console.WriteLine("Confirm your password: ");
ConsoleKeyInfo confirmKey;
int retryCount = 3;
string finalPass = "";
do{
confirmKey = Console.ReadKey(true);
// Backspace Should Not Work
if (confirmKey.Key != ConsoleKey.Backspace && confirmKey.Key != ConsoleKey.Enter)
{
confirmPass += confirmKey.KeyChar;
Console.Write("*");
}
else if (confirmKey.Key == ConsoleKey.Backspace && pass.Length > 0)
{
confirmPass = confirmPass.Substring(0, (confirmPass.Length - 1));
Console.Write("\b \b");
}
//
// Stops Receving Keys Once Enter is Pressed
} while ((confirmKey.Key != ConsoleKey.Enter));
//Console.WriteLine("");
//string confirmPass = "";
do
{
if (confirmPass != pass)
{
Console.WriteLine("Re-enter Password: (" + retryCount + " tries remaining)");
do
{
confirmKey = Console.ReadKey(true);
// Backspace Should Not Work
if (confirmKey.Key != ConsoleKey.Backspace && confirmKey.Key != ConsoleKey.Enter)
{
confirmPass += confirmKey.KeyChar;
Console.Write("*");
}
else if (confirmKey.Key == ConsoleKey.Backspace && pass.Length > 0)
{
confirmPass = confirmPass.Substring(0, (confirmPass.Length - 1));
Console.Write("\b \b");
}
//
// Stops Receving Keys Once Enter is Pressed
} while ((confirmKey.Key != ConsoleKey.Enter));
retryCount--;
}
else if (confirmPass == pass)
{
Console.WriteLine("Enter password to log in :");
finalPass = Console.ReadLine();
if (finalPass == pass)
{
Console.WriteLine("Login Successful. Welcome " + username + "!");
Console.WriteLine();
Console.WriteLine("Test Successful. Press Enter to quit.");
}
}
if (retryCount == 0)
{
Console.WriteLine("Exceeded number of tries!!");
Console.ReadLine();
}
} while (confirmPass != pass && retryCount != 0);
}
}

Based on your code, this is what I came up with:
public static void Main(string[] args)
{
string username = "";
string pass = "";
string confirmPass = "";
Console.Write("Enter username: ");
username = Console.ReadLine();
Console.WriteLine();
Console.Write("Enter your password: ");
ConsoleKeyInfo key;
do
{
key = Console.ReadKey(true);
// Backspace Should Not Work
if (key.Key != ConsoleKey.Backspace && key.Key != ConsoleKey.Enter)
{
pass += key.KeyChar;
Console.Write("*");
}
else if (key.Key == ConsoleKey.Backspace && pass.Length > 0)
{
pass = pass.Substring(0, (pass.Length - 1));
Console.Write("\b \b");
}
// Stops Receving Keys Once Enter is Pressed
} while (key.Key != ConsoleKey.Enter);
Console.WriteLine("");
Console.WriteLine("Confirm your password: ");
ConsoleKeyInfo confirmKey;
int retryCount = 3;
string finalPass = "";
do
{
confirmKey = Console.ReadKey(true);
// Backspace Should Not Work
if (confirmKey.Key != ConsoleKey.Backspace && confirmKey.Key != ConsoleKey.Enter)
{
confirmPass += confirmKey.KeyChar;
Console.Write("*");
}
else if (confirmKey.Key == ConsoleKey.Backspace && pass.Length > 0)
{
confirmPass = confirmPass.Substring(0, (confirmPass.Length - 1));
Console.Write("\b \b");
}
// Stops Receving Keys Once Enter is Pressed
} while ((confirmKey.Key != ConsoleKey.Enter));
do
{
if (confirmPass != pass)
{
confirmPass = "";
Console.WriteLine("Re-enter Password: (" + retryCount + " tries remaining)");
do
{
confirmKey = Console.ReadKey(true);
// Backspace Should Not Work
if (confirmKey.Key != ConsoleKey.Backspace && confirmKey.Key != ConsoleKey.Enter)
{
confirmPass += confirmKey.KeyChar;
Console.Write("*");
}
else if (confirmKey.Key == ConsoleKey.Backspace && pass.Length > 0)
{
confirmPass = confirmPass.Substring(0, (confirmPass.Length - 1));
Console.Write("\b \b");
}
// Stops Receving Keys Once Enter is Pressed
} while ((confirmKey.Key != ConsoleKey.Enter));
retryCount--;
}
else if (confirmPass == pass)
{
Console.WriteLine("Enter password to log in :");
finalPass = Console.ReadLine();
if (finalPass == pass)
{
Console.WriteLine("Login Successful. Welcome " + username + "!");
Console.WriteLine();
Console.WriteLine("Test Successful. Press Enter to quit.");
Console.ReadLine();
break;
}
}
if (retryCount == 0)
{
Console.WriteLine("Exceeded number of tries!!");
Console.ReadLine();
}
} while (retryCount != 0);
}
Basically I made the following changes:
When you enter that last do loop, and enter the first if statement, I set confirmPass = "";. This is the reason your code did not do that part initially, because you were appending their new confirmPass to the original. Thus, if they typed password, then password2, then to retry they typed password, confirmPass was password2password.
After changing this, the flow did not work correctly, so I removed the confirmPass != pass && bit from that while. This allowed you to get to the elseif portion in that loop.
Still, I assume this was a bit wrong as once you hit that elseif it would infinite loop. So I told it to break inside that elseif which kills that do/while loop.
If this solution is not correct, please clarify your requirements so that we may better assist you.

Your program is rather convoluted, which is why it will be much harder to spot any errors. You should really learn how to use sub routines (i.e. functions), so that you don't have to repeat blocks of code that do the same thing over and over again (like reading a password).
If we were to refactor your code a little bit, by moving those repeated parts out into a method named ReadPassword, like this:
public static string ReadPassword()
{
var pass = "";
while (true)
{
var key = Console.ReadKey(true);
if (key.Key == ConsoleKey.Enter)
// Stop Receving Keys Once Enter is Pressed
break;
// Backspace Should Not Work
if (key.Key != ConsoleKey.Backspace)
{
pass += key.KeyChar;
Console.Write("*");
}
else if (pass.Length > 0)
{
pass = pass.Substring(0, (pass.Length - 1));
Console.Write("\b \b");
}
}
return pass;
}
And used it to replace all those do ... while loops used for reading a password, without changing anything else in your code (so the bugs are still there):
public static void Main(params string[] args)
{
string username = "";
string pass = "";
string confirmPass = "";
Console.Write("Enter username: ");
username = Console.ReadLine();
Console.WriteLine();
Console.Write("Enter your password: ");
pass = ReadPassword(); // changed to function call
Console.WriteLine("");
Console.WriteLine("Confirm your password: ");
int retryCount = 3;
string finalPass = "";
confirmPass = ReadPassword(); // changed to function call
do
{
if (confirmPass != pass) // <==== this is performed before the "else"
{
Console.WriteLine("Re-enter Password: (" + retryCount + " tries remaining)");
confirmPass = ReadPassword(); // changed to function call
retryCount--;
}
else if (confirmPass == pass) // <==== "else": bug
{
// Bug: it will only get here, if the confirmed password
// was entered correctly the first time
Console.WriteLine("Enter password to log in :");
finalPass = Console.ReadLine();
if (finalPass == pass)
{
Console.WriteLine("Login Successful. Welcome " + username + "!");
Console.WriteLine();
Console.WriteLine("Test Successful. Press Enter to quit.");
}
// else <==== now what?
}
if (retryCount == 0)
{
Console.WriteLine("Exceeded number of tries!!");
Console.ReadLine();
}
} while (confirmPass != pass && retryCount != 0);
}
Now the bugs become much easier to spot. I highlighted them using <=== in the comments. I will leave it to you to figure out what is wrong, and how you can fix it.

Related

Wait for user to input specific number of commands before proceeding to process the business logic?

I am creating an application that wait's for the user to input a command.
I need n #'s of command from the user before I proceed to process the business logic.
string GetInputCommand()
{
var response = string.Empty;
var num = 0;
while (true)
{
response = Console.ReadLine().ToUpper();
if (validCommand.Contains(response.ToUpper().Substring(0, 1)) && response.ToUpper().Substring(0, 1) == "W")
{
if (isValidWinningCommand(response))
break;
else if (response.Length >= 3)
Console.WriteLine("Invalid Horse Number: {0}", response.Substring(2));
else
Console.WriteLine("Please Enter Horse Number");
}
else if (int.TryParse(response.ToUpper().Substring(0, 1), out num))
{
if (num >= 1 && num <= 7)
{
if (isValidWagerCommand(response))
break;
else if (response.Length >= 3)
Console.WriteLine("Invalid Bet: {0}", response.Substring(2));
else
Console.WriteLine("Please Enter Bet Amount");
}
else
Console.WriteLine("Invalid Command: {0}", response);
}
else if (validCommand.Contains(response))
{
break;
}
else
Console.WriteLine("Invalid Command: {0}", response);
}
return response;
}
This is how I'm calling the GetInputCommand
var command = p.GetInputCommand();
foreach (string cmd in command)
{
//logic to process all the command 1 once.
}

How do I tell my console app not to continue until i enter a number?

static int beverageSelection()
{
Console.WriteLine();
int brand;
string _val = "";
Console.Write("Enter number: ");
ConsoleKeyInfo key;
do
{
key = Console.ReadKey(true);
if (key.Key != ConsoleKey.Backspace)
{
double val = 0;
bool _x = double.TryParse(key.KeyChar.ToString(), out val);
if (_x)
{
_val += key.KeyChar;
Console.Write(key.KeyChar);
}
}
else
{
if (key.Key == ConsoleKey.Backspace && _val.Length > 0)
{
_val = _val.Substring(0, (_val.Length - 1));
Console.Write("\b \b");
}
}
}
while (key.Key != ConsoleKey.Enter);
brand = Convert.ToInt32(Console.ReadLine());
return brand;
}
The method above is giving me a headache. I cannot figure out how to tell the console app that it shouldn't let me input any character or even the enter button until I have typed a number into the console. Then and only then should I be able to press enter.
In any case this program is a vending machine I created for fun and I do not fully understand the do while loop in this code just to be clear.
Use Console.ReadLine instead of Console.ReadKey:
int brand = 0;
while (true)
{
string val = Console.ReadLine();
if (int.TryParse(val, out brand)) break;
}
Improved your original code to display and accept only numbers
static void Main(string[] args)
{
Console.WriteLine();
int brand;
string _val = "";
Console.Write("Enter number: ");
while(true)
{
var key = Console.ReadKey(true);
if (key.Key == ConsoleKey.Enter && int.TryParse(_val, out brand))
{
Console.WriteLine();
break;
}
if (key.Key != ConsoleKey.Backspace)
{
int val;
if (int.TryParse(key.KeyChar.ToString(), out val))
{
_val += key.KeyChar;
Console.Write(key.KeyChar);
}
}
else
{
if (_val.Length > 0)
{
_val = _val.Substring(0, _val.Length - 1);
Console.Write("\b \b");
}
}
}
Console.WriteLine("Brand: {0}", brand);
Console.ReadKey();
}
For the sake of completeness, if anyone wants the console to prevent non-numeric characters until a numeric value is chosen, you can simply use this unerring code to accomplish your task:
int inputBoundary = Console.CursorLeft;
string consoleInput = String.Empty;
ConsoleKeyInfo inputKey;
while((inputKey = Console.ReadKey(true)).Key != ConsoleKey.Enter || consoleInput == String.Empty)
{
if (inputKey.Key == ConsoleKey.Backspace && Console.CursorLeft != inputBoundary)
{
Console.Write("\b \b");
consoleInput = consoleInput.Remove(consoleInput.Length - 1);
continue;
}
if (Char.IsNumber(inputKey.KeyChar))
{
Console.Write(inputKey.KeyChar);
consoleInput += inputKey.KeyChar;
}
}
int selection = Int32.Parse(consoleInput);

How to make number Count in loop in c#?

This is a simple begginer program with setter and getter concept
now i have to make it to first enter user name and password to get welcomed
and IF I ENTER WRONG INFO IT SHOULD DISPLAY INVALID AND 5 TRIES LEFT THEN if i again enter wrong info it should display 4 tries left and so on and finally when all tries are over it should hang the program or lock the screen or so
using System;
namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
demo obj = new demo();
string uname, pass;
Console.ForegroundColor = ConsoleColor.Green;
label1:
Console.Clear();
Console.WriteLine("Enter username");
uname = Console.ReadLine();
Console.WriteLine("Enter Password");
pass = Console.ReadLine();
obj.setName(uname);
obj.setPass(pass);
if (obj.getName() == "niit")
{
if (obj.getPass() == "1234")
{
Console.WriteLine("welcome");
}
}
else
{
Console.Clear();
Console.WriteLine("Invalid");
Console.WriteLine("\n \n \n To try again enter y");
int n = 5;
string yes = Console.ReadLine();
if (yes == "y")
{
while (n >= 1)
{
Console.Write(n + " Tries left");
goto label1;
n = --n;
}
}
}
Console.ReadKey();
}
}
class demo
{
private string name, pass;
public void setName(string name)
{
this.name = name;
}
public string getName()
{
return name;
}
public void setPass(string pass)
{
this.pass = pass;
}
public string getPass()
{
return pass;
}
}
}
Please suggest a simple begginers code to make the loop work and make the count down
A while loop should suffice. Using a boolean to detect successful password entry.
When entered, it will break out of the loop.
invalid attempts will decrement the AttemptsLeft int.
Note: I haven't tried this in Visual Studio, the logic should be sound, but I recommend debugging and stepping through it to test if it meets your criteria.
static void Main(string[] args)
{
demo obj = new demo();
string uname, pass;
Console.ForegroundColor = ConsoleColor.Green;
label1:
Console.Clear();
Console.WriteLine("Enter username");
uname = Console.ReadLine();
Console.WriteLine("Enter Password");
bool SuccessfulPassword = false;
int AttemptsLeft = 5;
while(!SuccessfulPassword && AttemptsLeft > 0){
pass = Console.ReadLine();
obj.setName(uname);
obj.setPass(pass);
if (obj.getName() == "niit")
{
if (obj.getPass() == "1234")
{
Console.WriteLine("welcome");
SuccessfulPassword = true;
}
}
else
{
AttemptsLeft--;
Console.Clear();
Console.WriteLine("Invalid");
Console.WriteLine("\n \n \n To try again enter y");
int n = 5;
string yes = Console.ReadLine();
if (yes == "y")
{
Console.Write(AttemptsLeft + " Tries left");
}
}
Console.ReadKey();
}
}
try this updated main method:
static void Main(string[] args)
{
demo obj = new demo();
int n = 5;
string uname, pass;
Console.ForegroundColor = ConsoleColor.Green;
//Console.Clear();
label1:
Console.WriteLine("\n");
Console.WriteLine("Enter username");
uname = Console.ReadLine();
Console.WriteLine("Enter Password");
pass = Console.ReadLine();
obj.setName(uname);
obj.setPass(pass);
if (obj.getName() == "niit" && obj.getPass() == "1234")
{
Console.WriteLine("welcome");
}
else
{
//Console.Clear();
if (n < 1)
{
//Add ur screenlock n hang prog code
Console.Clear();
Console.WriteLine("ScreenLock");
}
else
{
Console.WriteLine("\n Invalid");
Console.WriteLine("\n To try again enter y");
string yes = Console.ReadLine();
Console.WriteLine("\n");
if (yes == "y")
{
while (n >= 1)
{
Console.Write(n + " Tries left");
n = --n;
goto label1;
}
}
}
}
Console.ReadKey();
}
namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
demo obj = new demo();
string uname, pass;
boolean successful = false;
int32 tries = 5;
Console.ForegroundColor = ConsoleColor.Green;
label1:
Do
{
Console.Clear();
Console.WriteLine("Enter username");
uname = Console.ReadLine();
Console.WriteLine("Enter Password");
pass = Console.ReadLine();
obj.setName(uname);
obj.setPass(pass);
if (obj.getName() == "niit")
{
if (obj.getPass() == "1234")
{
Console.WriteLine("welcome");
successful = true;
}
}
if (!successful)
{
tries--;
Console.Clear();
Console.WriteLine("Invalid");
if (tries > 1)
{
Console.WriteLine("Have " + tries + " attempts left");
}
ElseIf (tries == 1)
{
Console.WriteLine("Have only one more attempt left");
}
Else
{
Console.WriteLine("Maximum number of tries exceed");
Console.WriteLine("Goodbye");
}
}
} While(!successful && Tries > 0);
}
}
Everytime you get the wrong input, you remaking your count int n = 5;
so everytime you have 5 tries left.
What you can do is to declare your count outside of the static void Main(string args[]) method
just like:
int n =5;
static void Main(string args[])
{
}
you problems are the following nested if-contitions
if (obj.getName() == "niit")
{
if (obj.getPass() == "1234")
{
Console.WriteLine("welcome");
}
}
else
{
\\...
}
If the username is correct and the pass not, it wont enter the else branch.
a better solution to ask for input until it is valid id a do ... while loop
Following example has a lot of improvements over yours.
static void Main(string[] args)
{
demo obj = new demo();
string uname, pass;
Console.ForegroundColor = ConsoleColor.Green;
int maxTries;
int tries = maxTries = 5;
do
{
if (tries != maxTries)//second and more
{
Console.Clear();
Console.WriteLine("Invalid");
Console.Write("\n\t" + tries + " Tries left");
Console.WriteLine("\n\n\n\tTry again? (y/n)");
string input;
do
{
input = Console.ReadLine();
} while (input != "y" && input != "n");
if (input == "n")
{
return; // exit the program
}
}
Console.Clear();
Console.WriteLine("Enter username");
uname = Console.ReadLine();
Console.WriteLine("Enter Password");
pass = Console.ReadLine();
obj.setName(uname);
obj.setPass(pass);
tries--;
} while (obj.getName() != "niit" || obj.getPass() != "1234");
Console.WriteLine("Wellcome");
}
PS: Classes should start with a capital letter.
goto is a relict of old times, it will mess with your programm structure and make things more complicated than they are. The only propper use i know is for fallthrough in switches, which also is needed only in rare cases.
A madeup one would be:
string vehicleType = "car";
switch(vehicleType)
{
case "truck":
Console.WriteLine("two wheeles and");
goto case "car";
case "car":
Console.WriteLine("two wheeles and");
goto case "motor cycle";
case "motor cycle":
Console.WriteLine("two wheeles");
break;
case "boat":
Console.WriteLine("no wheeles");
break;
}

Password masking console application

I tried the following code...
string pass = "";
Console.Write("Enter your password: ");
ConsoleKeyInfo key;
do
{
key = Console.ReadKey(true);
// Backspace Should Not Work
if (key.Key != ConsoleKey.Backspace)
{
pass += key.KeyChar;
Console.Write("*");
}
else
{
Console.Write("\b");
}
}
// Stops Receving Keys Once Enter is Pressed
while (key.Key != ConsoleKey.Enter);
Console.WriteLine();
Console.WriteLine("The Password You entered is : " + pass);
But this way the backspace functionality doesn't work while typing the password.
Any suggestion?
Console.Write("\b \b"); will delete the asterisk character from the screen, but you do not have any code within your else block that removes the previously entered character from your pass string variable.
Here's the relevant working code that should do what you require:
var pass = string.Empty;
ConsoleKey key;
do
{
var keyInfo = Console.ReadKey(intercept: true);
key = keyInfo.Key;
if (key == ConsoleKey.Backspace && pass.Length > 0)
{
Console.Write("\b \b");
pass = pass[0..^1];
}
else if (!char.IsControl(keyInfo.KeyChar))
{
Console.Write("*");
pass += keyInfo.KeyChar;
}
} while (key != ConsoleKey.Enter);
For this you should use the System.Security.SecureString
public SecureString GetPassword()
{
var pwd = new SecureString();
while (true)
{
ConsoleKeyInfo i = Console.ReadKey(true);
if (i.Key == ConsoleKey.Enter)
{
break;
}
else if (i.Key == ConsoleKey.Backspace)
{
if (pwd.Length > 0)
{
pwd.RemoveAt(pwd.Length - 1);
Console.Write("\b \b");
}
}
else if (i.KeyChar != '\u0000' ) // KeyChar == '\u0000' if the key pressed does not correspond to a printable character, e.g. F1, Pause-Break, etc
{
pwd.AppendChar(i.KeyChar);
Console.Write("*");
}
}
return pwd;
}
Complete solution, vanilla C# .net 3.5+
Cut & Paste
:)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleReadPasswords
{
class Program
{
static void Main(string[] args)
{
Console.Write("Password:");
string password = Orb.App.Console.ReadPassword();
Console.WriteLine("Sorry - I just can't keep a secret!");
Console.WriteLine("Your password was:\n<Password>{0}</Password>", password);
Console.ReadLine();
}
}
}
namespace Orb.App
{
/// <summary>
/// Adds some nice help to the console. Static extension methods don't exist (probably for a good reason) so the next best thing is congruent naming.
/// </summary>
static public class Console
{
/// <summary>
/// Like System.Console.ReadLine(), only with a mask.
/// </summary>
/// <param name="mask">a <c>char</c> representing your choice of console mask</param>
/// <returns>the string the user typed in </returns>
public static string ReadPassword(char mask)
{
const int ENTER = 13, BACKSP = 8, CTRLBACKSP = 127;
int[] FILTERED = { 0, 27, 9, 10 /*, 32 space, if you care */ }; // const
var pass = new Stack<char>();
char chr = (char)0;
while ((chr = System.Console.ReadKey(true).KeyChar) != ENTER)
{
if (chr == BACKSP)
{
if (pass.Count > 0)
{
System.Console.Write("\b \b");
pass.Pop();
}
}
else if (chr == CTRLBACKSP)
{
while (pass.Count > 0)
{
System.Console.Write("\b \b");
pass.Pop();
}
}
else if (FILTERED.Count(x => chr == x) > 0) { }
else
{
pass.Push((char)chr);
System.Console.Write(mask);
}
}
System.Console.WriteLine();
return new string(pass.Reverse().ToArray());
}
/// <summary>
/// Like System.Console.ReadLine(), only with a mask.
/// </summary>
/// <returns>the string the user typed in </returns>
public static string ReadPassword()
{
return Orb.App.Console.ReadPassword('*');
}
}
}
Taking the top answer, as well as the suggestions from its comments, and modifying it to use SecureString instead of String, test for all control keys, and not error or write an extra "*" to the screen when the password length is 0, my solution is:
public static SecureString getPasswordFromConsole(String displayMessage) {
SecureString pass = new SecureString();
Console.Write(displayMessage);
ConsoleKeyInfo key;
do {
key = Console.ReadKey(true);
// Backspace Should Not Work
if (!char.IsControl(key.KeyChar)) {
pass.AppendChar(key.KeyChar);
Console.Write("*");
} else {
if (key.Key == ConsoleKey.Backspace && pass.Length > 0) {
pass.RemoveAt(pass.Length - 1);
Console.Write("\b \b");
}
}
}
// Stops Receving Keys Once Enter is Pressed
while (key.Key != ConsoleKey.Enter);
return pass;
}
Mine ignores control characters and handles line wrapping:
public static string ReadLineMasked(char mask = '*')
{
var sb = new StringBuilder();
ConsoleKeyInfo keyInfo;
while ((keyInfo = Console.ReadKey(true)).Key != ConsoleKey.Enter)
{
if (!char.IsControl(keyInfo.KeyChar))
{
sb.Append(keyInfo.KeyChar);
Console.Write(mask);
}
else if (keyInfo.Key == ConsoleKey.Backspace && sb.Length > 0)
{
sb.Remove(sb.Length - 1, 1);
if (Console.CursorLeft == 0)
{
Console.SetCursorPosition(Console.BufferWidth - 1, Console.CursorTop - 1);
Console.Write(' ');
Console.SetCursorPosition(Console.BufferWidth - 1, Console.CursorTop - 1);
}
else Console.Write("\b \b");
}
}
Console.WriteLine();
return sb.ToString();
}
This masks the password with a red square, then reverts back to the original colours once the password has been entered.
It doesn't stop the user from using copy/paste to get the password, but if it's more just about stopping someone looking over your shoulder, this is a good quick solution.
Console.Write("Password ");
ConsoleColor origBG = Console.BackgroundColor; // Store original values
ConsoleColor origFG = Console.ForegroundColor;
Console.BackgroundColor = ConsoleColor.Red; // Set the block colour (could be anything)
Console.ForegroundColor = ConsoleColor.Red;
string Password = Console.ReadLine(); // read the password
Console.BackgroundColor= origBG; // revert back to original
Console.ForegroundColor= origFG;
Reading console input is hard, you need to handle special keys like Ctrl, Alt, also cursor keys and Backspace/Delete. On some keyboard layouts, like Swedish Ctrl is even needed to enter keys that exist directly on US keyboard. I believe that trying to handle this using the "low-level" Console.ReadKey(true) is just very hard, so the easiest and most robust way is to just to disable "console input echo" during entering password using a bit of WINAPI.
The sample below is based on answer to Read a password from std::cin question.
private enum StdHandle
{
Input = -10,
Output = -11,
Error = -12,
}
private enum ConsoleMode
{
ENABLE_ECHO_INPUT = 4
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetStdHandle(StdHandle nStdHandle);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out int lpMode);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetConsoleMode(IntPtr hConsoleHandle, int dwMode);
public static string ReadPassword()
{
IntPtr stdInputHandle = GetStdHandle(StdHandle.Input);
if (stdInputHandle == IntPtr.Zero)
{
throw new InvalidOperationException("No console input");
}
int previousConsoleMode;
if (!GetConsoleMode(stdInputHandle , out previousConsoleMode))
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not get console mode.");
}
// disable console input echo
if (!SetConsoleMode(stdInputHandle , previousConsoleMode & ~(int)ConsoleMode.ENABLE_ECHO_INPUT))
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not disable console input echo.");
}
// just read the password using standard Console.ReadLine()
string password = Console.ReadLine();
// reset console mode to previous
if (!SetConsoleMode(stdInputHandle , previousConsoleMode))
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not reset console mode.");
}
return password;
}
I found a bug in shermy's vanilla C# 3.5 .NET solution which otherwise works a charm. I have also incorporated Damian LeszczyƄski - Vash's SecureString idea here but you can use an ordinary string if you prefer.
THE BUG: If you press backspace during the password prompt and the current length of the password is 0 then an asterisk is incorrectly inserted in the password mask. To fix this bug modify the following method.
public static string ReadPassword(char mask)
{
const int ENTER = 13, BACKSP = 8, CTRLBACKSP = 127;
int[] FILTERED = { 0, 27, 9, 10 /*, 32 space, if you care */ }; // const
SecureString securePass = new SecureString();
char chr = (char)0;
while ((chr = System.Console.ReadKey(true).KeyChar) != ENTER)
{
if (((chr == BACKSP) || (chr == CTRLBACKSP))
&& (securePass.Length > 0))
{
System.Console.Write("\b \b");
securePass.RemoveAt(securePass.Length - 1);
}
// Don't append * when length is 0 and backspace is selected
else if (((chr == BACKSP) || (chr == CTRLBACKSP)) && (securePass.Length == 0))
{
}
// Don't append when a filtered char is detected
else if (FILTERED.Count(x => chr == x) > 0)
{
}
// Append and write * mask
else
{
securePass.AppendChar(chr);
System.Console.Write(mask);
}
}
System.Console.WriteLine();
IntPtr ptr = new IntPtr();
ptr = Marshal.SecureStringToBSTR(securePass);
string plainPass = Marshal.PtrToStringBSTR(ptr);
Marshal.ZeroFreeBSTR(ptr);
return plainPass;
}
(My) nuget package to do this, based on the top answer:
install-package PanoramicData.ConsoleExtensions
Usage:
using PanoramicData.ConsoleExtensions;
...
Console.Write("Password: ");
var password = ConsolePlus.ReadPassword();
Console.WriteLine();
Project URL: https://github.com/panoramicdata/PanoramicData.ConsoleExtensions
Pull requests welcome.
Here's a version that adds support for the Escape key (which returns a null string)
public static string ReadPassword()
{
string password = "";
while (true)
{
ConsoleKeyInfo key = Console.ReadKey(true);
switch (key.Key)
{
case ConsoleKey.Escape:
return null;
case ConsoleKey.Enter:
return password;
case ConsoleKey.Backspace:
if (password.Length > 0)
{
password = password.Substring(0, (password.Length - 1));
Console.Write("\b \b");
}
break;
default:
password += key.KeyChar;
Console.Write("*");
break;
}
}
}
Jeez guys
static string ReadPasswordLine()
{
string pass = "";
ConsoleKeyInfo key;
do
{
key = Console.ReadKey(true);
if (key.Key != ConsoleKey.Enter)
{
if (!(key.KeyChar < ' '))
{
pass += key.KeyChar;
Console.Write("*");
}
else if (key.Key == ConsoleKey.Backspace && pass.Length > 0)
{
Console.Write(Convert.ToChar(ConsoleKey.Backspace));
pass = pass.Remove(pass.Length - 1);
Console.Write(" ");
Console.Write(Convert.ToChar(ConsoleKey.Backspace));
}
}
} while (key.Key != ConsoleKey.Enter);
return pass;
}
You could append your keys to an accumulating linked list.
When a backspace key is received, remove the last key from the list.
When you receive the enter key, collapse your list into a string and do the rest of your work.
I made some changes for backspace
string pass = "";
Console.Write("Enter your password: ");
ConsoleKeyInfo key;
do
{
key = Console.ReadKey(true);
// Backspace Should Not Work
if (key.Key != ConsoleKey.Backspace)
{
pass += key.KeyChar;
Console.Write("*");
}
else
{
pass = pass.Remove(pass.Length - 1);
Console.Write("\b \b");
}
}
// Stops Receving Keys Once Enter is Pressed
while (key.Key != ConsoleKey.Enter);
Console.WriteLine();
Console.WriteLine("The Password You entered is : " + pass);
I have updated Ronnie's version after spending way too much time trying to enter a password only to find out that I had my CAPS LOCK on!
With this version what ever the message is in _CapsLockMessage will "float" at the end of the typing area and will be displayed in red.
This version takes a bit more code and does require a polling loop. On my computer CPU usage about 3% to 4%, but one could always add a small Sleep() value to decrease CPU usage if needed.
private const string _CapsLockMessage = " CAPS LOCK";
/// <summary>
/// Like System.Console.ReadLine(), only with a mask.
/// </summary>
/// <param name="mask">a <c>char</c> representing your choice of console mask</param>
/// <returns>the string the user typed in</returns>
public static string ReadLineMasked(char mask = '*')
{
// Taken from http://stackoverflow.com/a/19770778/486660
var consoleLine = new StringBuilder();
ConsoleKeyInfo keyInfo;
bool isDone;
bool isAlreadyLocked;
bool isCapsLockOn;
int cursorLeft;
int cursorTop;
ConsoleColor originalForegroundColor;
isDone = false;
isAlreadyLocked = Console.CapsLock;
while (isDone == false)
{
isCapsLockOn = Console.CapsLock;
if (isCapsLockOn != isAlreadyLocked)
{
if (isCapsLockOn)
{
cursorLeft = Console.CursorLeft;
cursorTop = Console.CursorTop;
originalForegroundColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("{0}", _CapsLockMessage);
Console.SetCursorPosition(cursorLeft, cursorTop);
Console.ForegroundColor = originalForegroundColor;
}
else
{
cursorLeft = Console.CursorLeft;
cursorTop = Console.CursorTop;
Console.Write("{0}", string.Empty.PadRight(_CapsLockMessage.Length));
Console.SetCursorPosition(cursorLeft, cursorTop);
}
isAlreadyLocked = isCapsLockOn;
}
if (Console.KeyAvailable)
{
keyInfo = Console.ReadKey(intercept: true);
if (keyInfo.Key == ConsoleKey.Enter)
{
isDone = true;
continue;
}
if (!char.IsControl(keyInfo.KeyChar))
{
consoleLine.Append(keyInfo.KeyChar);
Console.Write(mask);
}
else if (keyInfo.Key == ConsoleKey.Backspace && consoleLine.Length > 0)
{
consoleLine.Remove(consoleLine.Length - 1, 1);
if (Console.CursorLeft == 0)
{
Console.SetCursorPosition(Console.BufferWidth - 1, Console.CursorTop - 1);
Console.Write(' ');
Console.SetCursorPosition(Console.BufferWidth - 1, Console.CursorTop - 1);
}
else
{
Console.Write("\b \b");
}
}
if (isCapsLockOn)
{
cursorLeft = Console.CursorLeft;
cursorTop = Console.CursorTop;
originalForegroundColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("{0}", _CapsLockMessage);
Console.CursorLeft = cursorLeft;
Console.CursorTop = cursorTop;
Console.ForegroundColor = originalForegroundColor;
}
}
}
Console.WriteLine();
return consoleLine.ToString();
}
Here is my simple version.
Every time you hit a key, delete all from console and draw as many '*' as the length of password string is.
int chr = 0;
string pass = "";
const int ENTER = 13;
const int BS = 8;
do
{
chr = Console.ReadKey().KeyChar;
Console.Clear(); //imediately clear the char you printed
//if the char is not 'return' or 'backspace' add it to pass string
if (chr != ENTER && chr != BS) pass += (char)chr;
//if you hit backspace remove last char from pass string
if (chr == BS) pass = pass.Remove(pass.Length-1, 1);
for (int i = 0; i < pass.Length; i++)
{
Console.Write('*');
}
}
while (chr != ENTER);
Console.Write("\n");
Console.Write(pass);
Console.Read(); //just to see the pass
If I understand this correctly, you're trying to make backspace delete both the visible * character on screen and the cached character in your pass variable?
If so, then just change your else block to this:
else
{
Console.Write("\b");
pass = pass.Remove(pass.Length -1);
}
I just improve code from ask I simple and just work
string pass = ""; //create empty password string
Console.Write("Enter your password: ");
ConsoleKeyInfo key;
int passLen = 0; // base password length
do
{
key = Console.ReadKey(true); //reading keyboard key
if (key.Key == ConsoleKey.Escape) Environment.Exit(0); // If key is escape console will close (optional)
if (key.Key != ConsoleKey.Backspace && key.Key != ConsoleKey.Enter) // on key with is not bacspase and enter
{
pass += key.KeyChar; //password string add key value
Console.Write("*"); // and print star as masked char
passLen++; // upgrading password length
}
else if (passLen > 0 && key.Key == ConsoleKey.Backspace) //if password have a any symbol and u press Backspace
{
Console.Write("\b \b"); //Backspace delete star symbol and coursor back in line
passLen--; // password length is one less
pass = pass[0..^1]; // new string passowrd is string without last charter
}
}
while (key.Key != ConsoleKey.Enter); // if you press enter this stop execute with your password
Console.WriteLine();
Console.WriteLine("The Password You entered is : " + pass);
string pass = "";
Console.WriteLine("Enter your password: ");
ConsoleKeyInfo key;
do {
key = Console.ReadKey(true);
if (key.Key != ConsoleKey.Backspace) {
pass += key.KeyChar;
Console.Write("*");
} else {
Console.Write("\b \b");
char[] pas = pass.ToCharArray();
string temp = "";
for (int i = 0; i < pass.Length - 1; i++) {
temp += pas[i];
}
pass = temp;
}
}
// Stops Receving Keys Once Enter is Pressed
while (key.Key != ConsoleKey.Enter);
Console.WriteLine();
Console.WriteLine("The Password You entered is : " + pass);

How do i prevent a StackOverflow error in the following code?

I have the follwing code that provides an auto refresh feature to a WCF Console Hosted Application.
When the Console.ReadKey accepts an invalid character, it restarts the ReadKey method. If the user mashes the keyboard long enough on this code it will go into a StackOverflowException.
Does anyone have a way to re-write this code so that it doesn't cause the stack to blow?
[STAThread]
static void Main(string[] args)
{
bool restart = true;
while(restart)
{
using (var myWcfHost = new MyWcfHost())
{
myWcfHost.start();
Console.WriteLine("Press Enter to quit or Ctrl+R to restart");
restart = WaitForRestart();
}
}
}
private static bool WaitForRestart()
{
// clear users input
Console.CursorLeft = 0;
Console.Write(' ');
Console.CursorLeft = 0;
// read users input
var key = Console.ReadKey();
if ((key.Modifiers & ConsoleModifiers.Control) != 0
&& key.Key == ConsoleKey.R)
{
// refersh the settings
ConfigurationManager.RefreshSection("appSettings");
return true;
}
if (key.Key == ConsoleKey.Enter || key.Key == ConsoleKey.Escape)
{
return false;
}
return WaitForRestart();
}
Replace recursion with a loop:
private static bool WaitForRestart()
{
while (true)
{
// clear users input
Console.CursorLeft = 0;
Console.Write(' ');
Console.CursorLeft = 0;
// read users input
var key = Console.ReadKey();
if ((key.Modifiers & ConsoleModifiers.Control) != 0
&& key.Key == ConsoleKey.R)
{
// refersh the settings
ConfigurationManager.RefreshSection("appSettings");
return true;
}
if (key.Key == ConsoleKey.Enter || key.Key == ConsoleKey.Escape)
{
return false;
}
}
}
It looks like each time there's an invalid key pressed, you push another WaitForRestart onto the stack, eventually resulting in an overflow exception. I think this would fix:
private static bool WaitForRestart()
{
// clear users input
Console.CursorLeft = 0;
Console.Write(' ');
Console.CursorLeft = 0;
while (true)
{
// read users input
var key = Console.ReadKey();
if ((key.Modifiers & ConsoleModifiers.Control) != 0
&& key.Key == ConsoleKey.R)
{
// refersh the settings
ConfigurationManager.RefreshSection("appSettings");
return true;
}
if (key.Key == ConsoleKey.Enter || key.Key == ConsoleKey.Escape)
{
return false;
}
}
}

Categories

Resources