¿Qué es y cómo solucionar el error System.TypeInitializationException?

private static void Main(string[] args)
{
    string str = null;
    Logger.InitUserLogWithRotation();            // <--- error occur
    ...
}

Cuando construyo el proyecto, no tiene ningún error. Pero cuando lo ejecuto, siempre abortado.

He intentado depurar el proyecto, pero System.TypeInitializationException error se produjo en la primera línea.

Ya he intentado buscar en Google, pero no he encontrado ninguna solución.

Parece que cualquier código de inicialización de variables es incorrecto, pero no puedo encontrarlo.

Por favor, ayúdame. Soy nuevo en C#.

Gracias.

※ Aquí está el código de la clase Logger

public class Logger
{
    private static int HDLOG_PRIORITY_DEBUG = 4;
    private static int HDLOG_PRIORITY_ERROR = 1;
    private static int HDLOG_PRIORITY_FATAL = 0;
    private static int HDLOG_PRIORITY_INFO = 3;
    private static int HDLOG_PRIORITY_WARNING = 2;
    public static int LOG_LEVEL_DEBUG = 4;
    public static int LOG_LEVEL_ERROR = 2;
    public static int LOG_LEVEL_FATAL = 1;
    public static int LOG_LEVEL_INFO = 5;
    public static int LOG_LEVEL_WARNING = 3;
    private static string s_bstCommonAppData = Path.Combine(s_commonAppData, "XXXX");
    private static string s_bstUserDataDir = Path.Combine(s_bstCommonAppData, "UserData");
    private static string s_commonAppData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
    private static bool s_consoleLogging = false;
    private static FileStream s_fileStream;
    public static HdLoggerCallback s_HdLoggerCallback;
    private static string s_logDir = null;
    private static string s_logFileName = "XXXX";
    private static string s_logFilePath = null;
    public static int s_logFileSize = 0xa00000;
    private static bool s_loggerInited = false;
    private static string s_logLevels = null;
    private static int s_logRotationTime = 0x7530;
    private static string s_logStringDebug = "DEBUG";
    private static string s_logStringError = "ERROR";
    private static string s_logStringFatal = "FATAL";
    private static string s_logStringInfo = "INFO";
    private static string s_logStringWarning = "WARNING";
    private static int s_processId = -1;
    private static string s_processName = "Unknown";
    private static object s_sync = new object();
    public static int s_totalLogFileNum = 5;
    private static TextWriter writer = Console.Error;

    private static void Close()
    {
        if (!s_consoleLogging)
        {
            writer.Close();
            s_fileStream.Dispose();
            writer.Dispose();
        }
    }

    public static void Debug(string msg)
    {
        Debug("{0}", new object[] { msg });
    }

    public static void Debug(string fmt, params object[] args)
    {
        Print(LOG_LEVEL_DEBUG, s_processName, fmt, args);
    }

    private static void DoLogRotation()
    {
    Label_0000:
        Thread.Sleep(s_logRotationTime);
        try
        {
            lock (s_sync)
            {
                FileInfo info = new FileInfo(s_logFilePath);
                if (info.Length >= s_logFileSize)
                {
                    string destFileName = s_logFilePath + ".1";
                    string path = s_logFilePath + "." + s_totalLogFileNum;
                    if (File.Exists(path))
                    {
                        File.Delete(path);
                    }
                    for (int i = s_totalLogFileNum - 1; i >= 1; i--)
                    {
                        string str3 = s_logFilePath + "." + i;
                        string str4 = s_logFilePath + "." + (i + 1);
                        if (File.Exists(str3))
                        {
                            File.Move(str3, str4);
                        }
                    }
                    File.Move(s_logFilePath, destFileName);
                }
            }
            goto Label_0000;
        }
        catch (Exception)
        {
            goto Label_0000;
        }
    }

    public static void Error(string msg)
    {
        Error("{0}", new object[] { msg });
    }

    public static void Error(string fmt, params object[] args)
    {
        Print(LOG_LEVEL_ERROR, s_processName, fmt, args);
    }

    public static void Fatal(string msg)
    {
        Fatal("{0}", new object[] { msg });
    }

    public static void Fatal(string fmt, params object[] args)
    {
        Print(LOG_LEVEL_FATAL, s_processName, fmt, args);
    }

    private static string GetLogDir(bool userSpecificLog)
    {
        string str;
        if (s_logDir != null)
        {
            return s_logDir;
        }
        try
        {
            if (userSpecificLog)
            {
                str = Path.Combine(s_bstUserDataDir, "Logs");
            }
            else
            {
                str = (string) Registry.LocalMachine.OpenSubKey(@"Software\XXXX").GetValue("LogDir");
            }
        }
        catch (Exception)
        {
            str = Path.Combine(s_bstUserDataDir, "Logs");
        }
        s_logDir = str;
        return str;
    }

    private static string GetPrefix(string tag, string logLevel)
    {
        int managedThreadId = Thread.CurrentThread.ManagedThreadId;
        DateTime now = DateTime.Now;
        return string.Format("{0:D4}-{1:D2}-{2:D2} {3:D2}:{4:D2}:{5:D2}.{6:D3} {7}:{8:X8} ({9}). {10}: ", new object[] { now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, now.Millisecond, s_processId, managedThreadId, tag, logLevel });
    }

    public static TextWriter GetWriter()
    {
        return new Writer(delegate (string msg) {
            Print(msg);
        });
    }

    private static void HdLogger(int prio, uint tid, string tag, string msg)
    {
        int level = 0;
        if (prio == HDLOG_PRIORITY_FATAL)
        {
            level = LOG_LEVEL_FATAL;
        }
        else if (prio == HDLOG_PRIORITY_ERROR)
        {
            level = LOG_LEVEL_ERROR;
        }
        else if (prio == HDLOG_PRIORITY_WARNING)
        {
            level = LOG_LEVEL_WARNING;
        }
        else if (prio == HDLOG_PRIORITY_INFO)
        {
            level = LOG_LEVEL_INFO;
        }
        else if (prio == HDLOG_PRIORITY_DEBUG)
        {
            level = LOG_LEVEL_DEBUG;
        }
        Print(level, tag, "{0:X8}: {1}", new object[] { tid, msg });
    }

    public static void Info(string msg)
    {
        Info("{0}", new object[] { msg });
    }

    public static void Info(string fmt, params object[] args)
    {
        Print(LOG_LEVEL_INFO, s_processName, fmt, args);
    }

    public static void InitConsoleLog()
    {
        InitLog("-", true, false);
    }

    public static void InitLog(string logFileName, bool userSpecificLog, bool doLogRotation)
    {
        s_loggerInited = true;
        s_HdLoggerCallback = new HdLoggerCallback(Logger.HdLogger);
        s_processId = Process.GetCurrentProcess().Id;
        s_processName = Process.GetCurrentProcess().ProcessName;
        if (logFileName == "-")
        {
            writer = Console.Error;
            s_consoleLogging = true;
        }
        else
        {
            if (logFileName == null)
            {
                logFileName = s_logFileName;
            }
            if (userSpecificLog)
            {
                logFileName = logFileName + "Users";
            }
            string logDir = GetLogDir(userSpecificLog);
            string str2 = string.Format(@"{0}\{1}.log", logDir, logFileName);
            if (!Directory.Exists(logDir))
            {
                Directory.CreateDirectory(logDir);
            }
            s_logFilePath = str2;
            LogLevelsInit();
            lock (s_sync)
            {
                Open();
            }
            if (doLogRotation)
            {
                new Thread(() => DoLogRotation()) { IsBackground = true }.Start();
            }
        }
    }

    public static void InitSystemLog()
    {
        InitLog(null, false, false);
    }

    public static void InitSystemLogWithRotation()
    {
        InitLog(null, false, true);
    }

    public static void InitUserLog()
    {
        InitLog(null, true, false);
    }

    public static void InitUserLogWithRotation()
    {
        InitLog(null, true, true);
    }

    private static bool IsLogLevelEnabled(string tag, string level)
    {
        if (s_logLevels == null)
        {
            return false;
        }
        return (s_logLevels.StartsWith("ALL") || s_logLevels.Contains((tag + ":" + level).ToUpper()));
    }

    private static void LogLevelsInit()
    {
        string name = @"Software\XXXX\Config";
        try
        {
            using (RegistryKey key = Registry.LocalMachine.OpenSubKey(name))
            {
                s_logLevels = (string) key.GetValue("DebugLogs");
            }
        }
        catch (Exception)
        {
            return;
        }
        if (s_logLevels != null)
        {
            s_logLevels = s_logLevels.ToUpper();
        }
    }

    private static void Open()
    {
        if (!s_consoleLogging)
        {
            if (!s_loggerInited)
            {
                InitLog("-", false, false);
                s_loggerInited = true;
            }
            else
            {
                s_fileStream = new FileStream(s_logFilePath, FileMode.Append, FileAccess.Write, FileShare.Delete | FileShare.ReadWrite);
                writer = new StreamWriter(s_fileStream, Encoding.UTF8);
            }
        }
    }

    public static void Print(string msg)
    {
        Print("{0}", new object[] { msg });
    }

    public static void Print(string fmt, params object[] args)
    {
        Print(LOG_LEVEL_INFO, s_processName, fmt, args);
    }

    public static void Print(int level, string tag, string fmt, params object[] args)
    {
        string str = "UNKNOWN";
        if (level == LOG_LEVEL_FATAL)
        {
            str = s_logStringFatal;
        }
        else if (level == LOG_LEVEL_ERROR)
        {
            str = s_logStringError;
        }
        else if (level == LOG_LEVEL_WARNING)
        {
            str = s_logStringWarning;
        }
        else if (level == LOG_LEVEL_INFO)
        {
            str = s_logStringInfo;
        }
        else if (level == LOG_LEVEL_DEBUG)
        {
            str = s_logStringDebug;
        }
        if ((level != LOG_LEVEL_DEBUG) || IsLogLevelEnabled(tag, str))
        {
            lock (s_sync)
            {
                Open();
                writer.WriteLine(GetPrefix(tag, str) + fmt, args);
                writer.Flush();
                Close();
            }
        }
    }

    public static void SetLogDir(string logDir)
    {
        s_logDir = logDir;
    }

    public static void Warning(string msg)
    {
        Warning("{0}", new object[] { msg });
    }

    public static void Warning(string fmt, params object[] args)
    {
        Print(LOG_LEVEL_WARNING, s_processName, fmt, args);
    }

    public delegate void HdLoggerCallback(int prio, uint tid, string tag, string msg);

    public class Writer : TextWriter
    {
        private WriteFunc writeFunc;

        public Writer(WriteFunc writeFunc)
        {
            this.writeFunc = writeFunc;
        }

        public override void WriteLine(string msg)
        {
            this.writeFunc(msg);
        }

        public override void WriteLine(string fmt, object obj)
        {
            this.writeFunc(string.Format(fmt, obj));
        }

        public override void WriteLine(string fmt, object[] objs)
        {
            this.writeFunc(string.Format(fmt, objs));
        }

        public override System.Text.Encoding Encoding
        {
            get
            {
                return System.Text.Encoding.UTF8;
            }
        }

        public delegate void WriteFunc(string msg);
    }
}
Solución

Siempre que se lance una TypeInitializationException, comprueba toda la lógica de inicialización del tipo al que te refieres por primera vez en la sentencia donde se lanza la excepción - en tu caso: Logger.

La lógica de inicialización incluye: el constructor estático del tipo (que - si no me lo he perdido - no tienes para Logger) y la inicialización de campos.

La inicialización de campos es bastante "acrítica" en Logger excepto por las siguientes líneas:

private static string s_bstCommonAppData = Path.Combine(s_commonAppData, "XXXX");
private static string s_bstUserDataDir = Path.Combine(s_bstCommonAppData, "UserData");
private static string s_commonAppData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);

s_commonAppDataesnullen el punto dondePath.Combine(s_commonAppData, "XXXX");es llamado. En lo que a mi respecta, estas inicializaciones ocurren en el **exacto** orden en que las escribiste - así que adelantas_commonAppData` al menos dos líneas ;)

Comentarios (0)

La System.TypeInitializationException ocurre cuando el código que se ejecuta durante el proceso de carga del tipo lanza una excepción.

Cuando .NET carga el tipo, debe preparar todos sus campos estáticos antes de la primera vez que se utiliza el tipo. A veces, la inicialización requiere la ejecución de código. Cuando ese código falla se produce una System.TypeInitializationException.

En tu caso concreto, los tres campos estáticos siguientes ejecutan código:

private static string s_bstCommonAppData = Path.Combine(s_commonAppData, "XXXX");
private static string s_bstUserDataDir = Path.Combine(s_bstCommonAppData, "UserData");
private static string s_commonAppData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);

Ten en cuenta que s_bstCommonAppData depende de s_commonAppData, pero se declara antes que su dependencia. Por lo tanto, el valor de s_commonAppData es null en el momento en que se llama a Path.Combine, dando lugar a una ArgumentNullException. Lo mismo ocurre con s_bstUserDataDir y s_bstCommonAppData: se declaran en orden inverso al orden de inicialización deseado.

Reordena las líneas para solucionar este problema:

private static string s_commonAppData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
private static string s_bstCommonAppData = Path.Combine(s_commonAppData, "XXXX");
private static string s_bstUserDataDir = Path.Combine(s_bstCommonAppData, "UserData");
Comentarios (0)

Estas líneas son su problema (o al menos uno de sus problemas, si hay más):

private static string s_bstCommonAppData = Path.Combine(s_commonAppData, "XXXX");
private static string s_bstUserDataDir = Path.Combine(s_bstCommonAppData, "UserData");
private static string s_commonAppData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);

Usted hace referencia a algunos miembros estáticos en los inicializadores de otros miembros estáticos. Esto es una mala idea, ya que el compilador no sabe en qué orden inicializarlos. El resultado es que durante la inicialización de s_bstCommonAppData, el campo dependiente s_commonAppData aún no ha sido inicializado, por lo que estás llamando a Path.Combine(null, "XXXX") y este método no acepta argumentos null.

Puedes solucionar esto asegurándote de que los campos utilizados en la inicialización de otros campos se declaran primero:

private static string s_commonAppData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
private static string s_bstCommonAppData = Path.Combine(s_commonAppData, "XXXX");
private static string s_bstUserDataDir = Path.Combine(s_bstCommonAppData, "UserData");

O utilizar un constructor estático para ordenar explícitamente las asignaciones:

private static string s_bstCommonAppData;
private static string s_bstUserDataDir;
private static string s_commonAppData;

static Logger()
{
    s_commonAppData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
    s_bstCommonAppData = Path.Combine(s_commonAppData, "XXXX");
    s_bstUserDataDir = Path.Combine(s_bstCommonAppData, "UserData");
}
Comentarios (1)