I am storing a PNG as an embedded resource in an assembly. From within the same assembly I have some code like this:
Bitmap image = new Bitmap(typeof(MyClass), "Resources.file.png");
The file, named "file.png" is stored in the "Resources" folder (within Visual Studio), and is marked as an embedded resource.
The code fails with an exception saying:
Resource MyNamespace.Resources.file.png cannot be found in class MyNamespace.MyClass
I have identical code (in a different assembly, loading a different resource) which works. So I know the technique is sound. My problem is I end up spending a lot of time trying to figure out what the correct path is. If I could simply query (eg. in the debugger) the assembly to find the correct path, that would save me a load of headaches.
This will get you a string array of all the resources:
System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames();
I find myself forgetting how to do this every time as well so I just wrap the two one-liners that I need in a little class:
public class Utility
{
/// <summary>
/// Takes the full name of a resource and loads it in to a stream.
/// </summary>
/// <param name="resourceName">Assuming an embedded resource is a file
/// called info.png and is located in a folder called Resources, it
/// will be compiled in to the assembly with this fully qualified
/// name: Full.Assembly.Name.Resources.info.png. That is the string
/// that you should pass to this method.</param>
/// <returns></returns>
public static Stream GetEmbeddedResourceStream(string resourceName)
{
return Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
}
/// <summary>
/// Get the list of all emdedded resources in the assembly.
/// </summary>
/// <returns>An array of fully qualified resource names</returns>
public static string[] GetEmbeddedResourceNames()
{
return Assembly.GetExecutingAssembly().GetManifestResourceNames();
}
}
I'm guessing that your class is in a different namespace. The canonical way to solve this would be to use the resources class and a strongly typed resource:
ProjectNamespace.Properties.Resources.file
Use the IDE's resource manager to add resources.
I use the following method to grab embedded resources:
protected static Stream GetResourceStream(string resourcePath)
{
Assembly assembly = Assembly.GetExecutingAssembly();
List<string> resourceNames = new List<string>(assembly.GetManifestResourceNames());
resourcePath = resourcePath.Replace(#"/", ".");
resourcePath = resourceNames.FirstOrDefault(r => r.Contains(resourcePath));
if (resourcePath == null)
throw new FileNotFoundException("Resource not found");
return assembly.GetManifestResourceStream(resourcePath);
}
I then call this with the path in the project:
GetResourceStream(#"DirectoryPathInLibrary/Filename");
The name of the resource is the name space plus the "pseudo" name space of the path to the file. The "pseudo" name space is made by the sub folder structure using \ (backslashes) instead of . (dots).
public static Stream GetResourceFileStream(String nameSpace, String filePath)
{
String pseduoName = filePath.Replace('\\', '.');
Assembly assembly = Assembly.GetExecutingAssembly();
return assembly.GetManifestResourceStream(nameSpace + "." + pseduoName);
}
The following call:
GetResourceFileStream("my.namespace", "resources\\xml\\my.xml")
will return the stream of my.xml located in the folder-structure resources\xml in the name space: my.namespace.
Related
I wish to replace a TextBox, with a webbrowser to display messages in chat app
So I can add hyperlinks to videos, sites and pics, emoticons ...
But When I run the app, it cannot find the myfile.html which is in Debug folder ...
It does exists and loadable in firefox
List<string> lines = new List<string>();
lines.Add("<html>");
lines.Add("<body><table width=100%><br><br>Chat:");
lines.Add("<tr><font size=6><a href=music\\" + artist + ">
<img src=music\\" + artist + "\\info\\default.jpg alt=" + art + " height=200 width=200 />
</a>"+" " + path2 +"</font><br><br></tr>");
lines.Add("</table></body>");
lines.Add("</html>");
File.WriteAllLines("myfile.html", lines);
wb_Messages.Navigate("myfile.html");
I have tried
wb_Messages.Navigate("file:///myfile.html");
wb_Messages.Navigate(Application.StartupPath + #"myfile.html");
No Joy ...
Any Help Thanks ...
i can't use comments for this but could you try to use the following helpers i dug up from my project(s):
/// <summary>
/// Helpers for working with <see cref="System.IO.Path"/> or working with paths in general.
/// </summary>
public static class PathHelpers
{
/// <summary>
/// The default output directory, based on the on the currently executing <see cref="System.Reflection.Assembly"/>
/// </summary>
public static string CurrentOutPutDirectory => Path.GetDirectoryName(AssemblyHelper.ExecutableAssembly.GetName().CodeBase)?.Substring(6);
public static string CurrentOutPutDirectoryRoot => Path.GetPathRoot(CurrentOutPutDirectory);
/// <summary>
/// Checks if a string could be used in a path
/// <see href="https://stackoverflow.com/questions/2435894/net-how-do-i-check-for-illegal-characters-in-a-path#comment30899588_2435894"/>
/// </summary>
/// <param name="path">Path to check</param>
/// <returns></returns>
public static bool FilePathHasInvalidChars(string path)
{
return (!string.IsNullOrEmpty(path) && path.IndexOfAny(System.IO.Path.GetInvalidPathChars()) >= 0 && path.IndexOfAny(System.IO.Path.GetInvalidFileNameChars()) > 0);
}
}
/// <summary>
/// Helpers for dealing with <see cref="System.Reflection.Assembly"/>
/// </summary>
public static class AssemblyHelper
{
/// <summary>
/// The assembly that holds the executable. For this, <see cref="Assembly.GetEntryAssembly()"/> is used.
/// This looks at the current <see cref="AppDomain"/>.
/// <warn>A fallback value is used to get the executing assembly!</warn>
/// </summary>
public static Assembly ExecutableAssembly => Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly();
}
Then use var alltext = File.ReadAllText(Path.Combine(PathHelpers.CurrentOutPutDirectory, "YourFileIntheDebugFolder.txt)) (pseudo code) to check if it works and loads the html directly.
EDIT to display a html file in the webbrowser, use this top answer, and pass the alltext string.
https://stackoverflow.com/a/20351048/4122889
I think it's because you use only file name instead of full path.
File.WriteAllLines("myfile.html", lines);
In this method you didn't specify full path, so it will try to save file in some default location. What location? No need to think about it now.
wb_Messages.Navigate("myfile.html");
This method tries to read data from file, but only file name is specified. It doesn't know where to find this file, so it will look in some default location also.
But default locations for both methods can be different. I don't know how it works internally, but maybe you will get some info in Microsoft documentation.
Simple solution:
// now filePath becomes "C:\something...\myfile.html"
string filePath = Path.GetFullPath("myfile.html");
// save data using full path
File.WriteAllLines(filePath, lines);
// navigate to file using full path
wb_Messages.Navigate(filePath);
I am trying to debug code from https://nunrar.codeplex.com/. In branch DOCUMENTATION -> Basic Usage there is the following code:
RarArchive archive = RarArchive.Open(source);
foreach (RarArchiveEntry entry in archive.Entries)
{
string path = Path.Combine(destination, Path.GetFileName(entry.FilePath));
entry.ExtractToFile(path);
}
Well.. I 've downloaded source code, find RarArchiveEntry class and started to looking for method ExtractToFile(), but there isn't any method there neither in class, nor in project!
Please help me to find this method or method which i can call to look through how this programm can unpack rar files step-by-step?
It looks like the documentation is obsolete and the name of the method was changed, there is an extension method found in RarArchiveEntry.Exensions.cs called WriteToFile.
/// <summary>
/// Extract to specific file
/// </summary>
public static void WriteToFile(this RarArchiveEntry entry, string destinationFileName, ExtractOptions options = ExtractOptions.Overwrite)
{
entry.WriteToFile(destinationFileName, new NullRarExtractionListener(), options);
}
Giving this one more try
I have an Enums.cs file where I have the following code
public enum AuthorizingLevels
{
[Display(Name = "AuthorizingLevels_SysSupport", ResourceType = typeof(Resources.Enums))]
SysSupport
}
And when I try calling it to display the Name, it doesn't work
ViewBag.AuthGroupFullName = Enums.AuthorizingLevels.SysSupport.ToString();
It just displays the SysSupport instead of the full name Systems Support
I went to a link provided in my previous question (How to get the Display Name Attribute of an Enum member via MVC razor code?) and added the code by Peter Kerr
/// <summary>
/// A generic extension method that aids in reflecting
/// and retrieving any attribute that is applied to an `Enum`.
/// </summary>
public static string GetDisplayName(this Enum enumValue)
{
var displayAttrib = enumValue.GetType()
.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>();
var name = displayAttrib.Name;
var resource = displayAttrib.ResourceType;
return String.IsNullOrEmpty(name) ? enumValue.ToString()
: resource == null ? name
: new ResourceManager(resource).GetString(name);
}
However, I get an error on the line
: new ResourceManager(resource).GetString(name);
An exception of type 'System.Resources.MissingManifestResourceException' occurred in mscorlib.dll but was not handled in user code
Additional information: Could not find any resources appropriate for the specified culture or the neutral culture. Make sure "Resources.Enums.resources" was correctly embedded or linked into assembly "FWS" at compile time, or that all the satellite assemblies required are loadable and fully signed.
The resource file is the right one minus the .resources at the end... not sure if that should be there or not.
What am I doing wrong? I'm learning MVC and c# as I go so any help would be greatly appreciated.
Thank you
Try this. It is fairly hacky but it should hopefully give you what you need. I use Assembly.GetExecutingAssembly().GetManifestResourceNames() to get the names of all the resources in the executing assembly and then I attempt to match the file up correctly using some linq. If it finds a file that looks ok, a new ResourceManager is created as you were using in your original code.
/// <summary>
/// A generic extension method that aids in reflecting
/// and retrieving any attribute that is applied to an `Enum`.
/// </summary>
public static string GetDisplayName(this Enum enumValue)
{
var displayAttrib = enumValue.GetType()
.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>();
var name = displayAttrib.Name;
if (String.IsNullOrEmpty(name))
{
return enumValue.ToString();
}
else
{
var resource = displayAttrib.ResourceType;
if (resource != null)
{
var resources = Assembly.GetExecutingAssembly().GetManifestResourceNames()
.Where(x => x.EndsWith(String.Format("{0}.resources", resource.Name)))
.Select(x => x.Replace(".resources", string.Empty)).ToList();
if (resources.Any())
{
return new ResourceManager(resources.First(), Assembly.GetExecutingAssembly()).GetString(name);
}
}
return name;
}
}
I'm looking for a way to programmatically get the summary portion of Xml-comments of a method in ASP.net.
I have looked at the previous related posts and they do not supply a way of doing so in a web environment.
I can not use any 3rd party apps and due to a web environment, Visual studio plugin's aren't much use either.
The closest thing I have found to a working solution was the JimBlackler project, but it only works on DLL's.
Naturally, something like 'supply .CS file, get XML documentation' would be optimal.
Current situation
I have a web-service and trying to dynamically generate documentation for it.
Reading the Methods, and properties is easy, but getting the Summary for each method is throwing me off a bit.
/// <summary>
/// This Is what I'm trying to read
/// </summary>
public class SomeClass()
{
/// <summary>
/// This Is what I'm trying to read
/// </summary>
public void SomeMethod()
{
}
}
A Workaround - Using reflection on Program.DLL/EXE together with Program.XML file
If you take a look at the sibling .XML file generated by Visual Studio you will see that there is a fairly flat hierarchy of /members/member.
All you have to do is get hold on each method from your DLL via MethodInfo object. Once you have this object you turn to the XML and use XPATH to get the member containing the XML documentation for this method.
Members are preceded by a letter. XML doc for methods are preceded by "M:" for class by "T:" etc.
Load your sibling XML
string docuPath = dllPath.Substring(0, dllPath.LastIndexOf(".")) + ".XML";
if (File.Exists(docuPath))
{
_docuDoc = new XmlDocument();
_docuDoc.Load(docuPath);
}
Use this xpath to get the member representing the method XML docu
string path = "M:" + mi.DeclaringType.FullName + "." + mi.Name;
XmlNode xmlDocuOfMethod = _docuDoc.SelectSingleNode(
"//member[starts-with(#name, '" + path + "')]");
Now scan childnodes for all the rows of "///"
Sometimes the /// Summary contains extra blanks, if this bothers use this to remove
var cleanStr = Regex.Replace(row.InnerXml, #"\s+", " ");
The XML summary isn't stored in the .NET assembly - it's optionally written out to an XML file as part of your build (assuming you're using Visual Studio).
Consequently there is no way to "pull out" the XML summaries of each method via reflection on a compiled .NET assembly (either .EXE or .DLL) - because the data simply isn't there for you to pull out. If you want the data, you'll have to instruct your build environment to output the XML files as part of your build process and parse those XML files at runtime to get at the summary information.
You could 'document' your method using the System.ComponentModel.DataAnnotations.DisplayAttribute attribute, e.g.
[Display(Name = "Foo", Description = "Blah")]
void Foo()
{
}
then use reflection to pull the description at runtime.
A deleted post, made by #OleksandrIeremenko, on this thread links to this article https://jimblackler.net/blog/?p=49 which was the basis for my solution.
Below is a modification of Jim Blackler's code making extension methods off the MemberInfo and Type objects and adding code that returns the summary text or an empty string if not available.
Usage
var typeSummary = typeof([Type Name]).GetSummary();
var methodSummary = typeof([Type Name]).GetMethod("[Method Name]").GetSummary();
Extension Class
/// <summary>
/// Utility class to provide documentation for various types where available with the assembly
/// </summary>
public static class DocumentationExtensions
{
/// <summary>
/// Provides the documentation comments for a specific method
/// </summary>
/// <param name="methodInfo">The MethodInfo (reflection data ) of the member to find documentation for</param>
/// <returns>The XML fragment describing the method</returns>
public static XmlElement GetDocumentation(this MethodInfo methodInfo)
{
// Calculate the parameter string as this is in the member name in the XML
var parametersString = "";
foreach (var parameterInfo in methodInfo.GetParameters())
{
if (parametersString.Length > 0)
{
parametersString += ",";
}
parametersString += parameterInfo.ParameterType.FullName;
}
//AL: 15.04.2008 ==> BUG-FIX remove “()” if parametersString is empty
if (parametersString.Length > 0)
return XmlFromName(methodInfo.DeclaringType, 'M', methodInfo.Name + "(" + parametersString + ")");
else
return XmlFromName(methodInfo.DeclaringType, 'M', methodInfo.Name);
}
/// <summary>
/// Provides the documentation comments for a specific member
/// </summary>
/// <param name="memberInfo">The MemberInfo (reflection data) or the member to find documentation for</param>
/// <returns>The XML fragment describing the member</returns>
public static XmlElement GetDocumentation(this MemberInfo memberInfo)
{
// First character [0] of member type is prefix character in the name in the XML
return XmlFromName(memberInfo.DeclaringType, memberInfo.MemberType.ToString()[0], memberInfo.Name);
}
/// <summary>
/// Returns the Xml documenation summary comment for this member
/// </summary>
/// <param name="memberInfo"></param>
/// <returns></returns>
public static string GetSummary(this MemberInfo memberInfo)
{
var element = memberInfo.GetDocumentation();
var summaryElm = element?.SelectSingleNode("summary");
if (summaryElm == null) return "";
return summaryElm.InnerText.Trim();
}
/// <summary>
/// Provides the documentation comments for a specific type
/// </summary>
/// <param name="type">Type to find the documentation for</param>
/// <returns>The XML fragment that describes the type</returns>
public static XmlElement GetDocumentation(this Type type)
{
// Prefix in type names is T
return XmlFromName(type, 'T', "");
}
/// <summary>
/// Gets the summary portion of a type's documenation or returns an empty string if not available
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static string GetSummary(this Type type)
{
var element = type.GetDocumentation();
var summaryElm = element?.SelectSingleNode("summary");
if (summaryElm == null) return "";
return summaryElm.InnerText.Trim();
}
/// <summary>
/// Obtains the XML Element that describes a reflection element by searching the
/// members for a member that has a name that describes the element.
/// </summary>
/// <param name="type">The type or parent type, used to fetch the assembly</param>
/// <param name="prefix">The prefix as seen in the name attribute in the documentation XML</param>
/// <param name="name">Where relevant, the full name qualifier for the element</param>
/// <returns>The member that has a name that describes the specified reflection element</returns>
private static XmlElement XmlFromName(this Type type, char prefix, string name)
{
string fullName;
if (string.IsNullOrEmpty(name))
fullName = prefix + ":" + type.FullName;
else
fullName = prefix + ":" + type.FullName + "." + name;
var xmlDocument = XmlFromAssembly(type.Assembly);
var matchedElement = xmlDocument["doc"]["members"].SelectSingleNode("member[#name='" + fullName + "']") as XmlElement;
return matchedElement;
}
/// <summary>
/// A cache used to remember Xml documentation for assemblies
/// </summary>
private static readonly Dictionary<Assembly, XmlDocument> Cache = new Dictionary<Assembly, XmlDocument>();
/// <summary>
/// A cache used to store failure exceptions for assembly lookups
/// </summary>
private static readonly Dictionary<Assembly, Exception> FailCache = new Dictionary<Assembly, Exception>();
/// <summary>
/// Obtains the documentation file for the specified assembly
/// </summary>
/// <param name="assembly">The assembly to find the XML document for</param>
/// <returns>The XML document</returns>
/// <remarks>This version uses a cache to preserve the assemblies, so that
/// the XML file is not loaded and parsed on every single lookup</remarks>
public static XmlDocument XmlFromAssembly(this Assembly assembly)
{
if (FailCache.ContainsKey(assembly))
{
throw FailCache[assembly];
}
try
{
if (!Cache.ContainsKey(assembly))
{
// load the docuemnt into the cache
Cache[assembly] = XmlFromAssemblyNonCached(assembly);
}
return Cache[assembly];
}
catch (Exception exception)
{
FailCache[assembly] = exception;
throw;
}
}
/// <summary>
/// Loads and parses the documentation file for the specified assembly
/// </summary>
/// <param name="assembly">The assembly to find the XML document for</param>
/// <returns>The XML document</returns>
private static XmlDocument XmlFromAssemblyNonCached(Assembly assembly)
{
var assemblyFilename = assembly.Location;
if (!string.IsNullOrEmpty(assemblyFilename))
{
StreamReader streamReader;
try
{
streamReader = new StreamReader(Path.ChangeExtension(assemblyFilename, ".xml"));
}
catch (FileNotFoundException exception)
{
throw new Exception("XML documentation not present (make sure it is turned on in project properties when building)", exception);
}
var xmlDocument = new XmlDocument();
xmlDocument.Load(streamReader);
return xmlDocument;
}
else
{
throw new Exception("Could not ascertain assembly filename", null);
}
}
}
You can use Namotion.Reflection NuGet package to get these information:
string summary = typeof(Foo).GetXmlDocsSummary();
You can look at https://github.com/NSwag/NSwag - source for nuget NSwag.CodeGeneration - it gets summary as well, usage
var generator = new WebApiAssemblyToSwaggerGenerator(settings);<br/>
var swaggerService = generator.GenerateForController("namespace.someController");<br/>
// string with comments <br/>
var swaggerJson = swaggerService.ToJson();
(try ILSPY decompiler against your dll, you check code and comments)
If you have access to the source code you're trying to get comments for, then you can use Roslyn compiler platform to do that. It basically gives you access to all the intermediary compiler metadata and you can do anything you want with it.
It's a bit more complicated than what other people are suggesting, but depending on what your needs are, might be an option.
It looks like this post has a code sample for something similar.
I need to access the assembly of my project in C#.
I can see the GUID in the 'Assembly Information' dialog in under project properties, and at the moment I have just copied it to a const in the code. The GUID will never change, so this is not that bad of a solution, but it would be nice to access it directly. Is there a way to do this?
Try the following code. The value you are looking for is stored on a GuidAttribute instance attached to the Assembly
using System.Runtime.InteropServices;
static void Main(string[] args)
{
var assembly = typeof(Program).Assembly;
var attribute = (GuidAttribute)assembly.GetCustomAttributes(typeof(GuidAttribute),true)[0];
var id = attribute.Value;
Console.WriteLine(id);
}
Or, just as easy:
string assyGuid = Assembly.GetExecutingAssembly().GetCustomAttribute<GuidAttribute>().Value.ToUpper();
It works for me...
Another way is to use Marshal.GetTypeLibGuidForAssembly.
According to MSDN:
When assemblies are exported to type libraries, the type library is assigned a LIBID. You can set the LIBID explicitly by applying the System.Runtime.InteropServices.GuidAttribute at the assembly level, or it can be generated automatically. The Tlbimp.exe (Type Library Importer) tool calculates a LIBID value based on the identity of the assembly. GetTypeLibGuid returns the LIBID that is associated with the GuidAttribute, if the attribute is applied. Otherwise, GetTypeLibGuidForAssembly returns the calculated value. Alternatively, you can use the GetTypeLibGuid method to extract the actual LIBID from an existing type library.
You should be able to read the GUID attribute of the assembly via reflection. This will get the GUID for the current assembly:
Assembly asm = Assembly.GetExecutingAssembly();
object[] attribs = asm.GetCustomAttributes(typeof(GuidAttribute), true);
var guidAttr = (GuidAttribute) attribs[0];
Console.WriteLine(guidAttr.Value);
You can replace the GuidAttribute with other attributes as well, if you want to read things like AssemblyTitle, AssemblyVersion, etc.
You can also load another assembly (Assembly.LoadFrom and all) instead of getting the current assembly - if you need to read these attributes of external assemblies (for example, when loading a plugin).
For an out-of-the-box working example, this is what I ended up using based on the previous answers.
using System.Reflection;
using System.Runtime.InteropServices;
label1.Text = "GUID: " + ((GuidAttribute)Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(), typeof(GuidAttribute), false)).Value.ToUpper();
Alternatively, this way allows you to use it from a static class:
/// <summary>
/// public GUID property for use in static class </summary>
/// <returns>
/// Returns the application GUID or "" if unable to get it. </returns>
static public string AssemblyGuid
{
get
{
object[] attributes = Assembly.GetEntryAssembly().GetCustomAttributes(typeof(GuidAttribute), false);
if (attributes.Length == 0) { return String.Empty; }
return ((System.Runtime.InteropServices.GuidAttribute)attributes[0]).Value.ToUpper();
}
}
There wasn't any luck here with the other answers, but I managed to work it out with this nice one-liner:
((GuidAttribute)(AppDomain.CurrentDomain.DomainManager.EntryAssembly).GetCustomAttributes(typeof(GuidAttribute), true)[0]).Value
To get the appID you could use the following line of code:
var applicationId = ((GuidAttribute)typeof(Program).Assembly.GetCustomAttributes(typeof(GuidAttribute), true)[0]).Value;
For this you need to include the System.Runtime.InteropServices;
Use:
string AssemblyID = Assembly.GetEntryAssembly().GetCustomAttribute<GuidAttribute>().Value;
Or in VB.NET:
Dim AssemblyID As String = Assembly.GetEntryAssembly.GetCustomAttribute(Of GuidAttribute).Value