【问题标题】:Mask Password Input in a Console App在控制台应用程序中屏蔽密码输入
【发布时间】:2025-12-26 05:45:11
【问题描述】:

我正在使用 BDE 2006 编写一个控制台应用程序,我希望它能够在用户键入时提示输入密码字符串并用“*”屏蔽它。我环顾四周,但找不到如何执行此操作的示例。我看到的一切都是如何在 TEdit 中做到这一点。关于如何实现这一点的任何指示?

提前致谢,

网卡

【问题讨论】:

  • 如果您不打算使用 TEdit,您希望使用什么控件让用户输入用户名和密码?
  • 在控制台窗口中。他提到了。

标签: delphi console console-application


【解决方案1】:

这是一个可行的解决方案:

program Project2;

{$APPTYPE CONSOLE}

uses
  SysUtils, Windows;

function GetPassword(const InputMask: Char = '*'): string;
var
  OldMode: Cardinal;
  c: char;
begin
  GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), OldMode);
  SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), OldMode and not (ENABLE_LINE_INPUT or ENABLE_ECHO_INPUT));
  try
    while not Eof do
    begin
      Read(c);
      if c = #13 then // Carriage Return
        Break;
      Result := Result + c;
      if c = #8 then // Back Space
        Write(#8)
      else
        Write(InputMask);
    end;
  finally
    SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), OldMode);
  end;
end;

begin
  try
    Writeln(Format(sLineBreak + 'pswd=%s',[GetPassword]));
    Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

更新:请注意,上面的代码直观地处理 BackSpaces,但将它们嵌入密码中,这可能不是您想要的。
在这种情况下,以下代码会将它们过滤掉:

  if c = #13 then // Carriage Return
    Break;
  if (c = #8) and (Length(Result) > 0) then  // Back Space
  begin
    Delete(Result, Length(Result), 1);
    Write(#8);
  end
  else
  begin
    Result := Result + c;
    Write(InputMask);
  end;

【讨论】:

  • 通过将result := result+c移动到else子句而不是先添加然后删除它而不是添加#8不是更容易吗?
  • 如果他们执行退格,你会想要删除前一个字符。
  • 啊,是的,我明白你的意思了。
  • “Write(#8)”位对我来说很奇怪:光标向后移动,但“”字符仍然存在。我很难看出这怎么可能是故意的,我把它归结为要么被忽视/没人愿意评论,要么我的机器上的行为不同。就是说,愿意打赌这不是我的机器,因为代码似乎按照它所说的去做。我对此的解决方法是“Write(#8' '#8)”而不是“Write(#8)”。这和以前一样返回 1 个字符,但用空格覆盖 '',然后写入 #8 以再次返回 1 个字符。
  • 还有一件小事,当没有输入任何内容时按 Backspace 会写出一个星号。次要代码更改: if c = #13 then // 回车中断; if c = #8 then // Back Space begin if Result '' then begin Delete(Result, Length(Result), 1);写(#8' '#8);结尾;结束否则开始结果:=结果+ c;写(输入掩码);结束;
【解决方案2】:

我有一个带有procedure ConsoleGetPassword(const caption: String; var Password: string); 的单元,可以满足您的需求

http://gist.github.com/570659

【讨论】:

    【解决方案3】:

    这行得通。

    program Project2;
    
    {$APPTYPE CONSOLE}
    
    uses
      SysUtils, Windows;
    
    const
      BUF_LEN = 1024;
    
    var
      amt, i, cmode: cardinal;
      buf: packed array[0..BUF_LEN - 1] of char;
    
    begin
      try
    
        Write('Enter password: ');
        GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), cmode);
        SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), cmode and not ENABLE_ECHO_INPUT);
        ReadConsole(GetStdHandle(STD_INPUT_HANDLE), @buf[0], BUF_LEN, amt, nil);
        SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), cmode);
    
        Writeln;
        Writeln;
    
        Writeln('You entered: ');
        for i := 0 to amt - 3 do
          Write(buf[i]);
        Writeln;
        Writeln;
    
        Writeln('Done');
        Readln;
    
      except
        on E:Exception do
        begin
          Writeln(E.Classname, ': ', E.Message);
          Readln;
        end;
      end;
    end.
    

    【讨论】:

    • 我尝试了 Francesca 的解决方案和您的解决方案。关键区别在于您的不屏蔽(因此不显示密码长度或任何输入活动,同时仍“支持”左/右/退格/删除)。一个小问题,不输入密码会导致一些随机内存的喷射。另一个小问题是按向上/向下键并按 Enter 会将先前输入的命令放入密码中(尽管以前输入的密码不会进入命令历史记录);也许应该过滤掉向上/向下键。总的来说,我更喜欢 Francesca 的解决方案。
    【解决方案4】:

    请参阅CodeProject 上的这篇文章,它可能使用 C#,但它确实为您提供了正确的线索和方向,涉及 ReadConsoleInputWriteConsole API

    【讨论】: