import os import sys import win32com.client import tkinter as tk from tkinter import messagebox # 隐藏 tkinter 主窗口 root = tk.Tk() root.withdraw() def main(): # 检查是否通过拖拽传入文件 if len(sys.argv) != 2: messagebox.showerror("错误", "请将一个 Access 数据库文件拖拽到此 EXE 上运行!") return access_file = sys.argv[1] # 检查文件是否存在且是 Access 文件 if not os.path.exists(access_file): messagebox.showerror("错误", f"文件不存在:\n{access_file}") return ext = os.path.splitext(access_file)[1].lower() if ext not in ['.accdb', '.mdb']: messagebox.showwarning("警告", "这不是一个 Access 数据库文件(.accdb 或 .mdb)\n将尝试继续,但可能失败。") # 获取脚本所在目录(EXE 同目录) if getattr(sys, 'frozen', False): script_dir = os.path.dirname(sys.executable) else: script_dir = os.path.dirname(os.path.abspath(__file__)) vba_txt_file = os.path.join(script_dir, "vba.txt") if not os.path.exists(vba_txt_file): messagebox.showerror("错误", f"未找到 vba.txt 文件!\n请确保 vba.txt 与 EXE 在同一目录。\n路径: {vba_txt_file}") return # 读取新 VBA 代码 try: with open(vba_txt_file, "r", encoding="utf-8") as f: new_code = f.read() except Exception as e: messagebox.showerror("错误", f"读取 vba.txt 失败:\n{e}") return try: # 启动 Access(使用 DispatchEx 强制创建新实例,避免与已打开的 Access 冲突) access = win32com.client.DispatchEx("Access.Application") access.Visible = False # 后台运行,不显示窗口 # 打开数据库 access.OpenCurrentDatabase(access_file) all_forms = access.CurrentProject.AllForms form_count = all_forms.Count if form_count == 0: messagebox.showinfo("完成", "数据库中没有窗体,无需替换。") access.CloseCurrentDatabase() access.Quit() return replaced = 0 for i in range(form_count): form_name = all_forms.Item(i).Name try: access.DoCmd.OpenForm(form_name, 1) # acDesign = 1 form = access.Forms(form_name) module = form.Module line_count = module.CountOfLines if line_count > 0: module.DeleteLines(1, line_count) if new_code.strip(): module.InsertLines(1, new_code) access.DoCmd.Close(0, form_name, 1) # acForm=0, acSaveYes=1 replaced += 1 except Exception as e: access.DoCmd.Close(0, form_name, 0) # 尝试关闭,避免卡住 print(f"处理窗体 {form_name} 时出错: {e}") # 刷新所有链接表 refreshed_tables = 0 failed_tables = 0 try: db = access.CurrentDb() table_count = db.TableDefs.Count for i in range(table_count): table_def = db.TableDefs(i) # 检查是否是链接表(Connect属性不为空) if table_def.Connect: try: table_def.RefreshLink() print(f"已刷新链接表: {table_def.Name}") refreshed_tables += 1 except Exception as e: print(f"刷新链接表 {table_def.Name} 失败: {e}") failed_tables += 1 except Exception as e: print(f"刷新链接表时出错: {e}") access.CloseCurrentDatabase() access.Quit() # 构建结果消息 result_msg = f"操作完成!\n\n" result_msg += f"已处理 {form_count} 个窗体,成功替换 {replaced} 个。\n" if refreshed_tables > 0 or failed_tables > 0: result_msg += f"已刷新 {refreshed_tables} 个链接表" if failed_tables > 0: result_msg += f",{failed_tables} 个失败" result_msg += "。\n" result_msg += f"\n文件已保存: {os.path.basename(access_file)}" messagebox.showinfo("成功", result_msg) except Exception as e: messagebox.showerror("运行错误", f"操作失败:\n{e}\n\n请检查是否已启用 VBA 项目访问信任。") if __name__ == "__main__": main() # pyinstaller --onefile --noconsole --name "VBA代码批量替换" vbareplace.py