Can't find .XSL file embedded in project DLL - c#

Quick question that's wrecked my morning and is driving me nuts.
I have a small project that includes a DLL from another project. The DLL has an XSL file embedded in it that I want to extract and apply to a webbrowser control.
I have no problem with extracting / accessing embedded resources in the main EXE file, but I cannot find the means of accessing it in the DLL!?
I've tried:
"SolutionName.DllName.Resource.xsl"
"ExeName.DllName.Resource.xsl"
"ProjectNamespace.DllNamespace.Resource.xsl"
...and pretty much every permutation thereof, but it's never able to find it.
I don't have a dot-notation reference for it in C# to use with nameof(), and I can't find any apparent reference / access to it with:
System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames();
So, what's the correct naming (or other?) method for retrieving this file?
In case any of this helps, here's some additional details:
Project Name: DataBuilder
Project Namespace: DataBuilder
DLL Name: CobCommon
DLL Namespaces: CobCommon, CobCommon.Classes, CobCommon.Classes.Data, CobCommon.Winforms, CobCommon.WinForms.Controls
XSL Resource Name: XmlFormating.xsl
The specified resource file operation is "Embedded Resource" and it's located in the "root" area of the DLL project.
Accessing global:: gives me CobCommon, and DataBuilder amongst the available choices, but CobCommon doesn't have either a .Properties or a .Resources option, and DataBuilder which does have .Properties.Resources gives "Culture" as the only reference.
The XSL file is listed on the DLL Project's "Properties|Resources|Files" tab.
What am I missing?

Using GetExecutingAssembly() will probably always refer to your assembly. Instead, create an instance of some innocuous, (hopefully) simple object declared in that external DLL, then use that instance object's...
<object-from-DLL>.GetType().Assembly.GetManifestResourceStream("what.youre.looking.for")
to get a stream handle to your embedded object.

So this was the way that I finally managed to put together a generic function to retrieve any text-encoded embedded resource from any project assembly (and that's working as intended in my project):
First, I extended the Assembly class to facilitate grabbing just the relevant leading portion of Assembly.FullName that we need to use to search for the requested resource:
/// <summary>Adds a function to dereference just the base name portion of an Assembly from the FullName.</summary>
/// <returns>The extracted base name if able, otherwise just the FullName.</returns>
public static string ExtractName(this Assembly root)
{
string pattern = #"^(?<assy>(?:[a-zA-Z\d]+[.]?)+)(?>,).*$", work = root.FullName;
MatchCollection matches = Regex.Matches(work, pattern, RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
if (matches.Count > 0) return matches[0].Groups["assy"].Value;
if (work.IndexOf(",") > 3) return work.Substring(0, work.IndexOf(','));
return root.FullName;
}
Then I wrote this function to search for the specified assembly + embedded resource file and return its contents as a string, if found:
/// <summary>Searches all loaded assemblies in a project for the specified embedded resource text file.</summary>
/// <returns>If the specified resource is found, its contents as a string, otherwise throws a DllNotFoundException.</returns>
/// <exception cref="DllNotFoundException"></exception>
public static string FetchAssemblyResourceFile(string name)
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
int i = -1; while ((++i < assemblies.Length) && !Regex.IsMatch(name, "^(" + assemblies[i].ExtractName() + ")", RegexOptions.IgnoreCase)) ;
if (i < assemblies.Length)
{
try {
using (System.IO.Stream stream = assemblies[i].GetManifestResourceStream(name))
using (System.IO.StreamReader reader = new System.IO.StreamReader(stream))
return reader.ReadToEnd();
}
catch (Exception innerExc)
{
Exception result = new Exception("The requested resource (\"" + name + "\") was not found.");
result.InnerException = innerExc;
throw result;
}
}
throw new DllNotFoundException("The requested assembly resource (\"" + name + "\") could not be found.");
}

Related

Location of original assembly(not the current executing)

Suppose I have a solution which contains 4 projects, A, A_UnitTests, B, and B_UnitTests.
Project A has a data file, which is added as a link to A_UnitTests and set to copy to the output directory. When unit tests are run or when the code is executed in production, the path to that file is correctly identified using the following code snippet:
public static string GetFullPath(string relativePath)
{
string retVal = string.Empty;
if (System.Web.HttpContext.Current == null)
{
string locationBeforeShadowCopy = typeof(A.SomeClassInA).Assembly.CodeBase;
UriBuilder uri = new UriBuilder(locationBeforeShadowCopy);
string locationWithoutUriPrefixes = Uri.UnescapeDataString(uri.Path);
string dir = Path.GetDirectoryName(locationWithoutUriPrefixes);
retVal = Path.Combine(dir, relativePath);
}
else
{
// stuff that doesn't matter
}
return retVal;
}
However, I have a new testcase in B_UnitTests which attempts to use this code path to find the file location. However, even though I call typeof(A.SomeClassInA).Assembly.CodeBase, it is being called from B_UnitTests, using its referenced DLLs. This means the path return is the B_UnitTests output directory + a relative path. So it doesn't find the data file.
Without resorting to hard coded settings and build scripts, what could I use to specify the correct path?
Update (clarification)
The real issue is with typeForClassInA.Assembly.CodeBase returning the path of the executing assembly rather than A itself. It seems very wrong to provide a type that comes from some assembly but instead of returning the original assembly location, it returns the path to the executing assembly which happens to have a reference to it.
If there is a reference to 'typeForClassInA', then its assembly will be being copied into the output directory of B_UnitTests. So when you ask for CodeBase of that type's assembly from a test in B_UnitTests, it is (correctly) pointing at the version of assembly A in the B_UnitTests output folder, because that's where it's being loaded from.
I admit that I avoid using Shadow Copy to avoid exactly these kinds of problems of locating resources which are alongside the assembly, since ShadowCopy doesn't understand that they are needed, and they don't get shadow copied.
Another thing which can help is to build all the projects into the same output folder by changing all the project output folders to be "..\bin". For example, this would mean that A_UnitTests would not need the link to the resource file (once shadow copy is off).
I have a method similar to the one you've shown which goes "up" from the assembly's location (which for me is the shared bin folder) to the solution's location; and my relative paths are 'rooted' at that folder.
If that all sounds too complex, you could just use the same approach that A_UnitTests did, of including a link to it from B_UnitTests.

Doxygen not generating documentation for internal C# functions

I developed a Registry function "class" for Ketarin, and I'm using Doxygen for Windows to doc it.
When I build the documentation (using Doxywizard) following the tutorial on: http://www.tech-coder.com/2016/12/generate-html-documentation-using.html
It docs only the two first functions and nothing else. I tried to check all boxes on the "Build" column at the "Expert" tab but no avail.
The code (including the documentation - that only show 2 functions) can be found on GitHub at https://github.com/coldscientist/RegClassCS
For example, the internal method below is showed normally on docs:
/// <summary>
/// Creates a new subkey or opens an existing subkey for write access.
/// </summary>
/// <param name="rootName">The HKEY to open.</param>
/// <param name="keyName">The name or path of the subkey to create or open. This string is not case-sensitive.</param>
/// <returns>The newly created subkey, or false if the operation failed. If a zero-length string is specified for subkey, the current RegistryKey object is returned.</returns>
Func<string, string, bool> RegCreateKey = new Func<string, string, bool>( (rootName, keyName) =>
{
try
{
Ketarin.Forms.LogDialog.Log("RegCreateKey(" + rootName + ", " + keyName + ")");
Microsoft.Win32.RegistryKey localKey = RegOpenSubKey(rootName);
// localKey = localKey.OpenSubKey(keyName, writable: true);
using (localKey)
{
RegCreateKey:
if (localKey != null)
{
localKey.CreateSubKey( keyName );
}
else
{
// Abort("Key " + rootName + #"\" + keyName + " not found.");
goto RegCreateKey;
}
}
}
catch (Exception ex)
{
// Abort(ex.ToString());
return false;
}
return true;
});
But the following method (below that one) is not documented:
/// <summary>
/// Deletes a subkey and any child subkeys recursively. No warning will be provided.
/// </summary>
/// <param name="rootName">The HKEY to open.</param>
/// <param name="subKeyName">The subkey to delete. This string is not case-sensitive.</param>
/// <returns>Returns false if the operation failed.</returns>
Func<string, string, bool> RegDeleteKey = new Func<string, string, bool>( (rootName, subKeyName) =>
{
try
{
Ketarin.Forms.LogDialog.Log("RegDeleteKey(" + rootName + ", " + subKeyName + ")");
string keyName = subKeyName.Substring(0, subKeyName.LastIndexOf(#"\"));
string subTreeName = subKeyName.Substring(subKeyName.LastIndexOf(#"\")+1);
Microsoft.Win32.RegistryKey localKey = RegOpenSubKey(rootName);
localKey = localKey.OpenSubKey(keyName, writable: true);
using (localKey)
{
if (localKey != null)
{
localKey.DeleteSubKeyTree(subTreeName);
}
}
}
catch (Exception ex)
{
return false;
// Abort(ex.ToString());
}
return true;
});
I'm suspecting that it is related to the way I coded it (the code compiles normally). Maybe there is a comma missing or some code formatting that is making Doxygen missing itself.
Here is the Doxygen output, if it helps:
Searching for include files...
Searching for example files...
Searching for images...
Searching for dot files...
Searching for msc files...
Searching for dia files...
Searching for files to exclude
Searching INPUT for files to process...
Searching for files in directory S:/Applications/Scripts/RegClassCS
Searching for files in directory S:/Applications/Scripts/RegClassCS/doc
Reading and parsing tag files
Parsing files
Reading S:/Applications/Scripts/RegClassCS/README.md...
Preprocessing S:/Applications/Scripts/RegClassCS/RegClass.cs...
Parsing file S:/Applications/Scripts/RegClassCS/RegClass.cs...
Building group list...
Building directory list...
Building namespace list...
Building file list...
Building class list...
Associating documentation with classes...
Computing nesting relations for classes...
Building example list...
Searching for enumerations...
Searching for documented typedefs...
Searching for members imported via using declarations...
Searching for included using directives...
Searching for documented variables...
Building interface member list...
Building member list...
Searching for friends...
Searching for documented defines...
Computing class inheritance relations...
Computing class usage relations...
Flushing cached template relations that have become invalid...
Computing class relations...
Add enum values to enums...
Searching for member function documentation...
Creating members for template instances...
Building page list...
Search for main page...
Computing page relations...
Determining the scope of groups...
Sorting lists...
Freeing entry tree
Determining which enums are documented
Computing member relations...
Building full member lists recursively...
Adding members to member groups.
Computing member references...
Inheriting documentation...
Generating disk names...
Adding source references...
Adding xrefitems...
Sorting member lists...
Computing dependencies between directories...
Generating citations page...
Counting data structures...
Resolving user defined references...
Finding anchors and sections in the documentation...
Transferring function references...
Combining using relations...
Adding members to index pages...
Generating style sheet...
Generating search indices...
Generating example documentation...
Generating file sources...
Generating file documentation...
Generating docs for file README.md...
Generating docs for file RegClass.cs...
Generating page documentation...
Generating docs for page md_README...
Generating group documentation...
Generating class documentation...
Generating namespace index...
Generating graph info page...
Generating directory documentation...
Generating index page...
Generating page index...
Generating module index...
Generating namespace index...
Generating namespace member index...
Generating annotated compound index...
Generating alphabetical compound index...
Generating hierarchical class index...
Generating member index...
Generating file index...
Generating file member index...
Generating example index...
finalizing index lists...
writing tag file...
lookup cache used 2/65536 hits=2 misses=2
finished...
S:/Applications/Scripts/RegClassCS/README.md:3: warning: Unexpected html tag  found within context
*** Doxygen has finished
I give up and migrate to Natural Docs. But I notice that after the internal functions that were not printed before by Doxygen, it showed up internal methods that doesn't exist on my code (but apparently it is obtained by internal methods args - see RegSetValue below). If I remove "RegCreateKey", the method below that ("string") is removed too (obviously). Maybe in Doxygen is happening the same thing, but instead of continue, it stop processing the file (I'm not sure of it). I'm just posting it to help other people and Doxygen to (maybe) check this on the future.
On Natural Docs, I can hide these undesired internal methods (that, in fact, doesn't exist) using the arg -do when building docs, so it shows up only documented internal methods.

DLL not found when running outside debug folder

Application names, paths and urls have been renamed to 'MyApp' and 'example' just for easier reading.
Hello, I currently use 1 dll file for my application and that is log4net in c#. Now I include my dll in references from
C:\Users\ashle\AppData\Roaming\MyApp
Simply because I will be publicly releasing my application. Now it works fine outside debug mode and inside, but when I run the exe outside the /bin/debug folder it throws an error..
Unhandled Exception: System.IO.FileNotFoundException: Could not load
file or assembly 'log4net, Version=1.2.15.0, Culture=neutral,
PublicKeyToken=669e0ddf0bb1aa2a' or one of its dependencies. The
system cannot find the file specified.
at MyApp.Program.Main(String[] args)
I have also put this code in which I thought would stop it happening.. but what am I doing wrong? this code should cover my a**
if (!Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/MyApp"))
{
Console.WriteLine("Created directory: " + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/MyApp");
Directory.CreateDirectory(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/MyApp");
}
if (!File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/MyApp/log4net.dll"))
{
Console.WriteLine("Please wait while we download some files...");
string downloadUrl = "http://example.com";
if (checkWebsiteAvalibility(downloadUrl))
{
WebClient webClient = new WebClient();
webClient.DownloadFileAsync(new Uri(downloadUrl + "/downloads/log4net.dll"),
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/MyApp/log4net.dll");
Console.Clear();
But when running the exe solo outside /bin/debug it doesn't even display the "Please wait while we download some files..." line
Before publishing your project, delete the dll file from your reference. Add the same dll through Add Reference. Then try to publish it.
It worked for me.
When I ran into this issue, I deployed the .dll to the running location of the executable. Add the dll to the project Resources and set its Build Action to Content.
class Program
{
//Programmatically, the Program() method is called prior to Main() and before any registrations are made.
//This is where we write the dll to the disk.
static Program()
{
//Path.GetDirectoryName() returns the folder path to a particular file.
//Assembly.GetExecutingAssembly().Location returns the path to the current running location of the compiled executable, with the name. E.G. C:\MyProject\MyProgram.exe
//We combine this with Path.GetDirectoryName to get the folder, and then write the dll into this folder. That way, when this method finishes and main is called, it will find the dll in the folder.
File.WriteAllBytes(string.Format("{0}{1}", Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "\\log4net.dll"), FindResourceByName("log4net"));
}
/// <summary>
/// Returns a byte array of the object searched for
/// </summary>
/// <param name="objectName">Name of the resource object</param>
/// <returns>Byte array of the specified resource object</returns>
private static byte[] FindResourceByName(string objectName)
{
object obj = Properties.Resources.ResourceManager.GetObject(objectName);
return ((byte[])(obj));
}
//Rest of your code goes here...
}
In my situation, my DLL was called "Microsoft.Win32.TaskScheduler.dll" If this is the case, with your application, the periods will be replaced with an underscore when your DLL is added as a resource. Make sure that you reflect this when calling FindResourceByName or your lookup will fail:
File.WriteAllBytes(string.Format("{0}{1}", Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "\\Microsoft.Win32.TaskScheduler.dll"), FindResourceByName("Microsoft_Win32_TaskScheduler"));

How to get Current Project Directory path using C#

okay, here is the question. I have two projects one is C# Console and other is Class library.
I am accessing/calling Class library method from the console app.
There is a folder called Files within the class library project.
I need to get the path of the Class library's files folder but whenever I use
System.IO.Directory.GetCurrentDirectory();
and
Environment.CurrentDirectory;
it is giving me path of the Console project which I am using to call the method.
Above methods are giving me path like
C:\\ConsolePro\\bin\\Debug
but I need the path of Class library project
C:\\ClassLibPro\\bin\\Debug
Please advise
Once the code is compiled and running, 'Project Path' has no meaning. All you can determine are the file locations of the compiled assemblies. And you can only do what you are asking if your Console project references the built 'class library' DLL directly, rather than via a Project Reference.
Then, you can make use of Reflection to get Assembly paths like;
string path = Assembly.GetAssembly(typeof (SomeClassInOtherProject)).Location;
You should be able to use Directory.GetParent(Directory.GetCurrentDirectory()) a few times to get higher level directories and then add the path of the lib directory to the end of that.
I believe the problem is:
Since the Console project has the DLL file reference it is using DLL to call any methods.
At this time it is returning the class library projct's DLL location which is located in console project's bin directory and it doesn't know about the physical location of class library project.
so essentially it is returning the same project path. I will have to move both projects in same directory in order to solve this issue.
If you loading the class library from another assembly.
string Path = System.Reflection.Assembly.GetAssembly(typeof({LibraryClassName})).Location;
string PathToClassLibPro = Path.GetDirectoryName( Path);
Replace {LibraryClassName} with the class name of your library.
I hope I understand u corretly:
Path.GetDirectoryName(typeof(Foo.MyFooClass).Assembly.Location);
I would recommend one of two options.
If the files are small include them in the class library and stream them to a temp location when needed
Other option is to copy the files during the build to the output directory and use them that way. In cases of multiple shared projects it is best to have a common bin folder that you copy assemblies to and run from that location.
Despite i cant find a good solution i use this trick :
as long as you want to come back to your ideal path u should add Directory.GetParent() instead of ...
Directory.GetParent(...(Directory.GetParent(Directory.GetCurrentDirectory()).ToString()...).ToString()
I use the following approach to get the current project path at runtime:
public static class ProjectInfo {
public static string appDirectory = AppDomain.CurrentDomain.BaseDirectory;
public static string projectPath = appDirectory.Substring(0, appDirectory.IndexOf("\\bin"));
}
I had this exact issue as well where I couldn't access the file in my namespace's bin/debug folder. My solution was to manipulate the string using Split() then construct a new string which is the absolute path to the json file I have in my namespace.
private static string GetFilePath()
{
const char Escape = '\\'; //can't have '\' by itself, it'll throw the "Newline in constant" error
string directory = Environment.CurrentDirectory;
string[] pathOccurences = directory.Split(Escape);
string pathToReturn = pathOccurences[0] + Escape; //prevents index out of bounds in upcoming loop
for(int i = 1; i < pathOccurences.Length; i++)
{
if (pathOccurences[i] != pathOccurences[i - 1]) //the project file name and the namespace file name are the same
pathToReturn += pathOccurences[i] + Escape;
else
pathToReturn += typeof(thisClass).Namespace + Escape; //In the one occurrence of the duplicate substring, I replace it with my class Namespace name
}
return pathToReturn + "yourFile.json";
}
I personally don't like this solution, but it was the only answer I could think of.

C# - Fastest way to get resource string from assembly

I really don't know/have the answer, knowledge to find a resource value using a key from a resx file in a assembly using c#.(or may be i am ignorant).
What would be the fastest code, way i can retrieve string values or values using a key from a resource file which is embedded as resource in a assembly. I am storing friendly messages for exceptions in the resource file and would like to use them when required.
Does a static class exist for this purpose?
Are there open source mature projects i can use for this?
If the resource is in the same assembly as the code, then the following will do:
String resourceValue = MyAssemblyNameSpace.Properties.Resources.ResourceName
Taken from this SO answer.
Assembly assembly = this.GetType().Assembly;
ResourceManager resourceManager = new ResourceManager("Resources.Strings", assembly);
string myString = resourceManager.GetString("value");
string val = Resources.ResourceManager.GetString("resource_name");
Given "resource_name" you can retrieve resource value.
you can use ResourceManger to get the string value from Assembly
Get Resource from Assembly
ResourceManager ResManager= new ResourceManager("yourResource",
Assembly.GetExecutingAssembly());
String strResourveValue = ResManager.GetString("YourStringKey");
var thread = System.Threading.Thread.CurrentThread.CurrentUICulture.Name;
var culture = new CultureInfo(thread);
var resourceManager = new ResourceManager(typeof(Resources.Resource));
string value = resourceManager.GetString(name, culture);
When I made a new project for my unit tests of type C# class library called UnitTests, I right clicked and Added a new Resource. I named that UnitTestsResources. I added 2 strings to that resource. I was then able to conveniently able to access them like this
UnitTestsResources.NoDeviceRequireMsg
I was curious how that worked so i pulled up the code behind the resource file and it makes sense. Visual Studio made a internal class with static accessors.. It looks like this for me
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class UnitTestsResources {
//Some auto generated code
/// <summary>
/// Looks up a localized string similar to OPOS Device is required for test.
/// </summary>
internal static string DeviceRequireMsg {
get {
return ResourceManager.GetString("DeviceRequireMsg", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to OPOS Device must not be installed for test.
/// </summary>
internal static string NoDeviceRequireMsg {
get {
return ResourceManager.GetString("NoDeviceRequireMsg", resourceCulture);
}
}
}
Since it is only for my unit tests I am content with this. Hope it helps someone else.
To improve on Herzbube's answer I will show how I implemented this...
Rather than creating projects or folders for the resource file, just right click your project and do add -> new item, then choose resources file. Open the resources file stick in your strings, save as a useful name, and navigate over to C# where you want to use them, then it is just:
String resourceValue = MyProjectName.MyResourceFileName.MyResourceRowItem;
If that isnt working pay attention to the access modifier drop down when inside your resx file.

Categories