FormatMessage与GetLastError配合使用,排查windows api调用过程中的错误

来源:互联网 发布:hadoop需要java基础吗 编辑:程序博客网 时间:2024/05/17 08:17
FormatMessage与GetLastError配合使用,排查windows api调用过程中的错误

前一段时间在学习windows api调用过程中,遇到过一些调用错误或者程序没能显示预期的结果,或者直接出现vc运行时错误。

这对新手来说是司空见惯的事,因为不太熟悉难免会出错,出错的信息如果能显示很好的关键字到网上搜索一下倒是很好的,例如

返回错误代码:2。你可以使用Visual studio套件里面的Error Lookup查询一下系统消息列表中的请求消息(system message-ta

ble resource(s) for the requested message):便可得知“系统找不到指定的文件”。有的错误根本就不显示什么错误信息,

这可难倒新手了,我也有一些c程序因此没能完成放置那里窖藏了,不懂什么时候才能碰到他。

 

偶然间看到网上文章讲解C#中使用托管代码来排查windows api调用,让我想起了以前下载的一段代码,也有类似的函数,于是用

工具搜索到了这段代码,本文的代码片段来自于Expat 2.1.0的win32filemap.c文件

复制代码
static voidwin32perror(const TCHAR *s){  LPVOID buf;  if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER                    | FORMAT_MESSAGE_FROM_SYSTEM,                    NULL,                    GetLastError(),                    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),                    (LPTSTR) &buf,                    0,                    NULL)) {    _ftprintf(stderr, _T("%s: %s"), s, buf);    fflush(stderr);    LocalFree(buf);  }  else    _ftprintf(stderr, _T("%s: unknown Windows error\n"), s);}
复制代码

在此基础上稍稍修改了一下写了一个测试代码,如下:

以下在windows xp sp3,visual c++ 6编译通过

复制代码
#include <windows.h>#include <stdio.h>#include <tchar.h>#define STRICT 1#define WIN32_LEAN_AND_MEAN 1#ifndef UNICODE#define UNICODE#endifstatic voidwin32perror(const TCHAR *s){    LPVOID buf;    if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS        | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK,        NULL,        GetLastError(),        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),        (LPTSTR) &buf,        0,        NULL)) {        _ftprintf(stderr, _T("%s: %s"), s, buf);        fflush(stderr);        LocalFree(buf);    }    else        _ftprintf(stderr, _T("%s: unknown Windows error\n"), s);}void main(void){    DWORD dwLastError;    const TCHAR *wSZError = {"错误信息\0"};    GetFileAttributes("FileNotexist.txt");    dwLastError = GetLastError();        printf("操作系统返回的错误代码:%d\n",dwLastError);    win32perror(wSZError);}
复制代码

程序运行结果:

以下是网络上的C#代码处理GetLastError错误代码,版权归原作者所有。

复制代码
using System;using System.ComponentModel;// 使用平台调用技术进行互操作性之前,首先需要添加这个命名空间using System.Runtime.InteropServices;namespace FormatMessageDotNet{    class Program    {        // Win32 API         //  DWORD WINAPI GetFileAttributes(        //  _In_  LPCTSTR lpFileName        //);        // 在托管代码中对非托管函数进行声明        [DllImport("Kernel32.dll",SetLastError=true,CharSet=CharSet.Unicode)]        public static extern uint GetFileAttributes(string filename);        static void Main(string[] args)        {            // 试图获得一个不存在文件的属性            // 此时调用Win32函数会发生错误            GetFileAttributes("FileNotexist.txt");            // 在应用程序的Bin目录下存在一个test.txt文件,此时调用会成功            //GetFileAttributes("test.txt");            // 获得最后一次获得的错误            int lastErrorCode = Marshal.GetLastWin32Error();            // 将Win32的错误码转换为托管异常            //Win32Exception win32exception = new Win32Exception();            Win32Exception win32exception = new Win32Exception(lastErrorCode);            if (lastErrorCode != 0)            {                Console.WriteLine("调用Win32函数发生错误,错误信息为 : {0}", win32exception.Message);            }            else            {                Console.WriteLine("调用Win32函数成功,返回的信息为: {0}", win32exception.Message);            }            Console.Read();        }    }}
复制代码

附:据说使用FormatMessage会有潜在的问题,所以以上代码绕开了这个函数。

FormatMessage函数原型:

复制代码
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);
复制代码

其中第一个参数dwFlags开关如果用到以下参数的确会有潜在的问题。

FORMAT_MESSAGE_ALLOCATE_BUFFER 0x00000100

Windows Server 2003 and Windows XP:  

If the length of the formatted message exceeds 128K bytes, then FormatMessage will not automatically fail with an error of ERROR_MORE_DATA.

也就是消息格式化超过128字节了就会出现问题,并且不会自动报告 ERROR_MORE_DATA错误,以后会单独写一篇FormatMessage函数的详细用法

努力与获得往往不成正比,但不至于成反比。