none
get Default namespace name for Assembly.GetManifestResourceStream() method

    Question

  • Hi, Visual Studio has a way for you to set the assembly name and default namespace name through a project's properties. Assembly.GetManifestResourceStream() method needs the default namespace to get an embedded resources. i.e. DefaultNamespace.EmbeddedResourceFileName

    My question is how would one attain this default namespace to get the resource. I know the method has an overload to accept a type and use that types namespace, but the type could always be altered not to use the Default Namespace. Is there a way of using this method without specifically using a type that is in accordance with Visual Studio's default namespace or explicitly putting it in? Or is this method Visual Studio dependant?
    Wednesday, November 04, 2009 3:40 PM

Answers

  • You could use a custom attribute, defined in a separate common assembly, and applied to each of your assemblies at the assembly level. 

    For example...

    1. Create a console application.
    2. Create a class library.
    3. Reference the class library from the console application.
    4. Add the following code to the class library:

    namespace System
    {
        public class DefaultNamespaceAttribute : Attribute
        {
            private string _defaultNamespace;

            public DefaultNamespaceAttribute(string defaultNamespace)
            {
                _defaultNamespace = defaultNamespace;
            }

            public string DefaultNamespace { get { return _defaultNamespace; } }
        }
    }

    5. Add the following code somewhere in your console application:

    [assembly:DefaultNamespace("SomeValue")]

    6. To fetch this value, once you have the assembly reference, use the following code (assuming the reference to the assembly is called "assm")

    DefaultNamespaceAttribute attr = (DefaultNamespaceAttribute) assm
                                         .GetCustomAttributes(
                                         typeof (DefaultNamespaceAttribute), false)[0];
    Console.WriteLine(attr.DefaultNamespace);


    Coding Light - Illuminated Ideas and Algorithms in Software
    Coding Light WikiLinkedInForumsBrowser
    • Marked as answer by Roahn Luo Friday, November 06, 2009 8:30 AM
    Wednesday, November 04, 2009 5:12 PM
    Moderator

All replies

  • You can't get the default namespace of a compiled assembly... it's not stored there.  The default namespace only exists for Visual Studio to determine the default namespace to use when new classes and other items are created during development. 

    You'll have to use something like Assembly.GetTypes to find the type with the name "Resources", and then check to see if it has a GeneratedCodeAttribute as a custom attribute on the class.  If it does, there's a high likelihood you got the right class.  Beyond that, there's no way to perfectly get this data.
    Coding Light - Illuminated Ideas and Algorithms in Software
    Coding Light WikiLinkedInForumsBrowser
    Wednesday, November 04, 2009 3:44 PM
    Moderator
  • My question is how would one attain this default namespace to get the resource. I know the method has an overload to accept a type and use that types namespace, but the type could always be altered not to use the Default Namespace. Is there a way of using this method without specifically using a type that is in accordance with Visual Studio's default namespace or explicitly putting it in? Or is this method Visual Studio dependant?
    If this is really a reflection issue, one can browse the classes regardless of namespace. If I am not misunderstanding you I provide such an example on my blog entitled: Reflect Interface from Unknown Assembly in C# . HTH
    William Wegerson (www.OmegaCoder.Com)
    Wednesday, November 04, 2009 4:05 PM
    Moderator
  • You can't get the default namespace of a compiled assembly... it's not stored there.  The default namespace only exists for Visual Studio to determine the default namespace to use when new classes and other items are created during development. 

    You'll have to use something like Assembly.GetTypes to find the type with the name "Resources", and then check to see if it has a GeneratedCodeAttribute as a custom attribute on the class.  If it does, there's a high likelihood you got the right class.  Beyond that, there's no way to perfectly get this data.
    Coding Light - Illuminated Ideas and Algorithms in Software
    Coding Light WikiLinkedInForumsBrowser
    That is no guarantee though, I can change the namespace of that as well, so this method is truly visual studio dependent? Is there anyway to assign a namespace the the embedded resource?
    Wednesday, November 04, 2009 5:00 PM
  • You could use a custom attribute, defined in a separate common assembly, and applied to each of your assemblies at the assembly level. 

    For example...

    1. Create a console application.
    2. Create a class library.
    3. Reference the class library from the console application.
    4. Add the following code to the class library:

    namespace System
    {
        public class DefaultNamespaceAttribute : Attribute
        {
            private string _defaultNamespace;

            public DefaultNamespaceAttribute(string defaultNamespace)
            {
                _defaultNamespace = defaultNamespace;
            }

            public string DefaultNamespace { get { return _defaultNamespace; } }
        }
    }

    5. Add the following code somewhere in your console application:

    [assembly:DefaultNamespace("SomeValue")]

    6. To fetch this value, once you have the assembly reference, use the following code (assuming the reference to the assembly is called "assm")

    DefaultNamespaceAttribute attr = (DefaultNamespaceAttribute) assm
                                         .GetCustomAttributes(
                                         typeof (DefaultNamespaceAttribute), false)[0];
    Console.WriteLine(attr.DefaultNamespace);


    Coding Light - Illuminated Ideas and Algorithms in Software
    Coding Light WikiLinkedInForumsBrowser
    • Marked as answer by Roahn Luo Friday, November 06, 2009 8:30 AM
    Wednesday, November 04, 2009 5:12 PM
    Moderator
  • There is another way, and I cannot find any reason that it shouldn't be completely generic.

            /// <summary>
            /// Use the list of Manifest Resource Names returned by method
            /// GetManifestResourceNames on a specified assembly. Each of several
            /// methods employs a different mechanism to identify the assembly of
            /// interest.
            /// </summary>
            /// <param name="pstrResourceName">
            /// Specify the name of the file from which the embedded resource was
            /// created. Typically, this will be the local name of the file in the
            /// source code tree.
            /// </param>
            /// <param name="pasmSource">
            /// Pass a reference to the Assembly that is supposed to contain the
            /// desired resource.
            /// </param>
            /// <returns>
            /// If the function succeeds, the return value is the internal name of
            /// the requested resource, which is fed to GetManifestResourceStream on
            /// the same assembly, which returns a read-only Stream backed by the
            /// embedded resource. If the specified resource is not found, it
            /// returns null (Nothing in Visual Basic).
            /// </returns>
            private static string GetInternalResourceName (
                string pstrResourceName , 
                Assembly pasmSource )
            {
                foreach ( string strManifestResourceName in pasmSource.GetManifestResourceNames ( ) )
                    if ( strManifestResourceName.EndsWith ( pstrResourceName ) )
                        return strManifestResourceName;
    
                return null;
            }   // private static string GetInternalResourceName
    

    I marked my method as Private, because it is intended for use with the following trio of public static methods.

            /// <summary>
            /// Load the lines of a plain ASCII text file that has been stored with
            /// the assembly as a embedded resource into an array of native strings.
            /// </summary>
            /// <param name="pstrResourceName">
            /// Specify the fully qualified resource name, which is its source file
            /// name appended to the default application namespace.
            /// </param>
            /// <returns>
            /// The return value is an array of Unicode strings, each of which is
            /// the text of a line from the original text file, sans terminator.
            /// </returns>
            /// <see cref="LoadTextFileFromAnyAssembly"/>
            /// <seealso cref="LoadTextFileFromEntryAssembly"/>
            public static string [ ] LoadTextFileFromCallingAssembly (
                string pstrResourceName )
            {
                return LoadTextFileFromAnyAssembly (
                    pstrResourceName ,
                    Assembly.GetCallingAssembly ( ) );
            }   // public static string [ ] LoadTextFileFromCallingAssembly
    
    
            /// <summary>
            /// Load the lines of a plain ASCII text file that has been stored with
            /// the assembly as a embedded resource into an array of native strings.
            /// </summary>
            /// <param name="pstrResourceName">
            /// Specify the fully qualified resource name, which is its source file
            /// name appended to the default application namespace.
            /// </param>
            /// <returns>
            /// The return value is an array of Unicode strings, each of which is
            /// the text of a line from the original text file, sans terminator.
            /// </returns>
            /// <see cref="LoadTextFileFromAnyAssembly"/>
            /// <seealso cref="LoadTextFileFromCallingAssembly"/>
            public static string [ ] LoadTextFileFromEntryAssembly (
                string pstrResourceName )
            {
                return LoadTextFileFromAnyAssembly (
                    pstrResourceName ,
                    Assembly.GetEntryAssembly ( ) );
            }   // public static string [ ] LoadTextFileFromEntryAssembly
    
    
            /// <summary>
            /// <param name="pstrResourceName">
            /// Specify the fully qualified resource name, which is its source file
            /// name appended to the default application namespace.
            /// </param>
            /// </summary>
            /// <param name="pstrResourceName">
            /// Specify the fully qualified resource name, which is its source file
            /// name appended to the default application namespace.
            /// </param>
            /// <param name="pasmSource">
            /// Pass in a reference to the Assembly from which you expect to load
            /// the text file. Use any means at your disposal to obtain a reference
            /// from the System.Reflection namespace.
            /// </param>
            /// <returns></returns>
            /// <seealso cref="LoadTextFileFromCallingAssembly"/>
            /// <seealso cref="LoadTextFileFromEntryAssembly"/>
            private static string [ ] LoadTextFileFromAnyAssembly (
                string pstrResourceName ,
                Assembly pasmSource )
            {
                string strInternalName = GetInternalResourceName (
                    pstrResourceName ,
                    pasmSource );
    
                if ( strInternalName == null )
                    throw new Exception (
                        string.Format (
                            Properties.Resources.ERRMSG_EMBEDDED_RESOURCE_NOT_FOUND ,
                            pstrResourceName ,
                            pasmSource.FullName ) );
    
                Stream stroTheFile = pasmSource.GetManifestResourceStream ( strInternalName );
    
                //  ----------------------------------------------------------------
                //  The character count is used several times, always as an integer.
                //  Cast it once, and keep it, since implicit casts create new local
                //  variables.
                //
                //  The integer is immediately put to use, to allocate a byte array,
                //  which must have room for every character in the input file.
                //  ----------------------------------------------------------------
    
                int intTotalBytesAsInt = ( int ) stroTheFile.Length;
                byte [ ] abytWholeFile = new Byte [ intTotalBytesAsInt ];
                int intBytesRead = stroTheFile.Read (
                    abytWholeFile ,                         // Buffer sufficient to hold it.
                    BEGINNING_OF_BUFFER ,                   // Read from the beginning of the file.
                    intTotalBytesAsInt );                   // Swallow the file whole.
    
                //  ----------------------------------------------------------------
                //  Though its backing store is a resource embedded in the assembly,
                //  it must be treated like any other stream. Investigating in the
                //  Visual Studio Debugger showed me that it is implemented as an
                //  UnmanagedMemoryStream. That "unmanaged" prefix is a clarion call
                //  that the stream must be cloaed, disposed, and destoryed.
                //  ----------------------------------------------------------------
    
                stroTheFile.Close ( );
                stroTheFile.Dispose ( );
                stroTheFile = null;
    
                //  ----------------------------------------------------------------
                //  In the unlikely event that the byte count is short (or long),
                //  the program must croak. Since the three items that we want to
                //  include in the report are stored in local variables, including
                //  the reported file length, we can go ahead and close the stream
                //  before the count of bytes read is evaluated. HOWEVER, you must
                //  USE them, or you get a null reference exception that masks the
                //  real error.
                //  ----------------------------------------------------------------
    
                if ( intBytesRead != intTotalBytesAsInt )
                    throw new InvalidDataException (
                        string.Format (
                            Properties.Resources.ERRMSG_EMBEDDED_RESOURCE_READ_ERROR ,
                            new object [ ]
                            {
                                strInternalName ,
                                intTotalBytesAsInt ,
                                intBytesRead ,
                                Environment.NewLine
                            } ) );
    
                //  ----------------------------------------------------------------
                //  The file is stored in single-byte ASCII characters. The native 
                //  character set of the Common Language Runtime is Unicode. A new
                //  array of Unicode characters serves as a translation buffer which
                //  is filled a character at a time from the byte array.
                //  ----------------------------------------------------------------
    
                char [ ] achrWholeFile = new char [ intTotalBytesAsInt ];
    
                for ( int intCurrentByte = BEGINNING_OF_BUFFER ;
                          intCurrentByte < intTotalBytesAsInt ;
                          intCurrentByte++ )
                    achrWholeFile [ intCurrentByte ] = ( char ) abytWholeFile [ intCurrentByte ];
    
                //  ----------------------------------------------------------------
                //  The character array converts to a Unicode string in one fell
                //  swoop. Since the new string vanishes when StringOfLinesToArray
                //  returns, the constructor call is nested in StringOfLinesToArray,
                //  which splits the lines of text, with their DOS line termiators,
                //  into the required array of strings.
                //  ----------------------------------------------------------------
    
                return WizardWrx.TextBlocks.StringOfLinesToArray ( new string ( achrWholeFile ) );
            }   // private static string [ ] LoadTextFileFromAnyAssembly
            #endregion  // Public Methods
    

    The last statement calls a library routine that I wrote six years ago, which follows.

            /// <summary>
            /// Split a string containing lines of text into an array of strings.
            /// </summary>
            /// <param name="pstrLines">
            /// String containing lines of text, terminated by CR/LF pairs.
            /// </param>
            /// <returns>
            /// Array of strings, one line per string. Blank lines are preserved as
            /// empty strings.
            /// </returns>
            public static string[] StringOfLinesToArray (string pstrLines)
            {
                if (pstrLines==null)
                    return new string []{};     // Return an empty array.
    
                if (pstrLines.Length == 0)
                    return new string []{};     // Return an empty array.
    
                return pstrLines.Split(
                    new string[]{ Environment.NewLine } ,
                    StringSplitOptions.None);
            }   // static method StringOfLinesToArray
    
    It looks like a lot, but the vast majority of the code in this message is the XML documentation, which I decided to leave.


    David A. Gray Irving, Texas, USA http://www.wizardwrx.com/

    Thursday, July 31, 2014 11:07 PM