StringCchCopy

基本概述

StringCchCopyWindows提供的字符串安全拷贝函数,属于StrSafe.h头文件下的StrSafe库,目的是替代传统的strcpy等字符串拷贝函数(避免缓冲区溢出),它会严格检查目标缓冲区的大小,确保拷贝操作不会越界,提升代码安全性。

基本语法

1
2
3
4
5
STRSAFEAPI StringCchCopyA(
[out] STRSAFE_LPSTR pszDest,// 目标字符串缓冲区
[in] size_t cchDest,// 目标缓冲区的大小(以字符数为单位,包含终止符 '\0')
[in] STRSAFE_LPCSTR pszSrc // 源字符串
);

返回值
HRESULT类型,常见返回值:
S_OK:拷贝成功,且目标字符串以\0终止。
STRSAFE_E_INSUFFICIENT_BUFFER:目标缓冲区空间不足,拷贝失败(此时目标缓冲区会被置为空字符串)。

数据类型
STRSAFE_LPSTR:

1
typedef _Null_terminated_ char* STRSAFE_LPSTR;

_Null_terminated_:空终止属性标注;
这是微软编译器(MSVC)的批注(Annotation),不是标准C/C++关键字,作用是:
告诉编译器 / 静态分析工具:这个指针指向的字符串必须是以'\0'结尾的空终止字符串。

STRSAFE_LPCSTR:

1
typedef _Null_terminated_ const char* STRSAFE_LPCSTR;

代表只读、空终止的窄字符串指针,相比STRSAFE_LPSTR多了const,只能读取字符串内容,不能修改。

简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <Windows.h>
#include <StrSafe.h>
#pragma comment(lib, "StrSafe.lib") // 链接StrSafe库

int main()
{
// 定义目标缓冲区(宽字符),大小为 10 个字符(包含终止符)
wchar_t szDest[10] = { 0 };
// 源字符串
LPCWSTR szSrc = L"Hello World"; // 长度为 11(含'\0'),超过目标缓冲区大小

// 调用StringCchCopy进行安全拷贝
HRESULT hr = StringCchCopy(szDest, _countof(szDest), szSrc);

// 检查拷贝结果
if (SUCCEEDED(hr))
{
wprintf(L"拷贝成功:%s\n", szDest);
}
else if (hr == STRSAFE_E_INSUFFICIENT_BUFFER)
{
wprintf(L"拷贝失败:目标缓冲区空间不足\n");
wprintf(L"当前目标缓冲区内容:%s\n", szDest); // 此时缓冲区为空字符串
}
else
{
wprintf(L"拷贝失败:未知错误,错误码:0x%X\n", hr);
}

// 测试成功场景:源字符串长度小于缓冲区
wchar_t szDest2[10] = { 0 };
LPCWSTR szSrc2 = L"Hello";
hr = StringCchCopy(szDest2, _countof(szDest2), szSrc2);
if (SUCCEEDED(hr))
{
wprintf(L"\n成功示例:%s\n", szDest2);
}

return 0;
}

ExpandEnvironmentStrings

基本概述

ExpandEnvironmentStringsWindows提供的环境变量解析函数,属于Windows.h头文件,常用于将包含环境变量占位符的字符串(如%SystemRoot%\System32)转换为实际的路径(如C:\Windows\System32),是处理系统路径、用户目录等场景的常用工具。

基本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
// 宽字符(Unicode)版本
DWORD ExpandEnvironmentStringsW(
[in] LPCWSTR lpSrc,// 包含环境变量的源字符串
[out, optional] LPWSTR lpDst,// 接收解析结果的目标缓冲区
[in] DWORD nSize // 目标缓冲区的大小
);

// ANSI版本
DWORD ExpandEnvironmentStringsA(
[in] LPCSTR lpSrc,
[out, optional] LPSTR lpDst,
[in] DWORD nSize
);

返回值
成功:返回解析后字符串的字符数(ANSI是字节数,Unicodewchar_t字符数),不包含终止符\0
失败:返回0;若目标缓冲区太小,返回值会大于nSize(提示所需的最小缓冲区大小)。

简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <Windows.h>
#include <wchar.h>

int main()
{
// 示例1:先获取所需缓冲区大小,再分配空间解析
LPCWSTR szSrc = L"%USERPROFILE%\\Desktop\\test.txt"; // 包含用户桌面环境变量
DWORD dwRequiredSize = 0;

// 第一步:传入NULL获取所需缓冲区大小(返回值是字符数,不含\0)
dwRequiredSize = ExpandEnvironmentStringsW(szSrc, NULL, 0);
if (dwRequiredSize == 0)
{
wprintf(L"获取缓冲区大小失败,错误码:%d\n", GetLastError());
return 1;
}

// 第二步:分配缓冲区(+1 确保包含终止符,实际dwRequiredSize已包含所需字符数,直接分配即可)
wchar_t* szDest = new wchar_t[dwRequiredSize + 1];
if (szDest == NULL)
{
wprintf(L"内存分配失败\n");
return 1;
}

// 第三步:解析环境变量
DWORD dwResult = ExpandEnvironmentStringsW(szSrc, szDest, dwRequiredSize + 1);
if (dwResult == 0 || dwResult > dwRequiredSize + 1)
{
wprintf(L"解析失败,错误码:%d\n", GetLastError());
delete[] szDest;
return 1;
}

// 输出结果
wprintf(L"源字符串:%s\n", szSrc);
wprintf(L"解析后:%s\n", szDest);

// 示例2:直接使用固定大小缓冲区(适合已知长度的场景)
wchar_t szDest2[MAX_PATH] = { 0 }; // MAX_PATH是Windows定义的最大路径长度(260)
LPCWSTR szSrc2 = L"%TEMP%\\temp_file.tmp";
dwResult = ExpandEnvironmentStringsW(szSrc2, szDest2, MAX_PATH);
if (dwResult > 0 && dwResult <= MAX_PATH)
{
wprintf(L"\n源字符串:%s\n", szSrc2);
wprintf(L"解析后:%s\n", szDest2);
}
else
{
wprintf(L"\n解析失败,所需缓冲区大小:%d,当前缓冲区大小:%d\n", dwResult, MAX_PATH);
}

// 释放内存
delete[] szDest;
return 0;
}

StringCchPrintf

基本概述

StringCchPrintfStrSafe库的核心函数,替代传统sprintf/swprintf等打印函数,核心优势是通过严格检查缓冲区字符数避免溢出,它的本质是 “格式化字符串 + 安全写入缓冲区”,支持和printf完全一致的格式化语法,但更安全、更易排查错误。

基本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 宽字符(Unicode)版本
STRSAFEAPI StringCchPrintfW(
[out] STRSAFE_LPWSTR pszDest,// 目标缓冲区
[in] size_t cchDest,// 缓冲区总字符数(含 '\0',关键!)
[in] STRSAFE_LPCWSTR pszFormat,// 格式化模板(L"" 包裹)
... // 可变参数(和模板占位符对应)
);

// ANSI版本
STRSAFEAPI StringCchPrintfA(
[out] STRSAFE_LPSTR pszDest,
[in] size_t cchDest,
[in] STRSAFE_LPCSTR pszFormat,
...
);

简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <Windows.h>
#include <StrSafe.h>
#pragma comment(lib, "StrSafe.lib")

int main()
{
// 正确示例:宽字符 + _countof 传字符数
wchar_t buf[50] = { 0 };
HRESULT hr = StringCchPrintfW(
buf,
_countof(buf), // 传字符数(50),不是字节数!
L"姓名:%s,年龄:%d,身高:%.2f米", // L"" 必须加
L"李四", // 宽字符参数
25,
1.75
);

if (SUCCEEDED(hr)) {
wprintf(L"格式化结果:%s\n", buf); // 输出:姓名:李四,年龄:25,身高:1.75米
} else {
wprintf(L"失败,错误码:0x%X\n", hr);
}

// 错误示例1:少加 L"",导致编码不匹配
// StringCchPrintfW(buf, _countof(buf), "姓名:%s", "李四"); // 编译警告,运行乱码
// 错误示例2:传字节数而非字符数
// StringCchPrintfW(buf, sizeof(buf), L"测试", ...); // sizeof(buf)=100 字节,远大于50字符,逻辑错误

return 0;
}