“SimpleNotepad”的版本间的差异

来自姬鸿昌的知识库
跳到导航 跳到搜索
(建立内容为“<syntaxhighlight lang="c++"> #define _CRT_SECURE_NO_WARNINGS 1 #include <tchar.h> #include <windows.h> #include <commdlg.h> // 用于文件对话框 #include <st…”的新页面)
 
(没有差异)

2025年9月15日 (一) 09:36的最新版本

#define _CRT_SECURE_NO_WARNINGS 1
#include <tchar.h>
#include <windows.h>
#include <commdlg.h>  // 用于文件对话框
#include <stdio.h>
#include <string.h>
#include <shlobj.h>  // 用于目录选择对话框
#include <wincrypt.h>  // 用于Base64解码
#include <rpcdce.h>  // 包含UUID相关函数声明

#include "Scintilla.h"  // Scintilla头文件
#include "Lexilla.h"  // Scintilla头文件

#pragma comment(lib, "shell32.lib")  // 链接Shell32库
#pragma comment(lib, "crypt32.lib")  // 链接加密库

#define SCI_SETLEXER 2001
#define SCLEX_CPP 1  // C/C++ 解析器 ID
#define SCE_C_WORD 5  // C 语言关键字样式 ID
// 释放内存的宏(避免重复代码)
#define SAFE_FREE(ptr) if (ptr) { LocalFree(ptr); ptr = NULL; }

// 在现有宏定义处添加SCI_PASTE消息定义(Scintilla粘贴命令)
#define SCI_PASTE 2178

// 在现有宏定义后添加以下内容(自动换行相关)
#define SCI_SETWRAPMODE 2268  // 设置自动换行模式的消息
#define SC_WRAP_NONE 0        // 不自动换行
#define SC_WRAP_WORD 1        // 按单词换行(推荐,保持单词完整性)
#define SC_WRAP_CHAR 2        // 按字符换行

// 全局变量
HWND hScintilla;  // Scintilla控件句柄
char g_szFileName[MAX_PATH] = "";  // 当前打开的文件名

// 函数声明
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void InitScintilla(HWND hWnd);  // 初始化Scintilla控件
void DoFileOpen(HWND hWnd);    // 打开文件
void DoFileSave(HWND hWnd);    // 保存文件
void HandleConvertAction(HWND hWnd, HWND hScintilla);
// 声明解码函数(需与之前的函数实现放在同一编译单元)
//BYTE* Base64DecodeWide(WCHAR * base64Wide, DWORD * outSize);
BOOL Base64DecodeWide(WCHAR * base64Wide, BYTE * *outBuffer, DWORD * outSize);
BOOL WriteBytesToFile(const BYTE * bytes, DWORD byteCount, LPCWSTR filePath);
char* GenerateUUID();
LPCWSTR ConcatToLPCWSTR(LPCWSTR wideStr, const char* ansiStr);
char* ConcatAnsiStrings(int count, ...);

/**
 * 将WCHAR*类型的Base64字符串解码为字节数组
 * @param base64Wide 宽字符Base64字符串(WCHAR*)
 * @param outSize 输出:解码后的字节数组长度(成功时有效)
 * @return 成功返回解码后的字节数组(需用LocalFree释放),失败返回NULL
 */
//BYTE* Base64DecodeWide(WCHAR* base64Wide, DWORD* outSize) {
//    // 初始化输出长度为0
//    *outSize = 0;
//
//    // 步骤1:将宽字符串转换为UTF-8多字节字符串
//    int utf8Length = WideCharToMultiByte(
//        CP_UTF8, 0, base64Wide, -1, NULL, 0, NULL, NULL
//    );
//    if (utf8Length == 0) return NULL;
//
//    char* base64Utf8 = (char*)LocalAlloc(LPTR, utf8Length * sizeof(char));
//    if (!base64Utf8) return NULL;
//
//    if (WideCharToMultiByte(
//        CP_UTF8, 0, base64Wide, -1, base64Utf8, utf8Length, NULL, NULL
//    ) == 0) {
//        SAFE_FREE(base64Utf8);
//        return NULL;
//    }
//
//    // 步骤2:计算Base64解码所需的缓冲区大小
//    DWORD decodedSize = 0;
//    if (!CryptStringToBinaryA(
//        base64Utf8, 0, CRYPT_STRING_BASE64, NULL, &decodedSize, NULL, NULL
//    )) {
//        SAFE_FREE(base64Utf8);
//        return NULL;
//    }
//
//    // 步骤3:分配缓冲区并执行解码
//    BYTE* decodedBytes = (BYTE*)LocalAlloc(LPTR, decodedSize);
//    if (!decodedBytes) {
//        SAFE_FREE(base64Utf8);
//        return NULL;
//    }
//
//    if (!CryptStringToBinaryA(
//        base64Utf8, 0, CRYPT_STRING_BASE64, decodedBytes, &decodedSize, NULL, NULL
//    )) {
//        SAFE_FREE(decodedBytes);
//        SAFE_FREE(base64Utf8);
//        return NULL;
//    }
//
//    // 输出结果并清理中间变量
//    *outSize = decodedSize;
//    SAFE_FREE(base64Utf8);
//    return decodedBytes;  // 返回解码后的字节数组
//}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {

    // 加载Scintilla动态库并注册窗口类
    HMODULE hSci = LoadLibrary(L"Scintilla.dll"); // 需确保该DLL在程序目录下

    if (hSci == NULL) {
        MessageBox(NULL, L"无法加载Scintilla.dll", L"错误", MB_ICONERROR);
        return 0;
    }

    const char CLASS_NAME[] = "SimpleNotepadClass";
    WNDCLASS wc = { 0 };

    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);

    RegisterClass(&wc);

    // 创建主窗口
    HWND hWnd = CreateWindowEx(
        0, CLASS_NAME, L"Base64Converter",
        WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,  // WS_CLIPCHILDREN避免子控件重绘问题
        CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
        NULL, NULL, hInstance, NULL
    );

    if (hWnd == NULL) return 0;

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    // 消息循环
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

// 窗口消息处理
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {

    case WM_CREATE: {
        // 创建菜单
        HMENU hMenu = CreateMenu();

        // 只添加一个菜单项(按钮),ID设为1,文本可自定义
        AppendMenu(hMenu, MF_STRING, 1, L"转成文件导出到目录");  // 单个菜单项,&C表示Alt+C为快捷键

        SetMenu(hWnd, hMenu);

        // 创建Scintilla编辑控件(添加WS_TABSTOP样式)
        hScintilla = CreateWindowEx(
            0, L"Scintilla", L"",
            WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE | WS_TABSTOP, // 增加WS_TABSTOP
            0, 0, 0, 0, hWnd, (HMENU)100, ((LPCREATESTRUCT)lParam)->hInstance, NULL
        );
        InitScintilla(hWnd);

        // 创建后主动设置焦点到Scintilla
        SetFocus(hScintilla);

        break;
    }

    case WM_SIZE: {
        // 窗口大小改变时,调整Scintilla控件大小
        RECT rc;
        GetClientRect(hWnd, &rc);
        MoveWindow(hScintilla, 0, 0, rc.right, rc.bottom, TRUE);
        break;
    }

    case WM_COMMAND: {
        switch (LOWORD(wParam)) {
        case 1:  // 这里的1对应上面定义的菜单项ID
            // 调用封装后的处理函数
            HandleConvertAction(hWnd, hScintilla);
            break;
        case 4: DestroyWindow(hWnd); break;
        }
        break;
    }

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

        // 处理粘贴消息
    case WM_PASTE:
        // 将粘贴消息转发给Scintilla控件
        SendMessage(hScintilla, SCI_PASTE, 0, 0);
        return 0;

        // 处理键盘快捷键(确保Ctrl+V能触发粘贴)
    case WM_KEYDOWN:
        // 检测Ctrl+V组合键
        if (wParam == 'V' && (GetKeyState(VK_CONTROL) & 0x8000)) {
            SendMessage(hScintilla, SCI_PASTE, 0, 0);
            return 0;
        }
        break;

    case WM_SETFOCUS:
        // 当窗口获得焦点时,将焦点传递给Scintilla控件
        SetFocus(hScintilla);
        return 0;

    default:
        return DefWindowProc(hWnd, msg, wParam, lParam);
    }
    return 0;
}



// 转换处理函数的实现
void HandleConvertAction(HWND hWnd, HWND hScintilla) {

    // 获取Scintilla中的文本长度
    int textLength = SendMessage(hScintilla, SCI_GETLENGTH, 0, 0);

    if (textLength == 0) {
        // 文本为空时弹窗提示
        MessageBox(hWnd, L"输入区域不能为空,请先输入内容!", L"提示", MB_ICONWARNING | MB_OK);
        return;
    }
    
    // 弹出目录选择对话框
    TCHAR szDir[MAX_PATH] = { 0 };  // 存储选择的目录路径
    BROWSEINFOW bi = { 0 };
    bi.hwndOwner = hWnd;  // 父窗口句柄
    bi.pszDisplayName = szDir;  // 接收选择的目录名称
    bi.lpszTitle = L"请选择导出目录";  // 对话框标题
    bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;  // 只允许选择文件系统目录,使用新样式

    // 显示目录选择对话框
    LPITEMIDLIST pidl = SHBrowseForFolderW(&bi);
    if (pidl == NULL) {
        // 用户取消选择
        MessageBox(hWnd, L"已取消导出", L"提示", MB_OK);
        return;
    }

    // 将选择的目录ID转换为实际路径
    if (!SHGetPathFromIDListW(pidl, szDir)) {
        MessageBox(hWnd, L"获取目录路径失败", L"错误", MB_ICONERROR | MB_OK);
        CoTaskMemFree(pidl);  // 释放内存
        return;
    }

    // 释放资源
    CoTaskMemFree(pidl);

    //输出方式仍需根据项目的字符集(ANSI / Unicode) 选择对应方法
    //右键点击项目名称→“属性”→“配置属性”→“高级”→“高级属性”→“字符集”

    // 3. 显示选择的目录(此处可替换为实际导出逻辑)
    // 设置控制台为 UTF-8 编码(避免乱码)
    SetConsoleOutputCP(CP_UTF8);

    
    /*
    调试输出(自动适配 Unicode/ANSI)
    OutputDebugString 是一个宏,会根据字符集自动映射到:
        Unicode 环境 → OutputDebugStringW(接收宽字符串 wchar_t*)
        ANSI 环境 → OutputDebugStringA(接收ANSI字符串 char*)
    _T("字符串")也会自动适配:Unicode 环境下为宽字符串(L"字符串"),ANSI 环境下为 ANSI字符串("字符串")。
    */
    // 
    OutputDebugString(_T("当前目录:"));       // 输出固定字符串
    OutputDebugString(szDir);                  // 输出 TCHAR 数组
    OutputDebugString(_T("\n"));              // 输出换行

        // 2. 获取Scintilla中的Base64字符串
        // 分配缓冲区(+1用于存储终止符)
        //WCHAR* base64Str = (WCHAR*)LocalAlloc(LPTR, (textLength + 1) * sizeof(WCHAR));
        /*if (!base64Str) {
            MessageBox(hWnd, L"内存分配失败", L"错误", MB_ICONERROR | MB_OK);
            return;
        }*/

    // 先获取 ANSI/UTF-8 编码的文本(用 char* 接收)
    char* ansiStr = (char*)LocalAlloc(LPTR, (textLength + 1) * sizeof(char));  // +1 留空字符位置
    SendMessage(hScintilla, SCI_GETTEXT, textLength + 1, (LPARAM)ansiStr);

    //  将 ANSI/UTF-8 转换为宽字符(WCHAR*)
    //      ->计算转换所需的宽字符数
    int wideCharCount = MultiByteToWideChar(
        CP_UTF8,          // 假设 Scintilla 用 UTF-8 编码(若为 ANSI 则用 CP_ACP)
        0,                // 转换选项(0 为默认)
        ansiStr,          // 源 ANSI/UTF-8 字符串
        -1,               // 自动计算源字符串长度(包含空字符)
        NULL,             // 目标缓冲区(先传 NULL 计算长度)
        0                 // 目标缓冲区大小(0 表示仅计算长度)
    );

    //      ->分配宽字符缓冲区并转换
    // WCHAR是宽字符串,每个字符占2个字节
    WCHAR* wideStr = (WCHAR*)LocalAlloc(LPTR, wideCharCount * sizeof(WCHAR));
    MultiByteToWideChar(
        CP_UTF8,          // 同上,与源编码一致
        0,
        ansiStr,
        -1,
        wideStr,          // 目标宽字符缓冲区
        wideCharCount     // 缓冲区大小(用上面计算的结果)
    );

    //OutputDebugStringW(L"获取到的Base64字符串:\n");
    //OutputDebugStringW(wideStr);  // 直接传 WCHAR*,无需转换
    //OutputDebugStringW(L"\n");

    BYTE* decodedBytes = NULL;
    DWORD decodedSize = 0;

    // 调用解码函数
    if (Base64DecodeWide(wideStr, &decodedBytes, &decodedSize)) {
        // 解码成功:decodedBytes 是解码后的字节数组,decodedSize 是长度
        OutputDebugStringW(L"解码成功!字节数:");
        WCHAR sizeStr[32] = { 0 };
        
        // 正确调用:添加缓冲区大小 _countof(sizeStr)
        _itow_s(decodedSize, sizeStr, _countof(sizeStr), 10); // 转换数字为宽字符串
        
        OutputDebugStringW(sizeStr);
        OutputDebugStringW(L"\n");


        // 初始化COM库(CoCreateGuid依赖COM)
        CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

        char* uuid = GenerateUUID();
        if (uuid) {
            OutputDebugStringW(L"生成的UUID: ");
            OutputDebugStringW(uuid);
            OutputDebugStringW(L"\n");



            LocalFree(uuid);  // 必须释放内存
        }
        else {
            printf("UUID生成失败\n");
        }

        // 释放COM库
        CoUninitialize();

        // 使用字节数组(例如写入文件、解析图片等)
        // ...

        // 4. 步骤2:调用字节数组写入文件函数(输出为PNG图片文件)
        //LPCWSTR outputPath = L"E:\\record\\2025\\09\\15\\test_output.png";  // 输出文件路径

        //const char* tempStr = '\\' + uuid + '.png';

        char str1[50] = "\\";
        strcat(str1, uuid);

        strcat(str1, ".png");

        LPCWSTR outputPath = ConcatToLPCWSTR(szDir, str1);
        //szDir
        BOOL writeOk = WriteBytesToFile(decodedBytes, decodedSize, outputPath);

        if (writeOk) {
            OutputDebugStringW(L"字节数组写入文件成功!");
        }
        else {
            OutputDebugStringW(L"字节数组写入文件失败!");
        }

        // 用完后释放内存
        LocalFree(decodedBytes);
        // 必须释放内存
        //free(combined);
    }
    else {
        OutputDebugStringW(L"解码失败!\n");
    }
    LocalFree(wideStr);
    LocalFree(ansiStr);
   
}






// 初始化Scintilla控件(设置语法高亮、行号等)
void InitScintilla(HWND hWnd) {
    // 必须先发送SCI_SETFONT以初始化字体
    SendMessage(hScintilla, SCI_STYLESETFONT, 0, (LPARAM)"Consolas");
    SendMessage(hScintilla, SCI_STYLESETSIZE, 10, 0);  // 字体大小

    // 显示行号
    SendMessage(hScintilla, SCI_SETMARGINTYPEN, 0, SC_MARGIN_NUMBER);  // 第0个边距用于行号
    SendMessage(hScintilla, SCI_SETMARGINWIDTHN, 0, 50);  // 行号边距宽度

    // 设置语法高亮(以C语言为例)
    SendMessage(hScintilla, SCI_SETLEXER, SCLEX_CPP, 0);  // 使用C++语法解析器

    // 设置关键字颜色(示例:关键字为蓝色)
    SendMessage(hScintilla, SCI_STYLESETFORE, SCE_C_WORD, 0x0000FF);  // 关键字颜色
    SendMessage(hScintilla, SCI_SETKEYWORDS, 0, (LPARAM)"if else for while int char");  // 关键字列表

    // 新增:启用自动换行(按单词换行)
    SendMessage(hScintilla, SCI_SETWRAPMODE, SC_WRAP_CHAR, 0);
}

// 打开文件并加载到Scintilla
void DoFileOpen(HWND hWnd) {
    OPENFILENAME ofn = { 0 };
    char szFile[MAX_PATH] = "";
    ofn.lStructSize = sizeof(ofn);
    ofn.hwndOwner = hWnd;
    ofn.lpstrFile = szFile;
    ofn.nMaxFile = sizeof(szFile);
    ofn.lpstrFilter = L"文本文件(*.txt)\0*.txt\0C文件(*.c)\0*.c\0所有文件(*.*)\0*.*\0";
    ofn.nFilterIndex = 1;
    ofn.lpstrFileTitle = NULL;
    ofn.nMaxFileTitle = 0;
    ofn.lpstrInitialDir = NULL;
    ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

    if (GetOpenFileName(&ofn)) {
        // 读取文件内容
        FILE* f = fopen(szFile, "rb");
        if (f) {
            fseek(f, 0, SEEK_END);
            int nSize = ftell(f);
            fseek(f, 0, SEEK_SET);
            char* pBuf = malloc(nSize + 1);
            fread(pBuf, 1, nSize, f);
            pBuf[nSize] = '\0';
            fclose(f);

            // 将内容显示到Scintilla
            SendMessage(hScintilla, SCI_CLEARALL, 0, 0);
            SendMessage(hScintilla, SCI_SETTEXT, 0, (LPARAM)pBuf);
            free(pBuf);

            // 记录文件名
            strcpy(g_szFileName, szFile);
            SetWindowText(hWnd, g_szFileName);  // 更新窗口标题
        }
    }
}

// 保存Scintilla内容到文件
void DoFileSave(HWND hWnd) {
    char szFile[MAX_PATH] = "";
    if (g_szFileName[0] == '\0') {  // 若未保存过,弹出另存为对话框
        OPENFILENAME ofn = { 0 };
        ofn.lStructSize = sizeof(ofn);
        ofn.hwndOwner = hWnd;
        ofn.lpstrFile = szFile;
        ofn.nMaxFile = sizeof(szFile);
        ofn.lpstrFilter = L"文本文件(*.txt)\0*.txt\0C文件(*.c)\0*.c\0所有文件(*.*)\0*.*\0";
        ofn.nFilterIndex = 1;
        ofn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;

        if (!GetSaveFileName(&ofn)) return;
        strcpy(g_szFileName, szFile);
    }

    // 从Scintilla获取文本内容
    int nLen = SendMessage(hScintilla, SCI_GETLENGTH, 0, 0) + 1;
    char* pBuf = malloc(nLen);
    SendMessage(hScintilla, SCI_GETTEXT, nLen, (LPARAM)pBuf);

    // 写入文件
    FILE* f = fopen(g_szFileName, "wb");
    if (f) {
        fwrite(pBuf, 1, nLen - 1, f);  // 减去最后的'\0'
        fclose(f);
        SetWindowText(hWnd, g_szFileName);  // 更新窗口标题
    }
    free(pBuf);
}

/**
 * 将 WCHAR* 类型的 Base64 字符串解码为字节数组
 * @param base64Wide 宽字符 Base64 字符串(WCHAR*)
 * @param outBuffer 输出:解码后的字节数组(需调用者释放)
 * @param outSize 输出:解码后的字节数组长度
 * @return 成功返回 TRUE,失败返回 FALSE
 */
BOOL Base64DecodeWide(WCHAR* base64Wide, BYTE** outBuffer, DWORD* outSize) {
    // 步骤1:将 WCHAR*(宽字符)转换为 UTF-8 多字节字符串(char*)
    int utf8Length = WideCharToMultiByte(
        CP_UTF8,       // 目标编码:UTF-8
        0,             // 转换选项(0 为默认)
        base64Wide,    // 源宽字符串
        -1,            // 自动计算长度(包含终止符)
        NULL,          // 目标缓冲区(先计算长度)
        0,             // 缓冲区大小(0 表示仅计算)
        NULL, NULL
    );
    if (utf8Length == 0) return FALSE;

    // 分配 UTF-8 缓冲区并转换
    char* base64Utf8 = (char*)LocalAlloc(LPTR, utf8Length * sizeof(char));
    if (!base64Utf8) return FALSE;

    if (WideCharToMultiByte(
        CP_UTF8, 0, base64Wide, -1,
        base64Utf8, utf8Length, NULL, NULL
    ) == 0) {
        SAFE_FREE(base64Utf8);
        return FALSE;
    }

    // 步骤2:使用 Windows API 解码 Base64 字符串为字节数组
    // 2.1 先获取解码所需的缓冲区大小
    DWORD decodedSize = 0;
    if (!CryptStringToBinaryA(
        base64Utf8,       // 源 Base64 字符串(UTF-8)
        0,                // 字符串长度(0 表示自动计算)
        CRYPT_STRING_BASE64,  // 编码类型:Base64
        NULL,             // 目标缓冲区(先计算大小)
        &decodedSize,     // 输出:所需缓冲区大小
        NULL, NULL
    )) {
        SAFE_FREE(base64Utf8);
        return FALSE;
    }

    // 2.2 分配缓冲区并解码
    *outBuffer = (BYTE*)LocalAlloc(LPTR, decodedSize);
    if (!*outBuffer) {
        SAFE_FREE(base64Utf8);
        return FALSE;
    }

    if (!CryptStringToBinaryA(
        base64Utf8, 0, CRYPT_STRING_BASE64,
        *outBuffer, &decodedSize, NULL, NULL
    )) {
        SAFE_FREE(*outBuffer);
        SAFE_FREE(base64Utf8);
        return FALSE;
    }

    // 输出结果
    *outSize = decodedSize;
    SAFE_FREE(base64Utf8);  // 释放中间缓冲区
    return TRUE;
}

/**
 * 将字节数组写入指定文件
 * @param bytes 要写入的字节数组
 * @param byteCount 字节数组的长度
 * @param filePath 目标文件路径(宽字符)
 * @return 成功返回TRUE,失败返回FALSE
 */
BOOL WriteBytesToFile(const BYTE* bytes, DWORD byteCount, LPCWSTR filePath) {
    // 参数合法性检查
    if (!bytes || byteCount == 0 || !filePath) {
        return FALSE;
    }

    // 打开/创建文件(覆盖现有文件)
    HANDLE hFile = CreateFileW(
        filePath,               // 文件路径
        GENERIC_WRITE,          // 写入权限
        0,                      // 不共享
        NULL,                   // 默认安全属性
        CREATE_ALWAYS,          // 存在则覆盖,不存在则创建
        FILE_ATTRIBUTE_NORMAL,  // 正常文件属性
        NULL                    // 无模板
    );

    if (hFile == INVALID_HANDLE_VALUE) {
        return FALSE; // 文件打开失败
    }

    // 写入字节数组
    DWORD bytesWritten = 0;
    BOOL writeSuccess = WriteFile(
        hFile,                  // 文件句柄
        bytes,                  // 要写入的数据
        byteCount,              // 数据长度
        &bytesWritten,          // 实际写入字节数
        NULL                    // 无重叠操作
    );

    // 检查是否完全写入
    BOOL success = writeSuccess && (bytesWritten == byteCount);

    // 关闭文件句柄(无论成功与否都要关闭)
    CloseHandle(hFile);

    return success;
}

// 生成UUID并返回字符串(格式:8-4-4-4-12,共36个字符)
// 注意:调用后需用LocalFree释放返回的字符串,避免内存泄漏
char* GenerateUUID() {
    GUID guid;
    HRESULT result = CoCreateGuid(&guid);
    if (result != S_OK) {
        return NULL;  // 生成失败返回NULL
    }

    // 转换GUID为宽字符串(格式带花括号,如 {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx})
    // 方法1:使用栈上的固定大小缓冲区(简单场景,无需手动释放)
    WCHAR wideUuid[40];  // 40足够容纳带花括号的UUID(38字符+终止符)
    if (StringFromGUID2(&guid, wideUuid, _countof(wideUuid)) == 0) {
        return NULL;  // 转换失败
    }
    //WCHAR* wideUuid;
    //if (StringFromGUID2(&guid, wideUuid, 40) == 0) {  // 40足够容纳UUID字符串
    //    return NULL;
    //}

    // 转换宽字符串为ANSI字符串(去掉花括号)
    char* ansiUuid = (char*)LocalAlloc(LPTR, 37);  // 36个字符 + 终止符
    if (!ansiUuid) {
        LocalFree(wideUuid);
        return NULL;
    }

    // 提取中间36个字符(去掉首尾的花括号)
    WideCharToMultiByte(CP_ACP, 0, wideUuid + 1, 36, ansiUuid, 36, NULL, NULL);
    ansiUuid[36] = '\0';  // 手动添加终止符

    // 释放宽字符串内存
    //LocalFree(wideUuid);

    return ansiUuid;
}

// 拼接LPCWSTR(宽字符串)和char*(窄字符串),返回LPCWSTR(需用LocalFree释放)
// 注意:返回的LPCWSTR本质是动态分配的WCHAR*,使用后必须释放
LPCWSTR ConcatToLPCWSTR(LPCWSTR wideStr, const char* ansiStr) {
    if (!wideStr || !ansiStr) {
        return NULL; // 输入参数无效
    }

    // 步骤1:将char*(窄字符串)转换为宽字符串WCHAR*
    int ansiLen = strlen(ansiStr);
    // 计算转换后的宽字符串长度(不含终止符)
    int wideConvLen = MultiByteToWideChar(
        CP_ACP,        // 源编码(ANSI,需与char*编码一致,UTF-8用CP_UTF8)
        0,             // 转换选项(0表示默认)
        ansiStr,       // 源窄字符串
        ansiLen,       // 源长度(不含终止符)
        NULL,          // 目标缓冲区(先计算长度)
        0              // 目标缓冲区大小(0表示仅计算长度)
    );
    if (wideConvLen == 0) {
        return NULL; // 转换失败
    }

    // 分配转换后的宽字符串缓冲区(+1用于终止符)
    WCHAR* convertedWide = (WCHAR*)LocalAlloc(LPTR, (wideConvLen + 1) * sizeof(WCHAR));
    if (!convertedWide) {
        return NULL; // 内存分配失败
    }

    // 执行转换
    if (MultiByteToWideChar(
        CP_ACP,
        0,
        ansiStr,
        ansiLen,
        convertedWide,
        wideConvLen
    ) == 0) {
        LocalFree(convertedWide); // 转换失败,释放内存
        return NULL;
    }
    convertedWide[wideConvLen] = L'\0'; // 手动添加宽字符串终止符


    // 步骤2:拼接两个宽字符串(原LPCWSTR + 转换后的宽字符串)
    int originalWideLen = wcslen(wideStr); // 原宽字符串长度(不含终止符)
    int totalLen = originalWideLen + wideConvLen; // 总长度(不含终止符)

    // 分配拼接后的缓冲区(+1用于终止符)
    WCHAR* result = (WCHAR*)LocalAlloc(LPTR, (totalLen + 1) * sizeof(WCHAR));
    if (!result) {
        LocalFree(convertedWide); // 内存分配失败,释放临时缓冲区
        return NULL;
    }

    // 安全复制原宽字符串到结果缓冲区
    wcscpy_s(result, totalLen + 1, wideStr);
    // 安全追加转换后的宽字符串
    wcscat_s(result, totalLen + 1, convertedWide);

    // 释放临时转换的宽字符串
    LocalFree(convertedWide);

    // WCHAR* 可隐式转换为 LPCWSTR(const WCHAR*)
    return result;
}

// 拼接多个const char*字符串,返回可作为const char*的结果(需用free释放)
char* ConcatAnsiStrings(int count, ...) {
    if (count <= 0) return NULL;

    va_list args;
    va_start(args, count);

    // 步骤1:计算所有字符串的总长度(不含终止符)
    size_t totalLen = 0;
    for (int i = 0; i < count; i++) {
        const char* str = va_arg(args, const char*);
        if (!str) {  // 忽略NULL,避免计算错误
            continue;
        }
        totalLen += strlen(str);
    }
    va_end(args);  // 重置参数列表

    // 步骤2:分配内存(总长度 + 1个终止符)
    char* result = (char*)malloc(totalLen + 1);
    if (!result) return NULL;

    // 步骤3:拼接所有字符串
    result[0] = '\0';  // 初始化空字符串
    va_start(args, count);
    for (int i = 0; i < count; i++) {
        const char* str = va_arg(args, const char*);
        if (!str) {
            continue;
        }
        // 安全追加字符串(确保不溢出)
        if (strcat_s(result, totalLen + 1, str) != 0) {
            free(result);
            va_end(args);
            return NULL;
        }
    }
    va_end(args);

    return result;
}