SimpleNotepad
跳到导航
跳到搜索
#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;
}