曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 4545|回复: 1
打印 上一主题 下一主题

FormatMessage() -- 格式化信息字符串

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34397
跳转到指定楼层
楼主
发表于 2011-9-7 19:44:52 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
函数原型:
DWORD WINAPI FormatMessage(
  __in      DWORD dwFlags,
  __in_opt  LPCVOID lpSource,
  __in      DWORD dwMessageId,
  __in      DWORD dwLanguageId,
  __out     LPTSTR lpBuffer,
  __in      DWORD nSize,
  __in_opt  va_list *Arguments
);
该函数将信息代码转换成有意义的以英语或者其它语言来表示的信息,并返回信息长度。

普通用法示例:
[C++] 纯文本查看 复制代码
int _tmain(int argc, _TCHAR* argv[])
{
    _tsetlocale(LC_CTYPE, (LPCWSTR)"");   //设置可以显示本地语言

    DWORD eMsglen;
    LPTSTR lpvSysMsg;

    FILE *fp = fopen("temp.txt", "r");   //temp.txt 文件实际上不存在,这里只是为了获取错误信息

    DWORD errNum = GetLastError();

    eMsglen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, errNum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpvSysMsg, 0, NULL);
    
    if (eMsglen > 0) {
            _ftprintf(stderr, _T("%s\n"), lpvSysMsg);
        }else {
            _ftprintf (stderr, _T("Last Error Number: %d.\n"), errNum);
        }
    return 0;
}

运行输出:
D:\WindowsAPP\caesar\Debug>caesar.exe
系统找不到指定的文件。

上面程序中,为了能够 FormatMessage() 函数能够显示简体中文,需要事先调用 _tsetlocale(LC_CTYPE, (LPCWSTR)"");  设置一下语言环境。

在 FormatMessage() 函数中:
第 1 个参数 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM 中的 FORMAT_MESSAGE_ALLOCATE_BUFFER 表示函数会为最终格式化后的信息存放提供足够的缓存空间,而 FORMAT_MESSAGE_FROM_SYSTEM 选项配合上面的 GetLastError() 函数使用,这样一来函数就会从系统信息表(system message-table) 中找到关于“错误”的预定于信息。

由于不需要自定义的信息,所以第 2 个参数设置为 NULL 。

第 3 个参数是消息指示符,这里使用 errNum 表示使用 GetLastError(); 所返回的错误代码所对应的“错误消息” 。

第 4 个参数使用了 MAKELANGID 宏,这里表示使用用户默认语言。如果将 SUBLANG_DEFAULT 换为 SUBLANG_SYS_DEFAULT 最后效果也一样,因为都会转换为 Unicode 码。

第 5 个参数是一个缓冲区指针,它所指向的缓冲区用来存放转换过的消息。这个缓冲区是由函数 LocalAlloc() 分配的。注意,这里传进的是指针的地址,即指针的指针类型。

第 6 个参数用来指定缓冲区大小,如果第 1 个参数中没有指定 FORMAT_MESSAGE_ALLOCATE_BUFFER 选项,那么这里需要明确指定缓冲区大小值,反之这里就设为 0,让函数为我们自动分配。

第 7 个参数这里设置为 NULL 。

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34397
沙发
 楼主| 发表于 2011-9-8 09:42:24 | 只看该作者

最后一个参数说明

当使用 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。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2025-6-18 07:34 , Processed in 0.080040 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表