FormatMessage函数的用法

来源:互联网 发布:查看网络连接没反应 编辑:程序博客网 时间:2024/05/18 01:02
 

FormatMessage函数的用法

 

在《windows核心编程》中第一个示例便是它的使用。

这个函数我用了几次之后终于明白它的用法;

这个函数是用来格式化消息字符串,就是处理消息资源的。消息资源是由mc.exe编译的,详细请在msdn中搜索mc.exe。

 

先来看下它的函数原型

 

FormatMessage的用法 - kulong0105 - kulong0105的博客DWORD WINAPI FormatMessage(
FormatMessage的用法 - kulong0105 - kulong0105的博客
FormatMessage的用法 - kulong0105 - kulong0105的博客  __in          DWORD dwFlags,
FormatMessage的用法 - kulong0105 - kulong0105的博客
FormatMessage的用法 - kulong0105 - kulong0105的博客  __in          LPCVOID lpSource,
FormatMessage的用法 - kulong0105 - kulong0105的博客
FormatMessage的用法 - kulong0105 - kulong0105的博客  __in          DWORD dwMessageId,
FormatMessage的用法 - kulong0105 - kulong0105的博客
FormatMessage的用法 - kulong0105 - kulong0105的博客  __in          DWORD dwLanguageId,
FormatMessage的用法 - kulong0105 - kulong0105的博客
FormatMessage的用法 - kulong0105 - kulong0105的博客  __out         LPTSTR lpBuffer,
FormatMessage的用法 - kulong0105 - kulong0105的博客
FormatMessage的用法 - kulong0105 - kulong0105的博客  __in          DWORD nSize,
FormatMessage的用法 - kulong0105 - kulong0105的博客
FormatMessage的用法 - kulong0105 - kulong0105的博客  __in          va_list* Arguments
FormatMessage的用法 - kulong0105 - kulong0105的博客
FormatMessage的用法 - kulong0105 - kulong0105的博客);
FormatMessage的用法 - kulong0105 - kulong0105的博客
FormatMessage的用法 - kulong0105 - kulong0105的博客

在使用这个函数的时候要明确以下几点

你要处理的消息资源来自哪里,这一点尤为重要。

你的消息ID来自哪里.

 

以下是每个参数的详细介绍:

 

dwFlags :

格式化选项,对lpSource参数值有指导作用。

dwFlags的低位值指定了函数如何处理输出缓冲区处理行转换,也可以指定格式化输出字符串输出行的最大宽度。它的位标示符如下:

Value

Meaning

FORMAT_MESSAGE_ALLOCATE_BUFFER
0x00000100

lpBuffer参数是一个PVOID指针,nSize参数指定按TCHARs为单位的分配给输出消息缓冲区的最小值。当你不适用这个缓冲区的时候也就是lpBuffer的时候需要用LocalFree将其释放

 

FORMAT_MESSAGE_ARGUMENT_ARRAY
0x00002000

Arguments参数不是一个va_list结构,但是它表示一个数组指针。这个标识符不能在64位整数值时使用,你如果要使用64位整数值,那么你必须使用va_list结构体

 

FORMAT_MESSAGE_FROM_HMODULE
0x00000800

lpSource参数是一个包含了消息表资源(Message-table resources)模块(dll)句柄。如果lpSource句柄为NULL,系统会自动搜索当前进程文件的消息资源。

这个标示符不可以和FORMAT_MESSAGE_FROM_STRING共用。

如果模块中没有资源表,这个函数执行失败并且返回ERROR_RESOURCE_TYPE_NOT_FOUND错误值。

 

FORMAT_MESSAGE_FROM_STRING
0x00000400

lpSource参数指向一个包含了消息定义的字符串.这个消息定义里面可能包含了插入序列(insert sequence),像消息表资源中包含消息文本一样.和这个标示符不和FORMAT_MESSAGE_FROM_HMODULE或者FORMAT_MESSAGE_FROM_SYSTEM一起使用.

 

FORMAT_MESSAGE_FROM_SYSTEM
0x00001000

函数将会搜索系统消息表资源来寻找所需消息资源。如果这个标示符同时定义了FORMAT_MESSAGE_FROM_HMODULE,那么如果函数在模块中没有搜索到所需消息的话将会在系统中搜索。这个标示符不能和FORMAT_MESSAGE_FROM_STRING一起使用.

当这个标示符设置的时候,可以使用GetLastError函数返回值来搜索这个错误码在系统定义错误中相应的消息文本。

 

FORMAT_MESSAGE_IGNORE_INSERTS
0x00000200

在消息定义中的插入序列将会被忽略,这个标示符在获取一个格式化好的消息十分有用,如果这个标示符设置好了,那么Arguments参数将被忽略。

 

 

开始在上面说了dwflags参数低位值作用,你可以使用以下值来设置低位值。

 

Value

Meaning

0

将不会有输出行宽度限制。

 

FORMAT_MESSAGE_MAX_WIDTH_MASK
0x000000FF

将会有限制输出行宽度,使用硬编码设定。

 

 

 

lpSource:

这个值是消息表资源来自哪里,这个值依靠dwFlags,详细请看FORMAT_MESSAGE_FROM_HMODULEFORMAT_MESSAGE_FROM_STRING,如果这两个标示符都没设置,那么lpSource将会被忽略。

 

dwMessageId 

所需格式化消息的标识符。当dwFlags设置了FORMAT_MESSAGE_FROM_STRING,这个参数将会被忽略。

 

dwLanguageId

格式化消息语言标识符。

 

lpBuffer:

 一个缓冲区指针来接受格式化后的消息。当dwFlags包括了FORMAT_MESSAGE_ALLOCATE_BUFFER标志符,这个函数将会使用LocalAlloc函数分配一块缓冲区,lpBuffer需要接受一个地址来使用这个缓冲区。(这里要注意传参一定要传地址)。

 

nSize

 如果FORMAT_MESSAGE_ALLOCATE_BUFFER没有设置,那么这个参数指定了输出缓冲区的消息,以TCHARs为单位。如果FORMAT_MESSAGE_ALLOCATE_BUFFER设置了,这个参数设置以TCHARs为单位的输出缓冲区的最小值。这个输出缓冲区不能大于64KB

 

Arguments:

一个数组中的值在格式化消息中作为插入值,根据消息文本的格式里面的内容(详见mc.exe使用),可以知道%n[!format_specifier!]为这个参数的指定形式。

比如说在格式字符串中的%1为数组中的第一个值,%2为第二个值。n就代表数组第几个值。

那么[format_specifier!]如何解释呢?这个格式化指定具体解释在 Format Specification Fields,也就是printf的格式形式安排。

msdn中可以搜索Format Specification Fields: printf and wprintf Functions关键字进行查询详细的值。

 

看完以上详细参数的解释其实我自己还没太明白如何用。msdn中的几个例子可以让人豁然开朗。

 

 

 

例一:

 

使用系统的消息资源来报错,这也是这个函数最有用的地方。

 

[cpp] view plaincopyprint?
  1. #include <windows.h>  
  2. #include <strsafe.h>  
  3.   
  4. void ErrorExit(LPTSTR lpszFunction)   
  5. {   
  6.     // Retrieve the system error message for the last-error code  
  7.   
  8.     LPVOID lpMsgBuf;  
  9.     LPVOID lpDisplayBuf;  
  10.     DWORD dw = GetLastError();   
  11.   
  12.     FormatMessage(  
  13.         FORMAT_MESSAGE_ALLOCATE_BUFFER |   
  14.         FORMAT_MESSAGE_FROM_SYSTEM |  
  15.         FORMAT_MESSAGE_IGNORE_INSERTS,  
  16.         NULL,  
  17.         dw,  
  18.         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),  
  19.         (LPTSTR) &lpMsgBuf,  
  20.         0, NULL );  
  21.   
  22.     // Display the error message and exit the process  
  23.   
  24.     lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,   
  25.         (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR));   
  26.     StringCchPrintf((LPTSTR)lpDisplayBuf,   
  27.         LocalSize(lpDisplayBuf),  
  28.         TEXT("%s failed with error %d: %s"),   
  29.         lpszFunction, dw, lpMsgBuf);   
  30.     MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);   
  31.   
  32.     LocalFree(lpMsgBuf);  
  33.     LocalFree(lpDisplayBuf);  
  34.     ExitProcess(dw);   
  35. }  
  36.   
  37. void main()  
  38. {  
  39.     // Generate an error  
  40.   
  41.     if(!GetProcessId(NULL))  
  42.         ErrorExit(TEXT("GetProcessId"));  
  43. }  

 

 

我们可以看到函数选项dwFlags

分别为FORMAT_MESSAGE_ALLOCATE_BUFFER由函数分配输出缓冲区,

FORMAT_MESSAGE_FROM_SYSTEM表示程序将会在系统消息表资源中搜索所需消息,

FORMAT_MESSAGE_IGNORE_INSERTS程序将会忽略搜索到消息中的插入序列。

 

lpSource值为NULL,并没有模块值和字符串直接传入所以为NULL,详细看以上各参数解析。

 

dwMessageIddw,即GetLastError的返回值。就是消息资源的ID号。

 

dwLanguageId 设置为本地默认

 

lpBuffer 输出缓冲区这里注意为什么要&呢? 因为 LPVOID lpMsgBuf只是一个指针对象,那么要必须要把它的地址传给lpBuffer参数。

 

剩下两个参数可以上面参数的详解。

 

最后注意一点:由于lpBuffer这个参数的值是FormatMessage函数动态分配的缓冲区,所以在不使用的时候要LocalFree.

 

 

例二:

 

格式化模块中的消息资源,这里加载的模块之中要有消息资源,如果FormatMessage函数中传递了FORMAT_MESSAGE_FROM_SYSTEM,如果消息模块中没有我们所需的资源,那么将会在系统中寻找;如果没有 FORMAT_MESSAGE_FROM_SYSTEM,而且消息模块中没有我们所需资源,函数调用失败。

[cpp] view plaincopyprint?
  1. #include <windows.h>  
  2. #include <stdio.h>  
  3.   
  4. #include <lmerr.h>  
  5.   
  6. void  
  7. DisplayErrorText(  
  8.     DWORD dwLastError  
  9.     );  
  10.   
  11. #define RTN_OK 0  
  12. #define RTN_USAGE 1  
  13. #define RTN_ERROR 13  
  14.   
  15. int  
  16. __cdecl  
  17. main(  
  18.     int argc,  
  19.     char *argv[]  
  20.     )  
  21. {  
  22.     if(argc != 2) {  
  23.         fprintf(stderr,"Usage: %s <error number>\n", argv[0]);  
  24.         return RTN_USAGE;  
  25.     }  
  26.   
  27.     DisplayErrorText( atoi(argv[1]) );  
  28.   
  29.     return RTN_OK;  
  30. }  
  31.   
  32. void  
  33. DisplayErrorText(  
  34.     DWORD dwLastError  
  35.     )  
  36. {  
  37.     HMODULE hModule = NULL; // default to system source  
  38.     LPSTR MessageBuffer;  
  39.     DWORD dwBufferLength;  
  40.   
  41.     DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |  
  42.         FORMAT_MESSAGE_IGNORE_INSERTS |  
  43.         FORMAT_MESSAGE_FROM_SYSTEM ;  
  44.   
  45.     //  
  46.     // If dwLastError is in the network range,   
  47.     //  load the message source.  
  48.     //  
  49.   
  50.     if(dwLastError >= NERR_BASE && dwLastError <= MAX_NERR) {  
  51.         hModule = LoadLibraryEx(  
  52.             TEXT("netmsg.dll"),  
  53.             NULL,  
  54.             LOAD_LIBRARY_AS_DATAFILE  
  55.             );  
  56.   
  57.         if(hModule != NULL)  
  58.             dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE;  
  59.     }  
  60.   
  61.     //  
  62.     // Call FormatMessage() to allow for message   
  63.     //  text to be acquired from the system   
  64.     //  or from the supplied module handle.  
  65.     //  
  66.   
  67.     if(dwBufferLength = FormatMessageA(  
  68.         dwFormatFlags,  
  69.         hModule, // module to get message from (NULL == system)  
  70.         dwLastError,  
  71.         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language  
  72.         (LPSTR) &MessageBuffer,  
  73.         0,  
  74.         NULL  
  75.         ))  
  76.     {  
  77.         DWORD dwBytesWritten;  
  78.   
  79.         //  
  80.         // Output message string on stderr.  
  81.         //  
  82.         WriteFile(  
  83.             GetStdHandle(STD_ERROR_HANDLE),  
  84.             MessageBuffer,  
  85.             dwBufferLength,  
  86.             &dwBytesWritten,  
  87.             NULL  
  88.             );  
  89.   
  90.         //  
  91.         // Free the buffer allocated by the system.  
  92.         //  
  93.         LocalFree(MessageBuffer);  
  94.     }  
  95.   
  96.     //  
  97.     // If we loaded a message source, unload it.  
  98.     //  
  99.     if(hModule != NULL)  
  100.         FreeLibrary(hModule);  
  101. }  


 

之前两个例子都是经常使用的,那么FormatMessage之中还有个参数我们没有用过的,Arguments,那么我们在什么情况下使用呢?

 我们前面已经详细解释了各个参数详细意义。我们先来看msdn两个有关使用这个值的例子:

 

[cpp] view plaincopyprint?
  1. #ifndef UNICODE  
  2. #define UNICODE  
  3. #endif  
  4.   
  5. #include <windows.h>  
  6. #include <stdio.h>  
  7.   
  8. void main(void)  
  9. {  
  10.     LPWSTR pMessage = L"%1!*.*s! %4 %5!*s!";  
  11.     DWORD_PTR pArgs[] = { (DWORD_PTR)4, (DWORD_PTR)2, (DWORD_PTR)L"Bill",  // %1!*.*s! refers back to the first insertion string in pMessage  
  12.          (DWORD_PTR)L"Bob",                                                // %4 refers back to the second insertion string in pMessage  
  13.          (DWORD_PTR)6, (DWORD_PTR)L"Bill" };                               // %5!*s! refers back to the third insertion string in pMessage  
  14.     const DWORD size = 100+1;  
  15.     WCHAR buffer[size];  
  16.   
  17.   
  18.     if (!FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,  
  19.                        pMessage,   
  20.                        0,  
  21.                        0,  
  22.                        buffer,   
  23.                        size,   
  24.                        (va_list*)pArgs))  
  25.     {  
  26.         wprintf(L"Format message failed with 0x%x\n", GetLastError());  
  27.         return;  
  28.     }  
  29.   
  30.     // Buffer contains "  Bi Bob   Bill".  
  31.     wprintf(L"Formatted message: %s\n", buffer);  
  32. }  


 

根据msdn中对Arguments参数的解释,这里插入序列遵循%n[!format_specifier!]

这个格式,如果format_specifer不清楚可以查阅printf输出格式。

FormatMessage的用法 - kulong0105 - kulong0105的博客LPWSTR pMessage = L"%1!*.*s! %4 %5!*s!";

的意义如下:

%1!*.*s!  表示为 %1取数组第一个位置的字符串的值,

!*.*s! 就是[!format_specfier!]的内容,所以我们就想知道 *.*s含义,

根据printf输出格式我们可以知道第一个星号* 表示输出宽度,点号(.)表示下面一个星号是输出精度。

故我们可以看到数组pArgs前面3个值,4 ,2,Bill 。4为要格式的宽度,2为要格式的精度,Bill为要格式的字符串。

%4 取数组第四个值的字符串,它没有format_specifier 所以按默认输出宽度和精度。

%5!*s! 表示输出的是取数组第五个值的字符串,宽度为6。

 

msdn还提供了一个使用va_list类型的例子:

[cpp] view plaincopyprint?
  1. #ifndef UNICODE  
  2. #define UNICODE  
  3. #endif  
  4.   
  5. #include <windows.h>  
  6. #include <stdio.h>  
  7.   
  8. LPWSTR GetFormattedMessage(LPWSTR pMessage, );  
  9.   
  10. void main(void)  
  11. {  
  12.     LPWSTR pBuffer = NULL;  
  13.     LPWSTR pMessage = L"%1!*.*s! %3 %4!*s!";  
  14.   
  15.     // The variable length arguments correspond directly to the format  
  16.     // strings in pMessage.  
  17.     pBuffer = GetFormattedMessage(pMessage, 4, 2, L"Bill", L"Bob", 6, L"Bill");  
  18.     if (pBuffer)  
  19.     {  
  20.         // Buffer contains "  Bi Bob   Bill".  
  21.         wprintf(L"Formatted message: %s\n", pBuffer);  
  22.         LocalFree(pBuffer);  
  23.     }  
  24.     else  
  25.     {  
  26.         wprintf(L"Format message failed with 0x%x\n", GetLastError());  
  27.     }  
  28. }  
  29.   
  30. // Formats a message string using the specified message and variable  
  31. // list of arguments.  
  32. LPWSTR GetFormattedMessage(LPWSTR pMessage, )  
  33. {  
  34.     LPWSTR pBuffer = NULL;  
  35.   
  36.     va_list args = NULL;  
  37.     va_start(args, pMessage);  
  38.   
  39.     FormatMessage(FORMAT_MESSAGE_FROM_STRING |  
  40.                   FORMAT_MESSAGE_ALLOCATE_BUFFER,  
  41.                   pMessage,   
  42.                   0,  
  43.                   0,  
  44.                   (LPWSTR)&pBuffer,   
  45.                   0,   
  46.                   &args);  
  47.   
  48.     va_end(args);  
  49.   
  50.     return pBuffer;  
  51. }  


 

那么我们已经看完了所有的使用方法了,但是可能我们还会想Argument到底有什么用按照以上所述。

在消息资源的消息文本中我们可能会使用插入序列,让消息文本显示更加灵活。

比如我们在消息资源中的一个消息里面定义一个消息文本内容如下:

FormatMessage的用法 - kulong0105 - kulong0105的博客%1!*.*s! %4 %5!*s!

那么我们在调用消息模块的时候代码如下:

[cpp] view plaincopyprint?
  1. DWORD_PTR pArgs[] = { (DWORD_PTR)4, (DWORD_PTR)2, (DWORD_PTR)L"Bill",  // %1!*.*s! refers back to the first insertion string in pMessage  
  2.              (DWORD_PTR)L"Bob",                                                // %4 refers back to the second insertion string in pMessage  
  3.              (DWORD_PTR)6, (DWORD_PTR)L"Bill" };                               // %5!*s! refers back to the third insertion string in pMessage  
  4.          if (hDll != NULL) {  
  5.   
  6.             fOk = FormatMessage(  
  7.                FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY |  
  8.                FORMAT_MESSAGE_ALLOCATE_BUFFER,  
  9.                hDll, dwError, systemLocale,  
  10.                (PTSTR) &hlocal, 0, (va_list*)pArgs);  
  11.             FreeLibrary(hDll);  
  12.          }  
  13.       }  


 

原文:http://www.cppblog.com/koople/archive/2009/12/03/102367.aspx

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 租户不变更地址怎么办 营业执照忘审了怎么办 工商营业执照年检过期怎么办 个体营业执照没有年报怎么办? 个体工商户一年没有申报怎么办 个体工商户逾期未申报怎么办 个体户没报税过怎么办 农业银行证书过期了怎么办 ca证书丢了怎么办 ca证书被锁怎么办 上个月忘记清卡怎么办 财务人员进入税务黑名单怎么办 社保本丢了怎么办 贷款车辆登记证书怎么办 发票薄丢了怎么办? 汽车发票丢了怎么办 税票弄丢了怎么办 交强险正本丢了怎么办 个体营业执照正本丢失怎么办 简易注销后税务怎么办 拒绝了日历邀请怎么办 老人走丢了怎么办 老人走丢找不到怎么办 没人给介绍对象怎么办 bate365账号被锁怎么办 qq号疑似被盗怎么办 不知道音乐名字怎么办 忘记支付宝登录怎么办 微信被老婆拉黑怎么办 微信群昵称改不了怎么办 微信号设置不了怎么办 修改微信号点不开怎么办 多屏设置失败怎么办 icould密码忘了怎么办 微信号换不了怎么办 微信号改不了怎么办? 无法设置微信号怎么办 公司改名后商标怎么办 公司名称变更后发票怎么办 被起诉公司企业变更怎么办 企业公章坏了怎么办