活动介绍
file-type

win32应用程序异常处理:.Net平台与DirectX解析

ZIP文件

下载需积分: 5 | 35KB | 更新于2024-12-22 | 126 浏览量 | 3 评论 | 0 下载量 举报 收藏
download 立即下载
Win32是一个广泛使用的应用程序接口(API),最初由微软公司为Windows NT系统设计,后来也应用于Windows 95及之后的32位版本。问题中提到的'拼接'可能指的是内存拼接操作,即对内存地址的不合法操作或访问,这可能导致程序运行时崩溃或抛出异常。由于涉及到Win32 API,我们可以推断这是一个底层系统调用的问题。 描述中询问是否是.Net应用程序、使用什么编程语言、运行在32位或64位平台、以及操作系统的具体类型。.Net是一个由微软开发的软件框架,支持多种编程语言如C#, VB.NET等,并且可以运行在Windows的32位和64位平台上。了解这些信息对于解决问题至关重要,因为不同的编程语言和平台可能需要不同的调试方法和工具。 提到的标签'DirectX'可能意味着程序中使用了DirectX技术。DirectX是微软提供的一系列应用程序接口(API),主要用于处理多媒体任务,如图形和声音。如果应用程序使用了DirectX,并且在进行底层图形处理时发生了异常,可能需要检查DirectX相关的代码,确保正确初始化和使用DirectX资源。 文件名称'exception-regarding-invalid-win32-application.pdf'暗示这可能是关于解决这类异常问题的官方文档或技术手册。文档可能包含有关如何识别和修复导致Win32应用程序无效拼接异常的信息,以及如何处理与DirectX相关的错误。 综合以上信息,知识点主要包括: 1. Win32 API:这是微软公司开发的一套为Windows平台提供编程接口的函数库,用于执行基本的操作系统任务,如窗口管理、文件操作等。 2. 应用程序异常:指程序在执行过程中出现的非预期情况,通常是由于编程错误导致。异常处理是程序设计中的重要部分,用于管理和修复这些错误情况。 3. .Net应用程序:这是一种软件框架,支持多种编程语言和平台。.Net应用程序在Windows上运行时可能会遇到各种异常问题。 4. 编程语言:在.Net框架中,C# 和 VB.NET 是常用的编程语言。不同的编程语言有不同的语法和编程习惯,这可能影响异常处理的方式。 5. 平台兼容性:32位和64位平台在硬件和软件架构上有差异,因此程序需要适配不同的平台才能正常运行。 6. 操作系统:不同版本的Windows操作系统可能对Win32 API调用有不同的支持和限制,了解运行平台的操作系统版本对于解决兼容性问题至关重要。 7. DirectX技术:一套用于处理图形和声音的API,常用于开发多媒体和游戏程序。DirectX的使用与异常问题的诊断可能有关联。 8. 内存管理:涉及到内存的分配、访问、释放等问题。错误的内存操作可能导致拼接异常,需要通过精确的内存管理来预防和修复。 9. 调试与诊断:在遇到异常时,通常需要使用调试工具来诊断问题的根源。了解异常信息、堆栈跟踪、错误日志等是解决问题的关键步骤。 10. 文档资料:专业文档如'exception-regarding-invalid-win32-application.pdf',通常提供详细的技术说明和解决方案,是解决此类问题的重要参考资源。"

相关推荐

filetype

:-1: error: LNK1181: 无法打开输入文件“INCLUDEPATH.obj” 下面是我的QT程序的pro文件 QT += core gui QMAKE_CC +=C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.16.27023/bin/Hostx64/x64/cl.exe QMAKE_CXX +=C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.16.27023/bin/Hostx64/x64/cl.exe greaterThan(QT_MAJOR_VERSION, 4): QT += widgets QT += serialport QT += serialbus INCLUDEPATH += $$QTDIR/include DEFINES += Q_BIG_ENDIAN=4321 Q_LITTLE_ENDIAN=1234 CONFIG += c++11 # The following define makes your compiler emit warnings if you use # any Qt feature that has been marked deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS # You can also make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. # You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ ImageBuffer.cpp \ MvCamera.cpp \ countthread.cpp \ grabthread.cpp \ keyboard.cpp \ main.cpp \ plccontrol.cpp \ resourcethread.cpp \ setup.cpp \ testdata.cpp \ tipwidget1.cpp \ tipwidget2.cpp \ widget.cpp HEADERS += \ ImageBuffer.h \ MvCamera.h \ countthread.h \ grabthread.h \ keyboard.h \ plccontrol.h \ resourcethread.h \ setup.h \ testdata.h \ tipwidget1.h \ tipwidget2.h \ vikey_sdk/Vikey.h \ widget.h FORMS += \ keyboard.ui \ plccontrol.ui \ setup.ui \ tipwidget1.ui \ tipwidget2.ui \ widget.ui # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target #DESTDIR += bin #海康相机配置 LIBS += -L$$PWD/Hik_Libs \ -lMvCameraControl \ INCLUDEPATH += $$PWD/Hik_Includes DEPENDPATH += $$PWD/Hik_Includes ##OpenCV配置 # OPENCV = D:/Opencv4.0.1/opencv/build # LIBS += -LD:/Opencv4.0.1/opencv/build/x64/vc15/lib/-lopencv_world401d # LIBS += -LC:/Windows/SysWOW64/ -lgdi32 # LIBS += -lgdi32 # INCLUDEPATH += $$OPENCV/include # DEPENDPATH += $$OPENCV/x64/vc15/bin # OPENCV = D:/Opencv4.0.1/opencv/build # INCLUDEPATH += D:/Opencv4.0.1/opencv/build/include/ # D:/Opencv4.0.1/opencv/build/include/opencv2 # D:/Opencv4.0.1/opencv/build/include/opencv # LIBS += -LD:/Opencv4.0.1/opencv/build/x64/vc15/lib/-lopencv_world401d.lib # win32:CONFIG(release, debug|release): LIBS += -L$$PWD/../../../../D:/Opencv4.0.1/opencv/build/x64/vc15/lib/ -lopencv_world401 # else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/../../../../D:/Opencv4.0.1/opencv/build/x64/vc15/lib/ -lopencv_world401d # else:unix: LIBS += -L$$PWD/../../../../D:/Opencv4.0.1/opencv/build/x64/vc15/lib/ -lopencv_world401 # INCLUDEPATH += $$PWD/../../../../Opencv4.0.1/opencv/build/include # DEPENDPATH += $$PWD/../../../../Opencv4.0.1/opencv/build/include # *************************Qt配置Halcon***************************** # MacOS specific settings. Note that while dyld will search under # /Library/Frameworks by default, the preprocessor/compiler/linker will not # and need to be told explicitly. #macx { # QMAKE_CXXFLAGS += -F/Library/Frameworks # QMAKE_LFLAGS += -F/Library/Frameworks # LIBS += -framework HALCONCpp #} #else { # #defines # win32:DEFINES += WIN32 # #includes # INCLUDEPATH += "$$(HALCONROOT)/include" # INCLUDEPATH += "$$(HALCONROOT)/include/halconcpp" # #libs # QMAKE_LIBDIR += "$$(HALCONROOT)/lib/$$(HALCONARCH)" # unix:LIBS += -lhalconcpp -lhalcon -lXext -lX11 -ldl -lpthread # win32:LIBS += "$$(HALCONROOT)/lib/$$(HALCONARCH)/halconcpp.lib" \ # "$$(HALCONROOT)/lib/$$(HALCONARCH)/halcon.lib" #} #win32:CONFIG(release, debug|release): LIBS += -LD:/opencv/build/x64/vc16/lib/ -lopencv_world4100 #else:win32:CONFIG(debug, debug|release): LIBS += -LD:/opencv/build/x64/vc16/lib/ -lopencv_world4100d #INCLUDEPATH += D:/opencv/build/x64/vc16 #DEPENDPATH += D:/opencv/build/x64/vc16 ## Release mode #win32:CONFIG(release, debug|release): LIBS += -LD:/Opencv4.0.1/opencv/opencv_build/install/x64/vc15/lib/ -lopencv_world401 ## Debug mode #else:win32:CONFIG(debug, debug|release): LIBS += -LD:/Opencv4.0.1/opencv/opencv_build/install/x64/vc15/lib/ -lopencv_world401d ## Include path for header files #INCLUDEPATH += D:/Opencv4.0.1/opencv/opencv_build/install/x64/vc15/include #DEPENDPATH += D:/Opencv4.0.1/opencv/opencv_build/install/x64/vc15/include win32:CONFIG(release, debug|release): LIBS += -LD:/Opencv4.0.1/opencv/opencv_build/install/x64/vc15/lib/ -lopencv_world401 else:win32:CONFIG(debug, debug|release): LIBS += -LD:/Opencv4.0.1/opencv/opencv_build/install/x64/vc15/lib/ -lopencv_world401d else:unix: LIBS += -LD:/Opencv4.0.1/opencv/opencv_build/install/x64/vc15/lib/ -lopencv_world401 INCLUDEPATH += D:/Opencv4.0.1/opencv/opencv_build/install/x64/vc15 DEPENDPATH += D:/Opencv4.0.1/opencv/opencv_build/install/x64/vc15 INCLUDEPATH += "D:/Opencv4.0.1/opencv/build/include" LIBS += -L"D:/Opencv4.0.1/opencv/build/x64/vc15/lib" -lopencv_world401

filetype

// YANGJEE_CUsuallyFunction.h 文件内容: #pragma once #include <atomic> #include <string> namespace YANGJEE { class YANGJEE_CUsuallyFunction { static std::atomic<bool> cancelToken; static std::atomic<bool> networkInit; public: // 检查文件是否存在 static bool fileExists(const std::string& filePath); static void initializeNetwork(); // 新增初始化函数 private: static bool isNetworkAvailable(const std::string& path); static std::string normalizePath(const std::string& path); }; } // YANGJEE_CUsuallyFunction.cpp 文件内容: #include "YANGJEE_CUsuallyFunction.h" //#include "YANGJEE_UfunBasic.h" using namespace std; //using namespace YANGJEE; using std::string; //获取网卡物理地址 #include <winsock2.h> #include <Iphlpapi.h> #include <stdio.h> #include <chrono> #include <fstream> #include <future> #include <system_error> // for std::errc #ifdef _WIN32 #include <windows.h> #include <winnetwk.h> #include <lm.h> // 包含网络资源类型定义 #pragma comment(lib, "Iphlpapi.lib") #pragma comment(lib, "netapi32.lib") // Windows网络库 #pragma comment(lib, "mpr.lib") // 添加网络库链接 //#else #include <sys/stat.h> //#include <unistd.h> #endif std::atomic<bool> YANGJEE::YANGJEE_CUsuallyFunction::cancelToken(false); std::atomic<bool> YANGJEE::YANGJEE_CUsuallyFunction::networkInit(false); // 新增初始化标志 // 新增网络初始化函数 void YANGJEE::YANGJEE_CUsuallyFunction::initializeNetwork() { #ifdef _WIN32 if (!networkInit.exchange(true)) { WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { throw std::runtime_error("WSAStartup failed"); } } #endif } // 检查文件是否存在 //超时关闭 bool YANGJEE::YANGJEE_CUsuallyFunction::fileExists(const std::string& filePath) { try { initializeNetwork(); // 确保网络初始化 const bool isNetworkPath = (filePath.size() >= 2 && filePath[0] == '\\' && filePath[1] == '\\') || (filePath.find("/mnt/") == 0) || (filePath.find("/cifs/") == 0); const auto timeout = isNetworkPath ? std::chrono::seconds(3) : std::chrono::milliseconds(500); auto future = std::async(std::launch::async, [&]() -> bool { try { const auto sanitizedPath = normalizePath(filePath); if (isNetworkPath && !isNetworkAvailable(sanitizedPath)) { return false; } // 增强型取消检查(带内存屏障) for (int i = 0; i < 30 && !cancelToken.load(std::memory_order_acquire); ++i) { std::this_thread::sleep_for(100ms); } if (cancelToken.load(std::memory_order_relaxed)) return false; #ifdef _WIN32 int wpath_len = MultiByteToWideChar(CP_UTF8, 0, sanitizedPath.c_str(), -1, nullptr, 0); if (wpath_len == 0) return false; std::wstring wpath(wpath_len, 0); MultiByteToWideChar(CP_UTF8, 0, sanitizedPath.c_str(), -1, &wpath[0], wpath_len); const DWORD attrs = GetFileAttributesW(wpath.c_str()); return (attrs != INVALID_FILE_ATTRIBUTES) && !(attrs & FILE_ATTRIBUTE_DIRECTORY); #else struct stat st; return (stat(sanitizedPath.c_str(), &st) == 0) && S_ISREG(st.st_mode); #endif } catch (const std::exception& ex) { // 记录异常日志 return false; } catch (...) { return false; } }); auto status = future.wait_for(timeout); if (status == std::future_status::ready) { return future.get(); } // 安全取消流程 cancelToken.store(true, std::memory_order_release); future.wait(); cancelToken.store(false, std::memory_order_relaxed); } catch (const std::exception& ex) { // 记录初始化异常 return false; } catch (...) { return false; } return false; } // 改进的网络可用性检查 bool YANGJEE::YANGJEE_CUsuallyFunction::isNetworkAvailable(const std::string& path) { #ifdef _WIN32 try { int wlen = MultiByteToWideChar(CP_UTF8, 0, path.c_str(), -1, NULL, 0); if (wlen == 0) return false; std::wstring wserverPath(wlen, 0); MultiByteToWideChar(CP_UTF8, 0, path.c_str(), -1, &wserverPath[0], wlen); std::wstring serverName = wserverPath.substr(2, wserverPath.find(L'\\', 2) - 2); wchar_t* nonConstServerName = _wcsdup(serverName.c_str()); if (!nonConstServerName) { // LOG_ERROR("Memory allocation failed"); return false; } NETRESOURCEW nr = { RESOURCE_GLOBALNET, RESOURCETYPE_DISK, RESOURCEDISPLAYTYPE_SERVER, 0, NULL, nonConstServerName, NULL, NULL }; DWORD result = WNetAddConnection2W(&nr, NULL, NULL, CONNECT_TEMPORARY | CONNECT_UPDATE_PROFILE); free(nonConstServerName); if (result == NO_ERROR) { // 安全断开连接 DWORD cancelResult = WNetCancelConnection2W(serverName.c_str(), 0, TRUE); if (cancelResult != NO_ERROR) { // LOG_WARN("Failed to disconnect: {}", cancelResult); } return true; } if (result == ERROR_EXTENDED_ERROR) { DWORD errCode = 0; WCHAR errorBuf[256] = { 0 }; WCHAR providerBuf[256] = { 0 }; DWORD bufSize = sizeof(errorBuf) / sizeof(WCHAR); DWORD provSize = sizeof(providerBuf) / sizeof(WCHAR); DWORD errResult = WNetGetLastErrorW( &errCode, errorBuf, bufSize, providerBuf, provSize ); if (errResult == NO_ERROR) { // LOG_ERROR("Network error [{}]: {}", errCode, errorBuf); } return false; } return (result == ERROR_SESSION_CREDENTIAL_CONFLICT) || (result == ERROR_NO_NETWORK); } catch (...) { return false; } #else return system("ping -c 1 8.8.8.8 > /dev/null 2>&1") == 0; #endif } // 路径标准化 std::string YANGJEE::YANGJEE_CUsuallyFunction::normalizePath(const std::string& path) { #ifdef _WIN32 std::string wPath = path; std::replace(wPath.begin(), wPath.end(), '/', '\\'); // 处理超长路径(Windows) if (wPath.length() >= 256) { wPath = "\\\\?\\" + wPath; } size_t pos = 2; while ((pos = wPath.find('\\', pos)) != std::string::npos) { wPath.replace(pos, 1, 1, '/'); pos += 2; } return wPath; #else return path; #endif } 我在项目中的调用方式: std::string YANGJEENXAnthorizefilePath = R"(\\\\192.168.70.165\\渝岚专用区\\重庆渝岚模具有限公司\\渝岚数据盘\\杨小林\\临时文件\\YANGJEE-SIEMENS_PLM_NX_Tools\\Anthorize\\YANGJEENXAnthorize.txt)"; YANGJEE::YANGJEE_CUsuallyFunction::initializeNetwork(); try { if (YANGJEE::YANGJEE_CUsuallyFunction::fileExists(YANGJEENXAnthorizefilePath)) { } else { // 处理文件不存在或网络错误 } } catch (const std::exception& ex) { // 处理异常 } 帮我检查一下上述代码。现在的问题是及时网络连接良好,并且手动可以访问YANGJEENXAnthorizefilePath文件,但在项目中却抛出“处理文件不存在或网络错误”;而且还会造成开发的软件直接崩溃掉,运行系统windows10。 帮我把代码优化后完整的展现出来

filetype

点击执行比较时,出现处理失败:‘NoneType’ object is not subscriptable #软件著作人:杨晨 2025.07.11 import os import subprocess import shutil import time import tkinter as tk from tkinter import filedialog, ttk, scrolledtext, messagebox, PhotoImage import threading import queue import traceback import webbrowser import datetime import configparser import win32com.client class DiffProcessorApp: def __init__(self, root): self.root = root root.title("ファイル比較実施工具") root.geometry("1000x700") root.configure(bg="#f5f5f5") self.style = ttk.Style() self.style.theme_use('clam') self.style.configure('TButton', font=('Segoe UI', 10, 'bold'), borderwidth=1, foreground="#333", background="#4CAF50", bordercolor="#388E3C", relief="flat", padding=8, anchor="center") self.style.map('TButton', background=[('active', '#388E3C'), ('disabled', '#BDBDBD')], foreground=[('disabled', '#9E9E9E')]) self.style.configure('View.TButton', font=('Segoe UI', 10, 'bold'), borderwidth=1, foreground="#008000", background="#4CAF50", bordercolor="#388E3C", relief="flat", padding=8, anchor="center") self.style.map('View.TButton', background=[('active', '#388E3C'), ('disabled', '#BDBDBD')], foreground=[('active', '#004D00'), ('disabled', '#808080')]) self.style.configure('TLabel', font=('Segoe UI', 9), background="#f5f5f5") self.style.configure('TLabelframe', font=('Segoe UI', 10, 'bold'), background="#f5f5f5", relief="flat", borderwidth=2) self.style.configure('TLabelframe.Label', font=('Segoe UI', 10, 'bold'), background="#f5f5f5", foreground="#2E7D32") self.style.configure('Treeview', font=('Segoe UI', 9), rowheight=25) self.style.configure('Treeview.Heading', font=('Segoe UI', 9, 'bold')) main_frame = ttk.Frame(root, padding="15") main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) header_frame = ttk.Frame(main_frame) header_frame.pack(fill=tk.X, pady=(0, 15)) try: icon = PhotoImage(file="folder_icon.png") self.icon_label = ttk.Label(header_frame, image=icon) self.icon_label.image = icon self.icon_label.pack(side=tk.LEFT, padx=(0, 10)) except: self.icon_label = ttk.Label(header_frame, text="📁", font=("Arial", 24)) self.icon_label.pack(side=tk.LEFT, padx=(0, 10)) title_label = ttk.Label(header_frame, text="ファイル比較実施工具", font=("Segoe UI", 18, "bold"), foreground="#2E7D32") title_label.pack(side=tk.LEFT) file_frame = ttk.LabelFrame(main_frame, text="文件夹选择", padding="12") file_frame.pack(fill=tk.X, pady=5) self.old_folder_entry, _ = self.create_folder_selector(file_frame, "原始文件夹:", "old_folder") self.new_folder_entry, _ = self.create_folder_selector(file_frame, "修改后文件夹:", "new_folder") options_frame = ttk.LabelFrame(main_frame, text="比较选项", padding="12") options_frame.pack(fill=tk.X, pady=5) self.recursive_var = tk.BooleanVar(value=True) recursive_check = ttk.Checkbutton(options_frame, text="递归比较子文件夹", variable=self.recursive_var) recursive_check.grid(row=0, column=0, padx=10, pady=5, sticky=tk.W) filter_frame = ttk.Frame(options_frame) filter_frame.grid(row=0, column=1, padx=10, pady=5, sticky=tk.W) ttk.Label(filter_frame, text="文件过滤:").pack(side=tk.LEFT, padx=(0, 5)) self.filter_var = tk.StringVar(value="*.*") filter_entry = ttk.Entry(filter_frame, textvariable=self.filter_var, width=15) filter_entry.pack(side=tk.LEFT) self.excel_frame = ttk.LabelFrame(main_frame, text="输出设置", padding="12") self.excel_frame.pack(fill=tk.X, pady=5) ttk.Label(self.excel_frame, text="目标Excel文件:").grid(row=0, column=0, sticky=tk.W, padx=5, pady=5) self.excel_file_entry = ttk.Entry(self.excel_frame, width=60) self.excel_file_entry.grid(row=0, column=1, padx=5, pady=5) ttk.Button(self.excel_frame, text="浏览...", command=lambda: self.select_file(self.excel_file_entry, "excel_file", [("Excel文件", "*.xlsx *.xlsm")])).grid(row=0, column=2, padx=5, pady=5) winmerge_frame = ttk.Frame(self.excel_frame) winmerge_frame.grid(row=1, column=0, columnspan=3, sticky=tk.W, padx=5, pady=5) ttk.Label(winmerge_frame, text="WinMerge路径:").grid(row=0, column=0, sticky=tk.W) self.winmerge_entry = ttk.Entry(winmerge_frame, width=60) self.winmerge_entry.grid(row=0, column=1, padx=5) self.winmerge_entry.insert(0, r"E:\App\WinMerge\WinMerge2.16.46.0\WinMergeU.exe") ttk.Button(winmerge_frame, text="浏览...", command=lambda: self.select_file(self.winmerge_entry, "winmerge_path", [("WinMerge 可执行文件", "*.exe")])).grid(row=0, column=2) delete_frame = ttk.Frame(self.excel_frame) delete_frame.grid(row=2, column=0, columnspan=3, sticky=tk.W, padx=5, pady=5) self.delete_temp_files_var = tk.BooleanVar(value=False) delete_check = ttk.Checkbutton( delete_frame, text="完成后删除临时文件", variable=self.delete_temp_files_var, command=self.update_view_button_state ) delete_check.grid(row=0, column=0, padx=5, sticky=tk.W) self.delete_status_label = ttk.Label( delete_frame, text="(勾选后将删除报告文件,无法查看)", foreground="#FF0000", font=("Segoe UI", 9) ) self.delete_status_label.grid(row=0, column=1, padx=5) button_frame = ttk.Frame(main_frame) button_frame.pack(fill=tk.X, pady=10) self.run_button = ttk.Button(button_frame, text="执行比较", command=self.start_processing, width=20, style='TButton') self.run_button.pack(side=tk.LEFT) self.stop_button = ttk.Button(button_frame, text="停止", command=self.stop_processing, width=10, state=tk.DISABLED) self.stop_button.pack(side=tk.LEFT, padx=10) self.view_folder_report_button = ttk.Button(button_frame, text="查看文件夹报告", command=lambda: self.view_report("folder"), width=15, state=tk.DISABLED, style='View.TButton') self.view_folder_report_button.pack(side=tk.LEFT, padx=10) self.progress = ttk.Progressbar(main_frame, orient=tk.HORIZONTAL, length=700, mode='determinate') self.progress.pack(fill=tk.X, pady=5) status_frame = ttk.Frame(main_frame) status_frame.pack(fill=tk.X, pady=5) self.status_var = tk.StringVar(value="准备就绪") status_label = ttk.Label(status_frame, textvariable=self.status_var, font=("Segoe UI", 9), foreground="#2E7D32") status_label.pack(side=tk.LEFT) notebook = ttk.Notebook(main_frame) notebook.pack(fill=tk.BOTH, expand=True, pady=5) tree_frame = ttk.Frame(notebook, padding="5") notebook.add(tree_frame, text="文件夹结构") self.tree = ttk.Treeview(tree_frame, columns=("Status"), show="tree") self.tree.heading("#0", text="文件夹结构", anchor=tk.W) self.tree.heading("Status", text="状态", anchor=tk.W) self.tree.column("#0", width=400) self.tree.column("Status", width=100) vsb = ttk.Scrollbar(tree_frame, orient="vertical", command=self.tree.yview) hsb = ttk.Scrollbar(tree_frame, orient="horizontal", command=self.tree.xview) self.tree.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set) self.tree.grid(row=0, column=0, sticky="nsew") vsb.grid(row=0, column=1, sticky="ns") hsb.grid(row=1, column=0, sticky="ew") log_frame = ttk.Frame(notebook, padding="5") notebook.add(log_frame, text="执行日志") self.log_text = scrolledtext.ScrolledText(log_frame, height=10, wrap=tk.WORD, font=("Consolas", 9)) self.log_text.pack(fill=tk.BOTH, expand=True) self.log_text.config(state=tk.DISABLED) tree_frame.grid_rowconfigure(0, weight=1) tree_frame.grid_columnconfigure(0, weight=1) self.processing = False self.queue = queue.Queue() self.folder_report_path = None self.files_dir = None self.copied_html_files = [] self.config_file = "folder_compare_config.ini" self.load_paths() self.root.after(100, self.process_queue) def update_view_button_state(self): if self.delete_temp_files_var.get(): self.view_folder_report_button.config(state=tk.DISABLED) self.style.configure('View.TButton', foreground='#808080', background='#BDBDBD') else: if self.folder_report_path and os.path.exists(self.folder_report_path): self.view_folder_report_button.config(state=tk.NORMAL) self.style.configure('View.TButton', foreground='#008000', background='#4CAF50') else: self.view_folder_report_button.config(state=tk.DISABLED) self.style.configure('View.TButton', foreground='#808080', background='#BDBDBD') self.view_folder_report_button.configure(style='View.TButton') def create_folder_selector(self, parent, label_text, config_key): frame = ttk.Frame(parent) frame.pack(fill=tk.X, pady=5) ttk.Label(frame, text=label_text).grid(row=0, column=0, sticky=tk.W, padx=5, pady=5) entry = ttk.Entry(frame, width=70) entry.grid(row=0, column=1, padx=5, pady=5) button = ttk.Button(frame, text="浏览文件夹...", command=lambda: self.select_folder(entry, config_key)) button.grid(row=0, column=2, padx=5, pady=5) return entry, button def select_folder(self, entry, config_key): initial_dir = self.get_last_path(config_key) if not initial_dir or not os.path.exists(initial_dir): initial_dir = os.getcwd() foldername = filedialog.askdirectory(initialdir=initial_dir) if foldername: entry.delete(0, tk.END) entry.insert(0, foldername) self.populate_folder_tree(foldername) self.save_path(config_key, foldername) def select_file(self, entry, config_key, filetypes=None): if filetypes is None: filetypes = [("所有文件", "*.*")] initial_dir = self.get_last_path(config_key) if not initial_dir or not os.path.exists(initial_dir): initial_dir = os.getcwd() if os.path.isfile(initial_dir): initial_dir = os.path.dirname(initial_dir) filename = filedialog.askopenfilename(filetypes=filetypes, initialdir=initial_dir) if filename: entry.delete(0, tk.END) entry.insert(0, filename) self.save_path(config_key, filename) def get_last_path(self, config_key): config = configparser.ConfigParser() if os.path.exists(self.config_file): config.read(self.config_file) if config.has_option('Paths', config_key): return config.get('Paths', config_key) return None def populate_folder_tree(self, path): self.tree.delete(*self.tree.get_children()) if not os.path.isdir(path): return root_node = self.tree.insert("", "end", text=os.path.basename(path), values=("文件夹",), open=True) self.add_tree_nodes(root_node, path) def add_tree_nodes(self, parent, path): try: for item in os.listdir(path): item_path = os.path.join(path, item) if os.path.isdir(item_path): node = self.tree.insert(parent, "end", text=item, values=("文件夹",)) self.add_tree_nodes(node, item_path) else: self.tree.insert(parent, "end", text=item, values=("文件",)) except PermissionError: self.log_message(f"权限错误: 无法访问 {path}") def log_message(self, message): self.queue.put(("log", message)) def update_progress(self, value): self.queue.put(("progress", value)) def update_status(self, message): self.queue.put(("status", message)) def process_queue(self): try: while not self.queue.empty(): msg_type, data = self.queue.get_nowait() if msg_type == "log": self.log_text.config(state=tk.NORMAL) self.log_text.insert(tk.END, data + "\n") self.log_text.see(tk.END) self.log_text.config(state=tk.DISABLED) elif msg_type == "progress": self.progress['value'] = data elif msg_type == "status": self.status_var.set(data) except queue.Empty: pass self.root.after(100, self.process_queue) def view_report(self, report_type): if report_type == "folder" and self.folder_report_path and os.path.exists(self.folder_report_path): try: webbrowser.open(self.folder_report_path) except Exception as e: messagebox.showerror("错误", f"无法打开文件夹报告: {str(e)}") else: messagebox.showwarning("警告", f"没有可用的{report_type}报告文件") def process_folders(self, old_path, new_path, excel_file): try: report_dir = os.path.dirname(excel_file) or os.getcwd() os.makedirs(report_dir, exist_ok=True) timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") self.folder_report_path = os.path.join(report_dir, f"folder_diff_report_{timestamp}.html") self.update_status("生成比较报告...") self.update_progress(30) winmerge_path = self.winmerge_entry.get() if not self.run_winmerge(winmerge_path, old_path, new_path): self.update_status("WinMerge执行失败") return if not os.path.exists(self.folder_report_path) or os.path.getsize(self.folder_report_path) == 0: self.log_message("警告: 文件夹报告为空或未生成") else: self.log_message(f"文件夹报告生成成功: {self.folder_report_path} ({os.path.getsize(self.folder_report_path)} bytes)") self.copy_detail_files(report_dir) self.update_status("打开Excel文件...") self.update_progress(80) if not self.open_excel_file(excel_file): self.update_status("打开Excel失败") return if self.delete_temp_files_var.get(): self.delete_winmerge_reports() self.log_message("已删除所有临时文件") else: self.log_message("保留临时文件,可查看报告") self.root.after(100, self.update_view_button_state) self.update_progress(100) self.update_status("处理完成!") self.log_message("文件夹比较流程执行完毕") messagebox.showinfo("完成", "已生成报告并打开Excel文件") except Exception as e: error_msg = f"执行过程中发生错误: {str(e)}\n{traceback.format_exc()}" self.log_message(error_msg) self.update_status("执行失败") messagebox.showerror("错误", f"处理失败: {str(e)}") finally: if self.processing: self.stop_processing() def delete_winmerge_reports(self): if not self.folder_report_path: return if os.path.exists(self.folder_report_path): try: os.remove(self.folder_report_path) self.log_message(f"已删除报告文件: {self.folder_report_path}") except Exception as e: self.log_message(f"删除报告文件失败: {str(e)}") base_path = os.path.splitext(self.folder_report_path)[0] files_dir = base_path + ".files" if os.path.exists(files_dir): try: shutil.rmtree(files_dir) self.log_message(f"已删除.files目录: {files_dir}") except Exception as e: self.log_message(f"删除.files目录失败: {str(e)}") report_dir = os.path.dirname(self.folder_report_path) if os.path.exists(report_dir) and hasattr(self, 'copied_html_files') and self.copied_html_files: deleted_count = 0 for file_path in self.copied_html_files: if os.path.exists(file_path): try: os.remove(file_path) deleted_count += 1 self.log_message(f"已删除复制的HTML文件: {file_path}") except Exception as e: self.log_message(f"删除HTML文件失败: {file_path} - {str(e)}") self.log_message(f"已删除 {deleted_count}/{len(self.copied_html_files)} 个复制的HTML文件") self.copied_html_files = [] self.folder_report_path = None self.view_folder_report_button.config(state=tk.DISABLED) self.root.after(100, self.update_view_button_state) def copy_detail_files(self, report_dir): base_path = os.path.splitext(self.folder_report_path)[0] files_dir = base_path + ".files" if not os.path.exists(files_dir): self.log_message(f"警告: 详细文件目录不存在 {files_dir}") return html_files = [f for f in os.listdir(files_dir) if f.lower().endswith('.html')] if not html_files: self.log_message(f"警告: 详细文件目录中没有HTML文件 {files_dir}") return if not hasattr(self, 'copied_html_files') or not self.copied_html_files: self.copied_html_files = [] copied_count = 0 for file_name in html_files: src_path = os.path.join(files_dir, file_name) dst_path = os.path.join(report_dir, file_name) try: shutil.copy2(src_path, dst_path) copied_count += 1 self.copied_html_files.append(dst_path) except Exception as e: self.log_message(f"复制文件失败: {file_name} - {str(e)}") self.log_message(f"已复制 {copied_count}/{len(html_files)} 个详细HTML文件到报告目录") def start_processing(self): if self.processing: self.log_message("警告: 处理正在进行中") return old_path = self.old_folder_entry.get() new_path = self.new_folder_entry.get() excel_file = self.excel_file_entry.get() validation_errors = [] if not old_path: validation_errors.append("原始文件夹路径为空") elif not os.path.isdir(old_path): validation_errors.append(f"原始文件夹路径无效: {old_path}") if not new_path: validation_errors.append("新文件夹路径为空") elif not os.path.isdir(new_path): validation_errors.append(f"新文件夹路径无效: {new_path}") if not excel_file: validation_errors.append("Excel文件路径为空") elif not excel_file.lower().endswith(('.xlsx', '.xlsm')): validation_errors.append("Excel文件必须是.xlsx或.xlsm格式") winmerge_path = self.winmerge_entry.get() if not winmerge_path or not os.path.exists(winmerge_path): validation_errors.append("WinMerge路径无效或未设置") if validation_errors: self.log_message("错误: " + "; ".join(validation_errors)) messagebox.showerror("输入错误", "\n".join(validation_errors)) return self.run_button.config(state=tk.DISABLED) self.stop_button.config(state=tk.NORMAL) self.view_folder_report_button.config(state=tk.DISABLED) self.processing = True self.copied_html_files = [] thread = threading.Thread(target=self.process_folders, args=(old_path, new_path, excel_file)) thread.daemon = True thread.start() self.log_message("处理线程已启动") def run_winmerge(self, winmerge_path, path1, path2): if not os.path.exists(winmerge_path): self.log_message(f"错误: WinMerge路径不存在 {winmerge_path}") return False os.makedirs(os.path.dirname(self.folder_report_path), exist_ok=True) cmd = [ winmerge_path, '/u', '/nosplash', '/dl', 'Base', '/dr', 'Modified', '/noninteractive', '/minimize' ] if self.recursive_var.get(): cmd.extend(['/r', '/s']) else: cmd.extend(['/r-', '/s-']) file_filter = self.filter_var.get() if file_filter and file_filter != "*.*": cmd.extend(['-f', file_filter]) cmd.extend(['/or', self.folder_report_path]) cmd.extend([path1, path2]) self.update_status("正在生成比较报告...") return self.execute_winmerge_command(cmd, "比较报告") def execute_winmerge_command(self, cmd, report_type): try: self.log_message(f"开始生成{report_type}...") self.log_message(f"执行命令: {' '.join(cmd)}") start_time = time.time() creation_flags = 0 if os.name == 'nt': creation_flags = subprocess.CREATE_NO_WINDOW process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, encoding='utf-8', errors='replace', creationflags=creation_flags ) timeout = 900 try: stdout, stderr = process.communicate(timeout=timeout) except subprocess.TimeoutExpired: process.kill() stdout, stderr = process.communicate() self.log_message(f"{report_type}生成超时({timeout}秒),已终止进程") return False elapsed = time.time() - start_time self.log_message(f"{report_type}生成完成,耗时: {elapsed:.2f}秒") if stdout.strip(): self.log_message(f"WinMerge输出:\n{stdout[:2000]}") if stderr.strip(): self.log_message(f"WinMerge错误:\n{stderr[:1000]}") if process.returncode == 0: self.log_message(f"{report_type}命令执行成功") return True elif process.returncode == 1: self.log_message(f"{report_type}命令执行完成(发现差异)") return True elif process.returncode == 2: self.log_message(f"{report_type}命令执行完成(发现错误)") return False else: error_msg = f"{report_type}生成失败(退出码{process.returncode})" self.log_message(error_msg) return False except Exception as e: self.log_message(f"{report_type}生成错误: {str(e)}\n{traceback.format_exc()}") return False def open_excel_file(self, excel_path): self.log_message("正在打开Excel文件并执行宏...") try: if not os.path.exists(excel_path): self.log_message(f"错误: Excel文件不存在 {excel_path}") return False # 创建Excel应用程序对象 excel_app = win32com.client.Dispatch("Excel.Application") excel_app.Visible = True # 让Excel界面可见 excel_app.DisplayAlerts = False # 禁用警告提示 # 打开工作簿 wb = excel_app.Workbooks.Open(os.path.abspath(excel_path)) # 执行Sheet1中的宏 try: # 获取Sheet1对象 sheet1 = wb.Sheets("Sheet1") # 执行宏"ExposedMacro" excel_app.Run("Sheet1.ExposedMacro") self.log_message("宏 'ExposedMacro' 执行成功") except Exception as macro_e: error_msg = f"执行宏失败: {str(macro_e)}" if hasattr(macro_e, 'excepinfo') and macro_e.excepinfo[5]: error_msg += f" (详细错误: {macro_e.excepinfo[5]})" self.log_message(error_msg) messagebox.showerror("宏执行错误", f"无法执行宏: {error_msg}") # 保存并关闭工作簿,退出Excel wb.Save() wb.Close() excel_app.Quit() self.log_message(f"Excel文件处理完成: {excel_path}") return True except Exception as e: error_msg = f"打开Excel文件失败: {str(e)}" if hasattr(e, 'excepinfo') and e.excepinfo[5]: error_msg += f" (详细错误: {e.excepinfo[5]})" self.log_message(error_msg) messagebox.showerror("Excel错误", error_msg) return False def stop_processing(self): self.processing = False self.stop_button.config(state=tk.DISABLED) self.run_button.config(state=tk.NORMAL) self.root.after(100, self.update_view_button_state) self.update_status("操作已停止") def load_paths(self): config = configparser.ConfigParser() if os.path.exists(self.config_file): try: config.read(self.config_file) if config.has_option('Paths', 'old_folder'): old_path = config.get('Paths', 'old_folder') self.old_folder_entry.delete(0, tk.END) self.old_folder_entry.insert(0, old_path) if os.path.isdir(old_path): self.populate_folder_tree(old_path) if config.has_option('Paths', 'new_folder'): new_path = config.get('Paths', 'new_folder') self.new_folder_entry.delete(0, tk.END) self.new_folder_entry.insert(0, new_path) if config.has_option('Paths', 'excel_file'): excel_path = config.get('Paths', 'excel_file') self.excel_file_entry.delete(0, tk.END) self.excel_file_entry.insert(0, excel_path) if config.has_option('Paths', 'winmerge_path'): winmerge_path = config.get('Paths', 'winmerge_path') self.winmerge_entry.delete(0, tk.END) self.winmerge_entry.insert(0, winmerge_path) self.log_message("已加载上次保存的路径") except Exception as e: self.log_message(f"加载配置文件失败: {str(e)}") else: self.log_message("未找到配置文件,将使用默认路径") def save_path(self, key, path): config = configparser.ConfigParser() if os.path.exists(self.config_file): config.read(self.config_file) if not config.has_section('Paths'): config.add_section('Paths') config.set('Paths', key, path) try: with open(self.config_file, 'w') as configfile: config.write(configfile) self.log_message(f"已保存路径: {key} = {path}") except Exception as e: self.log_message(f"保存路径失败: {str(e)}") def save_all_paths(self): config = configparser.ConfigParser() config.add_section('Paths') config.set('Paths', 'old_folder', self.old_folder_entry.get()) config.set('Paths', 'new_folder', self.new_folder_entry.get()) config.set('Paths', 'excel_file', self.excel_file_entry.get()) config.set('Paths', 'winmerge_path', self.winmerge_entry.get()) try: with open(self.config_file, 'w') as configfile: config.write(configfile) self.log_message("所有路径已保存") except Exception as e: self.log_message(f"保存所有路径失败: {str(e)}") def on_closing(self): self.save_all_paths() self.root.destroy() if __name__ == "__main__": root = tk.Tk() app = DiffProcessorApp(root) root.protocol("WM_DELETE_WINDOW", app.on_closing) root.mainloop()

资源评论
用户头像
WaiyuetFung
2025.08.12
明确应用语言和平台信息有助于快速定位问题原因。
用户头像
创业青年骁哥
2025.06.27
遇到win32拼接异常问题,关键在于提供更多应用细节。
用户头像
赵伊辰
2025.03.01
了解操作系统类型对解决兼容性问题至关重要。
紫藤花叶子
  • 粉丝: 286
上传资源 快速赚钱