feat: add Access (.accdb) import support to import_vba.py
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
176
import_vba.py
176
import_vba.py
@@ -27,8 +27,8 @@ except ImportError:
|
|||||||
|
|
||||||
# ==================== 配置区域 ====================
|
# ==================== 配置区域 ====================
|
||||||
# 从 .env 文件读取配置
|
# 从 .env 文件读取配置
|
||||||
# 目标 xlsm 文件路径(用于导入VBA代码)
|
# 目标文件路径(支持 .xlsm / .accdb / .mdb)
|
||||||
TARGET_XLSM_FILE = os.getenv("TARGET_XLSM_FILE", "").strip() or None
|
TARGET_FILE = os.getenv("TARGET_FILE", "").strip() or None
|
||||||
# VBA代码输出目录(如果未设置,则使用源文件同目录下的VBA文件夹)
|
# VBA代码输出目录(如果未设置,则使用源文件同目录下的VBA文件夹)
|
||||||
VBA_OUTPUT_DIR = os.getenv("VBA_OUTPUT_DIR", "").strip() or None
|
VBA_OUTPUT_DIR = os.getenv("VBA_OUTPUT_DIR", "").strip() or None
|
||||||
# =================================================
|
# =================================================
|
||||||
@@ -39,6 +39,24 @@ CLASS_MODULE_DIR = "ClassModules"
|
|||||||
DOCUMENT_MODULE_DIR = "DocumentModules"
|
DOCUMENT_MODULE_DIR = "DocumentModules"
|
||||||
FORMS_DIR = "Forms"
|
FORMS_DIR = "Forms"
|
||||||
|
|
||||||
|
# 支持的文件类型
|
||||||
|
ACCESS_EXTENSIONS = {'.accdb', '.mdb'}
|
||||||
|
EXCEL_EXTENSIONS = {'.xlsm', '.xls', '.xlsb'}
|
||||||
|
|
||||||
|
|
||||||
|
def get_file_type(file_path: Path) -> str:
|
||||||
|
"""
|
||||||
|
根据文件扩展名判断文件类型
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
'access' 或 'excel'
|
||||||
|
"""
|
||||||
|
ext = file_path.suffix.lower()
|
||||||
|
if ext in ACCESS_EXTENSIONS:
|
||||||
|
return 'access'
|
||||||
|
return 'excel'
|
||||||
|
|
||||||
|
|
||||||
class VBAImporter:
|
class VBAImporter:
|
||||||
"""VBA代码导入器"""
|
"""VBA代码导入器"""
|
||||||
|
|
||||||
@@ -289,22 +307,152 @@ class VBAImporter:
|
|||||||
try: excel.Quit()
|
try: excel.Quit()
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
|
def import_vba_access(self):
|
||||||
|
"""使用Access COM导入VBA代码"""
|
||||||
|
if not self.target_file.exists():
|
||||||
|
print(f"错误: 找不到目标 Access 文件: {self.target_file}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not self.vba_dir.exists():
|
||||||
|
print(f"错误: 找不到 VBA 代码目录: {self.vba_dir}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(f"正在打开 Access 数据库: {self.target_file.name} ...")
|
||||||
|
|
||||||
|
access = None
|
||||||
|
try:
|
||||||
|
access = win32.Dispatch("Access.Application")
|
||||||
|
access.Visible = False
|
||||||
|
access.OpenCurrentDatabase(str(self.target_file))
|
||||||
|
|
||||||
|
try:
|
||||||
|
vb_project = access.VBE.VBProjects(1)
|
||||||
|
except Exception:
|
||||||
|
print("错误: 无法访问 VBA 项目。请确保信任对 VBA 工程对象模型的访问。")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print("开始导入模块...\n")
|
||||||
|
|
||||||
|
# 扫描所有模块(Access 只导入标准模块和类模块)
|
||||||
|
scan_dirs = [
|
||||||
|
(STANDARD_MODULE_DIR, ".bas"),
|
||||||
|
(CLASS_MODULE_DIR, ".cls"),
|
||||||
|
]
|
||||||
|
modules = []
|
||||||
|
for dir_name, ext in scan_dirs:
|
||||||
|
dir_path = self.vba_dir / dir_name
|
||||||
|
if not dir_path.exists():
|
||||||
|
continue
|
||||||
|
for file_path in dir_path.glob(f"*{ext}"):
|
||||||
|
modules.append({
|
||||||
|
"name": file_path.stem,
|
||||||
|
"type": dir_name,
|
||||||
|
"file_path": file_path,
|
||||||
|
"ext": ext
|
||||||
|
})
|
||||||
|
|
||||||
|
if not modules:
|
||||||
|
print("警告: 未找到任何 VBA 模块文件")
|
||||||
|
return False
|
||||||
|
|
||||||
|
temp_files_created = []
|
||||||
|
|
||||||
|
for module_info in modules:
|
||||||
|
module_name = module_info["name"]
|
||||||
|
module_type_dir = module_info["type"]
|
||||||
|
source_code_path = module_info["file_path"]
|
||||||
|
|
||||||
|
component = None
|
||||||
|
try:
|
||||||
|
component = vb_project.VBComponents(module_name)
|
||||||
|
except:
|
||||||
|
component = None
|
||||||
|
|
||||||
|
# 移除已存在的组件
|
||||||
|
if component:
|
||||||
|
try:
|
||||||
|
vb_project.VBComponents.Remove(component)
|
||||||
|
except Exception as e:
|
||||||
|
print(f" [警告] 无法移除 {module_name}: {e},将尝试更新代码")
|
||||||
|
# 回退到字符串注入
|
||||||
|
try:
|
||||||
|
code_module = component.CodeModule
|
||||||
|
num_lines = code_module.CountOfLines
|
||||||
|
if num_lines > 0:
|
||||||
|
code_module.DeleteLines(1, num_lines)
|
||||||
|
with open(source_code_path, 'r', encoding='utf-8') as f:
|
||||||
|
new_code = f.read()
|
||||||
|
if new_code.strip():
|
||||||
|
code_module.AddFromString(new_code)
|
||||||
|
print(f" [更新] {module_name} ({module_type_dir}) - 代码已更新")
|
||||||
|
except Exception as e2:
|
||||||
|
print(f" [错误] 更新代码 {module_name} 失败: {e2}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 生成临时导入文件并导入
|
||||||
|
temp_file = self._reconstruct_file_content(source_code_path, module_name, module_type_dir)
|
||||||
|
temp_files_created.append(temp_file)
|
||||||
|
|
||||||
|
try:
|
||||||
|
vb_project.VBComponents.Import(str(temp_file))
|
||||||
|
print(f" [导入] {module_name} ({module_type_dir})")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" [错误] 导入 {module_name} 失败: {e}")
|
||||||
|
|
||||||
|
# 清理临时文件
|
||||||
|
for p in temp_files_created:
|
||||||
|
try:
|
||||||
|
if p.exists(): p.unlink()
|
||||||
|
except: pass
|
||||||
|
try:
|
||||||
|
temp_dir = Path(tempfile.gettempdir()) / "vba_import_temp"
|
||||||
|
if temp_dir.exists(): shutil.rmtree(temp_dir)
|
||||||
|
except: pass
|
||||||
|
|
||||||
|
print("\n正在保存...")
|
||||||
|
try:
|
||||||
|
import time
|
||||||
|
time.sleep(1) # 等待 VBE 完成
|
||||||
|
print("已保存更改。")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"保存时出错: {e}")
|
||||||
|
|
||||||
|
print(f"\n导入完成!目标文件: {self.target_file.name}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n发生未处理的错误: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return False
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if access:
|
||||||
|
try:
|
||||||
|
access.CloseCurrentDatabase()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
access.Quit()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
print("VBA代码导入工具 (V4.0 - 基于配置)")
|
print("VBA代码导入工具 (V5.0 - 支持Excel和Access)")
|
||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
print()
|
print()
|
||||||
|
|
||||||
script_dir = Path(__file__).parent
|
script_dir = Path(__file__).parent
|
||||||
|
|
||||||
# 检查配置
|
# 检查配置
|
||||||
if not TARGET_XLSM_FILE:
|
if not TARGET_FILE:
|
||||||
print("错误: 未配置 TARGET_XLSM_FILE")
|
print("错误: 未配置 TARGET_FILE")
|
||||||
print("请在 .env 文件中设置目标 Excel 文件路径")
|
print("请在 .env 文件中设置目标文件路径(支持.xlsm和.accdb)")
|
||||||
return
|
return
|
||||||
|
|
||||||
# 确定目标文件路径
|
# 确定目标文件路径
|
||||||
target_path = Path(TARGET_XLSM_FILE)
|
target_path = Path(TARGET_FILE)
|
||||||
if not target_path.is_absolute():
|
if not target_path.is_absolute():
|
||||||
target_path = script_dir / target_path
|
target_path = script_dir / target_path
|
||||||
|
|
||||||
@@ -312,8 +460,7 @@ def main():
|
|||||||
print(f"错误: 配置的文件不存在: {target_path}")
|
print(f"错误: 配置的文件不存在: {target_path}")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not target_path.suffix.lower() == '.xlsm':
|
file_type = get_file_type(target_path)
|
||||||
print(f"警告: 文件扩展名不是 .xlsm: {target_path.name}")
|
|
||||||
|
|
||||||
# 确定 VBA 代码目录
|
# 确定 VBA 代码目录
|
||||||
if VBA_OUTPUT_DIR:
|
if VBA_OUTPUT_DIR:
|
||||||
@@ -332,13 +479,14 @@ def main():
|
|||||||
print(" 2. 或在 .env 文件中设置 VBA_OUTPUT_DIR 指定代码目录")
|
print(" 2. 或在 .env 文件中设置 VBA_OUTPUT_DIR 指定代码目录")
|
||||||
return
|
return
|
||||||
|
|
||||||
print(f"目标 Excel 文件: {target_path.name}")
|
type_label = "Access" if file_type == "access" else "Excel"
|
||||||
|
print(f"目标文件: {target_path.name} ({type_label})")
|
||||||
print(f"VBA 代码目录: {vba_path}")
|
print(f"VBA 代码目录: {vba_path}")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
# 确认操作
|
# 确认操作
|
||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
print("警告: 此操作将覆盖目标 Excel 文件中的 VBA 代码。")
|
print(f"警告: 此操作将覆盖目标{type_label}文件中的 VBA 代码。")
|
||||||
choice = input("\n确认继续? (y/n): ").lower().strip()
|
choice = input("\n确认继续? (y/n): ").lower().strip()
|
||||||
|
|
||||||
if choice != 'y':
|
if choice != 'y':
|
||||||
@@ -354,7 +502,11 @@ def main():
|
|||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
print()
|
print()
|
||||||
|
|
||||||
success = importer.import_vba()
|
# 根据文件类型选择导入方法
|
||||||
|
if file_type == 'access':
|
||||||
|
success = importer.import_vba_access()
|
||||||
|
else:
|
||||||
|
success = importer.import_vba()
|
||||||
|
|
||||||
print()
|
print()
|
||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
|
|||||||
Reference in New Issue
Block a user