daShangDao_kfzgw-info/excel/excelDllTest.go
2025-12-22 19:09:56 +08:00

517 lines
16 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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