前面已经介绍过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;
}
1万+

被折叠的 条评论
为什么被折叠?



