Need to get current path of my created dll class library - c#

I can't get the currently path of my created dll class library, which is loaded as an ActiveX in a ASP.NET web site.
I have tried several commands, but no success. For example, I tried to get current path in variable _path:
namespace WebScanControl {
[ApplicationControl(Width=640,Height=480)]
public sealed partial class ScanControl:ApplicationControl {
public ScanControl() {
InitializeComponent();
}
protected override void Construct(ReadOnlyCollection<object> args) {
try {
string _path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
} catch(Exception ex) {
MessageBox.Show(string.Format("{1}{0}{2}", Environment.NewLine, ex.Message, ex.StackTrace), ex.GetType().Name, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
base.Construct(args);
}
}
}
However, variable _path gets:
C:\Users\miyahira\AppDataLocalTemp\3f3574554-2ef8-e8r9-9c8b-e40561d03bb
Which is not its path.
The current path of my dll class library is:
F:\webapp\WebScan\Scan\
As you see, this class library is an ApplicationControl. Is that the reason why it's not possible to get its current path?

Your main problem is that you are thinking that the library will stay on the disk.
ASP.NET during the start up get's all the assemblies, and moves it to the temporary folder, where it can use them as it wants.
So the C:\Users\miyahira\AppDataLocalTemp\3f3574554-2ef8-e8r9-9c8b-e40561d03bb is a correct path.
How does your ActiveX component being add to the Application? And what is the location of the ASP.NET site? You can try to get the path from the HTTPContext.ApplicationInstance property, but can't say for sure will it work or not.

Related

Determine C# code class file's parent directory path at design-time

Here is info about our technical development environment :
• .NET Core 3.1
• PostgreSQL 14.2, compiled by Visual C++ build 1914, 64-bit
• EntityFramework.Functions Version=1.5.0
• Microsoft.EntityFrameworkCore.Design Version=5.0.17
• Microsoft.EntityFrameworkCore.Tools Version=5.0.17
• Npgsql.EntityFrameworkCore.PostgreSQL Version=5.0.10
The location of a C# code file called JohnDoeCSharp.cs is located in VisualStudioProjectDir\Migrations\1.0.5
It contains the following code constructor:
public class JohnDoeCSharp
{
public JohnDoeCSharp()
{
}
}
Within the aforementioned constructor, I’m trying to get the name of the parent directory called 1.0.5
The problem with using the following code is that they give runtime directory paths values back:
string strExeFilePath =
System.Reflection.Assembly.GetExecutingAssembly().Location;
string strWorkPath = System.IO.Path.GetDirectoryName(strExeFilePath);
For lack of a better term, I suppose I’m trying to get the directory path values at design time / “coding time”.
What can I try next?
You can write a helper method that uses the CallerFilePath attribute to get the directory of a code file.
Helper class:
public static class CodeHelper
{
public static string GetCodeDirectory([CallerFilePath]string filepath = "")
{
return Path.GetDirectoryName(filepath);
}
}
Usage:
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine(CodeHelper.GetCodeDirectory());
}
}

How to properly load an instance from a DLL implementing a specific base class in C#?

I have a problem where I have a program that should load a plugin (DLL) from a specific directory where the DLL implements a specific base class. The problem is that my program that loads the DLL has a reference to an other DLL which the DLL being loaded also references. I will show an example of how the problem arises. This simple tests consists of 3 different solutions and 3 separate projects. NOTE: If I have all projects in the same solution, the problem does not arise.
Solution 1 - Project that defines the Base class and an interface
AdapterBase.cs
namespace AdapterLib
{
public interface IAdapter
{
void PrintHello();
}
public abstract class AdapterBase
{
protected abstract IAdapter Adapter { get; }
public void PrintHello()
{
Adapter.PrintHello();
}
}
}
Solution 2 - Project that defines an implementation of the base class
MyAdapter.cs
namespace MyAdapter
{
public class MyAdapter : AdapterBase
{
private IAdapter adapter;
protected override IAdapter Adapter
{
get { return adapter ?? (adapter = new ImplementedTestClass()); }
}
}
public class ImplementedTestClass : IAdapter
{
public void PrintHello()
{
Console.WriteLine("Hello beautiful worlds!");
}
}
}
Solution 3 - Main program which loads the DLL implementing AdapterBase*
**Program.cs
namespace MyProgram {
internal class Program {
private static void Main(string[] args) {
AdapterBase adapter = LoadAdapterFromPath("C:\\test\\Adapter");
adapter.PrintHello();
}
public static AdapterBase LoadAdapterFromPath(string dir) {
string[] files = Directory.GetFiles(dir, "*.dll");
AdapterBase moduleToBeLoaded = null;
foreach (var file in files) {
Assembly assembly = Assembly.LoadFrom(file);
foreach (Type type in assembly.GetTypes()) {
if (type.IsSubclassOf(typeof(AdapterBase))) {
try {
moduleToBeLoaded =
assembly.CreateInstance(type.FullName, false, BindingFlags.CreateInstance, null, null,
null, null) as AdapterBase;
} catch (Exception ex) {
}
if (moduleToBeLoaded != null) {
return moduleToBeLoaded;
}
}
}
}
return moduleToBeLoaded;
}
}
}
So now the main program MyProgram.cs will try to load the DLL from path C:\test\Adapter and this works fine if I ONLY put the file MyAdapter.dll in that folder. However Solution 2 (MyAdapter.cs), will put both MyAdapter.dll and AdapterBase.dll in the output bin/ directory. Now if copy both those files to c:\test\Adapter the instance from the DLL does not get loaded since the comparison
if (type.IsSubclassOf(typeof(AdapterBase))) { fails in MyProgram.cs.
Since MyProgram.cs already has a reference to AdapterBase.dll there seems to be some conflict in an other DLL gets loaded from a different path which references the same DLL. The loaded DLL seems to first resolve its dependencies against DLLs in the same folder. I think this has to do with assembly contexts and some problem with the LoadFrom method but I do not know how to get C# to realise that it actually is the same DLL which it already has loaded.
A solution is of course only to copy the only needed DLL but my program would be much more robust if it could handle that the other shared DLL also was there. I mean they are actually the same. So any solutions how to do this more robust?
Yes, this is a type identity problem. A .NET type's identity is not just the namespace and type name, it also includes the assembly it came from. Your plugin has a dependency on the assembly that contains IAdapter, when LoadFrom() loads the plugin it is also going to need that assembly. The CLR finds it in the LoadFrom context, in other words in the c:\test\adapter directory, normally highly desirable since that allows plugins to use their own DLL versions.
Just not in this case. This went wrong because you plugin solution dutifully copied the dependencies. Normally highly desirable, just not in this case.
You'll have to stop it from copying the IAdapter assembly:
Open the plugin solution and use Build > Clean.
Delete a remaining copy of the IAdapter assembly in the output directory with Explorer.
Select the IAdapter assembly in the plugin solution's References node. Set its Copy Local property to False.
Use Build > Build and verify that the IAdapter assembly indeed does not get copied anymore.
Copy Local is the essence, the rest of the bullets are there just to make sure that an old copy doesn't cause problems. Since the CLR can no longer find the IAdapter assembly the "easy way", it is forced to keep looking for it. Now finding it in the Load context, in other words, the directory where the host executable is installed. Already loaded, no need to load the one-and-only again. Problem solved.
I found a solution to my problem, although the path for the DLL can not be fully arbitrary. I was able to put the DLLs into, for example, bin/MyCustomFolder and load the DLL without getting the Type conflict problem.
The solution was to use the Assembly.Load() method which takes the full assembly name as argument. So first I find the name of the assembly by loadning all DLLs in the specified folder and the use Assembly.GetTypes() and checking if the Type is a subclass of AdapterBase. Then I use Assembly.Load() to actually load the assembly, which elegantly loads the DLL without any Type conflicts.
Program.cs
namespace MyProgram {
internal class Program {
private static void Main(string[] args) {
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
string dir = Path.GetDirectoryName(path);
string pathToLoad = Path.Combine(dir, "MyCustomFolder");
AdapterBase adapter = LoadAdapterFromPath(pathToLoad);
adapter.PrintHello();
}
/// <summary>
/// Loads the adapter from path. LoadFile will be used to find the correct type and then Assembly.Load will be used to actually load
/// and instantiate the class.
/// </summary>
/// <param name="dir"></param>
/// <returns></returns>
public static AdapterBase LoadAdapterFromPath(string dir) {
string assemblyName = FindAssembyNameForAdapterImplementation(dir);
Assembly assembly = Assembly.Load(assemblyName);
Type[] types = assembly.GetTypes();
Type adapterType = null;
foreach (var type in types)
{
if (type.IsSubclassOf(typeof(AdapterBase)))
{
adapterType = type;
break;
}
}
AdapterBase adapter;
try {
adapter = (AdapterBase)Activator.CreateInstance(adapterType);
} catch (Exception e) {
adapter = null;
}
return adapter;
}
public static string FindAssembyNameForAdapterImplementation(string dir) {
string[] files = Directory.GetFiles(dir, "*.dll");
foreach (var file in files)
{
Assembly assembly = Assembly.LoadFile(file);
foreach (Type type in assembly.GetTypes())
{
if (type.IsSubclassOf(typeof(AdapterBase)))
{
return assembly.FullName;
}
}
}
return null;
}
}
}
NOTE: It is also important to add the extra probing path for Assembly.Load() to find the assembly in bin/MyCustomFolder. The probing path must be a subdir of the executing assembly, therefore it is not possible to have the DLL in a completely arbitrary location. Update your App.config as below:
App.config
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="MyCustomFolder"/>
</assemblyBinding>
</runtime>
</configuration>
Tip: Actually I was having this problem for a Web application I had created. In that case you should update Web.config instead. Also in that case the probing path does not have the executing assembly as root but is actually the root of your web application. So in that case you can put you DLL folder MyCustomFolder directly in you web apps root folder, for example: inetpub\wwwroot\mywebapp\MyCustomFolder and then update Web.config as App.config above.

Installer Custom action not working

I am trying to remove some additional files under the user profile Local Application Data folder after the uninstall of the app.
i read about custom action, so i wrote this
namespace RemoveUserAppDataCA
{
[RunInstaller(true)]
public partial class Installer1 : System.Configuration.Install.Installer
{
public Installer1()
{
InitializeComponent();
}
[System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)]
public override void Uninstall(IDictionary savedState)
{
base.Uninstall(savedState);
// Very important! Removes all those nasty temp files.
DeleteUserDataProfile();
base.Dispose();
}
void DeleteUserDataProfile()
{
try
{
string path = Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "..\\MyCompanyFolder"));
if (Directory.Exists(path))
{
Directory.Delete(path,true);
}
}
catch (Exception)
{
throw;
}
}
}
}
I added the dll file to the project setup , then a added a custom actions , then under the uninstall , i added the ddl file of the RemoveUserAppDataCA . i built the system.
i did the installation , but when i uninstall the app the app folderthe user profile Local Application Data remains (does not get deleted).
What is wrong about the work ??
I found the problem , the folder path was wrong. The system was pointing at
"C:\Users\UserName\AppData\App_Folder" instead of "C:\Users\UserName\AppData\Local\App_Folder"

Unable to access a new class added to a class library

I have a class library that I added another class to that no matter what I try it will not be available in the project that I am referencing the library from. I have no problem with the original class I created in this library referencing and using.
I have tried all of the below:
Cleaning the project solution
Save and rebuild both the debug and release
Closing the project and reopening
Steps one through three on the library project I'm tyring to reference
In the project that I want to reference the library from I have tried loading the .dll form the bin/release folded, and the main library project .dll in the obj/release folder. Neater have made a difference because I still cannot get to the new class I added to the library. I am referencing the DotNetOpenAuth_Library.dll from the release folded in the bin.
If this makes a difference I'm using VS 2012 Express for Web that I downloaded last week.
The class I added to my library that has no build errors is:
namespace DotNetOpenAuth_Library
{
class EmbeddedResourceUrlService : IEmbeddedResourceRetrieval
{
private static string pathFormat = "{0}/Resource/GetWebResourceUrl? assemblyName= {1}&typeName={2}&resourceName={3}";
//private static string pathFormat = "{0}/Resource/GetWebResourceUrl";
public Uri GetWebResourceUrl(Type someTypeInResourceAssembly, string manifestResourceName)
{
if (manifestResourceName.Contains("http"))
{
return new Uri(manifestResourceName);
}
else
{
var assembly = someTypeInResourceAssembly.Assembly;
// HACK
string completeUrl = HttpContext.Current.Request.Url.ToString();
string host = completeUrl.Substring(0,
completeUrl.IndexOf(HttpContext.Current.Request.Url.AbsolutePath));
var path = string.Format(pathFormat,
host,
HttpUtility.UrlEncode(assembly.FullName),
HttpUtility.UrlEncode(someTypeInResourceAssembly.ToString()),
HttpUtility.UrlEncode(manifestResourceName));
return new Uri(path);
}
}
}
}
Put public in front of the class definition. If the class is marked internal1 it can only be accessed by other classes within the same assembly2.
namespace DotNetOpenAuth_Library
{
public class EmbeddedResourceUrlService : IEmbeddedResourceRetrieval
{
//(snip)
}
}
Here is a MSDN link explaining access modifiers.
1: If you do not put a modifier in front of the class it will default to internal.
2: unless you mark the other assembly a friend assembly
It looks to me like the problem is just the lack of an access modifier. By default the c# compiler treats classes as internal. It should work if you change the declaration to
public class EmbeddedResourceUrlService : IEmbeddedResourceRetrieval
The class EmbeddedResourceUrlService is private, use public modifier
namespace DotNetOpenAuth_Library
{
// make class public
public class EmbeddedResourceUrlService : IEmbeddedResourceRetrieval
{
private static string pathFormat = "{0}/Resource/GetWebResourceUrl? assemblyName= {1}&typeName={2}&resourceName={3}";
//private static string pathFormat = "{0}/Resource/GetWebResourceUrl";
public Uri GetWebResourceUrl(Type someTypeInResourceAssembly, string manifestResourceName)
{
if (manifestResourceName.Contains("http"))
{
return new Uri(manifestResourceName);
}
else
{
var assembly = someTypeInResourceAssembly.Assembly;
// HACK
string completeUrl = HttpContext.Current.Request.Url.ToString();
string host = completeUrl.Substring(0,
completeUrl.IndexOf(HttpContext.Current.Request.Url.AbsolutePath));
var path = string.Format(pathFormat,
host,
HttpUtility.UrlEncode(assembly.FullName),
HttpUtility.UrlEncode(someTypeInResourceAssembly.ToString()),
HttpUtility.UrlEncode(manifestResourceName));
return new Uri(path);
}
}
}
}
even if then the class does not shows up (has happended to a couple time)
clean solution
delete all bin folder from both project
rebuilt all
and error wont be there

c# Namespace already contains a definition for inherited class so how to add a constructor

I am carefully treading into WCF attempting to follow tutorials and convert my ASMX project to a new WCF project and I've stumbled upon a mystery about coding of my constructor in WCF.
My ASMX webservice allowed me to have a constructor (see: same name, no return value):
namespace sdkTrimFileServiceASMX
{
public class FileService : System.Web.Services.WebService
{
Database db;
string g_EventSource = "CBMI-TrimBroker";
string g_EventLog = "Application";
public FileService()
{
try
{
if (!EventLog.SourceExists(g_EventSource))
EventLog.CreateEventSource(g_EventSource, g_EventLog);
}
catch (InvalidOperationException e)
{
e.ToString();
}
}
My attempt to convert this to a WCF service app gives this compiler complaint:
The namespace 'sdkTRIMFileServiceWCF' already contains a definition for 'FileService'
Here is the WCF code (beginning snippet):
namespace sdkTRIMFileServiceWCF
{
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class FileService : IFileService // err msg points to this line
{
Database db;
string g_EventSource = "CBMI-TrimBroker";
string g_EventLog = "Application";
public FileService()
{
try
{
if (!EventLog.SourceExists(g_EventSource))
EventLog.CreateEventSource(g_EventSource, g_EventLog);
}
catch (InvalidOperationException e)
{
e.ToString();
}
}
This isn't related to the existence of the constructor. I am fairly sure this is a copy/paste error- perform a search for the word "FileService" in any files in your application and you will find another class or a namespace declaration with that name (in the same namespace).
Some things you could do:
do right mouse click > Find references on FileService.
Try a full search (ctrl+f) and search for FileService.
Check any partial classes for a second constructor.
Try clean solution and then rebuild the solution, see if this makes
any difference.
...

Categories

Resources