Win98 可用 SendMessage(WM_GETTEXT) 直接讀取密碼框內容,但 Win2K/XP/2003 加強安全性,密碼框對此訊息免疫。常見做法是注入 DLL 鉤子,但複雜。本文提供更簡便法:暫時移除密碼框樣式 → 讀取文字 → 恢復樣式。
WindowFromPoint 取得視窗控制代碼。GetWindowLong(GWL_STYLE) 判斷是否含 ES_PASSWORD (0x20) 樣式。SendMessage(EM_GETPASSWORDCHAR) 取得原密碼字元(如 *)。PostMessage(EM_SETPASSWORDCHAR, 0, 0) 設定密碼字元為 0(移除遮罩)※必須用 PostMessage,SendMessage 無效。SendMessage(WM_GETTEXTLENGTH) 取長度 → 配置記憶體 → SendMessage(WM_GETTEXT) 讀取明文。PostMessage(EM_SETPASSWORDCHAR, 原字元, 0) 恢復遮罩 → 釋放記憶體。unit uPassword;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TfrmPassword = class(TForm)
edPassword: TEdit; // 顯示密碼的編輯框
Timer1: TTimer; // 定時器
procedure Timer1Timer(Sender: TObject);
private
public
end;
var
frmPassword: TfrmPassword;
implementation
{$R *.dfm}
procedure TfrmPassword.Timer1Timer(Sender: TObject);
var
s: PChar;
i, n: Integer;
p: TPoint;
h: HWND;
c: Integer;
begin
GetCursorPos(p); // 取得游標位置
h := WindowFromPoint(p); // 取得該處視窗控制代碼
n := GetWindowLong(h, GWL_STYLE); // 取得視窗樣式
if (n and $20) <> 0 then begin // 判斷 ES_PASSWORD 樣式
c := SendMessage(h, EM_GETPASSWORDCHAR, 0, 0); // 取得原遮罩字元
PostMessage(h, EM_SETPASSWORDCHAR, 0, 0); // 移除遮罩(必須用 PostMessage)
i := SendMessage(h, WM_GETTEXTLENGTH, 0, 0);
Inc(i);
GetMem(s, i); // 配置記憶體
SendMessage(h, WM_GETTEXT, i, Longint(s)); // 讀取明文
edPassword.Text := s; // 顯示於編輯框
PostMessage(h, EM_SETPASSWORDCHAR, c, 0); // 恢復遮罩
FreeMem(s); // 釋放記憶體
end;
end;
end.