517 lines
16 KiB
Go
517 lines
16 KiB
Go
package main
|
||
|
||
import "C"
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"log"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
"syscall"
|
||
"unsafe"
|
||
|
||
"golang.org/x/sys/windows"
|
||
)
|
||
|
||
// ExcelManagerDLL 封装DLL操作
|
||
type ExcelManagerDLL struct {
|
||
dll *windows.LazyDLL
|
||
newExcelManager *windows.LazyProc
|
||
freeExcelManager *windows.LazyProc
|
||
readExcelData *windows.LazyProc
|
||
writeBatchData *windows.LazyProc
|
||
appendDataToExcel *windows.LazyProc
|
||
searchByKeyword *windows.LazyProc
|
||
searchRowsByKeyword *windows.LazyProc
|
||
createAndWriteExcel *windows.LazyProc
|
||
mergeExcelFilesEx *windows.LazyProc
|
||
mergeExcelFilesParallelEx *windows.LazyProc
|
||
mergeSheetsInFile *windows.LazyProc
|
||
freeCString *windows.LazyProc
|
||
}
|
||
|
||
// LoadExcelManagerDLL 加载DLL并获取函数指针
|
||
func LoadExcelManagerDLL(dllPath string) (*ExcelManagerDLL, error) {
|
||
// 使用windows包的LazyDLL加载DLL
|
||
dll := windows.NewLazyDLL(dllPath)
|
||
|
||
// 验证DLL是否成功加载
|
||
if err := dll.Load(); err != nil {
|
||
return nil, fmt.Errorf("加载DLL失败: %v", err)
|
||
}
|
||
|
||
return &ExcelManagerDLL{
|
||
dll: dll,
|
||
newExcelManager: dll.NewProc("NewExcelManagerInstance"),
|
||
freeExcelManager: dll.NewProc("FreeExcelManager"),
|
||
readExcelData: dll.NewProc("ReadExcelData"),
|
||
writeBatchData: dll.NewProc("WriteBatchData"),
|
||
appendDataToExcel: dll.NewProc("AppendDataToExcel"),
|
||
searchByKeyword: dll.NewProc("SearchByKeyword"),
|
||
searchRowsByKeyword: dll.NewProc("SearchRowsByKeyword"),
|
||
createAndWriteExcel: dll.NewProc("CreateAndWriteExcel"),
|
||
mergeExcelFilesEx: dll.NewProc("MergeExcelFilesEx"),
|
||
mergeExcelFilesParallelEx: dll.NewProc("MergeExcelFilesParallelEx"),
|
||
mergeSheetsInFile: dll.NewProc("MergeSheetsInFile"),
|
||
freeCString: dll.NewProc("FreeCString"),
|
||
}, nil
|
||
}
|
||
|
||
// NewExcelManagerInstance 安全地创建实例(使用句柄)
|
||
func (dll *ExcelManagerDLL) NewExcelManagerInstance() (int64, error) {
|
||
ret, _, err := dll.newExcelManager.Call()
|
||
if err != windows.ERROR_SUCCESS {
|
||
return 0, fmt.Errorf("调用NewExcelManagerInstance失败: %v", err)
|
||
}
|
||
return int64(ret), nil
|
||
}
|
||
|
||
func (dll *ExcelManagerDLL) FreeExcelManager(handle int64) error {
|
||
_, _, err := dll.freeExcelManager.Call(uintptr(handle))
|
||
if err != windows.ERROR_SUCCESS {
|
||
return fmt.Errorf("调用FreeExcelManager失败: %v", err)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (dll *ExcelManagerDLL) ReadExcelData(handle int64, filename, sheet string) (string, error) {
|
||
var resultPtr uintptr
|
||
// 转换字符串
|
||
filenamePtr, _ := syscall.BytePtrFromString(filename)
|
||
sheetPtr, _ := syscall.BytePtrFromString(sheet)
|
||
// 调用DLL函数
|
||
ret, _, _ := dll.readExcelData.Call(
|
||
uintptr(handle),
|
||
uintptr(unsafe.Pointer(filenamePtr)),
|
||
uintptr(unsafe.Pointer(sheetPtr)),
|
||
uintptr(unsafe.Pointer(&resultPtr)),
|
||
)
|
||
if int32(ret) != 0 {
|
||
return "", fmt.Errorf("读取Excel数据失败,错误码: %d", ret)
|
||
}
|
||
// 转换结果字符串
|
||
if resultPtr != 0 {
|
||
resultStr := C.GoString((*C.char)(unsafe.Pointer(resultPtr)))
|
||
// 释放C字符串
|
||
dll.FreeCString((*C.char)(unsafe.Pointer(resultPtr)))
|
||
return resultStr, nil
|
||
}
|
||
return "", nil
|
||
}
|
||
|
||
func (dll *ExcelManagerDLL) WriteBatchData(handle int64, filename, sheet string, cells, values []string) error {
|
||
if len(cells) != len(values) {
|
||
return fmt.Errorf("单元格和值数量不匹配")
|
||
}
|
||
// 转换字符串
|
||
filenamePtr, _ := syscall.BytePtrFromString(filename)
|
||
sheetPtr, _ := syscall.BytePtrFromString(sheet)
|
||
|
||
cellMarshal, err := json.Marshal(cells)
|
||
if err != nil {
|
||
return fmt.Errorf("信息cells转换失败!")
|
||
}
|
||
cellsString := string(cellMarshal)
|
||
cellsPtr, _ := syscall.BytePtrFromString(cellsString)
|
||
|
||
valuesMarshal, err := json.Marshal(values)
|
||
if err != nil {
|
||
return fmt.Errorf("信息values转换失败!")
|
||
}
|
||
valuesString := string(valuesMarshal)
|
||
valuesPtr, _ := syscall.BytePtrFromString(valuesString)
|
||
|
||
// 调用DLL函数
|
||
ret, _, _ := dll.writeBatchData.Call(
|
||
uintptr(handle),
|
||
uintptr(unsafe.Pointer(filenamePtr)),
|
||
uintptr(unsafe.Pointer(sheetPtr)),
|
||
uintptr(unsafe.Pointer(cellsPtr)),
|
||
uintptr(unsafe.Pointer(valuesPtr)),
|
||
uintptr(len(cells)),
|
||
)
|
||
|
||
if int32(ret) != 0 {
|
||
return fmt.Errorf("批量写入Excel数据失败,错误码: %d", ret)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (dll *ExcelManagerDLL) AppendDataToExcel(handle int64, filename, sheet string, values []string) error {
|
||
// 转换字符串
|
||
filenamePtr, _ := syscall.BytePtrFromString(filename)
|
||
sheetPtr, _ := syscall.BytePtrFromString(sheet)
|
||
|
||
valuesMarshal, err := json.Marshal(values)
|
||
if err != nil {
|
||
return fmt.Errorf("信息values转换失败!")
|
||
}
|
||
valuesString := string(valuesMarshal)
|
||
valuesPtr, _ := syscall.BytePtrFromString(valuesString)
|
||
|
||
// 调用DLL函数
|
||
ret, _, err := dll.appendDataToExcel.Call(
|
||
uintptr(handle),
|
||
uintptr(unsafe.Pointer(filenamePtr)),
|
||
uintptr(unsafe.Pointer(sheetPtr)),
|
||
uintptr(unsafe.Pointer(valuesPtr)),
|
||
uintptr(len(values)),
|
||
)
|
||
if int32(ret) != 0 {
|
||
return fmt.Errorf("追加Excel数据失败,错误码: %d", ret)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (dll *ExcelManagerDLL) SearchByKeyword(handle int64, filename, sheet, keyword string) (string, error) {
|
||
// 转换字符串
|
||
filenamePtr, _ := syscall.BytePtrFromString(filename)
|
||
sheetPtr, _ := syscall.BytePtrFromString(sheet)
|
||
keywordPtr, _ := syscall.BytePtrFromString(keyword)
|
||
// 分配内存存储结果指针
|
||
var resultPtr uintptr
|
||
// 调用DLL函数
|
||
ret, _, _ := dll.searchByKeyword.Call(
|
||
uintptr(handle),
|
||
uintptr(unsafe.Pointer(filenamePtr)),
|
||
uintptr(unsafe.Pointer(sheetPtr)),
|
||
uintptr(unsafe.Pointer(keywordPtr)),
|
||
uintptr(unsafe.Pointer(&resultPtr)),
|
||
)
|
||
if int32(ret) != 0 {
|
||
return "", fmt.Errorf("搜索Excel数据失败,错误码: %d", ret)
|
||
}
|
||
// 转换结果字符串
|
||
if resultPtr != 0 {
|
||
resultStr := C.GoString((*C.char)(unsafe.Pointer(resultPtr)))
|
||
// 释放C字符串
|
||
dll.FreeCString((*C.char)(unsafe.Pointer(resultPtr)))
|
||
return resultStr, nil
|
||
}
|
||
return "", nil
|
||
}
|
||
|
||
// CreateAndWriteExcel 创建新文件并写入数据
|
||
func (dll *ExcelManagerDLL) CreateAndWriteExcel(handle int64, filename, sheet string, data [][]string) error {
|
||
// 确保目录存在
|
||
dir := filepath.Dir(filename)
|
||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||
return fmt.Errorf("创建目录失败: %v", err)
|
||
}
|
||
|
||
fmt.Printf("调试信息:\n")
|
||
fmt.Printf(" - 文件名: %s\n", filename)
|
||
fmt.Printf(" - Sheet: %s\n", sheet)
|
||
|
||
marshal, err2 := json.Marshal(data)
|
||
if err2 != nil {
|
||
return err2
|
||
}
|
||
dataString := string(marshal)
|
||
filenamePtr, _ := syscall.BytePtrFromString(filename)
|
||
sheetPtr, _ := syscall.BytePtrFromString(sheet)
|
||
dataStringPtr, _ := syscall.BytePtrFromString(dataString)
|
||
|
||
// 调用DLL函数
|
||
ret, _, _ := dll.createAndWriteExcel.Call(
|
||
uintptr(handle),
|
||
uintptr(unsafe.Pointer(filenamePtr)),
|
||
uintptr(unsafe.Pointer(sheetPtr)),
|
||
uintptr(unsafe.Pointer(dataStringPtr)),
|
||
)
|
||
fmt.Printf(" - DLL调用返回: %d\n", int32(ret))
|
||
|
||
if int32(ret) != 0 {
|
||
return fmt.Errorf("创建并写入Excel文件失败,错误码: %d", ret)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (dll *ExcelManagerDLL) MergeExcelFilesEx(handle int64, config MergeConfig) error {
|
||
// 转换字符串
|
||
sourceDirPtr, _ := syscall.BytePtrFromString(config.SourceDir)
|
||
outputFilePtr, _ := syscall.BytePtrFromString(config.OutputFile)
|
||
sheetNamePtr, _ := syscall.BytePtrFromString(config.SheetName)
|
||
filePatternPtr, _ := syscall.BytePtrFromString(config.FilePattern)
|
||
sourceSheetPtr, _ := syscall.BytePtrFromString(config.SourceSheet)
|
||
|
||
marshal, err := json.Marshal(config.SpecificFiles)
|
||
if err != nil {
|
||
return fmt.Errorf("序列化失败: %s", err)
|
||
}
|
||
specificFilesString := string(marshal)
|
||
specificFilesPtr, _ := syscall.BytePtrFromString(specificFilesString)
|
||
|
||
// 调用DLL函数
|
||
ret, _, err := dll.mergeExcelFilesEx.Call(
|
||
uintptr(handle),
|
||
uintptr(unsafe.Pointer(sourceDirPtr)),
|
||
uintptr(unsafe.Pointer(specificFilesPtr)),
|
||
uintptr(unsafe.Pointer(outputFilePtr)),
|
||
uintptr(unsafe.Pointer(sheetNamePtr)),
|
||
uintptr(boolToInt(config.MergeByColumn)),
|
||
uintptr(boolToInt(config.IncludeHeaders)),
|
||
uintptr(boolToInt(config.SkipEmptyRows)),
|
||
uintptr(unsafe.Pointer(filePatternPtr)),
|
||
uintptr(unsafe.Pointer(sourceSheetPtr)),
|
||
uintptr(boolToInt(config.AddSourceColumn)),
|
||
uintptr(boolToInt(config.AddIndexColumn)),
|
||
)
|
||
|
||
if int32(ret) != 0 {
|
||
return fmt.Errorf("合并Excel文件失败,错误码: %d", ret)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (dll *ExcelManagerDLL) FreeCString(str *C.char) {
|
||
if str != nil {
|
||
dll.freeCString.Call(uintptr(unsafe.Pointer(str)))
|
||
}
|
||
}
|
||
|
||
//// MergeConfig 合并配置
|
||
//type MergeConfig struct {
|
||
// SourceDir string // 源目录
|
||
// SpecificFiles []string // 指定要合并的文件列表
|
||
// OutputFile string // 输出文件
|
||
// SheetName string // 目标sheet名称
|
||
// MergeByColumn bool // 是否按列合并(默认按行)
|
||
// IncludeHeaders bool // 是否包含表头(仅第一个文件)
|
||
// SkipEmptyRows bool // 是否跳过空行
|
||
// FilePattern string // 文件匹配模式,如 "*.xlsx"
|
||
// SourceSheet string // 源sheet名称(为空则使用第一个sheet)
|
||
// AddSourceColumn bool // 是否添加源文件列
|
||
// AddIndexColumn bool // 是否添加序号列
|
||
//}
|
||
|
||
// 辅助函数
|
||
func boolToInt(b bool) int32 {
|
||
if b {
|
||
return 1
|
||
}
|
||
return 0
|
||
}
|
||
|
||
// 创建测试Excel文件
|
||
func createTestExcelFile(filename string) error {
|
||
file, err := os.Create(filename)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer file.Close()
|
||
|
||
// 写入一些测试数据(简单的CSV格式)
|
||
content := `姓名,年龄,城市
|
||
张三,25,北京
|
||
李四,30,上海
|
||
王五,28,广州
|
||
测试数据,35,深圳`
|
||
|
||
_, err = file.WriteString(content)
|
||
return err
|
||
}
|
||
|
||
func main() {
|
||
fmt.Println("=== Excel Manager DLL 动态加载测试程序 ===")
|
||
|
||
// 1. 加载DLL
|
||
dllPath := "excel/dll/excel.dll"
|
||
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
|
||
log.Fatalf("找不到DLL文件: %s", dllPath)
|
||
}
|
||
|
||
fmt.Printf("1. 加载DLL: %s\n", dllPath)
|
||
excelDLL, err := LoadExcelManagerDLL(dllPath)
|
||
if err != nil {
|
||
log.Fatalf("加载DLL失败: %v", err)
|
||
}
|
||
fmt.Println(" DLL加载成功")
|
||
|
||
// 2. 创建ExcelManager实例
|
||
fmt.Println("\n2. 创建ExcelManager实例...")
|
||
handle, err := excelDLL.NewExcelManagerInstance()
|
||
if err != nil || handle == 0 {
|
||
log.Fatalf("创建ExcelManager实例失败: %v", err)
|
||
}
|
||
fmt.Printf(" ExcelManager实例创建成功,句柄: 0x%x\n", handle)
|
||
defer excelDLL.FreeExcelManager(handle)
|
||
|
||
//// 创建测试目录
|
||
//testDir := "./test_data"
|
||
//if err := os.MkdirAll(testDir, 0755); err != nil {
|
||
// log.Fatalf("创建测试目录失败: %v", err)
|
||
//}
|
||
|
||
//3. 测试CreateAndWriteExcel函数
|
||
fmt.Println("\n3. 测试CreateAndWriteExcel函数...")
|
||
newFile := filepath.Join("excel", "3128518632404838051.xlsx")
|
||
|
||
//// 创建测试数据
|
||
//testData := [][]string{
|
||
// {"序号", "姓名", "年龄", "城市"},
|
||
// {"1", "张三", "25", "北京"},
|
||
// {"2", "李四", "30", "上海"},
|
||
// {"3", "王五", "28", "广州"},
|
||
// {"4", "赵六", "35", "深圳"},
|
||
// {"5", "测试人员", "40", "杭州"},
|
||
//}
|
||
//
|
||
//fmt.Printf(" 创建新文件: %s\n", newFile)
|
||
//fmt.Printf(" 数据维度: %d行 × %d列\n", len(testData), len(testData[0]))
|
||
//
|
||
//if err := excelDLL.CreateAndWriteExcel(handle, newFile, "员工信息", testData); err != nil {
|
||
// fmt.Printf("CreateAndWriteExcel失败: %v\n", err)
|
||
//} else {
|
||
// fmt.Println("CreateAndWriteExcel成功")
|
||
// // 验证文件是否创建成功
|
||
// if _, err := os.Stat(newFile); err == nil {
|
||
// fmt.Println(" 文件已成功创建")
|
||
// // 尝试读取刚刚创建的文件
|
||
// fmt.Println(" 验证文件内容...")
|
||
result, err := excelDLL.ReadExcelData(handle, newFile, "订单操作记录")
|
||
if err != nil {
|
||
fmt.Printf(" 读取文件失败: %v\n", err)
|
||
} else {
|
||
fmt.Printf(" 原始数据:\n%s\n", result)
|
||
}
|
||
|
||
// 1. 去除首尾可能存在的空格
|
||
result = strings.TrimSpace(result)
|
||
//
|
||
// }
|
||
//}
|
||
//
|
||
//// 4. 测试批量写入
|
||
//fmt.Println("\n5. 测试批量写入...")
|
||
//cells := []string{"A6", "B6", "C6", "D6", "E6"}
|
||
//values := []string{"5", "批量2", "批量3", "批量4", "批量5"}
|
||
//if err := excelDLL.WriteBatchData(handle, newFile, "员工信息", cells, values); err != nil {
|
||
// fmt.Printf(" 批量写入失败: %v\n", err)
|
||
//} else {
|
||
// fmt.Println(" 批量写入成功")
|
||
//}
|
||
//
|
||
//// 5. 测试追加数据
|
||
//fmt.Println("\n6. 测试追加数据...")
|
||
//appendValues := []string{"6", "孙七", "32", "南京", "追加数据"}
|
||
//if err := excelDLL.AppendDataToExcel(handle, newFile, "员工信息", appendValues); err != nil {
|
||
// fmt.Printf(" 追加数据失败: %v\n", err)
|
||
//} else {
|
||
// fmt.Println(" 追加数据成功")
|
||
//}
|
||
//
|
||
//// 7. 测试搜索功能
|
||
//fmt.Println("\n7. 测试搜索功能...")
|
||
//keyword := "nihao"
|
||
//searchResult, err := excelDLL.SearchByKeyword(handle, newFile, "员工信息", keyword)
|
||
//if err != nil {
|
||
// fmt.Printf("搜索失败: %v\n", err)
|
||
//} else {
|
||
// if searchResult == "" {
|
||
// fmt.Printf("没有搜索到该信息: %s\n", keyword)
|
||
// } else {
|
||
// fmt.Printf("搜索成功,结果:\n%s\n", searchResult)
|
||
// }
|
||
//}
|
||
//
|
||
//// 8. 测试合并文件(需要至少两个文件)
|
||
//fmt.Println("\n8. 测试合并文件...")
|
||
//// 合并两个文件
|
||
//mergedFile := filepath.Join("excel", "merged_output.xlsx")
|
||
//fmt.Println(mergedFile)
|
||
//mergeConfig := MergeConfig{
|
||
// SourceDir: "excel",
|
||
// SpecificFiles: []string{newFile, "excel/new_file1.xlsx"},
|
||
// OutputFile: mergedFile,
|
||
// SheetName: "合并结果",
|
||
// MergeByColumn: false,
|
||
// IncludeHeaders: true,
|
||
// SkipEmptyRows: false,
|
||
// FilePattern: "",
|
||
// SourceSheet: "", // 使用默认sheet
|
||
// AddSourceColumn: true,
|
||
// AddIndexColumn: true,
|
||
//}
|
||
//
|
||
//if err := excelDLL.MergeExcelFilesEx(handle, mergeConfig); err != nil {
|
||
// fmt.Printf(" 合并文件失败: %v\n", err)
|
||
//} else {
|
||
// fmt.Println(" 合并文件成功")
|
||
// fmt.Printf(" 输出文件: %s\n", mergedFile)
|
||
//}
|
||
|
||
//// 9. 清理测试文件(可选)
|
||
//fmt.Println("\n9. 清理测试文件...")
|
||
//cleanup := true
|
||
//if cleanup {
|
||
// filesToRemove := []string{
|
||
// newFile,
|
||
// secondFile,
|
||
// filepath.Join(testDir, "merged_output.xlsx"),
|
||
// }
|
||
//
|
||
// for _, file := range filesToRemove {
|
||
// if _, err := os.Stat(file); err == nil {
|
||
// if err := os.Remove(file); err == nil {
|
||
// fmt.Printf(" 删除: %s\n", file)
|
||
// }
|
||
// }
|
||
// }
|
||
}
|
||
|
||
//fmt.Println("\n=== 所有测试完成 ===")
|
||
//
|
||
////显 示测试总结
|
||
//fmt.Println("\n测试总结:")
|
||
//fmt.Println("1. CreateAndWriteExcel - 创建新文件并写入数据 ✓")
|
||
//fmt.Println("2. WriteDataToExcel - 写入单个单元格 ✓")
|
||
//fmt.Println("3. WriteBatchData - 批量写入数据 ✓")
|
||
//fmt.Println("4. AppendDataToExcel - 追加数据 ✓")
|
||
//fmt.Println("5. SearchByKeyword - 搜索关键词 ✓")
|
||
//fmt.Println("6. MergeExcelFilesEx - 合并文件 ✓")
|
||
//fmt.Println("7. ReadExcelData - 读取数据 ✓")
|
||
//}
|
||
|
||
//func ParseExcelDataToJSON(excelData string) ([]map[string]string, error) {
|
||
// if excelData == "" {
|
||
// return nil, fmt.Errorf("Excel数据为空")
|
||
// }
|
||
//
|
||
// // 使用csv解析器解析数据
|
||
// reader := csv.NewReader(strings.NewReader(excelData))
|
||
// records, err := reader.ReadAll()
|
||
// if err != nil {
|
||
// return nil, fmt.Errorf("解析CSV数据失败: %v", err)
|
||
// }
|
||
//
|
||
// if len(records) < 2 {
|
||
// return nil, fmt.Errorf("数据行数不足")
|
||
// }
|
||
//
|
||
// // 提取表头(第一行)
|
||
// headers := records[0]
|
||
//
|
||
// // 将数据转换为JSON格式
|
||
// var jsonData []map[string]string
|
||
//
|
||
// // 从第二行开始(跳过表头)
|
||
// for i := 1; i < len(records); i++ {
|
||
// row := records[i]
|
||
// rowData := make(map[string]string)
|
||
//
|
||
// // 确保每行的列数与表头一致
|
||
// for j := 0; j < len(headers) && j < len(row); j++ {
|
||
// rowData[headers[j]] = row[j]
|
||
// }
|
||
// jsonData = append(jsonData, rowData)
|
||
// }
|
||
//
|
||
// return jsonData, nil
|
||
//}
|