[Python] 纯文本查看 复制代码
import tkinter as tkimport ctypes
from ctypes import wintypes
import threading
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import socket
import urllib.request
import json
import socket
# ================== 邮件发送配置 ==================
SMTP_SERVER = "smtp.163.com" #这里以163邮箱为例
SMTP_PORT = 465 # SSL 端口
SENDER_EMAIL = "XXXXXXXXXXXXX.com"#发件箱地址
SENDER_PASSWORD = "XXXXXXXXXP" # ⚠️ 请替换为实际发件箱授权码(不是登录密码)
RECEIVER_EMAIL = "xxxxxxxx.com" #这里修改为接收地址邮箱
EMAIL_SUBJECT = "电脑开机提醒"
EMAIL_BODY = "您的电脑已开机,正在输入密码"
# ================== Windows API 常量 ==================
WH_KEYBOARD_LL = 13
VK_MENU = 0x12 # Alt 键
VK_LWIN = 0x5B # 左 Win 键
VK_RWIN = 0x5C # 右 Win 键
VK_TAB = 0x09
VK_ESCAPE = 0x1B
VK_SPACE = 0x20
# ================== Windows API 结构体与函数 ==================
class KBDLLHOOKSTRUCT(ctypes.Structure):
_fields_ = [
('vkCode', wintypes.DWORD),
('scanCode', wintypes.DWORD),
('flags', wintypes.DWORD),
('time', wintypes.DWORD),
('dwExtraInfo', ctypes.POINTER(ctypes.c_ulong))
]
user32 = ctypes.windll.user32
kernel32 = ctypes.windll.kernel32
HOOKPROC = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_int, wintypes.WPARAM, wintypes.LPARAM)
SetWindowsHookEx = user32.SetWindowsHookExA
SetWindowsHookEx.argtypes = [wintypes.INT, HOOKPROC, wintypes.HINSTANCE, wintypes.DWORD]
SetWindowsHookEx.restype = wintypes.HHOOK
CallNextHookEx = user32.CallNextHookEx
CallNextHookEx.argtypes = [wintypes.HHOOK, wintypes.INT, wintypes.WPARAM, wintypes.LPARAM]
CallNextHookEx.restype = ctypes.c_ssize_t
UnhookWindowsHookEx = user32.UnhookWindowsHookEx
UnhookWindowsHookEx.argtypes = [wintypes.HHOOK]
UnhookWindowsHookEx.restype = wintypes.BOOL
GetModuleHandleW = kernel32.GetModuleHandleW
GetModuleHandleW.argtypes = [wintypes.LPCWSTR]
GetModuleHandleW.restype = wintypes.HINSTANCE
GetForegroundWindow = user32.GetForegroundWindow
GetForegroundWindow.argtypes = []
GetForegroundWindow.restype = wintypes.HWND
SetForegroundWindow = user32.SetForegroundWindow
SetForegroundWindow.argtypes = [wintypes.HWND]
SetForegroundWindow.restype = wintypes.BOOL
GetAsyncKeyState = user32.GetAsyncKeyState
GetAsyncKeyState.argtypes = [ctypes.c_int]
GetAsyncKeyState.restype = ctypes.c_short
# ================== 锁屏类 ==================
class LockScreen:
def __init__(self, password="123456"): #这里设置解锁密码
self.password = password
self.root = tk.Tk()
self.root.title("")
self.root.overrideredirect(True)
self.width = self.root.winfo_screenwidth()
self.height = self.root.winfo_screenheight()
self.root.geometry(f"{self.width}x{self.height}+0+0")
self.root.attributes('-topmost', True)
self.root.configure(bg='black')
self.root.update_idletasks()
self.hwnd = self.root.winfo_id()
self.root.grab_set()
self.root.focus_force()
# 创建界面
frame = tk.Frame(self.root, bg='gray', padx=20, pady=20)
frame.place(relx=0.5, rely=0.5, anchor='center')
label = tk.Label(frame, text="请输入密码解锁", font=("Arial", 16),
bg='gray', fg='white')
label.pack(pady=10)
self.entry = tk.Entry(frame, show="*", font=("Arial", 14), width=20)
self.entry.pack(pady=10)
self.entry.focus_set()
btn = tk.Button(frame, text="解锁", command=self.check_password,
font=("Arial", 12))
btn.pack(pady=5)
self.root.bind('<Return>', lambda event: self.check_password())
self.root.protocol("WM_DELETE_WINDOW", lambda: None)
# 安装键盘钩子
self.hook_id = None
self.install_hook()
# 启动前台监控
self.foreground_check_running = True
self.check_foreground()
# 启动邮件发送线程(不阻塞界面)
self.start_email_thread()
def get_local_ip(self):
"""获取本机内网IPv4地址(排除回环地址)"""
# 方法二:使用 socket.gethostbyname_ex(可能返回多个IP)
try:
hostname = socket.gethostname()
ips = socket.gethostbyname_ex(hostname)[2]
for ip in ips:
if not ip.startswith('127.'):
return ip
except Exception:
pass
return "无法获取内网IP"
def get_public_ip(self):
"""通过外部服务获取公网IP"""
try:
# 使用 ipify.org API(返回纯文本IP)
with urllib.request.urlopen('https://api.ipify.org', timeout=5) as response:
return response.read().decode('utf-8')
except Exception:
try:
# 备用服务:httpbin.org/ip 返回JSON
with urllib.request.urlopen('https://httpbin.org/ip', timeout=5) as response:
data = json.loads(response.read().decode('utf-8'))
return data.get('origin', '获取失败')
except Exception:
return "获取公网IP失败"
# ================== 邮件发送功能 ==================
def send_email(self):
"""尝试发送邮件(在子线程中运行)"""
try:
# 先简单检查网络(尝试连接SMTP服务器)
socket.create_connection((SMTP_SERVER, SMTP_PORT), timeout=5)
except Exception:
# 无法连接网络,直接返回
return
try:
# 获取本机信息
computer_name = socket.gethostname()
local_ip = self.get_local_ip()
public_ip = self.get_public_ip()
# 构建邮件正文
body = f"""电脑已开机,正在输入密码
计算机名:{computer_name}
内网IP:{local_ip}
公网IP:{public_ip}"""
# 创建邮件
msg = MIMEMultipart()
msg['From'] = SENDER_EMAIL
msg['To'] = RECEIVER_EMAIL
msg['Subject'] = EMAIL_SUBJECT
msg.attach(MIMEText(body, 'plain', 'utf-8'))
# 使用SSL连接发送
server = smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT, timeout=10)
server.login(SENDER_EMAIL, SENDER_PASSWORD)
server.send_message(msg)
server.quit()
except Exception as e:
# 发送失败忽略(可打印调试信息)
print(f"邮件发送失败: {e}")
def start_email_thread(self):
"""启动邮件发送线程"""
thread = threading.Thread(target=self.send_email, daemon=True)
thread.start()
# ================== 键盘钩子回调 ==================
def low_level_keyboard_proc(self, nCode, wParam, lParam):
if nCode < 0:
return CallNextHookEx(self.hook_id, nCode, wParam, lParam)
if nCode == 0:
kb_struct = ctypes.cast(lParam, ctypes.POINTER(KBDLLHOOKSTRUCT)).contents
vkCode = kb_struct.vkCode
# 阻止 Alt 和 Win 键本身
if vkCode in (VK_MENU, VK_LWIN, VK_RWIN):
return 1
# 检测 Alt 组合键
alt_pressed = (GetAsyncKeyState(VK_MENU) & 0x8000) != 0
if alt_pressed:
if vkCode in (VK_TAB, VK_ESCAPE, VK_SPACE):
return 1
return CallNextHookEx(self.hook_id, nCode, wParam, lParam)
def install_hook(self):
self.proc = HOOKPROC(self.low_level_keyboard_proc)
module_handle = GetModuleHandleW(None)
self.hook_id = SetWindowsHookEx(
WH_KEYBOARD_LL,
self.proc,
module_handle,
0
)
if not self.hook_id:
print("安装键盘钩子失败!")
else:
print("键盘钩子已安装")
def uninstall_hook(self):
if self.hook_id:
UnhookWindowsHookEx(self.hook_id)
self.hook_id = None
print("键盘钩子已卸载")
# ================== 前台监控 ==================
def check_foreground(self):
if not self.foreground_check_running:
return
try:
foreground = GetForegroundWindow()
if foreground != self.hwnd:
SetForegroundWindow(self.hwnd)
self.root.focus_force()
self.root.grab_set()
self.entry.focus_set()
except:
pass
self.root.after(500, self.check_foreground)
# ================== 密码验证 ==================
def check_password(self):
if self.entry.get() == self.password:
self.foreground_check_running = False
self.uninstall_hook()
self.root.destroy()
else:
self.entry.delete(0, tk.END)
def run(self):
self.root.mainloop()
if __name__ == "__main__":
lock = LockScreen()
lock.run()