当使用 FORMAT_MESSAGE_FROM_STRING 标志时,此时函数的第 2 个参数为一个字符串字符串指针,该字符串是一条消息定义,该消息定义包含了“插入序列” ,它用来指导如何格式化消息。这个标志不能和 FORMAT_MESSAGE_FROM_HMODULE 或者 FORMAT_MESSAGE_FROM_SYSTEM 标志一起使用。
FORMAT_MESSAGE_ARGUMENT_ARRAY 标志表示函数的最后一个参数不是一个 va_list 结构(实际上是一个字符串),而是一个数组指针,该数组里存放相关参数。
下面看一下最后一个参数,它可以是一个数组,数组中的的值用来指导如何格式化信息。在格式化字符串中, %1 表示数组元素的第 1 个元素,%2 表示第 2 个元素,以此类推。
看一下下面这个示例:
[C++] 纯文本查看 复制代码
void main(void)
{
LPWSTR pMessage = L"%3 %4 %6"; //将数组中的相应元素置入格式化字符串
DWORD_PTR pArgs[] = { (DWORD_PTR)4, (DWORD_PTR)2, (DWORD_PTR)L"Billxx",
(DWORD_PTR)L"Bob",
(DWORD_PTR)6, (DWORD_PTR)L"Bill" };
const DWORD size = 100+1;
WCHAR buffer[size];
if (!FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
pMessage,
0,
0,
buffer,
size,
(va_list*)pArgs))
{
wprintf(L"Format message failed with 0x%x\\n", GetLastError());
return;
}
wprintf(L"Formatted message: %s\\n", buffer);
}
D:\\WindowsAPP\\arrinsert\\Debug>arrinsert.exe
Formatted message: Billxx Bob Bill 上面 %3,%4, %6 分别对应着数组中的第 3,第4,第6 个元素,它们会被置放进格式化字符串中。%0 另有其意,它并不表示数组中的元素,而是用来结束消息文本,相当于在格式化字符串后添加了一个 '\\0' 。
如果上面的 pMessage 改为 LPWSTR pMessage = L"%1"; 那么就会访问出错,因为函数在访问数组元素时,是从这个数组元素所代表的地址处取出字符串信息,所以当用 %1 时,相当于去访问了 0x00000004 这个地址,这种低端地址都是受内核保护的地址,应用程序无法访问,如果访问了会抛出异常。
在说明 %n!format string! 格式化形式之前,先来看一下用 printf() 打印指定宽度和精度字符串的情况,考虑以下几种方式:
[C++] 纯文本查看 复制代码
printf("%8.5s\\n", "groad.net");
printf("%*s\\n", 15, "groad.net");
printf("%*.*s\\n", 6, 2, "groad.net");
运行输出:
[C++] 纯文本查看 复制代码
D:\\WindowsAPP\\arrinsert\\Debug>arrinsert.exe
groad
groad.net
gr
由上可见,我们在打印字符串时也可以指定宽度和精度。在第 2 和第 3 句 printf() 中,使用 '*' 号,这表示 '*' 号所代表的宽度或者精度从参数列表里获取。
知道了上面的规则后,我们返回来看 %n!format string! 这种情况。其中 %n 表示从数组中哪个元素开始,!format string! 这种形式中的 '!' 只是表示一个界定范围的分界符号,在两个 '!' 里面的内容就是要指定的宽度或精度。稍微修改 MSDN 中的一个示例如下:
[C++] 纯文本查看 复制代码
void main(void)
{
LPWSTR pMessage = L"%1!*.*s! %4!*s! %6!*s!";
DWORD_PTR pArgs[] = { (DWORD_PTR)4, (DWORD_PTR)2, (DWORD_PTR)L"Billxx",
(DWORD_PTR)10, (DWORD_PTR)L"Bob",
(DWORD_PTR)6, (DWORD_PTR)L"Bill" };
const DWORD size = 100+1;
WCHAR buffer[size];
if (!FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
pMessage,
0,
0,
buffer,
size,
(va_list*)pArgs))
{
wprintf(L"Format message failed with 0x%x\\n", GetLastError());
return;
}
wprintf(L"Formatted message:%s\\n", buffer);
}
运行输出:D:\\WindowsAPP\\arrinsert\\Debug>arrinsert.exe
Formatted message: Bi Bob Bill 上面程序中,%1!*.*s! 表示从第 1 个数组元素开始操作,'!' 里面的 *.* 表示取宽度和精度,第 1 个 '*' 所表示的精度就取自数组中的第 1 个元素,第 2 个 '*' 所取得宽度就取自数组中的第 2 个元素,而 's' 表示输出的是字符串,当然这个字符串就是紧接着的 "Billxx"。同样道理可以分析后面的 %4!*s! %6!*s! 这两种情况。
其它的一些控制符号参考 MSDN。 |