“C语言实现文件转Base64编码字符串”的版本间的差异

来自姬鸿昌的知识库
跳到导航 跳到搜索
(建立内容为“file_to_base64.c<syntaxhighlight lang="c"> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> // 用于获取文件大小 // Base64…”的新页面)
 
 
(未显示同一用户的1个中间版本)
第1行: 第1行:
file_to_base64.c<syntaxhighlight lang="c">
+
SimpleNotepad.c<syntaxhighlight lang="c">
 +
#define _CRT_SECURE_NO_WARNINGS 1
 +
 
 +
#include <tchar.h>
 +
#include <windows.h>
 +
 
 +
#include <commdlg.h>  // 用于文件对话框
 
#include <stdio.h>
 
#include <stdio.h>
#include <stdlib.h>
 
 
#include <string.h>
 
#include <string.h>
#include <sys/stat.h> // 用于获取文件大小
+
#include <shlobj.h>  // 用于目录选择对话框
 +
#include <wincrypt.h>  // 用于Base64解码
 +
#include <rpcdce.h> // 包含UUID相关函数声明
 +
#include <stdint.h>
 +
#include <stddef.h>
 +
#include "Scintilla.h"  // Scintilla头文件
 +
#include "Lexilla.h"  // Scintilla头文件
 +
#include "resource.h"
 +
 
 +
#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        // 按字符换行
 +
 
 +
// 文件类型枚举
 +
typedef enum {
 +
    MY_FILE_TYPE_UNKNOWN,
 +
    FILE_TYPE_PNG,
 +
    FILE_TYPE_JPG,
 +
    FILE_TYPE_PDF,
 +
    FILE_TYPE_DOC,    // Word 97-2003 (.doc)
 +
    FILE_TYPE_DOCX,  // Word 2007+ (.docx)
 +
    FILE_TYPE_ZIP,    // ZIP压缩包(包括docx/xlsx等)
 +
    FILE_TYPE_GIF,    // GIF图像
 +
    FILE_TYPE_BMP    // BMP图像
 +
} FileType;
 +
 
 +
// 全局变量
 +
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);
 +
// 声明解码函数(需与之前的函数实现放在同一编译单元)
 +
 
 +
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, ...);
 +
FileType GetFileTypeFromBytes(const BYTE* fileData, size_t fileSize);
 +
const void* my_memmem(const void* src, size_t src_len, const void* dest, size_t dest_len);
 +
 
 +
// 从资源提取DLL到临时目录并返回路径
 +
char* extract_dll_to_temp() {
 +
    // 1. 查找DLL资源
 +
    HRSRC hRes = FindResourceA(NULL, MAKEINTRESOURCEA(IDR_DLL1), "DLL");
 +
    if (!hRes) {
 +
        printf("FindResource failed: %d\n", GetLastError());
 +
        return NULL;
 +
    }
 +
 
 +
    // 2. 加载资源到内存
 +
    HGLOBAL hGlobal = LoadResource(NULL, hRes);
 +
    if (!hGlobal) {
 +
        printf("LoadResource failed: %d\n", GetLastError());
 +
        return NULL;
 +
    }
 +
 
 +
    // 3. 锁定资源并获取数据
 +
    LPVOID dllData = LockResource(hGlobal);
 +
    DWORD dllSize = SizeofResource(NULL, hRes);
 +
    if (!dllData || dllSize == 0) {
 +
        printf("LockResource failed: %d\n", GetLastError());
 +
        return NULL;
 +
    }
 +
 
 +
    // 4. 获取系统临时目录路径(如 C:\Users\用户名\AppData\Local\Temp\)
 +
    char tempDir[MAX_PATH] = { 0 };
 +
    if (!GetTempPathA(MAX_PATH, tempDir)) {
 +
        printf("GetTempPath failed: %d\n", GetLastError());
 +
        return NULL;
 +
    }
 +
 
 +
    // 5. 构造临时DLL路径(如 %TEMP%\external_temp.dll)
 +
    char tempDllPath[MAX_PATH] = { 0 };
 +
    sprintf(tempDllPath, "%sexternal_temp.dll", tempDir);  // 可自定义文件名
  
// Base64编码表
+
    // 6. 若临时文件已存在,先删除
const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
    DeleteFileA(tempDllPath);
  
// 计算Base64编码后的长度
+
    // 7. 将资源中的DLL数据写入临时文件(C标准库文件操作)
size_t base64_encoded_length(size_t input_len) {
+
    FILE* fp = fopen(tempDllPath, "wb");
    return (input_len + 2) / 3 * 4;
+
    if (!fp) {
 +
        printf("fopen failed: %d\n", GetLastError());
 +
        return NULL;
 +
    }
 +
    size_t written = fwrite(dllData, 1, dllSize, fp);
 +
    fclose(fp);
 +
    if (written != dllSize) {
 +
        printf("Write DLL to temp file failed\n");
 +
        DeleteFileA(tempDllPath);
 +
        return NULL;
 +
    }
 +
 
 +
    // 8. 返回临时DLL路径(需在程序退出前释放内存)
 +
    char* result = (char*)malloc(MAX_PATH);
 +
    strcpy(result, tempDllPath);
 +
    return result;
 
}
 
}
  
// 将二进制数据转换为Base64编码
+
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
char* data_to_base64(const unsigned char* input, size_t input_len) {
+
 
     // 计算输出缓冲区大小并分配内存
+
    // 1. 从资源提取DLL到临时目录
     size_t output_len = base64_encoded_length(input_len);
+
    char* tempDllPath = extract_dll_to_temp();
     char* output = (char*)malloc(output_len + 1); // +1 用于存储终止符
+
    if (!tempDllPath) {
     if (output == NULL) {
+
        printf("提取DLL失败\n");
         return NULL; // 内存分配失败
+
        return 1;
 +
    }
 +
 
 +
    // 加载Scintilla动态库并注册窗口类
 +
    //HMODULE hSci = LoadLibrary(L"Scintilla.dll"); // 需确保该DLL在程序目录下
 +
    HMODULE hSci = LoadLibraryA(tempDllPath);
 +
 
 +
    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) {
 +
            MessageBox(hWnd, L"UUID生成失败", L"错误", MB_ICONERROR);
 +
            LocalFree(decodedBytes);
 +
            LocalFree(wideStr);
 +
            LocalFree(ansiStr);
 +
            return;
 +
        }
 +
 
 +
        OutputDebugStringW(L"生成的UUID: ");
 +
        OutputDebugStringW(uuid);
 +
        OutputDebugStringW(L"\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);
 +
 
 +
        FileType fileType = GetFileTypeFromBytes(decodedBytes, decodedSize);
 +
       
 +
        if (fileType == FILE_TYPE_PNG) {
 +
            strcat(str1, ".png");
 +
        }
 +
        else if (fileType == FILE_TYPE_JPG) {
 +
            strcat(str1, ".jpg");
 +
        }
 +
        else if (fileType == FILE_TYPE_PDF) {
 +
            strcat(str1, ".pdf");
 +
        }
 +
        else if (fileType == FILE_TYPE_DOC) {
 +
            strcat(str1, ".doc");// Word 97-2003 (.doc)
 +
        }
 +
        else if (fileType == FILE_TYPE_DOCX) {
 +
            strcat(str1, ".docx");// Word 2007+ (.docx)
 +
        }
 +
        else if (fileType == FILE_TYPE_ZIP) {
 +
            strcat(str1, ".zip");// ZIP压缩包(包括docx/xlsx等)
 +
        }
 +
        else if (fileType == FILE_TYPE_GIF) {
 +
            strcat(str1, ".gif");// GIF图像
 +
        }
 +
        else if (fileType == FILE_TYPE_BMP) {
 +
            strcat(str1, ".bmp"); // BMP图像
 +
        }
 +
        else {
 +
            strcat(str1, ".unknown");
 +
        }
 +
 
 +
        LPCWSTR outputPath = ConcatToLPCWSTR(szDir, str1);
 +
        //szDir
 +
        BOOL writeOk = WriteBytesToFile(decodedBytes, decodedSize, outputPath);
 +
 
 +
        if (writeOk) {
 +
            OutputDebugStringW(L"字节数组写入文件成功!");
 +
            MessageBox(hWnd, L"完成!", L"提示", MB_OK);
 +
        }
 +
        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;
  
     int i, j;
+
     if (WideCharToMultiByte(
     for (i = 0, j = 0; i < input_len; i += 3, j += 4) {
+
        CP_UTF8, 0, base64Wide, -1,
         // 读取3个字节,不足的用0填充
+
        base64Utf8, utf8Length, NULL, NULL
        unsigned char byte1 = input[i];
+
     ) == 0) {
        unsigned char byte2 = (i + 1 < input_len) ? input[i + 1] : 0;
+
         SAFE_FREE(base64Utf8);
         unsigned char byte3 = (i + 2 < input_len) ? input[i + 2] : 0;
+
         return FALSE;
 +
    }
  
         // 将3个8位字节转换为4个6位值
+
    // 步骤2:使用 Windows API 解码 Base64 字符串为字节数组
         unsigned int val = (byte1 << 16) | (byte2 << 8) | byte3;
+
    // 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;
 +
    }
  
        // 提取每个6位值并映射到Base64字符
+
    // 2.2 分配缓冲区并解码
        output[j] = base64_chars[(val >> 18) & 0x3F];
+
    *outBuffer = (BYTE*)LocalAlloc(LPTR, decodedSize);
         output[j + 1] = base64_chars[(val >> 12) & 0x3F];
+
    if (!*outBuffer) {
 +
         SAFE_FREE(base64Utf8);
 +
        return FALSE;
 +
    }
  
         // 处理填充字符'='
+
    if (!CryptStringToBinaryA(
         output[j + 2] = (i + 1 < input_len) ? base64_chars[(val >> 6) & 0x3F] : '=';
+
         base64Utf8, 0, CRYPT_STRING_BASE64,
         output[j + 3] = (i + 2 < input_len) ? base64_chars[val & 0x3F] : '=';
+
         *outBuffer, &decodedSize, NULL, NULL
 +
    )) {
 +
        SAFE_FREE(*outBuffer);
 +
         SAFE_FREE(base64Utf8);
 +
        return FALSE;
 
     }
 
     }
  
     output[output_len] = '\0'; // 添加字符串终止符
+
     // 输出结果
     return output;
+
    *outSize = decodedSize;
 +
    SAFE_FREE(base64Utf8); // 释放中间缓冲区
 +
     return TRUE;
 
}
 
}
  
// 获取文件大小
+
/**
long get_file_size(const char* filename) {
+
* 将字节数组写入指定文件
     struct stat file_info;
+
* @param bytes 要写入的字节数组
     if (stat(filename, &file_info) != 0) {
+
* @param byteCount 字节数组的长度
         return -1; // 获取文件信息失败
+
* @param filePath 目标文件路径(宽字符)
 +
* @return 成功返回TRUE,失败返回FALSE
 +
*/
 +
BOOL WriteBytesToFile(const BYTE* bytes, DWORD byteCount, LPCWSTR filePath) {
 +
     // 参数合法性检查
 +
     if (!bytes || byteCount == 0 || !filePath) {
 +
         return FALSE;
 
     }
 
     }
     return file_info.st_size;
+
 
 +
    // 打开/创建文件(覆盖现有文件)
 +
    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个字符)
unsigned char* read_file_to_buffer(const char* filename, long* file_size) {
+
// 注意:调用后需用LocalFree释放返回的字符串,避免内存泄漏
     FILE* file = fopen(filename, "rb"); // 以二进制模式打开
+
char* GenerateUUID() {
     if (file == NULL) {
+
    GUID guid;
         perror("无法打开文件");
+
    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;
 
         return NULL;
 
     }
 
     }
  
     // 获取文件大小
+
     // 提取中间36个字符(去掉首尾的花括号)
     *file_size = get_file_size(filename);
+
     WideCharToMultiByte(CP_ACP, 0, wideUuid + 1, 36, ansiUuid, 36, NULL, NULL);
     if (*file_size < 0) {
+
    ansiUuid[36] = '\0';  // 手动添加终止符
         perror("无法获取文件大小");
+
 
         fclose(file);
+
    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;
 
         return NULL;
 
     }
 
     }
 +
    convertedWide[wideConvLen] = L'\0'; // 手动添加宽字符串终止符
 +
 +
 +
    // 步骤2:拼接两个宽字符串(原LPCWSTR + 转换后的宽字符串)
 +
    int originalWideLen = wcslen(wideStr); // 原宽字符串长度(不含终止符)
 +
    int totalLen = originalWideLen + wideConvLen; // 总长度(不含终止符)
  
     // 分配缓冲区
+
     // 分配拼接后的缓冲区(+1用于终止符)
     unsigned char* buffer = (unsigned char*)malloc(*file_size);
+
     WCHAR* result = (WCHAR*)LocalAlloc(LPTR, (totalLen + 1) * sizeof(WCHAR));
     if (buffer == NULL) {
+
     if (!result) {
         perror("内存分配失败");
+
         LocalFree(convertedWide); // 内存分配失败,释放临时缓冲区
        fclose(file);
 
 
         return NULL;
 
         return NULL;
 
     }
 
     }
  
     // 读取文件内容
+
     // 安全复制原宽字符串到结果缓冲区
     size_t bytes_read = fread(buffer, 1, *file_size, file);
+
     wcscpy_s(result, totalLen + 1, wideStr);
     if (bytes_read != *file_size) {
+
    // 安全追加转换后的宽字符串
         perror("文件读取失败");
+
    wcscat_s(result, totalLen + 1, convertedWide);
         free(buffer);
+
 
        fclose(file);
+
    // 释放临时转换的宽字符串
        return NULL;
+
    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;
 +
}
 +
  
     fclose(file);
+
// 将文件类型枚举转换为字符串
     return buffer;
+
const char* FileTypeToString(FileType type) {
 +
     switch (type) {
 +
    case FILE_TYPE_PNG:    return "PNG image";
 +
    case FILE_TYPE_JPG:    return "JPEG image";
 +
    case FILE_TYPE_PDF:    return "PDF document";
 +
    case FILE_TYPE_DOC:    return "Word (.doc) document";
 +
    case FILE_TYPE_DOCX:  return "Word (.docx) document";
 +
    case FILE_TYPE_ZIP:    return "ZIP archive";
 +
     default:              return "Unknown file type";
 +
    }
 
}
 
}
  
// 将文件转换为Base64编码并输出
+
// 根据文件字节数据判断文件类型
int file_to_base64(const char* filename, int output_to_file, const char* output_filename) {
+
// 参数:
     long file_size;
+
//  fileData - 整个文件的字节数据(BYTE*)
     unsigned char* file_data = read_file_to_buffer(filename, &file_size);
+
//  fileSize - 文件总大小(字节数),用于边界检查
    if (file_data == NULL) {
+
// 返回:
        return 1; // 读取文件失败
+
//  对应的FileType枚举值,未知类型返回FILE_TYPE_UNKNOWN
 +
FileType GetFileTypeFromBytes(const BYTE* fileData, size_t fileSize) {
 +
     // 检查输入有效性
 +
    if (fileData == NULL || fileSize == 0) {
 +
        return FILE_TYPE_UNKNOWN;
 +
     }
 +
 
 +
    // 1. 判断PNG (8字节魔术数字)
 +
    if (fileSize >= 8) {
 +
        const BYTE pngSignature[] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
 +
        if (memcmp(fileData, pngSignature, 8) == 0) {
 +
            return FILE_TYPE_PNG;
 +
        }
 
     }
 
     }
  
     // 转换为Base64
+
     // 2. 判断JPG (3字节魔术数字)
     char* base64_str = data_to_base64(file_data, file_size);
+
     if (fileSize >= 3) {
     if (base64_str == NULL) {
+
        const BYTE jpgSignature[] = { 0xFF, 0xD8, 0xFF };
         perror("Base64编码失败");
+
        if (memcmp(fileData, jpgSignature, 3) == 0) {
         free(file_data);
+
            return FILE_TYPE_JPG;
         return 1;
+
        }
 +
    }
 +
 
 +
    // 3. 判断PDF (4字节魔术数字)
 +
     if (fileSize >= 4) {
 +
         const BYTE pdfSignature[] = { 0x25, 0x50, 0x44, 0x46 }; // 对应 "%PDF"
 +
         if (memcmp(fileData, pdfSignature, 4) == 0) {
 +
            return FILE_TYPE_PDF;
 +
         }
 
     }
 
     }
  
     // 输出结果
+
     // 4. 判断Word (.doc) (8字节魔术数字 - OLE复合文档格式)
     if (output_to_file && output_filename != NULL) {
+
     if (fileSize >= 8) {
         // 输出到文件
+
         const BYTE docSignature[] = { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
        FILE* out_file = fopen(output_filename, "w");
+
         if (memcmp(fileData, docSignature, 8) == 0) {
         if (out_file == NULL) {
+
             return FILE_TYPE_DOC;
            perror("无法打开输出文件");
 
            free(file_data);
 
            free(base64_str);
 
             return 1;
 
 
         }
 
         }
 +
    }
 +
 +
    // 5. 判断ZIP及基于ZIP的格式 (4字节魔术数字)
 +
    if (fileSize >= 4) {
 +
        const BYTE zipSignature1[] = { 0x50, 0x4B, 0x03, 0x04 };
 +
        const BYTE zipSignature2[] = { 0x50, 0x4B, 0x05, 0x06 };
 +
        const BYTE zipSignature3[] = { 0x50, 0x4B, 0x07, 0x08 };
  
         fputs(base64_str, out_file);
+
         if (memcmp(fileData, zipSignature1, 4) == 0 ||
        fclose(out_file);
+
            memcmp(fileData, zipSignature2, 4) == 0 ||
        printf("Base64编码已保存到 %s\n", output_filename);
+
            memcmp(fileData, zipSignature3, 4) == 0) {
 +
 
 +
            // .docx是特殊的ZIP格式,可进一步通过内部文件结构判断
 +
            // 这里简化处理:如果是ZIP且文件大小足够,检查是否包含docx特征
 +
            if (fileSize >= 0x1000) { // 检查前4KB内是否有"[Content_Types].xml"
 +
                const char* docxMarker = "[Content_Types].xml";
 +
                if (my_memmem(fileData, 0x1000, docxMarker, strlen(docxMarker)) != NULL) {
 +
                    return FILE_TYPE_DOCX;
 +
                }
 +
            }
 +
 
 +
            return FILE_TYPE_ZIP;
 +
        }
 
     }
 
     }
     else {
+
 
         // 输出到控制台
+
     // 6. 判断GIF (6字节魔术数字)
         printf("文件 %s 的Base64编码:\n", filename);
+
    if (fileSize >= 6) {
         printf("%s\n", base64_str);
+
         const BYTE gifSignature1[] = { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }; // GIF87a
 +
         const BYTE gifSignature2[] = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }; // GIF89a
 +
        if (memcmp(fileData, gifSignature1, 6) == 0 ||
 +
            memcmp(fileData, gifSignature2, 6) == 0) {
 +
            return FILE_TYPE_GIF;
 +
         }
 +
    }
 +
 
 +
    // 7. 判断BMP (2字节魔术数字)
 +
    if (fileSize >= 2) {
 +
        const BYTE bmpSignature[] = { 0x42, 0x4D }; // 对应 "BM"
 +
        if (memcmp(fileData, bmpSignature, 2) == 0) {
 +
            return FILE_TYPE_BMP;
 +
        }
 
     }
 
     }
  
     // 清理内存
+
     // 可扩展其他文件类型(如MP3、TXT等)
    free(file_data);
+
     return MY_FILE_TYPE_UNKNOWN;
    free(base64_str);
 
     return 0;
 
 
}
 
}
  
int main(int argc, char* argv[]) {
+
// 自定义内存查找函数:在src内存块中查找dest内存块的首次出现
     // 检查命令行参数
+
// 参数:
     if (argc < 2) {
+
//  src: 源内存块指针
        printf("用法: %s <文件名> [输出文件名]\n", argv[0]);
+
//  src_len: 源内存块长度
        printf("如果提供输出文件名,则将Base64编码保存到文件,否则输出到控制台\n");
+
//  dest: 目标内存块指针
         return 1;
+
//  dest_len: 目标内存块长度
 +
// 返回:
 +
//  找到则返回src中匹配的起始位置指针,否则返回NULL
 +
const void* my_memmem(const void* src, size_t src_len, const void* dest, size_t dest_len) {
 +
     // 边界检查
 +
     if (src == NULL || dest == NULL || src_len == 0 || dest_len == 0 || dest_len > src_len) {
 +
         return NULL;
 
     }
 
     }
  
     const char* input_filename = argv[1];
+
     const unsigned char* src_ptr = (const unsigned char*)src;
     const char* output_filename = (argc >= 3) ? argv[2] : NULL;
+
     const unsigned char* dest_ptr = (const unsigned char*)dest;
 +
    size_t max_search_len = src_len - dest_len;  // 最大查找范围
 +
 
 +
    // 遍历源内存块,逐个位置比对
 +
    for (size_t i = 0; i <= max_search_len; i++) {
 +
        // 找到第一个匹配的字节后,完整比对目标内存块
 +
        if (src_ptr[i] == dest_ptr[0]) {
 +
            if (memcmp(&src_ptr[i], dest_ptr, dest_len) == 0) {
 +
                return &src_ptr[i]; // 找到匹配,返回起始位置
 +
            }
 +
        }
 +
    }
  
     // 执行转换
+
     return NULL;  // 未找到匹配
    return file_to_base64(input_filename, (argc >= 3), output_filename);
 
 
}
 
}
 
 
</syntaxhighlight>
 
</syntaxhighlight>

2025年9月18日 (四) 07:01的最新版本

SimpleNotepad.c

#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 <stdint.h>
#include <stddef.h>
#include "Scintilla.h"  // Scintilla头文件
#include "Lexilla.h"  // Scintilla头文件
#include "resource.h"

#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        // 按字符换行

// 文件类型枚举
typedef enum {
    MY_FILE_TYPE_UNKNOWN,
    FILE_TYPE_PNG,
    FILE_TYPE_JPG,
    FILE_TYPE_PDF,
    FILE_TYPE_DOC,    // Word 97-2003 (.doc)
    FILE_TYPE_DOCX,   // Word 2007+ (.docx)
    FILE_TYPE_ZIP,    // ZIP压缩包(包括docx/xlsx等)
    FILE_TYPE_GIF,    // GIF图像
    FILE_TYPE_BMP     // BMP图像
} FileType;

// 全局变量
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);
// 声明解码函数(需与之前的函数实现放在同一编译单元)

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, ...);
FileType GetFileTypeFromBytes(const BYTE* fileData, size_t fileSize);
const void* my_memmem(const void* src, size_t src_len, const void* dest, size_t dest_len);

// 从资源提取DLL到临时目录并返回路径
char* extract_dll_to_temp() {
    // 1. 查找DLL资源
    HRSRC hRes = FindResourceA(NULL, MAKEINTRESOURCEA(IDR_DLL1), "DLL");
    if (!hRes) {
        printf("FindResource failed: %d\n", GetLastError());
        return NULL;
    }

    // 2. 加载资源到内存
    HGLOBAL hGlobal = LoadResource(NULL, hRes);
    if (!hGlobal) {
        printf("LoadResource failed: %d\n", GetLastError());
        return NULL;
    }

    // 3. 锁定资源并获取数据
    LPVOID dllData = LockResource(hGlobal);
    DWORD dllSize = SizeofResource(NULL, hRes);
    if (!dllData || dllSize == 0) {
        printf("LockResource failed: %d\n", GetLastError());
        return NULL;
    }

    // 4. 获取系统临时目录路径(如 C:\Users\用户名\AppData\Local\Temp\)
    char tempDir[MAX_PATH] = { 0 };
    if (!GetTempPathA(MAX_PATH, tempDir)) {
        printf("GetTempPath failed: %d\n", GetLastError());
        return NULL;
    }

    // 5. 构造临时DLL路径(如 %TEMP%\external_temp.dll)
    char tempDllPath[MAX_PATH] = { 0 };
    sprintf(tempDllPath, "%sexternal_temp.dll", tempDir);  // 可自定义文件名

    // 6. 若临时文件已存在,先删除
    DeleteFileA(tempDllPath);

    // 7. 将资源中的DLL数据写入临时文件(C标准库文件操作)
    FILE* fp = fopen(tempDllPath, "wb");
    if (!fp) {
        printf("fopen failed: %d\n", GetLastError());
        return NULL;
    }
    size_t written = fwrite(dllData, 1, dllSize, fp);
    fclose(fp);
    if (written != dllSize) {
        printf("Write DLL to temp file failed\n");
        DeleteFileA(tempDllPath);
        return NULL;
    }

    // 8. 返回临时DLL路径(需在程序退出前释放内存)
    char* result = (char*)malloc(MAX_PATH);
    strcpy(result, tempDllPath);
    return result;
}

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

    // 1. 从资源提取DLL到临时目录
    char* tempDllPath = extract_dll_to_temp();
    if (!tempDllPath) {
        printf("提取DLL失败\n");
        return 1;
    }

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

    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) {
            MessageBox(hWnd, L"UUID生成失败", L"错误", MB_ICONERROR);
            LocalFree(decodedBytes);
            LocalFree(wideStr);
            LocalFree(ansiStr);
            return;
        }

        OutputDebugStringW(L"生成的UUID: ");
        OutputDebugStringW(uuid);
        OutputDebugStringW(L"\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);

        FileType fileType = GetFileTypeFromBytes(decodedBytes, decodedSize);
        
        if (fileType == FILE_TYPE_PNG) {
            strcat(str1, ".png");
        }
        else if (fileType == FILE_TYPE_JPG) {
            strcat(str1, ".jpg");
        }
        else if (fileType == FILE_TYPE_PDF) {
            strcat(str1, ".pdf");
        }
        else if (fileType == FILE_TYPE_DOC) {
            strcat(str1, ".doc");// Word 97-2003 (.doc)
        }
        else if (fileType == FILE_TYPE_DOCX) {
            strcat(str1, ".docx");// Word 2007+ (.docx)
        }
        else if (fileType == FILE_TYPE_ZIP) {
            strcat(str1, ".zip");// ZIP压缩包(包括docx/xlsx等)
        }
        else if (fileType == FILE_TYPE_GIF) {
            strcat(str1, ".gif");// GIF图像
        }
        else if (fileType == FILE_TYPE_BMP) {
            strcat(str1, ".bmp"); // BMP图像
        }
        else {
            strcat(str1, ".unknown");
        }

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

        if (writeOk) {
            OutputDebugStringW(L"字节数组写入文件成功!");
            MessageBox(hWnd, L"完成!", L"提示", MB_OK);
        }
        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';  // 手动添加终止符

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


// 将文件类型枚举转换为字符串
const char* FileTypeToString(FileType type) {
    switch (type) {
    case FILE_TYPE_PNG:    return "PNG image";
    case FILE_TYPE_JPG:    return "JPEG image";
    case FILE_TYPE_PDF:    return "PDF document";
    case FILE_TYPE_DOC:    return "Word (.doc) document";
    case FILE_TYPE_DOCX:   return "Word (.docx) document";
    case FILE_TYPE_ZIP:    return "ZIP archive";
    default:               return "Unknown file type";
    }
}

// 根据文件字节数据判断文件类型
// 参数:
//   fileData - 整个文件的字节数据(BYTE*)
//   fileSize - 文件总大小(字节数),用于边界检查
// 返回:
//   对应的FileType枚举值,未知类型返回FILE_TYPE_UNKNOWN
FileType GetFileTypeFromBytes(const BYTE* fileData, size_t fileSize) {
    // 检查输入有效性
    if (fileData == NULL || fileSize == 0) {
        return FILE_TYPE_UNKNOWN;
    }

    // 1. 判断PNG (8字节魔术数字)
    if (fileSize >= 8) {
        const BYTE pngSignature[] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
        if (memcmp(fileData, pngSignature, 8) == 0) {
            return FILE_TYPE_PNG;
        }
    }

    // 2. 判断JPG (3字节魔术数字)
    if (fileSize >= 3) {
        const BYTE jpgSignature[] = { 0xFF, 0xD8, 0xFF };
        if (memcmp(fileData, jpgSignature, 3) == 0) {
            return FILE_TYPE_JPG;
        }
    }

    // 3. 判断PDF (4字节魔术数字)
    if (fileSize >= 4) {
        const BYTE pdfSignature[] = { 0x25, 0x50, 0x44, 0x46 }; // 对应 "%PDF"
        if (memcmp(fileData, pdfSignature, 4) == 0) {
            return FILE_TYPE_PDF;
        }
    }

    // 4. 判断Word (.doc) (8字节魔术数字 - OLE复合文档格式)
    if (fileSize >= 8) {
        const BYTE docSignature[] = { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
        if (memcmp(fileData, docSignature, 8) == 0) {
            return FILE_TYPE_DOC;
        }
    }

    // 5. 判断ZIP及基于ZIP的格式 (4字节魔术数字)
    if (fileSize >= 4) {
        const BYTE zipSignature1[] = { 0x50, 0x4B, 0x03, 0x04 };
        const BYTE zipSignature2[] = { 0x50, 0x4B, 0x05, 0x06 };
        const BYTE zipSignature3[] = { 0x50, 0x4B, 0x07, 0x08 };

        if (memcmp(fileData, zipSignature1, 4) == 0 ||
            memcmp(fileData, zipSignature2, 4) == 0 ||
            memcmp(fileData, zipSignature3, 4) == 0) {

            // .docx是特殊的ZIP格式,可进一步通过内部文件结构判断
            // 这里简化处理:如果是ZIP且文件大小足够,检查是否包含docx特征
            if (fileSize >= 0x1000) { // 检查前4KB内是否有"[Content_Types].xml"
                const char* docxMarker = "[Content_Types].xml";
                if (my_memmem(fileData, 0x1000, docxMarker, strlen(docxMarker)) != NULL) {
                    return FILE_TYPE_DOCX;
                }
            }

            return FILE_TYPE_ZIP;
        }
    }

    // 6. 判断GIF (6字节魔术数字)
    if (fileSize >= 6) {
        const BYTE gifSignature1[] = { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }; // GIF87a
        const BYTE gifSignature2[] = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }; // GIF89a
        if (memcmp(fileData, gifSignature1, 6) == 0 ||
            memcmp(fileData, gifSignature2, 6) == 0) {
            return FILE_TYPE_GIF;
        }
    }

    // 7. 判断BMP (2字节魔术数字)
    if (fileSize >= 2) {
        const BYTE bmpSignature[] = { 0x42, 0x4D }; // 对应 "BM"
        if (memcmp(fileData, bmpSignature, 2) == 0) {
            return FILE_TYPE_BMP;
        }
    }

    // 可扩展其他文件类型(如MP3、TXT等)
    return MY_FILE_TYPE_UNKNOWN;
}

// 自定义内存查找函数:在src内存块中查找dest内存块的首次出现
// 参数:
//   src: 源内存块指针
//   src_len: 源内存块长度
//   dest: 目标内存块指针
//   dest_len: 目标内存块长度
// 返回:
//   找到则返回src中匹配的起始位置指针,否则返回NULL
const void* my_memmem(const void* src, size_t src_len, const void* dest, size_t dest_len) {
    // 边界检查
    if (src == NULL || dest == NULL || src_len == 0 || dest_len == 0 || dest_len > src_len) {
        return NULL;
    }

    const unsigned char* src_ptr = (const unsigned char*)src;
    const unsigned char* dest_ptr = (const unsigned char*)dest;
    size_t max_search_len = src_len - dest_len;  // 最大查找范围

    // 遍历源内存块,逐个位置比对
    for (size_t i = 0; i <= max_search_len; i++) {
        // 找到第一个匹配的字节后,完整比对目标内存块
        if (src_ptr[i] == dest_ptr[0]) {
            if (memcmp(&src_ptr[i], dest_ptr, dest_len) == 0) {
                return &src_ptr[i];  // 找到匹配,返回起始位置
            }
        }
    }

    return NULL;  // 未找到匹配
}