Beim Ableiten einer Klasse oder beim Implementieren eines Interface schaue ich häufig mit einem Disassembler (z.B. .NET-Reflector)  nach, wie Andere bestimmte Dinge in ähnlichen Situationen gelöst haben. Aktuell versuche ich die Klasse SerialStream mit kleinen Abwandlungen zu implementieren. Eines der Probleme ist die Ausgabe von Texten, z.B. bei Fehlermeldungen (Exceptions). Die .NET-Bibliotheken greifen bei der Ausgabe von Texten auf  lokalisierte Ressourcen zurück. Wenn man in eigenen Programmen diese Texte nicht alle neu erfinden will, wäre es hilfreich die bestehenden auslesen zu können.

Leider gibt es keine öffentliche Methode um diese Texte zu ermitteln. .NET benutzt hierzu die Methode GetString der privaten Klasse SR.

Throw New ArgumentNullException("Buffer", SR.GetString("ArgumentNull_Array"))

Jede Bibliothek besitzt eine solche Klasse, die den Zugriff auf die zur Bibliothek gehörenden Text-Ressourcen ermöglicht.

Jede Bibliothek besitzt eine Klasse SR

Zum Zugriff auf die Texte werden drei Objekte benötigt. Zunächst ist dies das Assembly, indem die Text-Ressource enthalten ist. Dann die Bezeichnung der Ressource und den Namen des Textes. Folgender Dump zeigt den Aufbau:

Ressourcen-Hierarchie

Die im folgenden beschriebene Klasse ermöglicht das Auslesen der Texte.

Klasse SystemMsg

SystemMsg stellt die Methode GetString in mehren Varianten bereit:

Variante Beschreibung
GetString(ByVal Name As String) In allen geladenen Assemblies wird nach dem angegebenen Literal gesucht. Wird es nicht gefunden, wird eine Exception geworfen.
GetString(ByVal Name As String, DefaultValue As String) In allen geladenen Assemblies wird nach dem angegebenen Literal gesucht. Wird es nicht gefunden, wird der angegebene Standard-Text zurückgeliefert.
 GetString(Assembly As Assembly, ByVal Name As String) As String Im angegebenen Assembly wird nach dem angegebenen Literal gesucht. Wird es nicht gefunden, wird eine Exception geworfen.
GetString(Assembly As Assembly, ByVal Name As String, DefaultValue As String) Im angegebenen Assembly wird nach dem angegebenen Literal gesucht. Wird es nicht gefunden, wird der angegebene Standard-Text zurückgeliefert.

Die Implementierung ist zugegebenermaßen nicht besonders ressourcenschonend und performant. Da aber in der Regel nur gelegentliche Aufrufe stattfinden, sollte dies kein Problem sein.

Imports System.Resources
Imports System.Reflection

''' <summary>
''' SystemResources stellt eine Methode bereit, mit der auf die lokalisierten Fehlertexte von .NET zugegriffen werden kann.
''' </summary>
Public Class SystemMsg
   ''' <summary>
   ''' Liefert die lokalisierte Meldung mit der angegebenen Bezeichnung.
   ''' </summary>
   ''' <param name="Name">Name des Strings.</param>
   ''' <returns>Die lokalisierte Meldung.</returns>
   ''' <remarks>Es wird in allen geladenen Assemblies gesucht. Wird der Text nicht gefunden, wird eine Exception geworfen.</remarks>
   ''' <exception cref="ArgumentException">Das angegebene Literal wurde nicht gefunden.</exception>
   Public Shared Function GetString(ByVal Name As String) As String
      For Each mAssembly In AppDomain.CurrentDomain.GetAssemblies()
         Try
            'Einrichten eines Ressourcen-Managers
            Dim mResources = New ResourceManager(mAssembly.GetName().Name, mAssembly) ' Der Ressourcen-Name ist "System"
            Dim txt = mResources.GetString(Name, Nothing)
            If txt <> "" Then
               Return txt
            End If
         Catch
         End Try
      Next

      Throw New ArgumentException(GetString("Arg_EnumLitValueNotFound", "Literal value was not found."), "Name")
   End Function
   ''' <summary>
   ''' Liefert die lokalisierte Meldung mit der angegebenen Bezeichnung.
   ''' </summary>
   ''' <param name="Name">Name des Strings.</param>
   ''' <param name="DefaultValue">Wenn das angegeben Literal nicht gefunden wurde, wird statt dessen dieser Wert ausgeben.</param>
   ''' <returns>Die lokalisierte Meldung.</returns>
   ''' <remarks>Es wird in allen geladenen Assemblies gesucht.</remarks>
   Public Shared Function GetString(ByVal Name As String, DefaultValue As String) As String
      For Each mAssembly In AppDomain.CurrentDomain.GetAssemblies()
         Try
            'Einrichten eines Ressourcen-Managers
            Dim mResources = New ResourceManager(mAssembly.GetName().Name, mAssembly) ' Der Ressourcen-Name ist "System"
            Dim txt = mResources.GetString(Name, Nothing)
            If txt <> "" Then
               Return txt
            End If
         Catch
         End Try
      Next

      Return DefaultValue
   End Function

   ''' <summary>
   ''' Liefert die lokalisierte Meldung mit der angegebenen Bezeichnung.
   ''' </summary>
   ''' <param name="Assembly">Assembly, in dessen Ressourcen gesucht werden soll.</param>
   ''' <param name="Name">Name des Strings.</param>
   ''' <returns>Die lokalisierte Meldung.</returns>
   ''' <remarks>Es wird in allen geladenen Assemblies gesucht. Wird der Text nicht gefunden, wird eine Exception geworfen.</remarks>
   ''' <exception cref="ArgumentException">Das angegebene Literal wurde nicht gefunden.</exception>
   Public Shared Function GetString(Assembly As Assembly, ByVal Name As String) As String
      Try
         'Einrichten eines Ressourcen-Managers
         Dim mResources = New ResourceManager(Assembly.GetName().Name, Assembly) ' Der Ressourcen-Name ist "System"
         Dim txt = mResources.GetString(Name, Nothing)
         If txt <> "" Then
            Return txt
         End If
      Catch
      End Try

      Throw New ArgumentException(GetString("Arg_EnumLitValueNotFound", "Literal value was not found."), "Name")
   End Function
   ''' <summary>
   ''' Liefert die lokalisierte Meldung mit der angegebenen Bezeichnung.
   ''' </summary>
   ''' <param name="Assembly">Assembly, in dessen Ressourcen gesucht werden soll.</param>
   ''' <param name="Name">Name des Strings.</param>
   ''' <param name="DefaultValue">Wenn das angegeben Literal nicht gefunden wurde, wird statt dessen dieser Wert ausgeben.</param>
   ''' <returns>Die lokalisierte Meldung.</returns>
   Public Shared Function GetString(Assembly As Assembly, ByVal Name As String, DefaultValue As String) As String
      Try
         'Einrichten eines Ressourcen-Managers
         Dim mResources = New ResourceManager(Assembly.GetName().Name, Assembly) ' Der Ressourcen-Name ist "System"
         Dim txt = mResources.GetString(Name, Nothing)
         If txt <> "" Then
            Return txt
         End If
      Catch
      End Try

      Return DefaultValue
   End Function
End Class

Wer sich nicht darauf verlassen möchte, dass die Texte bei folgenden Version immer noch funktionieren, kann mit diesem kleinen Tool die aktuell hinterlegten Texte auslesen:

Tool zum Auslesen von systeminternen Texten

Download Binary
Download Source