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 //}