• 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 void
    win32perror(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
    "), 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
    #endif
    
    static void
    win32perror(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
    "), s);
    }
    
    
    void main(void)
    {
        DWORD dwLastError;
        const TCHAR *wSZError = {"错误信息"};
        GetFileAttributes("FileNotexist.txt");
        dwLastError = GetLastError();
    
        
        printf("操作系统返回的错误代码:%d
    ",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函数的详细用法

  • 相关阅读:
    tomcat请求流程浅解
    jdk8为啥lambda表达式建议你用冒号形式调用方法
    打印目录树形结构
    类斐波那契数列的java实现
    sping boot 如何将外部引入的jar包打到fat jar里面
    java多线程之生产者消费者
    Hadoop、Hbase、ZooKeeper的搭建
    java 静态代码块、构造代码块、构造函数调用顺序
    MyBatis的 or 和and 问题
    mysql
  • 原文地址:https://www.cnblogs.com/passedbylove/p/6088096.html
Copyright © 2020-2023  润新知