C# - FormatMessage through P/Invoke and Manage...

We have seen in my previous post some common routines such GetLastWin32Message, FormatMesage and LocalFree. In this post, we well delve into the FormatMessage, because it shall be commonly used when we are dealing with some windows error code and when we need to process strings in/out the winapi.


you will need the following signature of dllimports

[DllImport("kernel32.dll")]
static extern uint FormatMessage(uint dwFlags, IntPtr lpSource,
   uint dwMessageId, uint dwLanguageId, [Out] StringBuilder lpBuffer,
   uint nSize, IntPtr Arguments);

// the version, the sample is built upon:
[DllImport("Kernel32.dll", SetLastError=true)]
static extern uint FormatMessage( uint dwFlags, IntPtr lpSource, 
   uint dwMessageId, uint dwLanguageId, ref IntPtr lpBuffer, 
   uint nSize, IntPtr pArguments);

// the parameters can also be passed as a string array:
[DllImport("Kernel32.dll", SetLastError=true)]
static extern uint FormatMessage( uint dwFlags, IntPtr lpSource, 
   uint dwMessageId, uint dwLanguageId, ref IntPtr lpBuffer, 
   uint nSize, string[] Arguments);

// see the sample code
[DllImport("kernel32.dll", SetLastError = true)]
static extern uint FormatMessage(uint dwFlags, IntPtr lpSource, uint dwMessageId, uint dwLanguageId, [Out] StringBuilder lpBuffer, uint nSize, string[] Arguments);
we will also need to use the following constants definition, which is copied from the header files.
// from header files
    const uint FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
    const uint FORMAT_MESSAGE_IGNORE_INSERTS  = 0x00000200;
    const uint FORMAT_MESSAGE_FROM_SYSTEM    = 0x00001000;
    const uint FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000;
    const uint FORMAT_MESSAGE_FROM_HMODULE = 0x00000800;
    const uint FORMAT_MESSAGE_FROM_STRING = 0x00000400;
And I have provided two method that I believe shall be commonly used.
static string FormatNativeMessage(string formatExpression, string[] formatArgs);
static string FormatLastWin32Message(uint error);
here is the code .
class Program
  {
    // check out the quick acess to pinovke
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern uint FormatMessage(uint dwFlags, IntPtr lpSource,
       uint dwMessageId, uint dwLanguageId, ref IntPtr lpBuffer,
       uint nSize, IntPtr pArguments);
    // an overload to the FormatMessage call
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern uint FormatMessage(uint dwFlags,
      IntPtr lpSource,
      uint dwMessageIg,
      uint dwLanguageId,
      ref IntPtr lpBuffer,
      uint nSize,
      string[] arguments);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern uint FormatMessage(
      uint dwFlags, 
      IntPtr lpSource, 
      uint dwMessageId, 
      uint dwLanguageId, 
      [Out] StringBuilder lpBuffer, 
      uint nSize, 
      string[] Arguments);
   [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr LocalFree(IntPtr hMem);
    // 
    // from header files
    const uint FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
    const uint FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
    const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
    const uint FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000;
    const uint FORMAT_MESSAGE_FROM_HMODULE = 0x00000800;
    const uint FORMAT_MESSAGE_FROM_STRING = 0x00000400;

    static string FormatNativeMessage(string formatExpression, string[] formatArgs)
    {
      try
      {
        StringBuilder msgBuilder = new StringBuilder(101);
        IntPtr formatPtr = Marshal.StringToHGlobalAnsi(formatExpression); // what is those global alloc and what is the local alloc?
        // Must specify the FORMAT_MESSAGE_ARGUMENT_ARRAY flag when pass an array
        uint length = FormatMessage(
          FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
          formatPtr,
          0,
          0,
          msgBuilder,
          101,
          formatArgs);

        if (length == 0)
        {
          FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, IntPtr.Zero, (uint)Marshal.GetLastWin32Error(), 0, msgBuilder, 101, null);
          Console.WriteLine("Error:" + msgBuilder.ToString());
        }
        else
        {
          Console.WriteLine("Format result : " + msgBuilder.ToString() + ", Length:" + length.ToString());
          return msgBuilder.ToString();
        }
      }
      catch (Exception ex_)
      {
        Console.WriteLine(ex_.ToString());
      }
      return null;
    }
    // normally you can do is just to throw out the Win32Exception with the error code as arguments
    static string FormatLastWin32Message(uint error)
    {
      IntPtr lpMsgBuffer = IntPtr.Zero;
      uint dwFlags = FormatMessage(
        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
        IntPtr.Zero,
        (uint) error,
        0, // default language
        ref lpMsgBuffer,
        0,
        IntPtr.Zero);
      if (dwFlags == 0)
      {
        // handle the error
        int le = Marshal.GetLastWin32Error();
        return null;
      }
      else
      {
        string sRet = Marshal.PtrToStringAnsi(lpMsgBuffer);
        LocalFree(lpMsgBuffer); // return the buffer
        return sRet;
      }
    }

    static string FormatLastWin32Message()
    {
      int nLastError = Marshal.GetLastWin32Error();
      return FormatLastWin32Message((uint) nLastError);
    }
And below is the drive code , which demonstrate the use of it.
static void Main(string[] args)
    {
      string formatExpression = "%1,%2%!"; // it accepts different set of format specifiers....
      string[] formatArgs = new[] { "Hello", "World" };

      string formattedMessage = FormatNativeMessage(formatExpression, formatArgs);
      if (!string.IsNullOrEmpty(formattedMessage))
      {
        Console.WriteLine("The fomatted message is {0}", formattedMessage);
      }

      var formattedLastWin32ErrorMsg = FormatLastWin32Message();
      if (!string.IsNullOrEmpty(formattedLastWin32ErrorMsg))
      {
        Console.WriteLine("LastWin32Message : {0}", formattedLastWin32ErrorMsg);
      }
    }


References
FormatMessage from PInvoke.net -  http://www.pinvoke.net/default.aspx/kernel32.formatmessage

你可能感兴趣的:(windows,C#)