FormatMessage 使用详解

本文翻译自MSDN

FormatMessage函数的功能是格式化一个消息字符串。函数需要一个消息定义(message definition)作为输入。这个消息定义可以通过一个缓冲区或一个已经加载了的消息资源表模块传入本函数。调用者还可以通过搜索系统消息资源表来传入这个消息定义。FormatMessage函数在一个消息资源表中是通过一个消息标识符和一个语言标识符来找到一个消息定义。函数如果被调用,则通过复制格式化的消息文本到一个输出缓冲区中来处理任何嵌入式插入序列(processing any embedded insert sequences if requested)。

函数原型:

DWORD FormatMessage(
  DWORD dwFlags,      // 资源处理方式选项   -in
  LPCVOID lpSource,   // 消息资源           -in
  DWORD dwMessageId,  // 消息标识符         -in
  DWORD dwLanguageId, // 语言标识符         -in
  LPTSTR lpBuffer,    // 消息缓冲区         -out
  DWORD nSize,        // 消息缓冲区的最大值 -in
  va_list *Arguments  // 插入的消息数组     -in
);

 

参数意义解释:

输入选项 dwFlags:表示消息具体被格式化的过程和怎么翻译 lpSource 参数。 其中dwFlags的低字节具体负责函数在输出缓冲区中怎么处理换行,也能够指定一个格式化输出行的最大宽度。 dwFlags总共有6个参数可供选用,可以选其中一个或几个组合使用:

FORMAT_MESSAGE_ALLOCATE_BUFFER : 指定lpBuffer 为一个LPVOID型指针,nSize为输出消息缓冲区指定最小的宽字符(THCHARs)长度。为格式化消息分配一个足够大的缓冲区,并将lpBuffer指向这个分配的缓冲区的地址。当这个分配的缓冲区用完之后,调用者需要使用LocalFree函数来释放掉这个lpBuffer所指向的这个缓冲区,以免造成内存泄露。

FORMAT_MESSAGE_FROM_INSERTS:消息定义中的插入序列将被忽略,并输出缓冲区输出这个消息,而消息本身将不会被改变(insert sequences in the message definition are to be ignored and passed through to the output buffer unchanged)这个标志对获取消息之后再进行格式化十分有用。如果这个标志被启用,则后面的Arguments数组将会被忽略掉。

FORMAT_MESSAGE_FROM_STRING : 将lpSource指向一个以NULL字符结尾的消息定义。这个消息定义可能包含了插入序列,就像一个消息资源表中有一个消息文本一样。不能和FORMAT_MESSAGE_FROM_HMODULE或FORMAT_MESSAGE_FROM_SYSTEM一起使用

FORMAT_MESSAGE_FROM_HMODULE:lpSource指定为一个搜索消息表资源的模块句柄。如果lpSource设置为NULL,将会搜索当前应用程序镜像文件的当前进程。不能和FORMAT_MESSAGE_FROM_STRING一起使用。

FORMAT_MESSAGE_FROM_SYSTEM:从系统消息资源表中来搜索所响应的消息。FORMAT_MESSAGE_FROM_SYSTEM和FROMAT_MESSAGE_FROM_HMODULE一起使用时,函数会先lpSource指定某一个具体的消息资源表中搜索响应的消息,如果没有搜索到,则会转而搜索系统消息资源表。不能和FROMAT_MESSAGE_FROM_STRING一起使用。如果启用了FORMAT_MESSAGE_FROM_SYSTEM ,那么应用程序可以根据GetLastError函数的返回值来检索相应的系统定义的错误的消息文本。

FORMAT_MESSAGE_FROM_ARRAY:表示Arguments参数不在是一个va_list结构,而是一个指向参数数组值的一个指针。

dwFlags的低位字节可以指定格式化输出行的最大宽度。可以通过使用FORMAT_MESSAGE_MAX_WIDTH_MASK常量和按位逻辑操作来设置和获得这个最大宽度值。

下面的将说明FormatMessage是怎样来解释它的低位字节值的:

值为0,则表示输出行没有宽度限制,函数将把消息定义文本中的换行存储到输出缓冲区中。

如果是一个非零值,且这个值也不是FORMAT_MESSAGE_MAX_WIDTH_MASK时,表示这个非零值就是一个输出行的最大字节数。函数将忽略消息定义文本中的换行标识。函数也不会在换行时将一个字符用分隔符断开。函数会把消息定义文本中的硬编码换行写入到输出缓冲区中。硬编码换行就是%n,即转义换行符号。

如果值是FORMAT_MESSAGE_MAX_WIDTH_MASK 则函数将忽略消息定义文本中的换行标识,并将消息定义文本中的硬换行输出到输出缓冲区中,并不再产生新的换行符。

 

输入项:lpSource,指向消息定义的位置,这个参数的类型依赖于dwFlags参数的设置。

如果dwFlags设置为:FORMAT_MESSAGE_FROM_HMODULE , 则lpSource是指向一个可供搜索的包含消息表的模块的句柄。

如果dwFlags设置为:FORMAT_MESSAGE_FROM_STRING , 则lpSource是指向一个没有格式的消息文本的指针。会根据插入序列和格式化信息来扫描。

如果dwFlags设置不是上面的两者之一,或不包含上面两者的任何一种情况,则lpSource则会被忽略。

 

输入项:dwMessageId,表示请求消息的消息标识符。如果dwFlags的参数项中包含FORMAT_MESSAGE_FROM_STRING,则dwMessageld会被忽略掉,即无效。

 

输入项:dwLanuageId, 说明请求消息的语言标识符。如果dwFlags的参数项中包含FORMAT_MESSAGE_FROM_STRING,则dwLanuageId会被忽略掉,即无效。

如果你传入了一个LANGID,FormatMessage仅仅会返回LANGID这个标识符的消息。如果找不到与LANGID相符的消息,将会返回ERROR_RESOURCE_LANG_NOT_FOUND。如果dwLanuageId设置为0,FormatMessage将会按下面的序列来搜索LANGIDs的消息

1.Language neutral (语言中立)

2.Thread LANGID (线程LANGID),基于线程局部值

3.User default LANGID, based on the user's default locale value

4.System default LANGID, based on the system default locale value

5.US English

 如果FormatMessage没有找到与上面这些LANGIDs相对应的消息, 则不管这个消息文本是什么语言表示,都将表示这个消息的文本原样返回,如果执行失败,则返回ERROR_RESOURCE_LANG_NOT_FOUND。

 

输出项:lpBuffer:指向一个格式化消息(以NULL结尾)的缓冲区的指针。如果dwFlags参数总包含了FORMAT_MESSAGE_ALLOCATE_BUFFER, 那么函数将调用LocalAlloc函数来为其分配一个缓冲区,并lpBuffer指针指向这个分配好的缓冲区地址。

输入项:nSize, 如果没有设置FORMAT_MESSAGE_ALLOCATE_BUFFER标志,那么nSize参数将指定能够存储在输出缓冲区中的最大的宽字符数。如果设置了FORMAT_MESSAGE_ALLOCATE_BUFFER,那么这个nSize参数指定了能够存储在输出缓冲区中的最小的宽字符数目。对于ANSI文本来说,nSize 即为字节数, 对于Unicode来说,则是字符数。

输入参数:Arguments,表示指向一个在格式化消息中被用作插入值的一个组数值的指针。在格式化字符串中 A%1 表示参数数组中的第一个值, a%2 表示第二个参数。

对于每一个值的解释与消息定义中的格式化信息有关。默认是作为一个指向以NULL结尾的字符串指针。

默认情况下,Arguments参数是一个va_list*类型,是一个为描述参数变量值的语言-和具体实施的数据类型。如果没有va_list*这个指针类型,那就设置为FORMAT_MESSAGE_ARGUMENT_ARRAY标识符,传入一个指向数组值的指针,这些是作为格式化消息中的插入值。每一个插入值在Array数组列表中必须有一个相对应的变量。

 

函数返回值:

如果执行成功,则返回输出缓冲区中能够存储宽字符TCHARs个数大小的值 包括以NULL这个空字符。其实就是返回lpBuffer 即返回申请到的缓冲区大小。

如果函数执行失败,则返回0.

 

FormatMessage函数可以根据GetLastError返回的系统定义的错误代码来获取错误消息文本。例如:

LPVOID lpMsgBuf;
FormatMessage(
	FORMAT_MESSAGE_ALLOCATE_BUFFER |
	FORMAT_MESSAGE_FROM_SYSTEM |
	FORMATE_MESSAGE_IGNORE_INSERTS,
	NULL,
	GetLastError(),
	MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), //Default language
	(LPTSTR) &lpMsgBuf,
	0,
	NULL
);

//Process any inserts in lpMsgBuf.
//......
//Display the string 
MessageBox(NULL,(LPCTSTR)lpMsgBuf,"Error",MB_OK|MB_ICONINFORMATION);
// Free the buffer.
LocalFree(lpMsgBuf);


在消息文本中,有几个转义符支持动态格式化消息。这些转义符和它们的意义如下:

%0   表示终止一个消息文本行而不需要一个新的换行符,这个转义符可以建立一个长行或终止一个消息文本行而不需要新建立一个换行符。对提示消息类型十分有用。

%n!printf format string!  这个转义符表示一个插入值,n的取值范围为1到99.打印格式符后面必须跟一个感叹符, 即printf format string!  在没有具体指定的情况下,打印格式默认为 !S !

 

在winXP中,有时候需要根据与网络相关的函数中返回的系统定义的错误代码来显示对应的错误文本信息。那么下面就来做这么一个根据返回的错误代码显示其对应的错误文本信息的事情。

通常系统网络返回的错误代码其对应的错误文本消息,可以在一个Netmsg.dll中查找到。这个Netmsg.dll在 %systemroot% \system32.  Netmsg.dll包含了从NERR_BASE(2100)到MAX_NERR(NERR_BASE+899)的错误文本信息。而对应的错误代码号则定义在SDK的lmerr.h文件中

可以用LoadLibrary 和 LoadLibraryEx函数来加载Netmsg.dll文件,FormatMessage函数将会根据Netmsg.dll文件模块句柄返回的错误代码号来将其映射到对应错误信息上。具体代码如下:

#include <windows.h>
#include <stdio.h>

#include <lmerr.h>

void 
DisplayErrorText(DWORD dwLastError);

#define RTN_OK 0
#define RTN_USAGE 1
#define RTN_ERROR 13

int  _cdec main( int argc, char *argv[])
{
	if(2 != argc)
	{
		fprintf(stderr,"Usage: %s <error number> \n",argv[0]);
		return RTN_USAGE;
	}

	DisplayErrorText(atoi(argv[1]));

	return RTN_OK;
}

void DisplayErrorText(DWORD dwLastError)
{
	HMODULE hModule = NULL; //default to system source
	LPSTR MessageBuffer;
	DWORD dwBufferLength;

	DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
		FORMAT_MESSAGE_IGNORE_INSERTS |
		FORMAT_MESSAGE_FROM_SYSTEM;

	//
	// If dwLastError is in the network range, 
	// load the message source.
	//

	if (dwLastError >= NERR_BASE && dwLastError <= MAX_NERR)
		{
			hModule = LoadLibraryEx(
				TEXT("netmsg.dll"),
				NULL,
				LOAD_LIBRARY_AS_DATAFILE
				);
			if(hModule !=NULL)
				dwFormatFlags != FORMAT_MESSAGE_FROM_HMODULE;
		}

	//
	// Call FormatMessage() to allow for message
	// text to be acquired from the system
	// or from the supplied module handle.
	//

	if(dwBufferLength = FormatMessageA(
		dwFormatFlags,
		hModule, // module to get message from (NULL == system)
		dwLastError,
		MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), // default language
		(LPSTR) &MessageBuffer,
		0,
		NULL))
		{
			DWORD dwBytesWritten;

			//
			// Output message string on stderr.
			//
			WriteFile(
				GetStdHandle(STD_ERROR_HANDLE),
				MessageBuffer,
				dwBufferLength,
				&dwBytesWritten,
				NULL
				);

			//
			// Free the buffer allocated by the system
			//
			LocalFree(MessageBuffer);
		}

	// 
	// If we loaded a message source , unload it.
	//
	if(hModule != NULL)
		FreeLibrary(hModule);
}


比如传入一个错误代码数字,它将根据此错误代码号来显示出对应的错误信息

C:\> netmsg 2453
Could not find domain controller for this domain

 

 

 

 

 

 

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值