# Windows API 中的字符串

> 原文： [http://zetcode.com/gui/winapi/strings/](http://zetcode.com/gui/winapi/strings/)

在 C 语言中，没有字符串数据类型。 程序中的字符串文字是字符数组。 每当我们说字符串时，都是指一个字符数组。

我们有五组处理字符串的函数； 在 C 运行时库（CRT）和 Windows API 中：

*   ANSI C 标准函数
*   安全性增强的 CRT 函数
*   Windows API 内核和用户函数
*   Windows API Shell 轻量级工具函数
*   Windows API StrSafe 函数

建议您使用安全性增强的标准函数或 Windows API 安全函数。

## ANSI C 字符串函数

C 运行时（CRT）库函数的开销很小，因为它们在下面调用 Windows API 函数。 这些函数提供了可移植性，但有一些限制。 如果使用不当，可能会导致安全隐患。

这些函数失败时不会返回错误值。

`ansic_functions.c`

```c
#include <windows.h>
#include <wchar.h>

#define STR_EQUAL 0

int wmain(void) {

    wchar_t str1[] = L"There are 15 pines";

    wprintf(L"The length of the string is %lld characters\n", wcslen(str1));

    wchar_t buf[20];
    wcscpy(buf, L"Wuthering");
    wcscat(buf, L" heights\n");
    wprintf(buf);

    if (wcscmp(L"rain", L"rainy")== STR_EQUAL) {

        wprintf(L"rain and rainy are equal strings\n");
    } else {

        wprintf(L"rain and rainy are not equal strings\n");
    } 

    return 0;
}

```

在示例中，我们介绍了 CRT 库中的一些字符串函数。

```c
wprintf(L"The length of the string is %lld characters\n", wcslen(str1));

```

`wcslen()`返回字符串中的宽字符数。

```c
wcscpy(buf, L"Wuthering");

```

`wcscpy()`将字符串复制到字符串缓冲区。

```c
wcscat(buf, L" heights\n");

```

`wcscat()`函数将字符串追加到字符串缓冲区。

```c
if (wcscmp(L"rain", L"rainy")== STR_EQUAL) {

    wprintf(L"rain and rainy are equal strings\n");
} else {

    wprintf(L"rain and rainy are not equal strings\n");
} 

```

`wcscmp()`比较两个字符串。

```c
C:\Users\Jano\Documents\Pelles C Projects\strings\ANSICFunctions>ANSICFunctions.exe
The length of the string is 18 characters
Wuthering heights
rain and rainy are not equal strings

```

这是示例的输出。

## 安全性增强的 CRT 函数

安全性 CRT 函数为 CRT 函数增加了额外的安全性。 （它们不是标准函数，而是 MS 扩展。）这些函数验证参数，获取大小缓冲区，检查字符串是否以 NULL 终止以及提供增强的错误报告。

安全 CRT 函数的后缀为`_s`。

`security_enhanced.c`

```c
#include <windows.h>
#include <wchar.h>

#define BUF_LEN 25

int wmain(void) {

    wchar_t str1[] = L"There are 15 pines";

    const int MAX_CHARS = 50;
    size_t count = wcsnlen_s(str1, MAX_CHARS); 

    wprintf(L"The length of the string is %ld characters\n", count);

    wchar_t buf[BUF_LEN] = {0};

    int r = wcscpy_s(buf, BUF_LEN, L"Wuthering");

    if (r != 0) {

        wprintf(L"wcscpy_s() failed %ld", r);
    }

    r = wcscat_s(buf, BUF_LEN, L" heights\n");

    if (r != 0) {

        wcscat_s(L"wcscat_s() failed %ld", r);
    }

    wprintf_s(buf);

    return 0;
}

```

在示例中，我们展示了四个函数：`wcsnlen_s()`，`wcscpy_s()`，`wcscat_s()`和`wprintf_s()`。

```c
const int MAX_CHARS = 50;
size_t count = wcsnlen_s(str1, MAX_CHARS);

```

`wcsnlen_s()`计算宽字符串的长度。 该函数仅检查前`MAX_CHARS`个字符。

```c
int r = wcscpy_s(buf, BUF_LEN, L"Wuthering");

```

使用`wcscpy_s()`函数，我们将`L"Wuthering"`字符串复制到缓冲区中。 该函数使用缓冲区中的最大字符数，如果失败，则返回错误代码。 该函数成功返回 0。

```c
r = wcscat_s(buf, BUF_LEN, L" heights\n");

```

`wcscat_s()`是`wcscat()`函数的安全扩展。

```c
wprintf_s(buf);

```

甚至还具有增强安全性的`wprintf()`函数； 它具有一些运行时约束。

```c
C:\Users\Jano\Documents\Pelles C Projects\strings\SecurityEnhanced>SecurityEnhanced.exe
The length of the string is 18 characters
Wuthering heights

```

这是`SecurityEnhanced.exe`示例的输出。

## Windows API 内核和用户字符串函数

这些函数特定于 Windows OS。 它们在`User32.lib`和`Kernel32.lib`中可用。 它们比 CRT 同类产品轻。

内核字符串函数起源于 Windows 内核的开发。 它们以`l`字母为前缀。

### 字符串长度

最常见的要求之一是弄清楚字符串的长度。 `lstrlen()`函数以字符为单位返回指定字符串的长度。 它不计算终止的空字符。

```c
int WINAPI lstrlenA(LPCSTR lpString);
int WINAPI lstrlenW(LPCWSTR lpString);

```

ANSI 和 UNICODE 函数将字符串作为参数，并返回字符串中的字符数。

`winapi_string_lenght.c`

```c
#include <windows.h>
#include <wchar.h>

int wmain(void)  {

    char *name = "Jane";
    wchar_t *town = L"Bratislava";

    wprintf(L"The length of the name string is %d\n", lstrlenA(name));
    wprintf(L"The town string length is %d\n", lstrlenW(town));

    return 0;
}

```

我们计算两个字符串的长度。 `lstrlen()`函数实际上是`lstrlenA()`或`lstrlenW()`的宏。 第一个用于 ANSI 字符串，第二个用于宽字符串。

```c
wprintf(L"The town string length is %d\n", lstrlenW(town));

```

我们使用`lstrlenW()`函数打印`L"Bratislava"`字符串的长度。

```c
C:\Users\Jano\Documents\Pelles C Projects\strings\WinapiStringLength>WinapiStringLength.exe
The length of the name string is 4
The town string length is 10

```

这是`WinapiStringLength.exe`程序的输出。

### 连接字符串

`lstrcatW()`函数将一个字符串附加到另一个字符串。

```c
LPWSTR WINAPI lstrcatW(LPWSTR lpString1, LPCWSTR lpString2);

```

第一个参数是缓冲区，其中应包含两个字符串。 它必须足够大以包含两个字符，包括`NULL`终止字符。 返回值是指向缓冲区的指针。

`winapi_string_concat.c`

```c
#include <windows.h>
#include <wchar.h>

int main(void) {

    wchar_t *s1 = L"ZetCode, ";
    wchar_t *s2 = L"tutorials ";
    wchar_t *s3 = L"for ";
    wchar_t *s4 = L"programmers.\n";

    int len = lstrlenW(s1) + lstrlenW(s2) 
        + lstrlenW(s3) + lstrlenW(s4);
    wchar_t buf[len+1];

    lstrcpyW(buf, s1);
    lstrcatW(buf, s2);
    lstrcatW(buf, s3);
    lstrcatW(buf, s4);

    wprintf(buf);    

    return 0;
}

```

在示例中，我们连接了四个字符串。

```c
wchar_t *s1 = L"ZetCode, ";
wchar_t *s2 = L"tutorials ";
wchar_t *s3 = L"for ";
wchar_t *s4 = L"programmers.\n";

```

这些是我们要连接的字符串。

```c
int len = lstrlenW(s1) + lstrlenW(s2) 
    + lstrlenW(s3) + lstrlenW(s4);

```

我们使用`lstrlenW()`函数计算四个字符串的长度。

```c
wchar_t buf[len+1];

```

我们创建一个缓冲区来保存最终的字符串。 请注意，我们加 1 来包含`NULL`字符。

```c
lstrcpyW(buf, s1);

```

我们使用`lstrcpyW()`函数将第一个字符串复制到缓冲区。

```c
lstrcatW(buf, s2);
lstrcatW(buf, s3);
lstrcatW(buf, s4);

```

我们用`lstrcatW()`函数附加其余的字符串。

```c
C:\Users\Jano\Documents\Pelles C Projects\strings\WinapiStringConcat>WinapiStringConcat.exe
ZetCode, tutorials for programmers.

```

这是`WinapiStringConcat.exe`程序的输出。

### 转换字符

我们有两种将字符转换为大写或小写的方法。 `CharLowerW()`函数将字符串或单个字符转换为小写。 `CharUpperW()`函数将字符串或单个字符转换为大写。 如果操作数是字符串，则该函数将就地转换字符。 换句话说，它们已被修改。

```c
LPWSTR WINAPI CharLowerW(LPWSTR lpsz);
LPWSTR WINAPI CharUpperW(LPWSTR lpsz);

```

这些函数在适当位置修改字符串，并返回指向修改后的字符串的指针。

`winapi_string_case.c`

```c
#include <windows.h>
#include <wchar.h>

#pragma comment(lib, "User32.lib")

int wmain(void) {

    wchar_t str[] = L"Europa";

    CharLowerW(str);
    wprintf(L"%ls\n", str);

    CharUpperW(str);
    wprintf(L"%ls\n", str);    

    return 0;
}

```

我们有一个字符串，可以将其转换为小写和大写形式。

```c
CharLowerW(str);
wprintf(L"%ls\n", str);

```

我们使用`CharLowerW()`方法将`str`字符串转换为小写。 字符串被修改。

```c
C:\winapi\examples2\strings\UpperLower>UpperLower.exe
europa
EUROPA

```

这是`UpperLower.exe`程序的输出。

### 比较字符串

`lstrcmpW()`函数比较两个字符串。 如果字符串相等，则返回 0。 比较是区分大小写的。 这意味着“杯”和“杯”是两个不同的字符串。 `lstrcmpiW()`产生不区分大小写的字符串比较。 对于此函数，“杯子”和“杯子”相等。

```c
int WINAPI lstrcmpW(LPCWSTR lpString1, LPCWSTR lpString2);
int WINAPI lstrcmpiW(LPCWSTR lpString1, LPCWSTR lpString2);

```

这些函数采用两个字符串作为参数。 返回值指示字符串的相等性。 对于相等的字符串，返回 0 值。

`winapi_string_compare.c`

```c
#include <windows.h>
#include <wchar.h>

#define STR_EQUAL 0

int wmain(void) {

    wchar_t *s1 = L"Strong";
    wchar_t *s2 = L"strong";

    if (lstrcmpW(s1, s2) == STR_EQUAL) {

        wprintf(L"%ls and %ls are equal\n", s1, s2);
    } else {

        wprintf(L"%ls and %ls are not equal\n", s1, s2);
    }

    wprintf(L"When applying case insensitive comparison:\n");

    if (lstrcmpiW(s1, s2) == STR_EQUAL) {

        wprintf(L"%ls and %ls are equal\n", s1, s2);
    } else {

        wprintf(L"%ls and %ls are not equal\n", s1, s2);
    }

    return 0;
}

```

我们有两个字符串。 我们使用区分大小写和不区分大小写的字符串比较来比较它们。

```c
if (lstrcmpW(s1, s2) == STR_EQUAL) {

      wprintf(L"%ls and %ls are equal\n", s1, s2);
} else {

      wprintf(L"%ls and %ls are not equal\n", s1, s2);
}

```

如果`lstrcmpW()`函数返回定义为 0 的`STR_EQUAL`，则我们向控制台显示两个字符串相等。 否则，我们打印出它们不相等。

```c
C:\Users\Jano\Documents\Pelles C Projects\strings\WinapiStringCompare>WinapiStringCompare.exe
Strong and strong are not equal
When applying case insensitive comparison:
Strong and strong are equal

```

`WinapiStringCompare.exe`程序给出以上输出。

### 填充缓冲区

在 C 编程中，用格式化的数据填充缓冲区至关重要。 `wsprintfW()`函数将格式化的数据写入指定的缓冲区。

```c
int __cdecl wsprintfW(LPWSTR lpOut, LPCWSTR lpFmt, ... );

```

该函数的第一个参数是要接收格式化输出的缓冲区。 第二个是包含格式控制规范的字符串。 然后，我们有一个或多个可选参数，它们对应于格式控制规范。

`winapi_string_fillbuffer.c`

```c
#include <windows.h>
#include <wchar.h>

#pragma comment(lib, "User32.lib")

int wmain(void) {

    SYSTEMTIME st = {0};
    wchar_t buf[128] = {0};

    GetLocalTime(&st);
    wsprintfW(buf, L"Today is %lu.%lu.%lu\n", st.wDay, 
        st.wMonth, st.wYear);

    wprintf(buf);

    return 0;
}

```

我们建立一个用当前日期填充的字符串。

```c
wchar_t buf[128] = {0};

```

在这种特殊情况下，我们可以安全地假设字符串不会超过 128 个字符。

```c
GetLocalTime(&st);

```

`GetLocalTime()`函数检索当前的本地日期和时间。

```c
wsprintfW(buf, L"Today is %lu.%lu.%lu\n", st.wDay, 
    st.wMonth, st.wYear);

```

`wsprintfW()`用宽字符串填充缓冲区。 根据格式说明符将参数复制到字符串。

```c
wprintf(buf);

```

缓冲区的内容将打印到控制台。

```c
C:\Users\Jano\Documents\Pelles C Projects\strings\WinapiStringFillBuffer>WinapiStringFillBuffer.exe
Today is 11.2.2016

```

这是`WinapiStringFillBuffer.exe`示例的输出。

### 字符类型

字符有多种类型。 它们可以是数字，空格，字母，标点符号或控制字符。

```c
BOOL WINAPI GetStringTypeW(DWORD dwInfoType, LPCWSTR lpSrcStr,
    int cchSrc, LPWORD lpCharType);

```

`GetStringTypeW()`函数检索指定 Unicode 字符串中字符的字符类型信息。 第一个参数是指定信息类型的标志。

| 标志 | 含义 |
| --- | --- |
| `CT_CTYPE1` | 检索字符类型信息。 |
| `CT_CTYPE2` | 检索双向布局信息。 |
| `CT_CTYPE3` | 检索文本处理信息。 |

Table: Character info types

第二个参数是要检索其字符类型的 Unicode 字符串。

第三个参数是字符串的大小。 最后一个参数是指向 16 位值数组的指针。 该数组的长度必须足够大，以便为源字符串中的每个字符接收一个 16 位值。 该数组将包含一个与源字符串中每个字符相对应的单词。

`GetStringTypeW()`函数返回一个值，该值是类型的组合。 我们可以使用&运算符查询特定类型。

| 值 | 含义 |
| --- | --- |
| `C1_DIGIT` | 小数位数 |
| `C1_SPACE` | 空格字符 |
| `C1_PUNCT` | 标点 |
| `C1_CNTRL` | 控制字符 |
| `C1_ALPHA` | 任何语言特征 |

Table: Partial list of character types

如果失败，该函数将返回 0。

`winapi_string_types.c`

```c
#include <windows.h>
#include <wchar.h>
#include <stdbool.h>

int wmain(void) {

    wchar_t str[] = L"7 white, 3 red roses.\n";

    int alphas = 0;
    int digits = 0;
    int spaces = 0;
    int puncts = 0;
    int contrs = 0;

    int size = lstrlenW(str);
    WORD types[size];
    ZeroMemory(types, size);

    bool rv = GetStringTypeW(CT_CTYPE1, str, size, types);

    if (!rv) {

        wprintf(L"Could not get character types (%ld)", GetLastError());
        return EXIT_FAILURE;
    }

    for (int i=0; i<size; i++) {

        if (types[i] & C1_ALPHA) {

            alphas++;
        }

        if (types[i] & C1_DIGIT) {

            digits++;
        }

        if (types[i] & C1_SPACE) {

            spaces++;
        }

        if (types[i] & C1_PUNCT) {

            puncts++;
        }

        if (types[i] & C1_CNTRL) {

            contrs++;
        }
    }

    wprintf(L"There are %ld letter(s), %ld digit(s), "
        L"%ld space(s), %ld punctuation character(s), "
        L"and %ld control character(s)\n", alphas, digits, 
        spaces, puncts, contrs);  

    return 0;
}

```

我们有一句话。 `GetStringTypeW()`函数用于确定字符串的字符类型。

```c
wchar_t str[] = L"7 white, 3 red roses.\n";

```

这是由各种宽字符组成的简短句子。

```c
int alphas = 0;
int digits = 0;
int spaces = 0;
int puncts = 0;
int contrs = 0;

```

这些变量将用于计算字母，数字，空格，标点和控制字符。

```c
int size = lstrlenW(str);
WORD types[size];
ZeroMemory(types, size);

```

我们获取字符串的大小，并创建值数组。 大小不包含`NULL`终止字符。 我们可以加 1 以包括它。 它将被算作控制字符。

```c
bool rv = GetStringTypeW(CT_CTYPE1, str, size, types);

```

我们得到句子的字符类型。 `types`数组填充有字符类型值。

```c
if (types[i] & C1_DIGIT) {
  digits++;
}

```

如果该值包含`C1_DIGIT`标志，我们将增加数字计数器。

```c
C:\Users\Jano\Documents\Pelles C Projects\strings\WinapiStringTypes>WinapiStringTypes.exe
There are 13 letter(s), 2 digit(s), 5 space(s), 2 punctuation character(s), 
and 1 control character(s)

```

这是`WinapiStringTypes.exe`示例的输出。

## Windows API Shell 轻量级工具函数

这些函数特定于 Windows OS。 它们在`Shlwapi.lib`中可用。

### 修剪字符串

`StrTrimW()`函数从字符串中删除指定的开头和结尾字符。 如果删除了任何字符，则返回`true`；否则返回`false`。 否则为假。

```c
BOOL WINAPI StrTrimW(LPWSTR psz, LPCWSTR pszTrimChars);

```

第一个参数是指向要修剪的字符串的指针。 当此函数成功返回时，`psz`接收到修剪后的字符串。 第二个参数是一个指向字符串的指针，该字符串包含要从`psz`中修剪的字符。

`winapi_shell_trim.c`

```c
#include <windows.h>
#include <wchar.h>
#include <stdbool.h>
#include "Shlwapi.h"

#pragma comment(lib, "Shlwapi.lib")

int wmain(void) {

    wchar_t buf[] = L"23tennis74";
    wchar_t trim[] = L"0123456789";

    wprintf(L"Original string: %ls\n", buf);

    bool r = StrTrimW(buf, trim);

    if (r == true) {

        wprintf(L"The StrTrim() trimmed some characters\n", buf);
    } else {

        wprintf(L"No characters were trimmed\n", buf);
    }

    wprintf(L"Trimmed string: %ls\n", buf);

    return 0;
}

```

在该示例中，我们从字符串中删除所有数字。

```c
wchar_t buf[] = L"23tennis74";

```

我们将从该字符串中删除所有数字。

```c
wchar_t trim[] = L"0123456789";

```

该字符串包含所有要删除的字符。

```c
bool r = StrTrimW(buf, trim);

```

使用`StrTrimW()`函数，我们可以修剪缓冲区中的数字。

```c
C:\Users\Jano\Documents\Pelles C Projects\strings\ShellTrimString>ShellTrimString.exe
Original string: 23tennis74
The StrTrim() trimmed some characters
Trimmed string: tennis

```

这是`ShellTrimString.exe`示例的输出。

### 将字符串转换为整数

`StrToIntExW()`将代表十进制或十六进制数字的字符串转换为整数。 成功时该函数返回`true`。

```c
BOOL WINAPI StrToIntExW(LPCWSTR pszString, DWORD dwFlags, int *piRet);

```

第一个参数是指向要转换的字符串的指针。 第二个参数是标志之一，用于指定应如何解析`pszString`以将其转换为整数。 第三个参数是指向接收转换后的字符串的`int`的指针。

`winapi_shell_convert.c`

```c
#include <windows.h>
#include <wchar.h>
#include <stdbool.h>
#include "Shlwapi.h"

#pragma comment(lib, "Shlwapi.lib")

int wmain(void) {

    wchar_t str1[] = L"512";
    wchar_t str2[] = L"0xAB12";
    int n = 0;

    bool r = StrToIntExW(str1, STIF_DEFAULT, &n);

    if (r == true) {

        wprintf(L"The value is %d\n", n);
    } else {

        wprintf(L"The first conversion failed\n");
        return 1; 
    }

    r = StrToIntExW(str2, STIF_SUPPORT_HEX, &n);

    if (r == true) {

        wprintf(L"The value is %d\n", n);
    } else {

        wprintf(L"The second conversion failed\n");
        return 1; 
    }

    return 0;
}

```

在示例中，我们转换了两个字符串； 一个代表十进制值，一个代表十六进制值。

```c
wchar_t str1[] = L"512";
wchar_t str2[] = L"0xAB12";

```

第一个字符串代表十进制数；第二个字符串代表十进制数。 第二个字符串表示一个十六进制数。

```c
bool r = StrToIntExW(str1, STIF_DEFAULT, &n);

```

使用`StrToIntExW()`函数，我们将第一个字符串转换为整数。 `STIF_DEFAULT`标志告诉函数转换十进制值。

```c
r = StrToIntExW(str2, STIF_SUPPORT_HEX, &n);

```

通过`STIF_SUPPORT_HEX`标志，我们告诉函数转换一个十六进制值。

```c
C:\Users\Jano\Documents\Pelles C Projects\strings\ShellConvertString>ShellConvertString.exe
The value is 512
The value is 43794

```

这是`ShellConvertString.exe`程序的输出。

### 搜索字符串

`StrStrW()`函数查找字符串中子字符串的首次出现。 比较是区分大小写的。

```c
LPWSTR WINAPI StrStrW(LPCWSTR pszFirst, LPCWSTR pszSrch);

```

第一个参数是指向要搜索的字符串的指针。 第二个参数是指向要搜索的子字符串的指针。 如果成功，该函数将返回匹配子字符串的第一个匹配项的地址，否则返回`NULL`。

`winapi_shell_search.c`

```c
#include <windows.h>
#include <wchar.h>
#include "Shlwapi.h"

#pragma comment(lib, "Shlwapi.lib")

int wmain(void) {

    wchar_t buf[] = L"Today is a rainy day.";
    wchar_t *search_word = L"rainy";
    int len = wcslen(search_word); 

    LPWSTR pr = StrStrW(buf, search_word);

    if (pr == NULL) {

        wprintf(L"No match\n", buf);
    } else {

        wprintf(L"%.*ls is found\n", len, pr);
    }

    return 0;
}

```

在代码示例中，我们在句子中搜索单词。

```c
wchar_t buf[] = L"Today is a rainy day.";

```

我们从这句话中搜索一个单词。

```c
wchar_t *search_word = L"rainy";

```

这是我们要搜索的词。

```c
LPWSTR pr = StrStrW(buf, search_word);

```

`StrStrW()`函数在句子中搜索单词。 如果成功，它将返回一个指向匹配子字符串的指针。

```c
C:\Users\Jano\Documents\Pelles C Projects\strings\ShellSearchString>ShellSearchString.exe
rainy is found

```

这是`ShellSearchString.exe`程序的输出。

## Windows API StrSafe 函数

为了提高应用的安全性，发布了 StrSafe 函数。 这些函数需要目标缓冲区的大小作为输入。 保证缓冲区为空终止。 该函数返回错误代码。 这样可以进行正确的错误处理。

每个函数都有相应的字符计数`Cch`或字节计数`Cb`版本。

### 字符串长度

`StringCchLengthW()`和`StringCbLengthW()`函数可以确定字符和字节的字符串长度。

```c
HRESULT StringCchLengthW(LPCWSTR psz, size_t cchMax, size_t *pcch);
HRESULT StringCbLengthW(LPCWSTR psz, size_t cbMax, size_t *pcb);

```

函数的第一个参数是要检查其长度的字符串。 第二个参数是`psz`参数中允许的最大字符数（字节）。 该值不能超过`STRSAFE_MAX_CCH`。 第三个参数是`psz`中的字符数（字节），不包括终止的空字符。

函数成功返回`S_OK`，失败返回`STRSAFE_E_INVALID_PARAMETER`。 如果`psz`中的值为`NULL`，`cchMax`大于`STRSAFE_MAX_CCH`或`psz`大于`cchMax`，则函数将失败。 `SUCCEEDED`和`FAILED`宏可用于检查函数的返回值。

`safe_length.c`

```c
#include <windows.h>
#include <strsafe.h>
#include <wchar.h>

int wmain(void) { 

    wchar_t str[] = L"ZetCode";
    size_t target_size = 0;

    size_t size = sizeof(str);

    HRESULT r = StringCbLengthW(str, size, &target_size);

    if (SUCCEEDED(r)) {

        wprintf(L"The string has %lld bytes\n", target_size);
    } else {

        wprintf(L"StringCbLengthW() failed\n"); 
        return 1;
    }

    size = sizeof(str)/sizeof(wchar_t);

    r = StringCchLengthW(str, size, &target_size);

    if (SUCCEEDED(r)) {

        wprintf(L"The string has %lld characters\n", target_size);
    } else {

        wprintf(L"StringCchLengthW() failed\n"); 
        return 1;
    }

    return 0;
}

```

该代码示例确定给定字符串（以字符和字节为单位）的长度。

```c
wchar_t str[] = L"ZetCode";

```

我们将确定此字符串的长度。

```c
size_t target_size = 0;

```

函数返回时，`target_size`变量将填充计数值。

```c
size_t size = sizeof(str);

```

使用`sizeof`运算符，我们获得字符数组的大小（以字节为单位）。 该值用作`StringCbLengthW()`函数的字符串中允许的最大字符数。

```c
HRESULT r = StringCbLengthW(str, size, &target_size);

```

使用`StringCbLengthW()`函数，我们可以确定字符串的长度（以字节为单位）。 长度存储在`target_size`变量中。

```c
if (SUCCEEDED(r)) {

    wprintf(L"The string has %lld bytes\n", target_size);
} else {

    wprintf(L"StringCbLengthW() failed\n"); 
    return 1;
}

```

我们使用`SUCCEEDED`宏检查返回的值。 成功后，我们将打印字符串中的字节数。 如果出现错误，我们将显示一条错误消息。

```c
size = sizeof(str)/sizeof(wchar_t);

```

在这里，我们确定字符串中允许的最大字符数。 `wchar_t`是一种宽字符类型； 它的大小取决于编译器。

```c
r = StringCchLengthW(str, size, &target_size);

```

使用`StringCchLengthW()`函数，我们可以得到字符串的大小（以字符为单位）。

```c
if (SUCCEEDED(r)) {

    wprintf(L"The string has %lld characters\n", target_size);
} else {

    wprintf(L"StringCchLengthW() failed\n"); 
    return 1;
}

```

成功后，我们将字符串中的字符数打印到控制台。 出现错误时，我们会打印一条错误消息。

```c
C:\Users\Jano\Documents\Pelles C Projects\strsafe\SafeLength>SafeLength.exe
The string has 14 bytes
The string has 7 characters

```

该字符串包含 14 个字节或 7 个字符。

### 读取标准输入

`StringCchGetsW()`从标准输入读取一行，包括换行符。

```c
HRESULT StringCchGetsW(LPWSTR pszDest, size_t cchDest);

```

第一个参数是目标缓冲区，它接收复制的字符。 第二个参数是目标缓冲区的大小（以字符为单位）。

`safe_gets.c`

```c
#include <windows.h>
#include <strsafe.h>
#include <wchar.h>

#define BUF_LEN 8191

int wmain(void) { 

    wchar_t buf[BUF_LEN] = {0};
    HRESULT r = StringCchGetsW(buf, ARRAYSIZE(buf));   

    if (SUCCEEDED(r)) {

        wprintf(L"You have entered: %ls\n", buf);

    } else {

        wprintf(L"StringCchGets() failed\n"); 
        return 1;
    }

    return 0;
}   

```

在示例中，我们从标准输入中读取了一行。 该行被打印回到控制台。

```c
#define BUF_LEN 8191

```

根据 MSDN 文档，命令提示符下的最大输入不能超过 8191 个字符。

```c
wchar_t buf[BUF_LEN] = {0};

```

我们为输入字符串创建一个缓冲区。

```c
HRESULT r = StringCchGetsW(buf, ARRAYSIZE(buf));

```

`StringCchGetsW()`从`stdin`读取一行。

```c
C:\Users\Jano\Documents\Pelles C Projects\strsafe\SafeGets>SafeGets.exe
Today is a rainy day.
You have entered: Today is a rainy day.

```

这是`SafeGets.exe`程序的示例运行。

### 复制字符串

`StringCchCopyW()`将一个字符串复制到另一个。

```c
HRESULT StringCchCopyW(LPTSTR pszDest, size_t cchDest, LPCWSTR pszSrc);

```

第一个参数是目标缓冲区，它接收复制的字符串。 第二个参数是目标缓冲区的大小（以字符为单位）。 第三个参数是源字符串。

`safe_copy.c`

```c
#include <windows.h>
#include <strsafe.h>
#include <wchar.h>

int wmain(void) { 

    wchar_t *sentence = L"Today is a rainy day.";

    size_t size = wcslen(sentence) + 1;

    wchar_t buf[size];
    ZeroMemory(buf, size); 

    HRESULT r = StringCchCopyW(buf, size, sentence);

    if (SUCCEEDED(r)) {

        wprintf(L"%ls\n", buf);

    } else {

        wprintf(L"StringCchCopyW() failed\n"); 
        return 1;
    }

    return 0;
}

```

在代码示例中，我们使用`StringCchCopyW()`函数复制一个字符串。

```c
wchar_t *sentence = L"Today is a rainy day.";

```

这是要复制的字符串。

```c
size_t size = wcslen(sentence) + 1;

```

我们用`wcslen()`函数确定它的长度； 换行符保留一个字符。

```c
wchar_t buf[size];
ZeroMemory(buf, size);

```

我们使用`ZeroMemory()`函数创建一个缓冲区，并使用零。

```c
HRESULT r = StringCchCopyW(buf, size, sentence);

```

使用`StringCchCopyW()`，我们将字符串复制到缓冲区中。 提供目标缓冲区的大小是为了确保它不会在该缓冲区的末尾写入。

```c
C:\Users\Jano\Documents\Pelles C Projects\strsafe\SafeCopy>SafeCopy.exe
Today is a rainy day.

```

这是`SafeCopy.exe`程序的输出。

### 连接字符串

`StringCchCatW()`将一个字符串连接到另一个字符串。

```c
HRESULT StringCchCatW(LPWSTR pszDest, size_t cchDest, LPCWSTR pszSrc);

```

第一个参数是目标缓冲区。 第二个参数是目标缓冲区的大小（以字符为单位）。 第三个参数是要连接到目标缓冲区末尾的源字符串。

`safe_concat.c`

```c
#include <windows.h>
#include <strsafe.h>
#include <wchar.h>

#define BUF_LEN 256

int wmain(void) { 

    wchar_t buf[BUF_LEN] = {0};      

    HRESULT r = StringCchCatW(buf, BUF_LEN, L"Hello "); 

    if (FAILED(r)) {

        wprintf(L"StringCchCatW() failed\n"); 
        return 1;
    }

    r = StringCchCatW(buf, BUF_LEN, L"there"); 

    if (FAILED(r)) {

        wprintf(L"StringCchCatW() failed\n"); 
        return 1;
    }

    wprintf(L"%ls\n", buf);

    return 0;
}

```

在代码示例中，我们使用`StringCchCatW()`函数将两个字符串连接在一起。

```c
HRESULT r = StringCchCatW(buf, BUF_LEN, L"Hello "); 

```

`StringCchCatW()`函数将`L"Hello "`字符串添加到`buf`数组。

```c
r = StringCchCatW(buf, BUF_LEN, L"there"); 

```

稍后，第二个字符串将添加到缓冲区。

```c
C:\Users\Jano\Documents\Pelles C Projects\strsafe\SafeConcat>SafeConcat.exe
Hello there

```

这是`SafeConcat.exe`程序的输出。

### 格式化字符串

`StringCchPrintfW()`函数将格式化的数据写入目标缓冲区。

```c
HRESULT StringCchPrintfW(LPWSTR pszDest, size_t cchDest, LPCWSTR pszFormat, ...);

```

第一个参数是目标缓冲区，它接收从`pszFormat`及其参数创建的格式化字符串。 第二个参数是目标缓冲区，以字符为单位。 第三个参数是格式字符串。 将以下参数插入`pszFormat`字符串中。

`safe_format.c`

```c
#include <windows.h>
#include <strsafe.h>
#include <wchar.h>

#define BUF_LEN 256

int wmain(void) { 

    wchar_t *word = L"table";
    int count = 6;

    wchar_t buf[BUF_LEN] = {0}; 
    wchar_t *line = L"There are %d %lss";

    HRESULT r = StringCchPrintfW(buf, ARRAYSIZE(buf), line, count, word);   

    if (SUCCEEDED(r)) {

        wprintf(L"%ls\n", buf);

    } else {

        wprintf(L"StringCchPrintfW() failed\n"); 
        return 1;
    }

    return 0;
} 

```

在代码示例中，我们使用`StringCchPrintfW()`函数创建格式化的字符串。

```c
wchar_t *line = L"There are %d %lss";

```

这是格式字符串； 它具有两个格式说明符：`%d`和`%ls`。

```c
HRESULT r = StringCchPrintfW(buf, ARRAYSIZE(buf), line, count, word);

```

使用`StringCchPrintfW()`函数，我们将两个值插入目标缓冲区。

```c
C:\Users\Jano\Documents\Pelles C Projects\strsafe\SafeFormat>SafeFormat.exe
There are 6 tables

```

这是`SafeFormat.exe`程序的输出。

在 Windows API 教程的这一部分中，我们使用了字符串。