Как получить Win32Exception на английском языке?

Я пытаюсь получить все сообщения Exception на английском языке, независимо от того, на каком языке работает моя программа.

Мне удалось получить почти все сообщения об исключениях на английском языке, используя ответы из следующих сообщений: Исключить сообщения на английском языке? и другое другое решение, которое я нашел (например, использование отражения для изменения значения по умолчанию CultureInfo). У меня есть особая проблема с SocketException. Независимо от того, что я делаю, я получаю ее на машинный язык по умолчанию.

Я создал тестовую программу, чтобы показать проблему: Эта тестовая программа будет печатать Исключения в языке по умолчанию:

using System;
using System.Text;
using System.Threading;
using System.IO;
using System.Net.Sockets;
using System.Reflection;
using System.Globalization;

namespace TestApp
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                //I'm not listening on the following port:
                TcpClient s = new TcpClient("localhost", 2121);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Socket exception: " + ex.Message);
            }
            try
            {
                //the following file doesn't exists:
                File.ReadAllText("filenotexist.txt");
            }
            catch (Exception ex)
            {
                Console.WriteLine("File exception: " + ex.Message);
            }
        }
    }
}

Этот результат на моей машине следующий текст:

H:\Shared>Test-def.exe
Socket exception: No connection could be made because the target machine actively refused it 127.0.0.1:2121
File exception: Could not find file 'H:\Shared\filenotexist.txt'.

На японском компьютере пишут все исключения на японском языке (чего я не понимаю):

Z:\>Test-def.exe
Socket exception: 対象のコンピューターによって拒否されたため、接続できませんでした。 127.0.0.1:2121
File exception: ファイル 'Z:\filenotexist.txt' が見つかりませんでした。

(Японский "выглядит по-другому на японской машине, но при копировании на мою машину он отображается как" \")

Итак, из сочетания ответов, которые я нашел, я применил следующее решение, поэтому теперь оно выглядит так:

namespace TestApp
{
    class Program
    {
        //will change CultureInfo to English, this should change all threads CultureInfo to English. 
        public static void SetEnglishCulture()
        {
            CultureInfo ci = new CultureInfo("en-US");
            //change CultureInfo for current thread:
            Thread.CurrentThread.CurrentUICulture = ci;
            Thread.CurrentThread.CurrentCulture = ci;

            //change CultureInfo for new threads:
            Type t = typeof(CultureInfo);
            try
            {
                t.InvokeMember("s_userDefaultCulture", BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Static, null, ci, new object[] { ci });
                t.InvokeMember("s_userDefaultUICulture", BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Static, null, ci, new object[] { ci });
            }
            catch { }
            try
            {
                t.InvokeMember("m_userDefaultCulture", BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Static, null, ci, new object[] { ci });
                t.InvokeMember("m_userDefaultUICulture", BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Static, null, ci, new object[] { ci });
            }
            catch { }
        }
        static void Main(string[] args)
        {
            //first thing: set CultureInfo to English:
            SetEnglishCulture();
            try
            {
                //I'm not listening on the following port:
                TcpClient s = new TcpClient("localhost", 2121);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Socket exception: " + ex.Message);
            }
            try
            {
                //the following file doesn't exists:
                File.ReadAllText("filenotexist.txt");
            }
            catch (Exception ex)
            {
                Console.WriteLine("File exception: " + ex.Message);
            }
        }
    }
}

Теперь на японском компьютере записываются исключения на английском языке, но исключения Net.socket все еще находятся на японском языке:

Z:\>Test-en.exe
Socket exception: 対象のコンピューターによって拒否されたため、接続できませんでした。 127.0.0.1:2121
File exception: Could not find file 'Z:\filenotexist.txt'.

Я также проверил некоторые другие исключения, некоторые исключения теперь показаны на английском языке, но не все из них, исключения сокетов являются постоянными. Как вы можете видеть, исключение файла было переведено на английский язык, но исключение сокета все еще находится на японском языке.

Я тестировал его почти в любой платформе .NET(от 2,1 до 4,5), все еще такой же.

  • Есть ли полное решение для всех исключений?
  • Я что-то пропустил?
  • Должен ли я делать что-нибудь еще?
  • Может быть, есть другой способ запустить программу на чужой машине и установить некоторую переменную среды, чтобы получить выход на английский язык?

Ответ 1

У меня есть решение, поэтому я загружу его здесь, если кому-то это понадобится. Если у кого-то есть лучшее решение, я буду рад узнать, пожалуйста, прокомментируйте.

В случае Win32Exception, мы можем использовать FormatMessage и перевести код ошибки на английский и по умолчанию языки и заменить значение по умолчанию на английском языке. Если я возьму английский без замены, я потеряю параметры. поэтому в случае неудачной замены я верну Exception с дополнительным описанием на английском языке.

Здесь мое полное решение:

using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.Globalization;
using System.Reflection;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace TestCulture
{
    class Program
    {
        static void SetEnglishCulture()
        {
            CultureInfo ci = new CultureInfo("en-US");
            Thread.CurrentThread.CurrentCulture = ci;
            Thread.CurrentThread.CurrentUICulture = ci;
            Type type = typeof(CultureInfo);
            try
            {
                type.InvokeMember("s_userDefaultCulture", BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Static, null, ci, new object[] { ci });
                type.InvokeMember("s_userDefaultUICulture", BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Static, null, ci, new object[] { ci });
            } catch { }
            try
            {
                type.InvokeMember("m_userDefaultCulture", BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Static, null, ci, new object[] { ci });
                type.InvokeMember("m_userDefaultUICulture", BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Static, null, ci, new object[] { ci });
            } catch { }
        }
        [DllImport("kernel32.dll")]
        static extern uint FormatMessage(uint dwFlags, IntPtr lpSource, uint dwMessageId, uint dwLanguageId, StringBuilder lpBuffer, uint nSize, IntPtr Arguments);
        public static string Win32ExceptionInEnglish(Win32Exception ex)
        {
            const int nCapacity = 820; // max error length
            const uint FORMAT_MSG_FROM_SYS = 0x01000;
            const uint engLangID = (0x01<<10) | 0x09;
            const uint defLangID = 0x0;
            StringBuilder engSb = new StringBuilder(nCapacity);
            StringBuilder defSb = new StringBuilder(nCapacity);
            FormatMessage(FORMAT_MSG_FROM_SYS,IntPtr.Zero, (uint)ex.ErrorCode, defLangID, defSb, nCapacity, IntPtr.Zero);
            FormatMessage(FORMAT_MSG_FROM_SYS,IntPtr.Zero, (uint)ex.ErrorCode, engLangID, engSb, nCapacity, IntPtr.Zero);
            string sDefMsg = defSb.ToString().TrimEnd(' ','.','\r','\n');
            string sEngMsg = engSb.ToString().TrimEnd(' ','.','\r','\n');
            if(sDefMsg == sEngMsg) //message already in English (or no english on machine?)
            {
                //nothing left to do:
                return ex.Message;
            }
            else
            {
                string msg = ex.Message.Replace(sDefMsg,sEngMsg);
                if (msg == ex.Message)
                {
                    //replace didn't worked, can be message with arguments in the middle.
                    //I such as case print both: original and translated. to not lose the arguments.
                    return ex.Message + " (In English: " + sEngMsg + ")";
                }
                else 
                {
                    //successfuly replaced!
                    return msg;
                }
            }       
        }

        public static void Main(string[] args)
        {           
            SetEnglishCulture();
            try {
                // generate any exception ...
                const int notListenningPort = 2121;
                new TcpClient("localhost", notListenningPort);
            }
            catch(Win32Exception ex)//first try to cach win32 Exceptions
            {
                Console.WriteLine("W32 Exception: " + Win32ExceptionInEnglish(ex));
            }
            catch(Exception ex)//this fit to the rest .NET exceptions which affected by CultureInfo
            {
                Console.WriteLine("Exception: " +ex.Message);
            }   
        }
    }
}

Ответ 2

A SocketException является Win32Exception. Как и все другие классы, полученные из Win32Exception, он получает свое сообщение из Windows, используя Win32Exception.GetErrorMessage(int error), который использует FormatMessage в Kernel32.DLL.

При этом сообщение эффективно поступает из Windows, а не из .NET. Windows вернет сообщение на языке отображения Windows и AFAIK, вы ничего не сможете сделать в своей программе .NET.

Ответ 3

Возможно, вам придется обернуть строку, которая выводит ошибку на консоль в отдельном потоке, который имеет локаль, установленный на английском языке, потому что код исключения Framework загружает сообщения об ошибках из своих ресурсов на основе текущего локали потока. вот что я говорю в немного кода:

    static void Main(string[] args) {

    try {
        TcpClient c = new TcpClient("localhost", 1234);
    }
    catch (Exception ex) {
        // thread that logs exception message to console
        Thread logger = new Thread(new ParameterizedThreadStart(PrintException));
        logger.CurrentCulture = new System.Globalization.CultureInfo("en-US");
        logger.Start(ex);
    }


}

private static void PrintException(object ex) {
    Console.WriteLine("Error: " + ((Exception)ex).Message);
}