Python 写的 Windows凭据添加工具 第二版 解决Win11 23H2, 25H2,26H1,共享文件和打印 一部份问题,不是全部!

说明:

随着 window11 不停发布新版23H2, 25H2,26H1,都要搞上共享文件和打印 的麻烦

为不想设置这么 只能用这个方法来搞好一部:

控制面板-》​编辑凭据管理器-》添加 Windows 凭据-》填入:对方计算名|用户:guest 密码:空

把这个操作写成程序方便那些 不会操作人员操作下!!! 记得打包成exe

局域网凭据一键添加工具

自动扫描局域网计算机,智能识别主机名,快速添加 Windows 网络凭据。

告别手动输入,3 秒完成全部扫描。

< 3s

扫描速度

254

最大扫描主机

30

并发 Ping

0

依赖安装

SCROLL

// 问题背景

为什么需要这个工具?

在日常局域网运维中,添加网络凭据是一件繁琐但高频的操作。

⌨️

手动输入易出错

需要记住每台计算机的名称或 IP 地址,手动逐个输入,容易拼写错误导致连接失败。

🔍

查找计算机名困难

面对几十台甚至上百台机器,不知道哪些在线、哪些关机,需要逐一排查。

命令行操作繁琐

使用 cmdkey 命令需要打开 CMD,手动输入完整参数,对非技术人员不友好。

// 核心功能

六大核心能力

从扫描到添加,全流程自动化,一键搞定。

🌐

智能局域网扫描

自动检测本机子网,通过 net view + Ping 扫描双重机制,确保不遗漏任何在线计算机。

30 路并发加速

使用线程池 30 路并发 Ping,254 个地址 2-3 秒内完成扫描,极速不等待。

🏷️

智能主机名解析

三重解析策略:DNS 反查 → NetBIOS → IP 直显,0.3 秒超时自动跳过,绝不卡顿。

📊

实时进度反馈

三阶段进度条(Net View → Ping 扫描 → 名称解析),每阶段独立显示进度与状态。

🎯

下拉选择 + 手动输入

扫描结果自动填充下拉列表,点击即选;查不到的机器也可以手动输入计算机名或 IP。

🔒

凭据一键添加

封装 cmdkey 命令,填写用户名密码即可添加,支持密码遮蔽显示,操作安全直观。

代码:

python 复制代码
import subprocess
import threading
import tkinter as tk
from tkinter import ttk, messagebox
import ipaddress
import socket
import re
from concurrent.futures import ThreadPoolExecutor, as_completed


def get_local_subnet():
    """获取本机所在子网"""
    try:
        result = subprocess.run("ipconfig", shell=True, capture_output=True,
                                text=True, encoding="gbk", errors="ignore")
        ip_pattern = r'IPv4 地址[^\d]*(\d+\.\d+\.\d+\.\d+)'
        mask_pattern = r'子网掩码[^\d]*(\d+\.\d+\.\d+\.\d+)'
        ip_match = re.search(ip_pattern, result.stdout)
        mask_match = re.search(mask_pattern, result.stdout)
        if ip_match and mask_match:
            ip = ip_match.group(1)
            mask = mask_match.group(1)
            network = ipaddress.IPv4Network(f"{ip}/{mask}", strict=False)
            return str(network), ip
    except Exception:
        pass
    return None, None


def ping_host(ip):
    """Ping 单个主机,返回 IP 或 None"""
    try:
        r = subprocess.run(
            f"ping -n 1 -w 200 {ip}",
            shell=True, capture_output=True, timeout=1
        )
        if r.returncode == 0:
            return str(ip)
    except Exception:
        pass
    return None


def resolve_hostname(ip, timeout=0.5):
    """
    快速反查主机名
    - 使用 socket 设置超时,超时就直接返回 IP
    - 不会卡住
    """
    old_timeout = socket.getdefaulttimeout()
    try:
        socket.setdefaulttimeout(timeout)  # 0.5秒超时,查不到就放弃
        hostname = socket.gethostbyaddr(ip)[0]
        short_name = hostname.split(".")[0]
        return short_name
    except (socket.herror, socket.gaierror, socket.timeout, OSError):
        return ip  # 解析失败,直接用 IP
    finally:
        socket.setdefaulttimeout(old_timeout)


def resolve_hostname_fast(ip, timeout=0.3):
    """
    多种方式快速解析,任何一种成功就返回
    优先级: gethostbyaddr → nslookup → 直接返回 IP
    """
    # 方法1: socket 反查(极短超时)
    old_timeout = socket.getdefaulttimeout()
    try:
        socket.setdefaulttimeout(timeout)
        hostname = socket.gethostbyaddr(ip)[0]
        name = hostname.split(".")[0]
        if name and name != ip:
            return name
    except Exception:
        pass
    finally:
        socket.setdefaulttimeout(old_timeout)

    # 方法2: nbtstat 查 NetBIOS 名(局域网最常用,速度很快)
    try:
        result = subprocess.run(
            f"nbtstat -A {ip}",
            shell=True, capture_output=True, text=True,
            timeout=0.5, encoding="gbk", errors="ignore"
        )
        # 找 <00> UNIQUE 那一行的名称
        for line in result.stdout.splitlines():
            if "<00>" in line and "UNIQUE" in line:
                name = line.split()[0].strip()
                if name and len(name) > 1:
                    return name
    except Exception:
        pass

    # 方法3: 都失败了,直接返回 IP
    return ip


def scan_lan(progress_callback=None):
    """
    扫描局域网中的计算机
    """
    computers = []

    # ======== 阶段1: net view ========
    if progress_callback:
        progress_callback(0, 1, 0, "正在执行 net view ...")

    try:
        result = subprocess.run(
            "net view", shell=True, capture_output=True,
            text=True, encoding="gbk", errors="ignore", timeout=15
        )
        if result.returncode == 0:
            for line in result.stdout.splitlines():
                match = re.match(r'\\\\(\S+)', line.strip())
                if match:
                    name = match.group(1)
                    if name not in computers:
                        computers.append(name)
    except Exception:
        pass

    net_view_count = len(computers)

    # ======== 阶段2: Ping 扫描 ========
    network, local_ip = get_local_subnet()
    if not network or not local_ip:
        if progress_callback:
            progress_callback(1, 1, net_view_count, "net view 完成,无法获取子网信息")
        computers.sort()
        return computers

    base = ".".join(local_ip.split(".")[:3]) + ".0/24"
    net = ipaddress.IPv4Network(base, strict=False)
    hosts = [str(ip) for ip in net.hosts() if str(ip) != local_ip]
    total = len(hosts)

    if progress_callback:
        progress_callback(0, total, net_view_count,
                          f"net view 发现 {net_view_count} 台,开始 ping 扫描...")

    # --- 2a: 多线程 Ping ---
    found_ips = []
    completed = 0

    with ThreadPoolExecutor(max_workers=30) as executor:
        future_to_ip = {executor.submit(ping_host, ip): ip for ip in hosts}
        for future in as_completed(future_to_ip):
            completed += 1
            result = future.result()
            if result:
                found_ips.append(result)
            if completed % 10 == 0 or completed == total:
                if progress_callback:
                    progress_callback(
                        completed, total, net_view_count + len(found_ips),
                        f"Ping 扫描 {completed}/{total}  |  存活 {len(found_ips)} 个"
                    )

    # --- 2b: 多线程并行反查主机名(关键加速点)---
    if progress_callback:
        progress_callback(total, total, net_view_count + len(found_ips),
                          f"Ping 完成,并行解析 {len(found_ips)} 个主机名...")

    names_found = []
    resolve_total = len(found_ips)
    resolve_done = 0

    if resolve_total > 0:
        with ThreadPoolExecutor(max_workers=20) as executor:
            future_to_ip = {
                executor.submit(resolve_hostname_fast, ip, 0.3): ip
                for ip in found_ips
            }
            for future in as_completed(future_to_ip):
                resolve_done += 1
                name = future.result()
                if name not in names_found:
                    names_found.append(name)

                if resolve_done % 5 == 0 or resolve_done == resolve_total:
                    if progress_callback:
                        progress_callback(
                            total, total, net_view_count + resolve_done,
                            f"解析主机名 {resolve_done}/{resolve_total}  |  "
                            f"已识别 {len(names_found)} 台"
                        )

    # 合并结果
    for name in names_found:
        if name not in computers:
            computers.append(name)

    if progress_callback:
        progress_callback(total, total, len(computers),
                          f"扫描完成,共发现 {len(computers)} 台计算机")

    computers.sort()
    return computers


def add_windows_credential(target_name, username="guest", password=""):
    """使用 cmdkey 命令添加 Windows 凭据"""
    try:
        cmd = f'cmdkey /add:{target_name} /user:{username}'
        if password:
            cmd += f' /pass:{password}'
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
        if result.returncode == 0:
            messagebox.showinfo("成功", f"成功添加凭据!\n目标计算机: {target_name}")
            return True
        else:
            messagebox.showerror("错误", f"添加凭据失败: {result.stderr}")
            return False
    except Exception as e:
        messagebox.showerror("错误", f"添加凭据时发生错误: {str(e)}")
        return False


class CredentialApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Windows 局域网凭据添加工具")
        self.root.geometry("500x480")
        self.root.resizable(False, False)

        style = ttk.Style()
        style.theme_use("clam")
        style.configure("Info.TLabel", font=("Microsoft YaHei UI", 9))
        style.configure("Scan.TButton", font=("Microsoft YaHei UI", 10), padding=6)
        style.configure("Action.TButton", font=("Microsoft YaHei UI", 10, "bold"), padding=6)

        # ---------- 顶部标题 ----------
        header = tk.Frame(root, bg="#0078D4", height=50)
        header.pack(fill="x")
        header.pack_propagate(False)
        tk.Label(header, text="局域网凭据添加工具", bg="#0078D4", fg="white",
                 font=("Microsoft YaHei UI", 15, "bold")).pack(expand=True)

        # ---------- 主内容区 ----------
        main_frame = tk.Frame(root, padx=20, pady=15)
        main_frame.pack(fill="both", expand=True)

        # ---- 计算机选择区 ----
        select_frame = tk.LabelFrame(main_frame, text=" 选择或输入目标计算机 ",
                                      font=("Microsoft YaHei UI", 10), padx=10, pady=8)
        select_frame.pack(fill="x", pady=(0, 8))

        combo_row = tk.Frame(select_frame)
        combo_row.pack(fill="x")
        self.computer_var = tk.StringVar()
        self.computer_combo = ttk.Combobox(
            combo_row, textvariable=self.computer_var,
            font=("Consolas", 11), width=35
        )
        self.computer_combo.pack(side="left", fill="x", expand=True, padx=(0, 10))
        self.scan_btn = ttk.Button(combo_row, text="扫描局域网", style="Scan.TButton",
                                   command=self.start_scan)
        self.scan_btn.pack(side="right")

        # ---- 进度条区域 ----
        self.progress_frame = tk.Frame(select_frame)

        self.progress_bar = ttk.Progressbar(self.progress_frame, mode="indeterminate", length=380)
        self.progress_bar.pack(fill="x")

        # 阶段指示器
        self.phase_label = tk.Label(self.progress_frame, text="",
                                     font=("Microsoft YaHei UI", 9, "bold"),
                                     fg="#0078D4", anchor="w")
        self.phase_label.pack(fill="x", pady=(3, 0))

        self.progress_label = tk.Label(self.progress_frame, text="",
                                        font=("Microsoft YaHei UI", 9), fg="#555555", anchor="w")
        self.progress_label.pack(fill="x")

        # 状态提示
        self.status_var = tk.StringVar(value="点击「扫描局域网」自动发现计算机,也可直接输入计算机名或 IP")
        self.status_label = ttk.Label(select_frame, textvariable=self.status_var,
                                       style="Info.TLabel", foreground="gray")
        self.status_label.pack(anchor="w", pady=(5, 0))

        # ---- 凭据设置区 ----
        cred_frame = tk.LabelFrame(main_frame, text=" 凭据设置 ",
                                    font=("Microsoft YaHei UI", 10), padx=10, pady=8)
        cred_frame.pack(fill="x", pady=(0, 8))

        user_row = tk.Frame(cred_frame)
        user_row.pack(fill="x", pady=3)
        tk.Label(user_row, text="用户名:", font=("Microsoft YaHei UI", 10),
                 width=8, anchor="e").pack(side="left")
        self.username_var = tk.StringVar(value="guest")
        tk.Entry(user_row, textvariable=self.username_var,
                 font=("Consolas", 11), width=30).pack(side="left", padx=5)

        pass_row = tk.Frame(cred_frame)
        pass_row.pack(fill="x", pady=3)
        tk.Label(pass_row, text="密  码:", font=("Microsoft YaHei UI", 10),
                 width=8, anchor="e").pack(side="left")
        self.password_var = tk.StringVar()
        self.pass_entry = tk.Entry(pass_row, textvariable=self.password_var,
                                   font=("Consolas", 11), width=30, show="•")
        self.pass_entry.pack(side="left", padx=5)
        self.show_pass_var = tk.BooleanVar(value=False)
        tk.Checkbutton(pass_row, text="显示", variable=self.show_pass_var,
                       command=self.toggle_password).pack(side="left")

        # ---- 按钮区 ----
        btn_frame = tk.Frame(main_frame)
        btn_frame.pack(pady=10)
        ttk.Button(btn_frame, text="添加凭据", style="Action.TButton",
                   command=self.add_credential).pack(side="left", padx=10)
        ttk.Button(btn_frame, text="清除", style="Scan.TButton",
                   command=self.clear_fields).pack(side="left", padx=10)
        ttk.Button(btn_frame, text="退出", style="Scan.TButton",
                   command=self.root.quit).pack(side="left", padx=10)

    def toggle_password(self):
        self.pass_entry.config(show="" if self.show_pass_var.get() else "•")

    def show_progress(self):
        self.progress_frame.pack(fill="x", pady=(6, 0))
        self.progress_bar.configure(mode="indeterminate", value=0, maximum=100)
        self.progress_bar.start(15)
        self.phase_label.config(text="")
        self.progress_label.config(text="准备中...")

    def switch_progress_to_determinate(self, total):
        self.progress_bar.stop()
        self.progress_bar.configure(mode="determinate", maximum=total, value=0)

    def update_progress(self, current, total, found_count, status_text):
        def _update():
            if self.progress_bar.cget("mode") == "indeterminate" and total > 0:
                self.switch_progress_to_determinate(total)

            if total > 0:
                pct = int(current / total * 100)
                self.progress_bar.configure(value=current)
                self.progress_label.config(text=f"{status_text}  ({pct}%)")

            # 自动识别阶段
            if "net view" in status_text.lower():
                self.phase_label.config(text="Phase 1/3: Net View", fg="#0078D4")
            elif "ping" in status_text.lower():
                self.phase_label.config(text="Phase 2/3: Ping 扫描", fg="#E67E22")
            elif "解析" in status_text.lower() or "识别" in status_text.lower():
                self.phase_label.config(text="Phase 3/3: 解析主机名", fg="#27AE60")
            elif "完成" in status_text.lower():
                self.phase_label.config(text="扫描完成!", fg="#27AE60")

        self.root.after(0, _update)

    def hide_progress(self):
        self.progress_bar.stop()
        self.progress_frame.pack_forget()

    def start_scan(self):
        self.scan_btn.config(state="disabled", text="扫描中...")
        self.computer_combo["values"] = []
        self.status_var.set("")
        self.show_progress()

        def do_scan():
            computers = scan_lan(
                progress_callback=lambda *args: self.root.after(0, self.update_progress, *args)
            )
            self.root.after(0, lambda: self.on_scan_done(computers))

        threading.Thread(target=do_scan, daemon=True).start()

    def on_scan_done(self, computers):
        self.scan_btn.config(state="normal", text="扫描局域网")
        self.root.after(1000, self.hide_progress)

        if computers:
            self.computer_combo["values"] = computers
            self.computer_var.set(computers[0])
            self.status_var.set(f"扫描完成,共发现 {len(computers)} 台计算机 --- 可下拉选择或手动输入")
        else:
            self.status_var.set("未扫描到计算机,请手动输入计算机名或 IP 地址")

    def add_credential(self):
        computer_name = self.computer_var.get().strip()
        if not computer_name:
            messagebox.showwarning("警告", "请输入或选择目标计算机!")
            return
        username = self.username_var.get().strip()
        password = self.password_var.get()
        if not username:
            messagebox.showwarning("警告", "请输入用户名!")
            return
        if add_windows_credential(computer_name, username, password):
            self.computer_var.set("")

    def clear_fields(self):
        self.computer_var.set("")
        self.username_var.set("guest")
        self.password_var.set("")


def main():
    root = tk.Tk()
    CredentialApp(root)
    root.mainloop()


if __name__ == "__main__":
    main()
相关推荐
神经蛙199614 小时前
🌍 别再硬编码中文了!Python Web 项目国际化(i18n)完全指南
后端·python
颜酱14 小时前
14 | 验证并修正 LLM 生成的 SQL
人工智能·python
颜酱15 小时前
13 | 使用 LangChain 生成 SQL
人工智能·python·langchain
春生野草15 小时前
个人笔记——C语言字符串、树
c语言·开发语言·笔记
风中芦苇啊15 小时前
Java EasyExcel 导入通用工具类:自定义注解映射字段 + 反射机制
java·开发语言
颜酱15 小时前
12 | 组装 SQL 生成上下文
人工智能·python
家有娇妻张兔兔15 小时前
Java 对接 PLC 主流型号最合适的方案:Apache PLC4X 实战指南
java·开发语言·plc·modbus·数据缓存·西门子s7·apache plc4x
The_Ticker16 小时前
大宗商品行情API接入教程
开发语言·python·websocket·程序人生·区块链
用户83562907805116 小时前
如何使用 Python 合并多个 Excel 文件
后端·python