基于duilib+zlib的解压缩

前面已经介绍过duilib与zlib库了,现在我们使用duilib结合zlib库做一个解压缩软件。

首先我们需要一个写一个界面。

一、准备部分

编译好zlib库,前面我们使用的zlib库,我在duilib中使用时,发现一些问题。

zlib报“LNK2001:无法解析的外部符号”错误 

后来在博客园中找到这篇博客,解决了这个问题

zlib报“LNK2001:无法解析的外部符号”错误 - SpartacusIn21 - 博客园

然后我们需要先写一个Duilib界面

Duilib界面部分:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Window size="800,600" caption="0,0,0,30">
	<Font id="1" name="微软雅黑" size="16" bold="true"/>
	<VerticalLayout>
		<!-- 顶部工具栏 -->
		<HorizontalLayout height="40" bkcolor="#FFE6E6E6" bordersize="0,0,1,0" bordercolor="#FFCCCCCC">
			<Button name="button1" text="添加压缩" textcolor="#FFFFFFFF" font="1" height="28" width="80" 
					bkcolor="#FF4CAF50" hotbkcoclor="#FF66BB6A" pushedbkcoclor="#FF388E3C" 
					margin="10,0,5,0"/>
			<Button name="button2" text="解压缩到" textcolor="#FFFFFFFF" font="1" height="28" width="80" 
					bkcolor="#FF2196F3" hotbkcoclor="#FF42A5F5" pushedbkcoclor="#FF1976D2" 
					margin="5,0,5,0"/>
			<Button name="button3" text="清空记录" textcolor="#FFFFFFFF" font="1" height="28" width="80" 
					bkcolor="#FFE309F4" hotbkcoclor="#FFE309F4" pushedbkcoclor="#FFE309F4" 
					margin="5,0,0,0"/>
			<Button name="button4" text="删除文件" textcolor="#FFFFFFFF" font="1" height="28" width="80" 
					bkcolor="#FFF44336" hotbkcoclor="#FFEF5350" pushedbkcoclor="#FFD32F2F" 
					margin="5,0,0,0"/>
		</HorizontalLayout>
		
		<!-- 文件路径选择区域 -->
		<HorizontalLayout height="40" bkcolor="#FFF5F5F5" bordersize="0,0,1,0" bordercolor="#FFE0E0E0">
			<Label text="文件路径:" textcolor="#FF333333" font="1" width="60" align="center" margin="10,0,0,0"/>
			<Richedit name="edit_file_path" text="" textcolor="#FF333333" font="1" width="500" 
					 bkcolor="#FFFFFFFF" bordersize="1" bordercolor="#FFCCCCCC" margin="5,0,5,0"/>
			<Button name="btn_browse" text="浏览" textcolor="#FFFFFFFF" font="1" width="60" height="28"
					bkcolor="#FF2196F3" hotbkcoclor="#FF42A5F5" pushedbkcoclor="#FF1976D2"/>
		</HorizontalLayout>
		
		<!-- 文件列表区域-->
		<VerticalLayout bkcolor="#FFFFFFFF" bordersize="1,1,1,1" bordercolor="#FFE0E0E0" margin="10,10,10,10">
			<!-- 列表标题栏 -->
			<HorizontalLayout height="30" bkcolor="#FFF5F5F5" bordersize="0,0,1,0" bordercolor="#FFE0E0E0">
				<CheckBox name="check_all" width="30" align="center"/>
				<Label text="文件名" textcolor="#FF666666" font="1" width="300" align="left" margin="5,0,0,0"/>
				<Label text="大小" textcolor="#FF666666" font="1" width="100" align="center"/>
				<Label text="修改时间" textcolor="#FF666666" font="1" width="150" align="center"/>
				<Label text="类型" textcolor="#FF666666" font="1" width="100" align="center"/>
			</HorizontalLayout>
			
			<!-- 文件列表 -->
			<List name="file_list" bkcolor="#FFFFFFFF" itemheight="25" itemtextcolor="#FF333333" 
				  itembkcolor="#FFFFFFFF" itemhotbkcoclor="#FFF0F0F0" itemselectedbkcoclor="#FFE3F2FD"
				  scrollbarwidth="12" vscrollbarpadding="0,0,0,0">
				<!-- 列表项模板会在运行时动态添加 -->
			</List>
		</VerticalLayout>
		
		<!-- 底部状态栏 -->
		<HorizontalLayout height="30" bkcolor="#FFF5F5F5" bordersize="1,0,0,0" bordercolor="#FFE0E0E0">
			<Label name="status_label" text="已选择 0 个文件" textcolor="#FF666666" font="1" margin="10,0,0,0"/>
			<Label name="size_label" text="总大小: 0 KB" textcolor="#FF666666" font="1" margin="20,0,0,0"/>
		</HorizontalLayout>
	</VerticalLayout>
</Window>

呈现出来效果大概是这样的
在这里插入图片描述

很简陋的一个界面但是基本够用,接下来就是实现解压缩的代码了。

二、解压缩功能实现部分

首先,先添加上所需要的头文件:

#include "framework.h"
#include "Duilib_Lua.h"

#include "UIlib.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <cstring>
#include <algorithm>
#include <commdlg.h>
#include <direct.h>
#include <sys/stat.h>
#include <ShTypes.h>
#include <shlobj.h>
#include <sstream>
#include <iomanip>
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}

#include "CLog32.h"

#include "zlib.h"
#include "minizip/zip.h"
#include "minizip/unzip.h"

#include "LuaBridge/LuaBridge.h"

然后我们来实现目录检查功能,目录创建功能,多级目录创建等

// 简单的目录存在检查
bool directoryExists(const std::string& path) {
    struct stat info;
    return stat(path.c_str(), &info) == 0 && (info.st_mode & S_IFDIR);
}

// 创建目录(跨平台)
bool createDirectory(const std::string& path) {
#ifdef _WIN32
    return _mkdir(path.c_str()) == 0;
#else
    return mkdir(path.c_str(), 0755) == 0;
#endif
}

// 创建多级目录
bool createDirectories(const std::string& path) {
    std::string currentPath;
    for (char c : path) {
        currentPath += c;
        if (c == '\\' || c == '/') {
            if (!directoryExists(currentPath) && !createDirectory(currentPath)) {
                return false;
            }
        }
    }
    return true;
}

// 获取文件大小
long getFileSize(const std::string& filename) {
    struct stat stat_buf;
    int rc = stat(filename.c_str(), &stat_buf);
    return rc == 0 ? stat_buf.st_size : -1;
}

// 格式化文件大小
std::string formatFileSize(long size) {
    std::stringstream ss;
    if (size < 1024) {
        ss << size << " B";
    }
    else if (size < 1024 * 1024) {
        ss << std::fixed << std::setprecision(2) << (size / 1024.0) << " KB";
    }
    else {
        ss << std::fixed << std::setprecision(2) << (size / (1024.0 * 1024.0)) << " MB";
    }
    return ss.str();
}

// 获取文件修改时间
std::string getFileModifiedTime(const std::string& filename) {
    struct stat result;
    if (stat(filename.c_str(), &result) == 0) {
        struct tm* timeinfo = localtime(&result.st_mtime);
        char buffer[80];
        strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", timeinfo);
        return std::string(buffer);
    }
    return "Unknown";
}

// 获取文件扩展名
std::string getFileExtension(const std::string& filename) {
    size_t dotPos = filename.find_last_of('.');
    if (dotPos != std::string::npos) {
        return filename.substr(dotPos + 1);
    }
    return "";
}

// 递归获取文件夹中的所有文件和子文件夹
void getAllFilesAndFoldersInDirectory(const std::string& directory, std::vector<std::string>& itemList) {
    WIN32_FIND_DATAA findFileData;
    HANDLE hFind;
    std::string searchPath = directory + "\\*";

    hFind = FindFirstFileA(searchPath.c_str(), &findFileData);

    if (hFind != INVALID_HANDLE_VALUE) {
        do {
            // 跳过 "." 和 ".."
            if (strcmp(findFileData.cFileName, ".") == 0 || strcmp(findFileData.cFileName, "..") == 0) {
                continue;
            }

            std::string fullPath = directory + "\\" + findFileData.cFileName;

            // 无论是文件还是文件夹,都添加到列表中
            itemList.push_back(fullPath);

            if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                // 如果是文件夹,递归处理
                getAllFilesAndFoldersInDirectory(fullPath, itemList);
            }
        } while (FindNextFileA(hFind, &findFileData) != 0);

        FindClose(hFind);
    }
}

然后需要编写一个解压缩类来实现解压缩功能:

class ZipUtility {
public:
    // 获取相对路径
    static std::string getRelativePath(const std::string& fullPath, const std::string& baseDir) {
        if (baseDir.empty()) {
            // 如果没有基础目录,返回文件名
            size_t lastSlash = fullPath.find_last_of("/\\");
            if (lastSlash != std::string::npos) {
                return fullPath.substr(lastSlash + 1);
            }
            return fullPath;
        }

        // 如果完整路径以基础目录开头,提取相对路径
        if (fullPath.find(baseDir) == 0) {
            std::string relativePath = fullPath.substr(baseDir.length());
            // 移除开头的路径分隔符
            if (!relativePath.empty() && (relativePath[0] == '\\' || relativePath[0] == '/')) {
                relativePath = relativePath.substr(1);
            }
            // 将反斜杠替换为正斜杠(zip标准)
            std::replace(relativePath.begin(), relativePath.end(), '\\', '/');
            return relativePath;
        }

        // 否则返回完整路径
        return fullPath;
    }

    // 创建标准 zip 文件(支持文件和目录),保持目录结构
    static bool createZipWithStructure(const std::string& zipFilename,
        const std::vector<std::string>& filesToAdd, const std::string& baseDir = "") {
        zipFile zf = zipOpen(zipFilename.c_str(), APPEND_STATUS_CREATE);
        if (!zf) {
            CLog(C_ERROR) << "无法创建 zip 文件: " << zipFilename;
            return false;
        }

        bool success = true;

        for (const auto& filepath : filesToAdd) {
            CLog(C_INFO) << "添加文件到 zip: " << filepath;

            // 检查是否是目录
            struct stat stat_buf;
            if (stat(filepath.c_str(), &stat_buf) != 0) {
                CLog(C_ERROR) << "无法访问文件: " << filepath;
                success = false;
                continue;
            }

            if (stat_buf.st_mode & _S_IFDIR) {
                // 处理目录,保持结构
                success = addDirectoryToZipWithStructure(zf, filepath, baseDir);
            }
            else {
                // 处理文件,保持结构
                success = addFileToZipWithStructure(zf, filepath, baseDir);
            }

            if (!success) {
                CLog(C_ERROR) << "添加文件失败: " << filepath;
            }
        }

        // 关闭 zip 文件
        zipClose(zf, nullptr);

        if (success) {
            CLog(C_INFO) << "成功创建 zip 文件: " << zipFilename;
            file_path = zipFilename;
        }
        return success;
    }

    // 添加文件到 zip,保持目录结构
    static bool addFileToZipWithStructure(zipFile zf, const std::string& filepath, const std::string& baseDir) {
        // 打开要添加的文件
        FILE* file = fopen(filepath.c_str(), "rb");
        if (!file) {
            CLog(C_ERROR) << "无法打开文件: " << filepath;
            return false;
        }

        // 获取文件大小
        fseek(file, 0, SEEK_END);
        long fileSize = ftell(file);
        fseek(file, 0, SEEK_SET);

        // 读取文件内容
        char* fileData = new char[fileSize];
        fread(fileData, 1, fileSize, file);
        fclose(file);

        // 设置 zip 文件信息
        zip_fileinfo zi = { 0 };

        // 计算在 zip 中的相对路径
        std::string zipPath = getRelativePath(filepath, baseDir);

        // 在 zip 文件中打开一个新文件
        int err = zipOpenNewFileInZip(zf, zipPath.c_str(), &zi,
            nullptr, 0, nullptr, 0, nullptr,
            Z_DEFLATED, Z_DEFAULT_COMPRESSION);

        if (err != ZIP_OK) {
            CLog(C_ERROR) << "无法在 zip 中创建文件条目: " << zipPath;
            delete[] fileData;
            return false;
        }

        // 写入文件数据
        err = zipWriteInFileInZip(zf, fileData, fileSize);
        if (err != ZIP_OK) {
            std::cerr << "写入 zip 文件失败: " << zipPath << std::endl;
            delete[] fileData;
            zipCloseFileInZip(zf);
            return false;
        }

        // 关闭 zip 中的文件
        zipCloseFileInZip(zf);
        delete[] fileData;
        return true;
    }

    // 添加目录到 zip(递归),保持目录结构
    static bool addDirectoryToZipWithStructure(zipFile zf, const std::string& dirpath, const std::string& baseDir) {
        WIN32_FIND_DATAA findData;
        HANDLE hFind;
        std::string searchPath = dirpath + "\\*";

        // 计算在 zip 中的相对路径
        std::string zipDirPath = getRelativePath(dirpath, baseDir) + "/";

        // 在zip中创建目录条目
        zip_fileinfo zi = { 0 };
        int err = zipOpenNewFileInZip(zf, zipDirPath.c_str(), &zi,
            nullptr, 0, nullptr, 0, nullptr,
            Z_DEFLATED, Z_DEFAULT_COMPRESSION);
        if (err == ZIP_OK) {
            zipCloseFileInZip(zf);
        }

        hFind = FindFirstFileA(searchPath.c_str(), &findData);
        if (hFind == INVALID_HANDLE_VALUE) {
            return false;
        }

        bool success = true;
        do {
            if (strcmp(findData.cFileName, ".") == 0 || strcmp(findData.cFileName, "..") == 0) {
                continue;
            }

            std::string fullPath = dirpath + "\\" + findData.cFileName;

            if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                // 递归处理子目录
                if (!addDirectoryToZipWithStructure(zf, fullPath, baseDir)) {
                    success = false;
                }
            }
            else {
                // 处理文件
                if (!addFileToZipWithStructure(zf, fullPath, baseDir)) {
                    success = false;
                }
            }
        } while (FindNextFileA(hFind, &findData) != 0);

        FindClose(hFind);
        return success;
    }

    // 创建标准 zip 文件(支持文件和目录)
    static bool createZip(const std::string& zipFilename,
        const std::vector<std::string>& filesToAdd) {
        zipFile zf = zipOpen(zipFilename.c_str(), APPEND_STATUS_CREATE);
        if (!zf) {
            CLog(C_ERROR) << "无法创建 zip 文件: " << zipFilename;
            return false;
        }

        bool success = true;

        for (const auto& filepath : filesToAdd) {
            CLog(C_INFO) << "添加文件到 zip: " << filepath;

            // 检查是否是目录
            struct stat stat_buf;
            if (stat(filepath.c_str(), &stat_buf) != 0) {
                CLog(C_ERROR) << "无法访问文件: " << filepath;
                success = false;
                continue;
            }

            if (stat_buf.st_mode & _S_IFDIR) {
                // 处理目录
                success = addDirectoryToZip(zf, filepath, "");
            }
            else {
                // 处理文件
                success = addFileToZip(zf, filepath, "");
            }

            if (!success) {
                CLog(C_ERROR) << "添加文件失败: " << filepath;
            }
        }

        // 关闭 zip 文件
        zipClose(zf, nullptr);

        if (success) {
            CLog(C_INFO) << "成功创建 zip 文件: " << zipFilename;
            file_path = zipFilename;
        }
        return success;
    }

    // 添加文件到 zip
    static bool addFileToZip(zipFile zf, const std::string& filepath, const std::string& basePath) {
        // 打开要添加的文件
        FILE* file = fopen(filepath.c_str(), "rb");
        if (!file) {
            CLog(C_ERROR) << "无法打开文件: " << filepath;
            return false;
        }

        // 获取文件大小
        fseek(file, 0, SEEK_END);
        long fileSize = ftell(file);
        fseek(file, 0, SEEK_SET);

        // 读取文件内容
        char* fileData = new char[fileSize];
        fread(fileData, 1, fileSize, file);
        fclose(file);

        // 设置 zip 文件信息
        zip_fileinfo zi = { 0 };

        // 提取相对路径
        std::string filename = filepath;
        size_t lastSlash = filepath.find_last_of("/\\");
        if (lastSlash != std::string::npos) {
            filename = filepath.substr(lastSlash + 1);
        }

        std::string zipPath = basePath + filename;

        // 在 zip 文件中打开一个新文件
        int err = zipOpenNewFileInZip(zf, zipPath.c_str(), &zi,
            nullptr, 0, nullptr, 0, nullptr,
            Z_DEFLATED, Z_DEFAULT_COMPRESSION);

        if (err != ZIP_OK) {
            CLog(C_ERROR) << "无法在 zip 中创建文件条目: " << zipPath;
            delete[] fileData;
            return false;
        }

        // 写入文件数据
        err = zipWriteInFileInZip(zf, fileData, fileSize);
        if (err != ZIP_OK) {
            CLog(C_ERROR) << "写入 zip 文件失败: " << zipPath;
            delete[] fileData;
            zipCloseFileInZip(zf);
            return false;
        }

        // 关闭 zip 中的文件
        zipCloseFileInZip(zf);
        delete[] fileData;
        return true;
    }

    // 添加目录到 zip(递归)
    static bool addDirectoryToZip(zipFile zf, const std::string& dirpath, const std::string& basePath) {
        WIN32_FIND_DATAA findData;
        HANDLE hFind;
        std::string searchPath = dirpath + "\\*";

        std::string dirName = dirpath;
        size_t lastSlash = dirpath.find_last_of("/\\");
        if (lastSlash != std::string::npos) {
            dirName = dirpath.substr(lastSlash + 1);
        }

        std::string newBasePath = basePath + dirName + "/";

        // 在zip中创建目录条目
        zip_fileinfo zi = { 0 };
        int err = zipOpenNewFileInZip(zf, newBasePath.c_str(), &zi,
            nullptr, 0, nullptr, 0, nullptr,
            Z_DEFLATED, Z_DEFAULT_COMPRESSION);
        if (err == ZIP_OK) {
            zipCloseFileInZip(zf);
        }

        hFind = FindFirstFileA(searchPath.c_str(), &findData);
        if (hFind == INVALID_HANDLE_VALUE) {
            return false;
        }

        bool success = true;
        do {
            if (strcmp(findData.cFileName, ".") == 0 || strcmp(findData.cFileName, "..") == 0) {
                continue;
            }

            std::string fullPath = dirpath + "\\" + findData.cFileName;

            if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                // 递归处理子目录
                if (!addDirectoryToZip(zf, fullPath, newBasePath)) {
                    success = false;
                }
            }
            else {
                // 处理文件
                if (!addFileToZip(zf, fullPath, newBasePath)) {
                    success = false;
                }
            }
        } while (FindNextFileA(hFind, &findData) != 0);

        FindClose(hFind);
        return success;
    }

    // 解压标准 zip 文件
    static bool extractZip(const std::string& zipFilename,
        const std::string& outputDir = "") {
        // 打开 zip 文件
        unzFile uf = unzOpen(zipFilename.c_str());
        if (!uf) {
            CLog(C_ERROR) << "无法打开 zip 文件: " << zipFilename;
            return false;
        }

        // 创建输出目录(如果不存在)
        std::string extractDir = outputDir;
        if (extractDir.empty()) {
            extractDir = zipFilename + "_extracted";
        }

        if (!directoryExists(extractDir)) {
            createDirectories(extractDir);
        }

        unz_global_info gi;
        if (unzGetGlobalInfo(uf, &gi) != UNZ_OK) {
            CLog(C_ERROR) << "无法读取 zip 文件信息";
            unzClose(uf);
            return false;
        }

        std::cout << "zip 文件包含 " << gi.number_entry << " 个文件" << std::endl;

        bool success = true;

        // 遍历所有文件
        for (int i = 0; i < gi.number_entry; i++) {
            unz_file_info file_info;
            char filename[256];
            if (unzGetCurrentFileInfo(uf, &file_info, filename,
                sizeof(filename), nullptr, 0, nullptr, 0) != UNZ_OK) {
                CLog(C_ERROR) << "无法读取文件信息";
                success = false;
                continue;
            }

            std::string outputFilename = extractDir + "\\" + filename;

            // 检查是否是目录
            if (filename[strlen(filename) - 1] == '/') {
                // 创建目录
                if (!directoryExists(outputFilename)) {
                    createDirectories(outputFilename);
                }
                CLog(C_INFO) << "创建目录: " << outputFilename;
            }
            else {
                // 创建父目录
                std::string parentDir = outputFilename.substr(0, outputFilename.find_last_of("\\/"));
                if (!directoryExists(parentDir)) {
                    createDirectories(parentDir);
                }

                CLog(C_INFO) << "解压: " << filename << " -> " << outputFilename;

                // 打开当前文件进行解压
                if (unzOpenCurrentFile(uf) != UNZ_OK) {
                    CLog(C_ERROR) << "无法打开 zip 中的文件: " << filename;
                    success = false;
                    continue;
                }

                // 创建输出文件
                FILE* outFile = fopen(outputFilename.c_str(), "wb");
                if (!outFile) {
                    CLog(C_ERROR) << "无法创建输出文件: " << outputFilename;
                    unzCloseCurrentFile(uf);
                    success = false;
                    continue;
                }

                // 读取并写入文件数据
                char buffer[8192];
                int bytesRead;
                while ((bytesRead = unzReadCurrentFile(uf, buffer, sizeof(buffer))) > 0) {
                    fwrite(buffer, 1, bytesRead, outFile);
                }

                fclose(outFile);
                unzCloseCurrentFile(uf);
            }

            // 移动到下一个文件
            if (i < gi.number_entry - 1) {
                if (unzGoToNextFile(uf) != UNZ_OK) {
                    CLog(C_ERROR) << "无法移动到下一个文件";
                    success = false;
                    break;
                }
            }
        }

        unzClose(uf);

        if (success) {
            CLog(C_INFO) << "解压完成到: " << extractDir;
        }
        return success;
    }
};

在Duilib界面类中直接实现对应按钮功能:

class CFrameWindowWnd : public CWindowWnd, public INotifyUI
{
public:
    CFrameWindowWnd() : m_selectedFilesCount(0), m_totalFileSize(0) {};
    ~CFrameWindowWnd() {

    }

    LPCTSTR GetWindowClassName() const { return _T("UIMainFrame"); };
    UINT GetClassStyle() const { return UI_CLASSSTYLE_FRAME | CS_DBLCLKS; };
    void OnFinalMessage(HWND /*hWnd*/) { delete this; };

    void Notify(TNotifyUI& msg)
    {
        if (msg.sType == _T("click")) {
            if (msg.pSender->GetName() == _T("closebtn")) { PostQuitMessage(0); return; }
            else if (msg.pSender->GetName() == _T("loginBtn")) { Close(); return; }
            else if (msg.pSender->GetName() == _T("connectBtn")) { return; }
            else if (msg.pSender->GetName() == _T("button1")) {
                onAddToArchive();
                return;
            }
            else if (msg.pSender->GetName() == _T("button2")) {
                onExtractArchive();
                return;
            }
            else if (msg.pSender->GetName() == _T("button3")) {
                onDeleteFiles();
                return;
            }
            else if (msg.pSender->GetName() == _T("button4")) {
                removeFile(file_path);
                return;
            }
            else if (msg.pSender->GetName() == _T("btn_browse")) {
                onBrowseFiles();
                return;
            }
            else if (msg.pSender->GetName() == _T("check_all")) {
                onSelectAllFiles();
                return;
            }
            else if (msg.pSender->GetName() == _T("button5")) {
                onAddFolderToArchive();
                return;
            }
            else {
                lua_State* L = GetLuaState();
                if (L) {
                    // 获取控件名称并转换为 UTF-8
                    std::string ctrlNameUtf8 = CDuiStringToUTF8(msg.pSender->GetName());

                    // 获取全局函数 mouse_message
                    lua_getglobal(L, "mouse_message");
                    if (lua_isfunction(L, -1)) {
                        // 压入参数:控件名称
                        lua_pushstring(L, ctrlNameUtf8.c_str());

                        // 安全调用函数 (1 个参数, 0 个返回值)
                        if (lua_pcall(L, 1, 0, 0) != 0) {
                            const char* errMsg = lua_tostring(L, -1);
                            lua_pop(L, 1); // 弹出错误信息
                        }
                    }
                    else {
                        lua_pop(L, 1); // 弹出非函数的值
                    }
                }
            }
        }
        else if (msg.sType == _T("itemselect")) {
            if (msg.pSender->GetName() == _T("accountcombo")) {
                CEditUI* pAccountEdit = static_cast<CEditUI*>(m_pm.FindControl(_T("accountedit")));
                if (pAccountEdit) pAccountEdit->SetText(msg.pSender->GetText());
            }
        }
    }

    // 浏览文件
    void onBrowseFiles() {
        OPENFILENAMEA ofn;
        char szFile[260] = { 0 };

        ZeroMemory(&ofn, sizeof(ofn));
        ofn.lStructSize = sizeof(ofn);
        ofn.hwndOwner = m_hWnd;
        ofn.lpstrFile = szFile;
        ofn.nMaxFile = sizeof(szFile);
        ofn.lpstrFilter = "All Files\0*.*\0ZIP Files\0*.zip\0";
        ofn.nFilterIndex = 1;
        ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;

        if (GetOpenFileNameA(&ofn) == TRUE) {
            CEditUI* pEdit = static_cast<CEditUI*>(m_pm.FindControl(_T("edit_file_path")));
            if (pEdit) {
                pEdit->SetText(CDuiString(szFile));
            }
        }
    }

    // 选择文件夹进行压缩
    void onAddFolderToArchive() {
        BROWSEINFOA bi = { 0 };
        bi.lpszTitle = "选择要压缩的文件夹";
        bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;

        LPITEMIDLIST pidl = SHBrowseForFolderA(&bi);

        if (pidl != NULL) {
            char selectedPath[MAX_PATH];
            if (SHGetPathFromIDListA(pidl, selectedPath)) {
                m_selectedFiles.clear();

                // 递归获取文件夹中的所有文件和子文件夹
                getAllFilesAndFoldersInDirectory(selectedPath, m_selectedFiles);

                updateFileList();

                // 选择输出zip文件
                char szOutputFile[260] = { 0 };
                strcpy_s(szOutputFile, "archive.zip");

                OPENFILENAMEA ofnSave;
                ZeroMemory(&ofnSave, sizeof(ofnSave));
                ofnSave.lStructSize = sizeof(ofnSave);
                ofnSave.hwndOwner = m_hWnd;
                ofnSave.lpstrFile = szOutputFile;
                ofnSave.nMaxFile = sizeof(szOutputFile);
                ofnSave.lpstrFilter = "ZIP Files\0*.zip\0";
                ofnSave.nFilterIndex = 1;
                ofnSave.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR;

                if (GetSaveFileNameA(&ofnSave) == TRUE) {
                    // 创建压缩包,保持目录结构
                    if (ZipUtility::createZipWithStructure(szOutputFile, m_selectedFiles, selectedPath)) {
                        CLog(C_INFO) << "压缩成功完成!";
                    }
                    else {
                        CLog(C_ERROR) << "压缩失败!";
                    }
                }
            }
            CoTaskMemFree(pidl);
        }
    }

    // 添加文件到压缩包
    void onAddToArchive() {
        // 选择是添加文件还是文件夹
        int choice = MessageBoxA(m_hWnd,
            "选择要压缩的内容:\n\n是 - 选择文件\n否 - 选择文件夹\n取消 - 取消操作",
            "选择压缩内容",
            MB_YESNOCANCEL | MB_ICONQUESTION);

        if (choice == IDYES) {
            // 选择文件
            selectAndAddFiles();
        }
        else if (choice == IDNO) {
            // 选择文件夹
            onAddFolderToArchive();
        }
    }

    void selectAndAddFiles() {
        // 选择要压缩的文件
        OPENFILENAMEA ofn;
        char szFiles[1024] = { 0 };

        ZeroMemory(&ofn, sizeof(ofn));
        ofn.lStructSize = sizeof(ofn);
        ofn.hwndOwner = m_hWnd;
        ofn.lpstrFile = szFiles;
        ofn.nMaxFile = sizeof(szFiles);
        ofn.lpstrFilter = "All Files\0*.*\0";
        ofn.nFilterIndex = 1;
        ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_EXPLORER;

        if (GetOpenFileNameA(&ofn) == TRUE) {
            // 解析选择的文件
            m_selectedFiles.clear();
            char* p = szFiles;
            std::string directory = p;
            p += directory.length() + 1;

            if (*p) {
                // 多个文件
                while (*p) {
                    std::string filename = p;
                    std::string fullpath = directory + "\\" + filename;
                    m_selectedFiles.push_back(fullpath);
                    p += filename.length() + 1;
                }
            }
            else {
                // 单个文件
                m_selectedFiles.push_back(directory);
            }

            updateFileList();

            // 选择输出zip文件
            promptForOutputFile();
        }
    }

    void promptForOutputFile() {
        char szOutputFile[260] = { 0 };
        strcpy_s(szOutputFile, "archive.zip");

        OPENFILENAMEA ofnSave;
        ZeroMemory(&ofnSave, sizeof(ofnSave));
        ofnSave.lStructSize = sizeof(ofnSave);
        ofnSave.hwndOwner = m_hWnd;
        ofnSave.lpstrFile = szOutputFile;
        ofnSave.nMaxFile = sizeof(szOutputFile);
        ofnSave.lpstrFilter = "ZIP Files\0*.zip\0";
        ofnSave.nFilterIndex = 1;
        ofnSave.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR;

        if (GetSaveFileNameA(&ofnSave) == TRUE) {
            // 创建压缩包
            if (ZipUtility::createZip(szOutputFile, m_selectedFiles)) {
                CLog(C_INFO) << "压缩成功完成!";
            }
            else {
                CLog(C_ERROR) << "压缩失败!";
            }
        }
    }

    // 解压文件
    void onExtractArchive() {
        CEditUI* pEdit = static_cast<CEditUI*>(m_pm.FindControl(_T("edit_file_path")));
        if (!pEdit) return;

        std::string zipFile = CDuiStringToUTF8(pEdit->GetText());
        if (zipFile.empty()) {
            CLog(C_WARNING) << "请先选择要解压的ZIP文件!";
            return;
        }

        // 选择解压目录
        BROWSEINFOA bi = { 0 };
        bi.hwndOwner = m_hWnd;
        bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;

        LPITEMIDLIST pidl = SHBrowseForFolderA(&bi);
        if (pidl != 0) {
            char path[MAX_PATH];
            SHGetPathFromIDListA(pidl, path);

            // 解压文件
            if (ZipUtility::extractZip(zipFile, path)) {
                CLog(C_ERROR) << "解压成功完成!";
            }
            else {
                CLog(C_ERROR) << "解压失败!";
            }

            CoTaskMemFree(pidl);
        }
    }

    // 删除文件
    void onDeleteFiles() {
        if (m_selectedFiles.empty()) {
            CLog(C_ERROR) << "没有选择要删除的文件!";
            return;
        }

        // 从列表中移除选中的文件
        // 这里简化处理,实际应该根据列表中的选中状态来删除
        m_selectedFiles.clear();
        updateFileList();
        CLog(C_WARNING) << "已从文件列表中删除选中的文件!";
    }

    // 全选文件
    void onSelectAllFiles() {
        CCheckBoxUI* pCheckAll = static_cast<CCheckBoxUI*>(m_pm.FindControl(_T("check_all")));
        if (!pCheckAll) return;

        bool checked = pCheckAll->GetCheck();
        // 这里应该更新列表中所有项目的选中状态
        // 简化处理,只更新状态显示
        updateStatusBar();
    }

    // 更新文件列表显示
    void updateFileList() {
        CListUI* pList = static_cast<CListUI*>(m_pm.FindControl(_T("file_list")));
        if (!pList) return;

        pList->RemoveAll();

        m_selectedFilesCount = 0;
        m_totalFileSize = 0;

        for (const auto& filepath : m_selectedFiles) {
            // 获取文件信息
            long fileSize = getFileSize(filepath);
            std::string modifiedTime = getFileModifiedTime(filepath);
            std::string extension = getFileExtension(filepath);

            // 检查是否是目录
            struct stat stat_buf;
            bool isDirectory = false;
            if (stat(filepath.c_str(), &stat_buf) == 0) {
                isDirectory = (stat_buf.st_mode & _S_IFDIR);
            }

            // 提取文件名
            std::string filename = filepath;
            size_t lastSlash = filepath.find_last_of("/\\");
            if (lastSlash != std::string::npos) {
                filename = filepath.substr(lastSlash + 1);
            }

            // 如果是目录,在文件名后添加标记
            if (isDirectory) {
                filename = "[文件夹] " + filename;
            }

            // 创建列表项 - 使用CListContainerElementUI保持选择效果
            CListContainerElementUI* pListItem = new CListContainerElementUI();
            pListItem->SetFixedHeight(25);

            // 关键设置:启用水平布局
            pListItem->SetAttribute(_T("inset"), _T("0,0,0,0"));
            pListItem->SetAttribute(_T("border"), _T("0"));

            // 创建一个水平布局容器作为列表项的内容
            CHorizontalLayoutUI* pHorzLayout = new CHorizontalLayoutUI();
            pHorzLayout->SetAttribute(_T("inset"), _T("2,2,2,2"));
            pHorzLayout->SetAttribute(_T("border"), _T("0"));
            pHorzLayout->SetAttribute(_T("bkcolor"), _T("0x00000000")); // 透明背景

            // 复选框
            CCheckBoxUI* pCheckBox = new CCheckBoxUI();
            pCheckBox->SetFixedWidth(30);
            pHorzLayout->Add(pCheckBox);

            // 文件名
            CLabelUI* pNameLabel = new CLabelUI();
            pNameLabel->SetFixedWidth(300);
            pNameLabel->SetText(CDuiString(filename.c_str()));
            pNameLabel->SetAttribute(_T("align"), _T("left"));
            pNameLabel->SetAttribute(_T("valign"), _T("center"));
            pHorzLayout->Add(pNameLabel);

            // 文件大小 - 如果是目录,显示为"文件夹"
            CLabelUI* pSizeLabel = new CLabelUI();
            pSizeLabel->SetFixedWidth(100);
            if (isDirectory) {
                pSizeLabel->SetText(_T("文件夹"));
            }
            else {
                pSizeLabel->SetText(CDuiString(formatFileSize(fileSize).c_str()));
            }
            pSizeLabel->SetAttribute(_T("align"), _T("center"));
            pSizeLabel->SetAttribute(_T("valign"), _T("center"));
            pHorzLayout->Add(pSizeLabel);

            // 修改时间
            CLabelUI* pTimeLabel = new CLabelUI();
            pTimeLabel->SetFixedWidth(150);
            pTimeLabel->SetText(CDuiString(modifiedTime.c_str()));
            pTimeLabel->SetAttribute(_T("align"), _T("center"));
            pTimeLabel->SetAttribute(_T("valign"), _T("center"));
            pHorzLayout->Add(pTimeLabel);

            // 文件类型
            CLabelUI* pTypeLabel = new CLabelUI();
            pTypeLabel->SetFixedWidth(100);
            if (isDirectory) {
                pTypeLabel->SetText(_T("文件夹"));
            }
            else {
                pTypeLabel->SetText(CDuiString(extension.c_str()));
            }
            pTypeLabel->SetAttribute(_T("align"), _T("center"));
            pTypeLabel->SetAttribute(_T("valign"), _T("center"));
            pHorzLayout->Add(pTypeLabel);

            // 将水平布局添加到列表项
            pListItem->Add(pHorzLayout);

            pList->Add(pListItem);

            m_selectedFilesCount++;
            if (!isDirectory && fileSize > 0) {
                m_totalFileSize += fileSize;
            }
        }

        updateStatusBar();
    }

    // 更新状态栏
    void updateStatusBar() {
        CLabelUI* pStatusLabel = static_cast<CLabelUI*>(m_pm.FindControl(_T("status_label")));
        if (pStatusLabel) {
            std::string status = "已选择 " + std::to_string(m_selectedFilesCount) + " 个项目";
            pStatusLabel->SetText(CDuiString(status.c_str()));
        }

        CLabelUI* pSizeLabel = static_cast<CLabelUI*>(m_pm.FindControl(_T("size_label")));
        if (pSizeLabel) {
            std::string sizeText = "总大小: " + formatFileSize(m_totalFileSize);
            pSizeLabel->SetText(CDuiString(sizeText.c_str()));
        }
    }

    void removeFile(std::string path)
    {
        std::remove(path.c_str());
        CLog(C_WARNING) << "文件 " << path << " zip已删除";
        onDeleteFiles();
    }


    LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        if (uMsg == WM_CREATE) {
            m_pm.Init(m_hWnd);
            CDialogBuilder builder;
            CControlUI* pRoot = builder.Create(_T("test1.xml"), (LPCTSTR)0, NULL, &m_pm);
            ASSERT(pRoot && "Failed to parse XML");
            m_pm.AttachDialog(pRoot);
            m_pm.AddNotifier(this);
            return 0;
        }
        else if (uMsg == WM_DESTROY) {
            ::PostQuitMessage(0);
        }
        else if (uMsg == WM_NCACTIVATE) {
            if (!::IsIconic(m_hWnd)) {
                return (wParam == 0) ? TRUE : FALSE;
            }
        }
        else if (uMsg == WM_NCHITTEST) {
            POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
            ::ScreenToClient(m_hWnd, &pt);
            RECT rcClient;
            ::GetClientRect(m_hWnd, &rcClient);
            RECT xmlCaption = m_pm.GetCaptionRect();
            RECT rcCaption = { 0, 0, rcClient.right, xmlCaption.bottom };

            // 检查是否在按钮区域
            CControlUI* pControl = m_pm.FindControl(pt);
            if (pControl != nullptr) {
                CDuiString sType = pControl->GetClass();
                if (sType == _T("Button") ||
                    sType == _T("Option") ||
                    sType == _T("Combo") ||
                    sType == _T("CheckBox") ||
                    sType == _T("Edit") ||
                    sType == _T("RichEdit") ||
                    sType == _T("Slider") ||
                    sType == _T("List") ||
                    sType == _T("TreeView")) {
                    return HTCLIENT;
                }
            }

            // 非按钮区域且在标题栏内时允许拖动
            if (::PtInRect(&rcCaption, pt)) {
                return HTCAPTION;
            }
            return HTCLIENT;
        }
        else if (uMsg == WM_NCLBUTTONDBLCLK) {
            if (wParam == HTCAPTION) return 0;
        }
        else if (uMsg == WM_NCCALCSIZE) {
            return 0;
        }
        else if (uMsg == WM_NCPAINT) {
            return 0;
        }
        else if (uMsg == WM_KEYDOWN) {
            if (wParam == VK_ESCAPE) {
                PostMessage(WM_CLOSE, 0, 0);
                return 0;
            }
        }
        LRESULT lRes = 0;
        if (m_pm.MessageHandler(uMsg, wParam, lParam, lRes)) return lRes;
        return CWindowWnd::HandleMessage(uMsg, wParam, lParam);
    }


    static void SetLuaState(lua_State* L) {
        s_LuaState = L;
    }

    lua_State* GetLuaState() {
        return s_LuaState;
    }


public:
    CPaintManagerUI m_pm;
private:
    // Lua回调函数引用
    int m_timerCallbackRef = LUA_NOREF;
    static lua_State* s_LuaState;
    std::vector<std::string> m_selectedFiles;
    int m_selectedFilesCount;
    DWORD m_totalFileSize;

    // 辅助函数:将 CDuiString 转换为 UTF-8 字符串
    static std::string CDuiStringToUTF8(const CDuiString& str) {
        if (str.IsEmpty()) return std::string();

#ifdef UNICODE
        int len = WideCharToMultiByte(CP_UTF8, 0, str.GetData(), str.GetLength(), NULL, 0, NULL, NULL);
        std::string utf8Str(len, 0);
        WideCharToMultiByte(CP_UTF8, 0, str.GetData(), str.GetLength(), &utf8Str[0], len, NULL, NULL);
        return utf8Str;
#else
        int wlen = MultiByteToWideChar(CP_ACP, 0, str.GetData(), str.GetLength(), NULL, 0);
        std::wstring wstr(wlen, L'\0');
        MultiByteToWideChar(CP_ACP, 0, str.GetData(), str.GetLength(), &wstr[0], wlen);

        int len = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), wlen, NULL, 0, NULL, NULL);
        std::string utf8Str(len, 0);
        WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), wlen, &utf8Str[0], len, NULL, NULL);
        return utf8Str;
#endif
    }
};

让我们来测试一下功能:

  • 添加压缩按钮

在这里插入图片描述

  • 选择文件压缩
    在这里插入图片描述

  • 选择文件夹压缩

在这里插入图片描述

  • 我们点击删除文件看一下
    在这里插入图片描述

会直接将最后压缩的压缩文件以及记录删除掉。

  • 我们看一下解压缩功能
    在这里插入图片描述
    ok,解压缩功能基本都可以成功。

当然这个程序压缩(解压缩)太大的文件会导致卡顿,但是也还是可以成功解压缩,由于只实现了zip格式的解压缩,目前之支持这个,如果需要可以添加其他的文件格式的解压缩功能

最后,源码奉上:

// Duilib_Lua.cpp : 定义应用程序的入口点。
//

#include "framework.h"
#include "Duilib_Lua.h"

#include "UIlib.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <cstring>
#include <algorithm>
#include <commdlg.h>
#include <direct.h>
#include <sys/stat.h>
#include <ShTypes.h>
#include <shlobj.h>
#include <sstream>
#include <iomanip>
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}

#include "CLog32.h"

#include "zlib.h"
#include "minizip/zip.h"
#include "minizip/unzip.h"

#include "LuaBridge/LuaBridge.h"

using namespace DuiLib;
using namespace luabridge;

// 简单的目录存在检查(跨平台)
bool directoryExists(const std::string& path) {
    struct stat info;
    return stat(path.c_str(), &info) == 0 && (info.st_mode & S_IFDIR);
}

// 创建目录(跨平台)
bool createDirectory(const std::string& path) {
#ifdef _WIN32
    return _mkdir(path.c_str()) == 0;
#else
    return mkdir(path.c_str(), 0755) == 0;
#endif
}

// 创建多级目录
bool createDirectories(const std::string& path) {
    std::string currentPath;
    for (char c : path) {
        currentPath += c;
        if (c == '\\' || c == '/') {
            if (!directoryExists(currentPath) && !createDirectory(currentPath)) {
                return false;
            }
        }
    }
    return true;
}

// 获取文件大小
long getFileSize(const std::string& filename) {
    struct stat stat_buf;
    int rc = stat(filename.c_str(), &stat_buf);
    return rc == 0 ? stat_buf.st_size : -1;
}

// 格式化文件大小
std::string formatFileSize(long size) {
    std::stringstream ss;
    if (size < 1024) {
        ss << size << " B";
    }
    else if (size < 1024 * 1024) {
        ss << std::fixed << std::setprecision(2) << (size / 1024.0) << " KB";
    }
    else {
        ss << std::fixed << std::setprecision(2) << (size / (1024.0 * 1024.0)) << " MB";
    }
    return ss.str();
}

// 获取文件修改时间
std::string getFileModifiedTime(const std::string& filename) {
    struct stat result;
    if (stat(filename.c_str(), &result) == 0) {
        struct tm* timeinfo = localtime(&result.st_mtime);
        char buffer[80];
        strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", timeinfo);
        return std::string(buffer);
    }
    return "Unknown";
}

// 获取文件扩展名
std::string getFileExtension(const std::string& filename) {
    size_t dotPos = filename.find_last_of('.');
    if (dotPos != std::string::npos) {
        return filename.substr(dotPos + 1);
    }
    return "";
}

// 递归获取文件夹中的所有文件和子文件夹
void getAllFilesAndFoldersInDirectory(const std::string& directory, std::vector<std::string>& itemList) {
    WIN32_FIND_DATAA findFileData;
    HANDLE hFind;
    std::string searchPath = directory + "\\*";

    hFind = FindFirstFileA(searchPath.c_str(), &findFileData);

    if (hFind != INVALID_HANDLE_VALUE) {
        do {
            // 跳过 "." 和 ".."
            if (strcmp(findFileData.cFileName, ".") == 0 || strcmp(findFileData.cFileName, "..") == 0) {
                continue;
            }

            std::string fullPath = directory + "\\" + findFileData.cFileName;

            // 无论是文件还是文件夹,都添加到列表中
            itemList.push_back(fullPath);

            if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                // 如果是文件夹,递归处理
                getAllFilesAndFoldersInDirectory(fullPath, itemList);
            }
        } while (FindNextFileA(hFind, &findFileData) != 0);

        FindClose(hFind);
    }
}

class ZipUtility {
public:
    // 获取相对路径
    static std::string getRelativePath(const std::string& fullPath, const std::string& baseDir) {
        if (baseDir.empty()) {
            // 如果没有基础目录,返回文件名
            size_t lastSlash = fullPath.find_last_of("/\\");
            if (lastSlash != std::string::npos) {
                return fullPath.substr(lastSlash + 1);
            }
            return fullPath;
        }

        // 如果完整路径以基础目录开头,提取相对路径
        if (fullPath.find(baseDir) == 0) {
            std::string relativePath = fullPath.substr(baseDir.length());
            // 移除开头的路径分隔符
            if (!relativePath.empty() && (relativePath[0] == '\\' || relativePath[0] == '/')) {
                relativePath = relativePath.substr(1);
            }
            // 将反斜杠替换为正斜杠(zip标准)
            std::replace(relativePath.begin(), relativePath.end(), '\\', '/');
            return relativePath;
        }

        // 否则返回完整路径
        return fullPath;
    }

    // 创建标准 zip 文件(支持文件和目录),保持目录结构
    static bool createZipWithStructure(const std::string& zipFilename,
        const std::vector<std::string>& filesToAdd, const std::string& baseDir = "") {
        zipFile zf = zipOpen(zipFilename.c_str(), APPEND_STATUS_CREATE);
        if (!zf) {
            CLog(C_ERROR) << "无法创建 zip 文件: " << zipFilename;
            return false;
        }

        bool success = true;

        for (const auto& filepath : filesToAdd) {
            CLog(C_INFO) << "添加文件到 zip: " << filepath;

            // 检查是否是目录
            struct stat stat_buf;
            if (stat(filepath.c_str(), &stat_buf) != 0) {
                CLog(C_ERROR) << "无法访问文件: " << filepath;
                success = false;
                continue;
            }

            if (stat_buf.st_mode & _S_IFDIR) {
                // 处理目录,保持结构
                success = addDirectoryToZipWithStructure(zf, filepath, baseDir);
            }
            else {
                // 处理文件,保持结构
                success = addFileToZipWithStructure(zf, filepath, baseDir);
            }

            if (!success) {
                CLog(C_ERROR) << "添加文件失败: " << filepath;
            }
        }

        // 关闭 zip 文件
        zipClose(zf, nullptr);

        if (success) {
            CLog(C_INFO) << "成功创建 zip 文件: " << zipFilename;
            file_path = zipFilename;
        }
        return success;
    }

    // 添加文件到 zip,保持目录结构
    static bool addFileToZipWithStructure(zipFile zf, const std::string& filepath, const std::string& baseDir) {
        // 打开要添加的文件
        FILE* file = fopen(filepath.c_str(), "rb");
        if (!file) {
            CLog(C_ERROR) << "无法打开文件: " << filepath;
            return false;
        }

        // 获取文件大小
        fseek(file, 0, SEEK_END);
        long fileSize = ftell(file);
        fseek(file, 0, SEEK_SET);

        // 读取文件内容
        char* fileData = new char[fileSize];
        fread(fileData, 1, fileSize, file);
        fclose(file);

        // 设置 zip 文件信息
        zip_fileinfo zi = { 0 };

        // 计算在 zip 中的相对路径
        std::string zipPath = getRelativePath(filepath, baseDir);

        // 在 zip 文件中打开一个新文件
        int err = zipOpenNewFileInZip(zf, zipPath.c_str(), &zi,
            nullptr, 0, nullptr, 0, nullptr,
            Z_DEFLATED, Z_DEFAULT_COMPRESSION);

        if (err != ZIP_OK) {
            CLog(C_ERROR) << "无法在 zip 中创建文件条目: " << zipPath;
            delete[] fileData;
            return false;
        }

        // 写入文件数据
        err = zipWriteInFileInZip(zf, fileData, fileSize);
        if (err != ZIP_OK) {
            CLog(C_ERROR) << "写入 zip 文件失败: " << zipPath;
            delete[] fileData;
            zipCloseFileInZip(zf);
            return false;
        }

        // 关闭 zip 中的文件
        zipCloseFileInZip(zf);
        delete[] fileData;
        return true;
    }

    // 添加目录到 zip(递归),保持目录结构
    static bool addDirectoryToZipWithStructure(zipFile zf, const std::string& dirpath, const std::string& baseDir) {
        WIN32_FIND_DATAA findData;
        HANDLE hFind;
        std::string searchPath = dirpath + "\\*";

        // 计算在 zip 中的相对路径
        std::string zipDirPath = getRelativePath(dirpath, baseDir) + "/";

        // 在zip中创建目录条目
        zip_fileinfo zi = { 0 };
        int err = zipOpenNewFileInZip(zf, zipDirPath.c_str(), &zi,
            nullptr, 0, nullptr, 0, nullptr,
            Z_DEFLATED, Z_DEFAULT_COMPRESSION);
        if (err == ZIP_OK) {
            zipCloseFileInZip(zf);
        }

        hFind = FindFirstFileA(searchPath.c_str(), &findData);
        if (hFind == INVALID_HANDLE_VALUE) {
            return false;
        }

        bool success = true;
        do {
            if (strcmp(findData.cFileName, ".") == 0 || strcmp(findData.cFileName, "..") == 0) {
                continue;
            }

            std::string fullPath = dirpath + "\\" + findData.cFileName;

            if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                // 递归处理子目录
                if (!addDirectoryToZipWithStructure(zf, fullPath, baseDir)) {
                    success = false;
                }
            }
            else {
                // 处理文件
                if (!addFileToZipWithStructure(zf, fullPath, baseDir)) {
                    success = false;
                }
            }
        } while (FindNextFileA(hFind, &findData) != 0);

        FindClose(hFind);
        return success;
    }

    // 创建标准 zip 文件(支持文件和目录)
    static bool createZip(const std::string& zipFilename,
        const std::vector<std::string>& filesToAdd) {
        zipFile zf = zipOpen(zipFilename.c_str(), APPEND_STATUS_CREATE);
        if (!zf) {
            CLog(C_ERROR) << "无法创建 zip 文件: " << zipFilename;
            return false;
        }

        bool success = true;

        for (const auto& filepath : filesToAdd) {
            CLog(C_INFO) << "添加文件到 zip: " << filepath;

            // 检查是否是目录
            struct stat stat_buf;
            if (stat(filepath.c_str(), &stat_buf) != 0) {
                CLog(C_ERROR) << "无法访问文件: " << filepath;
                success = false;
                continue;
            }

            if (stat_buf.st_mode & _S_IFDIR) {
                // 处理目录
                success = addDirectoryToZip(zf, filepath, "");
            }
            else {
                // 处理文件
                success = addFileToZip(zf, filepath, "");
            }

            if (!success) {
                CLog(C_ERROR) << "添加文件失败: " << filepath;
            }
        }

        // 关闭 zip 文件
        zipClose(zf, nullptr);

        if (success) {
            CLog(C_INFO) << "成功创建 zip 文件: " << zipFilename;
            file_path = zipFilename;
        }
        return success;
    }

    // 添加文件到 zip
    static bool addFileToZip(zipFile zf, const std::string& filepath, const std::string& basePath) {
        // 打开要添加的文件
        FILE* file = fopen(filepath.c_str(), "rb");
        if (!file) {
            CLog(C_ERROR) << "无法打开文件: " << filepath;
            return false;
        }

        // 获取文件大小
        fseek(file, 0, SEEK_END);
        long fileSize = ftell(file);
        fseek(file, 0, SEEK_SET);

        // 读取文件内容
        char* fileData = new char[fileSize];
        fread(fileData, 1, fileSize, file);
        fclose(file);

        // 设置 zip 文件信息
        zip_fileinfo zi = { 0 };

        // 提取相对路径
        std::string filename = filepath;
        size_t lastSlash = filepath.find_last_of("/\\");
        if (lastSlash != std::string::npos) {
            filename = filepath.substr(lastSlash + 1);
        }

        std::string zipPath = basePath + filename;

        // 在 zip 文件中打开一个新文件
        int err = zipOpenNewFileInZip(zf, zipPath.c_str(), &zi,
            nullptr, 0, nullptr, 0, nullptr,
            Z_DEFLATED, Z_DEFAULT_COMPRESSION);

        if (err != ZIP_OK) {
            CLog(C_ERROR) << "无法在 zip 中创建文件条目: " << zipPath;
            delete[] fileData;
            return false;
        }

        // 写入文件数据
        err = zipWriteInFileInZip(zf, fileData, fileSize);
        if (err != ZIP_OK) {
            CLog(C_ERROR) << "写入 zip 文件失败: " << zipPath;
            delete[] fileData;
            zipCloseFileInZip(zf);
            return false;
        }

        // 关闭 zip 中的文件
        zipCloseFileInZip(zf);
        delete[] fileData;
        return true;
    }

    // 添加目录到 zip(递归)
    static bool addDirectoryToZip(zipFile zf, const std::string& dirpath, const std::string& basePath) {
        WIN32_FIND_DATAA findData;
        HANDLE hFind;
        std::string searchPath = dirpath + "\\*";

        std::string dirName = dirpath;
        size_t lastSlash = dirpath.find_last_of("/\\");
        if (lastSlash != std::string::npos) {
            dirName = dirpath.substr(lastSlash + 1);
        }

        std::string newBasePath = basePath + dirName + "/";

        // 在zip中创建目录条目
        zip_fileinfo zi = { 0 };
        int err = zipOpenNewFileInZip(zf, newBasePath.c_str(), &zi,
            nullptr, 0, nullptr, 0, nullptr,
            Z_DEFLATED, Z_DEFAULT_COMPRESSION);
        if (err == ZIP_OK) {
            zipCloseFileInZip(zf);
        }

        hFind = FindFirstFileA(searchPath.c_str(), &findData);
        if (hFind == INVALID_HANDLE_VALUE) {
            return false;
        }

        bool success = true;
        do {
            if (strcmp(findData.cFileName, ".") == 0 || strcmp(findData.cFileName, "..") == 0) {
                continue;
            }

            std::string fullPath = dirpath + "\\" + findData.cFileName;

            if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                // 递归处理子目录
                if (!addDirectoryToZip(zf, fullPath, newBasePath)) {
                    success = false;
                }
            }
            else {
                // 处理文件
                if (!addFileToZip(zf, fullPath, newBasePath)) {
                    success = false;
                }
            }
        } while (FindNextFileA(hFind, &findData) != 0);

        FindClose(hFind);
        return success;
    }

    // 解压标准 zip 文件
    static bool extractZip(const std::string& zipFilename,
        const std::string& outputDir = "") {
        // 打开 zip 文件
        unzFile uf = unzOpen(zipFilename.c_str());
        if (!uf) {
            CLog(C_ERROR) << "无法打开 zip 文件: " << zipFilename;
            return false;
        }

        // 创建输出目录(如果不存在)
        std::string extractDir = outputDir;
        if (extractDir.empty()) {
            extractDir = zipFilename + "_extracted";
        }

        if (!directoryExists(extractDir)) {
            createDirectories(extractDir);
        }

        unz_global_info gi;
        if (unzGetGlobalInfo(uf, &gi) != UNZ_OK) {
            CLog(C_ERROR) << "无法读取 zip 文件信息";
            unzClose(uf);
            return false;
        }

        CLog(C_INFO) << "zip 文件包含 " << gi.number_entry << " 个文件";

        bool success = true;

        // 遍历所有文件
        for (int i = 0; i < gi.number_entry; i++) {
            unz_file_info file_info;
            char filename[256];
            if (unzGetCurrentFileInfo(uf, &file_info, filename,
                sizeof(filename), nullptr, 0, nullptr, 0) != UNZ_OK) {
                CLog(C_ERROR) << "无法读取文件信息";
                success = false;
                continue;
            }

            std::string outputFilename = extractDir + "\\" + filename;

            // 检查是否是目录
            if (filename[strlen(filename) - 1] == '/') {
                // 创建目录
                if (!directoryExists(outputFilename)) {
                    createDirectories(outputFilename);
                }
                std::cout << "创建目录: " << outputFilename;
            }
            else {
                // 创建父目录
                std::string parentDir = outputFilename.substr(0, outputFilename.find_last_of("\\/"));
                if (!directoryExists(parentDir)) {
                    createDirectories(parentDir);
                }

                CLog(C_INFO) << "解压: " << filename << " -> " << outputFilename;

                // 打开当前文件进行解压
                if (unzOpenCurrentFile(uf) != UNZ_OK) {
                    CLog(C_ERROR) << "无法打开 zip 中的文件: " << filename;
                    success = false;
                    continue;
                }

                // 创建输出文件
                FILE* outFile = fopen(outputFilename.c_str(), "wb");
                if (!outFile) {
                    CLog(C_ERROR) << "无法创建输出文件: " << outputFilename;
                    unzCloseCurrentFile(uf);
                    success = false;
                    continue;
                }

                // 读取并写入文件数据
                char buffer[8192];
                int bytesRead;
                while ((bytesRead = unzReadCurrentFile(uf, buffer, sizeof(buffer))) > 0) {
                    fwrite(buffer, 1, bytesRead, outFile);
                }

                fclose(outFile);
                unzCloseCurrentFile(uf);
            }

            // 移动到下一个文件
            if (i < gi.number_entry - 1) {
                if (unzGoToNextFile(uf) != UNZ_OK) {
                    CLog(C_ERROR) << "无法移动到下一个文件";
                    success = false;
                    break;
                }
            }
        }

        unzClose(uf);

        if (success) {
            CLog(C_INFO) << "解压完成到: " << extractDir;        // extractDir 为输出目录
        }
        return success;
    }
};

class CFrameWindowWnd : public CWindowWnd, public INotifyUI
{
public:
    CFrameWindowWnd() : m_selectedFilesCount(0), m_totalFileSize(0) {};
    ~CFrameWindowWnd() {

    }

    LPCTSTR GetWindowClassName() const { return _T("UIMainFrame"); };
    UINT GetClassStyle() const { return UI_CLASSSTYLE_FRAME | CS_DBLCLKS; };
    void OnFinalMessage(HWND /*hWnd*/) { delete this; };

    void Notify(TNotifyUI& msg)
    {
        if (msg.sType == _T("click")) {
            if (msg.pSender->GetName() == _T("closebtn")) { PostQuitMessage(0); return; }
            else if (msg.pSender->GetName() == _T("loginBtn")) { Close(); return; }
            else if (msg.pSender->GetName() == _T("connectBtn")) { return; }
            else if (msg.pSender->GetName() == _T("button1")) {
                onAddToArchive();
                return;
            }
            else if (msg.pSender->GetName() == _T("button2")) {
                onExtractArchive();
                return;
            }
            else if (msg.pSender->GetName() == _T("button3")) {
                onDeleteFiles();
                return;
            }
            else if (msg.pSender->GetName() == _T("button4")) {
                removeFile(file_path);
                return;
            }
            else if (msg.pSender->GetName() == _T("btn_browse")) {
                onBrowseFiles();
                return;
            }
            else if (msg.pSender->GetName() == _T("check_all")) {
                onSelectAllFiles();
                return;
            }
            else if (msg.pSender->GetName() == _T("button5")) {
                onAddFolderToArchive();
                return;
            }
            else {
                lua_State* L = GetLuaState();
                if (L) {
                    std::string ctrlNameUtf8 = CDuiStringToUTF8(msg.pSender->GetName());

                    // 获取全局函数 mouse_message 
                    lua_getglobal(L, "mouse_message");
                    if (lua_isfunction(L, -1)) {
                        lua_pushstring(L, ctrlNameUtf8.c_str());
                        if (lua_pcall(L, 1, 0, 0) != 0) {
                            const char* errMsg = lua_tostring(L, -1);
                            lua_pop(L, 1);
                        }
                    }
                    else {
                        lua_pop(L, 1);
                    }
                }
            }
        }
        else if (msg.sType == _T("itemselect")) {
            if (msg.pSender->GetName() == _T("accountcombo")) {
                CEditUI* pAccountEdit = static_cast<CEditUI*>(m_pm.FindControl(_T("accountedit")));
                if (pAccountEdit) pAccountEdit->SetText(msg.pSender->GetText());
            }
        }
    }

    // 浏览文件
    void onBrowseFiles() {
        OPENFILENAMEA ofn;
        char szFile[260] = { 0 };

        ZeroMemory(&ofn, sizeof(ofn));
        ofn.lStructSize = sizeof(ofn);
        ofn.hwndOwner = m_hWnd;
        ofn.lpstrFile = szFile;
        ofn.nMaxFile = sizeof(szFile);
        ofn.lpstrFilter = "All Files\0*.*\0ZIP Files\0*.zip\0";
        ofn.nFilterIndex = 1;
        ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;

        if (GetOpenFileNameA(&ofn) == TRUE) {
            CEditUI* pEdit = static_cast<CEditUI*>(m_pm.FindControl(_T("edit_file_path")));
            if (pEdit) {
                pEdit->SetText(CDuiString(szFile));
            }
        }
    }

    // 选择文件夹进行压缩
    void onAddFolderToArchive() {
        BROWSEINFOA bi = { 0 };
        bi.lpszTitle = "选择要压缩的文件夹";
        bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;

        LPITEMIDLIST pidl = SHBrowseForFolderA(&bi);

        if (pidl != NULL) {
            char selectedPath[MAX_PATH];
            if (SHGetPathFromIDListA(pidl, selectedPath)) {
                m_selectedFiles.clear();

                // 递归获取文件夹中的所有文件和子文件夹
                getAllFilesAndFoldersInDirectory(selectedPath, m_selectedFiles);

                updateFileList();

                // 选择输出zip文件
                char szOutputFile[260] = { 0 };
                strcpy_s(szOutputFile, "archive.zip");

                OPENFILENAMEA ofnSave;
                ZeroMemory(&ofnSave, sizeof(ofnSave));
                ofnSave.lStructSize = sizeof(ofnSave);
                ofnSave.hwndOwner = m_hWnd;
                ofnSave.lpstrFile = szOutputFile;
                ofnSave.nMaxFile = sizeof(szOutputFile);
                ofnSave.lpstrFilter = "ZIP Files\0*.zip\0";
                ofnSave.nFilterIndex = 1;
                ofnSave.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR;

                if (GetSaveFileNameA(&ofnSave) == TRUE) {
                    // 创建压缩包,保持目录结构
                    if (ZipUtility::createZipWithStructure(szOutputFile, m_selectedFiles, selectedPath)) {
                        CLog(C_INFO) << "压缩成功完成!";
                    }
                    else {
                        CLog(C_ERROR) << "压缩失败!";
                    }
                }
            }
            CoTaskMemFree(pidl);
        }
    }

    // 添加文件到压缩包
    void onAddToArchive() {
        // 让用户选择是添加文件还是文件夹
        int choice = MessageBoxA(m_hWnd,
            "选择要压缩的内容:\n\n是 - 选择文件\n否 - 选择文件夹\n取消 - 取消操作",
            "选择压缩内容",
            MB_YESNOCANCEL | MB_ICONQUESTION);

        if (choice == IDYES) {
            // 选择文件(原来的代码)
            selectAndAddFiles();
        }
        else if (choice == IDNO) {
            // 选择文件夹
            onAddFolderToArchive();
        }
    }

    void selectAndAddFiles() {
        // 选择要压缩的文件
        OPENFILENAMEA ofn;
        char szFiles[1024] = { 0 };

        ZeroMemory(&ofn, sizeof(ofn));
        ofn.lStructSize = sizeof(ofn);
        ofn.hwndOwner = m_hWnd;
        ofn.lpstrFile = szFiles;
        ofn.nMaxFile = sizeof(szFiles);
        ofn.lpstrFilter = "All Files\0*.*\0";
        ofn.nFilterIndex = 1;
        ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_EXPLORER;

        if (GetOpenFileNameA(&ofn) == TRUE) {
            // 解析选择的文件
            m_selectedFiles.clear();
            char* p = szFiles;
            std::string directory = p;
            p += directory.length() + 1;

            if (*p) {
                // 多个文件
                while (*p) {
                    std::string filename = p;
                    std::string fullpath = directory + "\\" + filename;
                    m_selectedFiles.push_back(fullpath);
                    p += filename.length() + 1;
                }
            }
            else {
                // 单个文件
                m_selectedFiles.push_back(directory);
            }

            updateFileList();

            // 选择输出zip文件
            promptForOutputFile();
        }
    }

    void promptForOutputFile() {
        char szOutputFile[260] = { 0 };
        strcpy_s(szOutputFile, "archive.zip");

        OPENFILENAMEA ofnSave;
        ZeroMemory(&ofnSave, sizeof(ofnSave));
        ofnSave.lStructSize = sizeof(ofnSave);
        ofnSave.hwndOwner = m_hWnd;
        ofnSave.lpstrFile = szOutputFile;
        ofnSave.nMaxFile = sizeof(szOutputFile);
        ofnSave.lpstrFilter = "ZIP Files\0*.zip\0";
        ofnSave.nFilterIndex = 1;
        ofnSave.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR;

        if (GetSaveFileNameA(&ofnSave) == TRUE) {
            // 创建压缩包
            if (ZipUtility::createZip(szOutputFile, m_selectedFiles)) {
                CLog(C_INFO) << "压缩成功完成!";
            }
            else {
                CLog(C_ERROR) << "压缩失败!";
            }
        }
    }

    // 解压文件
    void onExtractArchive() {
        CEditUI* pEdit = static_cast<CEditUI*>(m_pm.FindControl(_T("edit_file_path")));
        if (!pEdit) return;

        std::string zipFile = CDuiStringToUTF8(pEdit->GetText());
        if (zipFile.empty()) {
            CLog(C_WARNING) << "请先选择要解压的ZIP文件!";
            return;
        }

        // 选择解压目录
        BROWSEINFOA bi = { 0 };
        bi.hwndOwner = m_hWnd;
        bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;

        LPITEMIDLIST pidl = SHBrowseForFolderA(&bi);
        if (pidl != 0) {
            char path[MAX_PATH];
            SHGetPathFromIDListA(pidl, path);

            // 解压文件
            if (ZipUtility::extractZip(zipFile, path)) {
                CLog(C_INFO) << "解压成功完成!";
            }
            else {
                CLog(C_ERROR) << "解压失败!";
            }

            CoTaskMemFree(pidl);
        }
    }

    // 删除文件
    void onDeleteFiles() {
        if (m_selectedFiles.empty()) {
            return;
        }

        // 从列表中移除选中的文件
        // 这里简化处理,实际应该根据列表中的选中状态来删除
        m_selectedFiles.clear();
        updateFileList();
    }

    // 全选文件
    void onSelectAllFiles() {
        CCheckBoxUI* pCheckAll = static_cast<CCheckBoxUI*>(m_pm.FindControl(_T("check_all")));
        if (!pCheckAll) return;

        bool checked = pCheckAll->GetCheck();
        // 这里应该更新列表中所有项目的选中状态
        // 简化处理,只更新状态显示
        updateStatusBar();
    }

    // 更新文件列表显示
    void updateFileList() {
        CListUI* pList = static_cast<CListUI*>(m_pm.FindControl(_T("file_list")));
        if (!pList) return;

        pList->RemoveAll();

        m_selectedFilesCount = 0;
        m_totalFileSize = 0;

        for (const auto& filepath : m_selectedFiles) {
            // 获取文件信息
            long fileSize = getFileSize(filepath);
            std::string modifiedTime = getFileModifiedTime(filepath);
            std::string extension = getFileExtension(filepath);

            // 检查是否是目录
            struct stat stat_buf;
            bool isDirectory = false;
            if (stat(filepath.c_str(), &stat_buf) == 0) {
                isDirectory = (stat_buf.st_mode & _S_IFDIR);
            }

            // 提取文件名
            std::string filename = filepath;
            size_t lastSlash = filepath.find_last_of("/\\");
            if (lastSlash != std::string::npos) {
                filename = filepath.substr(lastSlash + 1);
            }

            // 如果是目录,在文件名后添加标记
            if (isDirectory) {
                filename = "[文件夹] " + filename;
            }

            // 创建列表项 - 使用CListContainerElementUI保持选择效果
            CListContainerElementUI* pListItem = new CListContainerElementUI();
            pListItem->SetFixedHeight(25);

            // 关键设置:启用水平布局
            pListItem->SetAttribute(_T("inset"), _T("0,0,0,0"));
            pListItem->SetAttribute(_T("border"), _T("0"));

            // 创建一个水平布局容器作为列表项的内容
            CHorizontalLayoutUI* pHorzLayout = new CHorizontalLayoutUI();
            pHorzLayout->SetAttribute(_T("inset"), _T("2,2,2,2"));
            pHorzLayout->SetAttribute(_T("border"), _T("0"));
            pHorzLayout->SetAttribute(_T("bkcolor"), _T("0x00000000")); // 透明背景

            // 复选框
            CCheckBoxUI* pCheckBox = new CCheckBoxUI();
            pCheckBox->SetFixedWidth(30);
            pHorzLayout->Add(pCheckBox);

            // 文件名
            CLabelUI* pNameLabel = new CLabelUI();
            pNameLabel->SetFixedWidth(300);
            pNameLabel->SetText(CDuiString(filename.c_str()));
            pNameLabel->SetAttribute(_T("align"), _T("left"));
            pNameLabel->SetAttribute(_T("valign"), _T("center"));
            pHorzLayout->Add(pNameLabel);

            // 文件大小 - 如果是目录,显示为"文件夹"
            CLabelUI* pSizeLabel = new CLabelUI();
            pSizeLabel->SetFixedWidth(100);
            if (isDirectory) {
                pSizeLabel->SetText(_T("文件夹"));
            }
            else {
                pSizeLabel->SetText(CDuiString(formatFileSize(fileSize).c_str()));
            }
            pSizeLabel->SetAttribute(_T("align"), _T("center"));
            pSizeLabel->SetAttribute(_T("valign"), _T("center"));
            pHorzLayout->Add(pSizeLabel);

            // 修改时间
            CLabelUI* pTimeLabel = new CLabelUI();
            pTimeLabel->SetFixedWidth(150);
            pTimeLabel->SetText(CDuiString(modifiedTime.c_str()));
            pTimeLabel->SetAttribute(_T("align"), _T("center"));
            pTimeLabel->SetAttribute(_T("valign"), _T("center"));
            pHorzLayout->Add(pTimeLabel);

            // 文件类型
            CLabelUI* pTypeLabel = new CLabelUI();
            pTypeLabel->SetFixedWidth(100);
            if (isDirectory) {
                pTypeLabel->SetText(_T("文件夹"));
            }
            else {
                pTypeLabel->SetText(CDuiString(extension.c_str()));
            }
            pTypeLabel->SetAttribute(_T("align"), _T("center"));
            pTypeLabel->SetAttribute(_T("valign"), _T("center"));
            pHorzLayout->Add(pTypeLabel);

            // 将水平布局添加到列表项
            pListItem->Add(pHorzLayout);

            pList->Add(pListItem);

            m_selectedFilesCount++;
            if (!isDirectory && fileSize > 0) {
                m_totalFileSize += fileSize;
            }
        }

        updateStatusBar();
    }

    // 更新状态栏
    void updateStatusBar() {
        CLabelUI* pStatusLabel = static_cast<CLabelUI*>(m_pm.FindControl(_T("status_label")));
        if (pStatusLabel) {
            std::string status = "已选择 " + std::to_string(m_selectedFilesCount) + " 个项目";
            pStatusLabel->SetText(CDuiString(status.c_str()));
        }

        CLabelUI* pSizeLabel = static_cast<CLabelUI*>(m_pm.FindControl(_T("size_label")));
        if (pSizeLabel) {
            std::string sizeText = "总大小: " + formatFileSize(m_totalFileSize);
            pSizeLabel->SetText(CDuiString(sizeText.c_str()));
        }
    }

    void removeFile(std::string path)
    {
        std::remove(path.c_str());
        CLog(C_WARNING) << "文件 " << path << " zip已删除";
        onDeleteFiles();
    }


    LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        if (uMsg == WM_CREATE) {
            m_pm.Init(m_hWnd);
            CDialogBuilder builder;
            CControlUI* pRoot = builder.Create(_T("test1.xml"), (LPCTSTR)0, NULL, &m_pm);
            ASSERT(pRoot && "Failed to parse XML");
            m_pm.AttachDialog(pRoot);
            m_pm.AddNotifier(this);
            return 0;
        }
        else if (uMsg == WM_DESTROY) {
            ::PostQuitMessage(0);
        }
        else if (uMsg == WM_NCACTIVATE) {
            if (!::IsIconic(m_hWnd)) {
                return (wParam == 0) ? TRUE : FALSE;
            }
        }
        else if (uMsg == WM_NCHITTEST) {
            POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
            ::ScreenToClient(m_hWnd, &pt);
            RECT rcClient;
            ::GetClientRect(m_hWnd, &rcClient);
            RECT xmlCaption = m_pm.GetCaptionRect();
            RECT rcCaption = { 0, 0, rcClient.right, xmlCaption.bottom };

            // 检查是否在按钮区域 
            CControlUI* pControl = m_pm.FindControl(pt);
            if (pControl != nullptr) {
                CDuiString sType = pControl->GetClass();
                if (sType == _T("Button") ||
                    sType == _T("Option") ||
                    sType == _T("Combo") ||
                    sType == _T("CheckBox") ||
                    sType == _T("Edit") ||
                    sType == _T("RichEdit") ||
                    sType == _T("Slider") ||
                    sType == _T("List") ||
                    sType == _T("TreeView")) {
                    return HTCLIENT;
                }
            }

            // 非按钮区域且在标题栏内时允许拖动 
            if (::PtInRect(&rcCaption, pt)) {
                return HTCAPTION;
            }
            return HTCLIENT;
        }
        else if (uMsg == WM_NCLBUTTONDBLCLK) {
            if (wParam == HTCAPTION) return 0;
        }
        else if (uMsg == WM_NCCALCSIZE) {
            return 0;
        }
        else if (uMsg == WM_NCPAINT) {
            return 0;
        }
        else if (uMsg == WM_KEYDOWN) {
            if (wParam == VK_ESCAPE) {
                PostMessage(WM_CLOSE, 0, 0);
                return 0;
            }
        }
        LRESULT lRes = 0;
        if (m_pm.MessageHandler(uMsg, wParam, lParam, lRes)) return lRes;
        return CWindowWnd::HandleMessage(uMsg, wParam, lParam);
    }


    static void SetLuaState(lua_State* L) {
        s_LuaState = L;
    }

    lua_State* GetLuaState() {
        return s_LuaState;
    }


public:
    CPaintManagerUI m_pm;
private:
    // Lua回调函数引用
    int m_timerCallbackRef = LUA_NOREF;
    static lua_State* s_LuaState;
    std::vector<std::string> m_selectedFiles;
    int m_selectedFilesCount;
    DWORD m_totalFileSize;

    // 辅助函数:将 CDuiString 转换为 UTF-8 字符串 
    static std::string CDuiStringToUTF8(const CDuiString& str) {
        if (str.IsEmpty()) return std::string();

#ifdef UNICODE
        int len = WideCharToMultiByte(CP_UTF8, 0, str.GetData(), str.GetLength(), NULL, 0, NULL, NULL);
        std::string utf8Str(len, 0);
        WideCharToMultiByte(CP_UTF8, 0, str.GetData(), str.GetLength(), &utf8Str[0], len, NULL, NULL);
        return utf8Str;
#else
        int wlen = MultiByteToWideChar(CP_ACP, 0, str.GetData(), str.GetLength(), NULL, 0);
        std::wstring wstr(wlen, L'\0');
        MultiByteToWideChar(CP_ACP, 0, str.GetData(), str.GetLength(), &wstr[0], wlen);

        int len = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), wlen, NULL, 0, NULL, NULL);
        std::string utf8Str(len, 0);
        WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), wlen, &utf8Str[0], len, NULL, NULL);
        return utf8Str;
#endif
    }
};
// 类外定义静态成员变量 
lua_State* CFrameWindowWnd::s_LuaState = nullptr;

// LuaBridge的 addFunction通常期望一个C++函数(比如函数对象、lambda、普通函数等)
void alert(const std::string& utf8Message) {
    int wlen = MultiByteToWideChar(CP_ACP, 0, utf8Message.c_str(), -1, NULL, 0);
    std::wstring unicodeMessage(wlen, L'\0');
    MultiByteToWideChar(CP_ACP, 0, utf8Message.c_str(), -1, &unicodeMessage[0], wlen);
    MessageBoxW(NULL, unicodeMessage.c_str(), L"Lua Alert", MB_OK);
}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int nCmdShow)
{
    CPaintManagerUI::SetInstance(hInstance);
    CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath());

    CFrameWindowWnd* pFrame = new CFrameWindowWnd();
    if (pFrame == NULL) return 0;
    pFrame->Create(NULL, _T("ZIP压缩工具"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);

    pFrame->ShowWindow(true);

    // 创建控制台用于调试
    if (AllocConsole()) {
        FILE* fDummy;
        freopen_s(&fDummy, "CONOUT$", "w", stdout);
        freopen_s(&fDummy, "CONOUT$", "w", stderr);
        freopen_s(&fDummy, "CONIN$", "r", stdin);
        std::cout.clear();
        std::clog.clear();
        std::cerr.clear();
        std::cin.clear();
        SetConsoleTitle("Duilib Screen");
    }

    lua_State* L = luaL_newstate();
    luaL_openlibs(L);			//加载Lua库

    CFrameWindowWnd::SetLuaState(L);

    lua_pushlightuserdata(L, pFrame);
    lua_setfield(L, LUA_REGISTRYINDEX, "MainWindow");
    //lua_register(L, "alert", lua_alert);	// 将函数暴露给Lua
    luabridge::getGlobalNamespace(L)
        .addFunction("alert", &alert);

    // 加载Lua脚本
    if (luaL_loadfile(L, "main.lua")) {
        const char* error = lua_tostring(L, -1);
        MessageBoxA(0, "未找到Lua脚本", "错误", MB_OK | MB_ICONERROR);
        printf("Error loading Lua file: %s\n", error);
        lua_pop(L, 1);
    }
    else {
        if (lua_pcall(L, 0, 0, 0)) {
            const char* error = lua_tostring(L, -1);
            printf("Error executing Lua file: %s\n", error);
            lua_pop(L, 1);
        }
    }

    CPaintManagerUI::MessageLoop();
    lua_close(L);
    FreeConsole();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值