0

I have a windows service that is being passed a fully qualified method name. It then uses this name to look up the appropriate method in whatever external DLL it happens to be in. My service has a reference to this DLL and everything seems to work correctly when I reference and load the DLL in a hard coded manner as such:

var DLL = Assembly.LoadFile(@"C:\Users\ishiim\Desktop\TFS\IRSystem\Technical Utilities\Monitoring\TestModules\PingerTests\PingerTests\bin\Debug\PingerTests.dll");
Type type = DLL.GetExportedTypes()[0];

However, I will not know the exact location of these DLLs ahead of time, and of course in this example I'm loading the dll that is produced in the solution's directory, something that won't be there for the service when it's installed.

What I am looking to do is simply load the dll by assembly name from the directory the service is currently running in - and indeed in my bin/debug folder the dll is there. But when I attempt to load it from the local directory using the fully qualified assembly name, the type returns null:

// Example type TestModuleNamespace.TestModuleClassName
// Example assembly name TestModuleClassName (They are the same in my case)
Type type = Type.GetType(typeName + ", " + assemblyName);

I should mention that in the preceeding code snippet - the code worked fine when the Class Library was in the same solution as my service, but now that I have separated the two, this method will no longer work, and the type returns null.

I have even attempted to load the DLL(s) using code that uses the current execution directory (allegedly C:\Windows\System32 even though the service exe is NOT there) and this has had no success either:

Assembly serviceAssembly = Assembly.LoadFrom(Path.Combine(Environment.CurrentDirectory, assemblyName + ".dll"));
AssemblyName[] referencedAssemblies = serviceAssembly.GetReferencedAssemblies();
List<Assembly> assemblyLst = AppDomain.CurrentDomain.GetAssemblies().ToList();
foreach (AssemblyName name in referencedAssemblies)
{
    Assembly.Load(name);
}

There have been many answers on SO about this but I have not been able to understand them as I am new to C# and .NET in general. My biggest point of confusion is simply this - if an external DLL is referenced in my service, and if that DLL is then copied into the bin directory with that service, then when the service is installed, why must the DLL be "loaded" and why can't it find it in its current "path" if it's supposedly in the same directory?

This feels harder than it should be and that is most likely due to my lack of understanding about how assemblies are loaded at runtime. (libraries, DLLS, these words seem to be used interchangeably for some reason - what is the real term?)

EDIT ------------------------------------------------

I feel like I've caused some confusion over what I am trying to do. My program does the following:

1-Takes a list of tasks that contain a fully qualified method name 2-Calls the method, via reflection, in whatever DLL it resides in and returns the value

These DLLs and their method names are not known to my program. They are passed in. I can reference these libraries at compile time, as other developers will be writing them, but as to when they are called, and what methods will be called, are completely unknown before hand to my service.

It seems like I have two options: 1-Make a reference to each DLL I am given that may potentially get called and get the type by using the fully qualified assembly name for a call by reflection 2-Place all the DLLs in a known location and load them by their exact path location on the filesystem and call them by reflection.

Solution 1 was working when the dll was developed and compiled in the same solution as my service. The second I moved the dll into its own solution, this code Type type = Type.GetType(typeName + ", " + assemblyName); started returning a null.

Solution 2 is also fine, however I cannot seem to call the assemblies with a relative path. I'd have to store the current path of the executing assembly (my service) and then append that to the target assembly name to get the type and then use reflection to invoke the method. This way seems very ugly to me but so be it.

EDIT ONCE MORE ---

This is what I ended up doing. I absolutely hate this, I don't understand why I have to LOAD THE ASSEMBLY AGAIN if I've already made a reference but this works:

String path = System.Reflection.Assembly.GetExecutingAssembly().Location;
testModuleLocation = System.IO.Path.GetDirectoryName(path);
Assembly asm = Assembly.LoadFile(System.IO.Path.Combine(testModuleLocation, assemblyName + DLL_FILE_EXT));
Type type = asm.GetExportedTypes()[0];
object instance = Activator.CreateInstance(type);
MethodInfo method = type.GetMethod(methodName);
method.Invoke(instance, arguments);

The "TestModuleLocation" being some sub-folder of my service that I will have to dump all the DLLs into when I build and then install the service.

I think there is a lot about a "DLL" that isn't simply a library and in no way works as it does in any other language I've used. I guess I have a lot of research to do to re-learn how a library works on windows machines.

Iofacture
  • 655
  • 3
  • 13
  • 39
  • Possible duplicate of [C# - Correct Way to Load Assembly, Find Class and Call Run() Method](http://stackoverflow.com/questions/1137781/c-sharp-correct-way-to-load-assembly-find-class-and-call-run-method) – MethodMan May 16 '17 at 20:27
  • Why aren't you just using references instead of Assembly.Load? I ask because you wrote `if an external DLL is referenced in my service`. – Conrad Frix May 16 '17 at 20:27
  • Because I won't know what is going to be called until runtime - the fully qualified method name is passed to me by a database object. – Iofacture May 16 '17 at 20:30
  • Not knowing the method name you need to use isn't the same as not knowing what assemblies you're going to use. – Conrad Frix May 16 '17 at 20:32
  • I will have multiple DLLs with multiple methods and I won't know ahead of time which ones to call - How can I call the method via reflection when `Type type = Type.GetType(typeName + ", " + assemblyName);` returns a null type? – Iofacture May 16 '17 at 20:34
  • Try `serviceAssembly = Assembly.GetExecutingAssembly()` instead of loading it – Conrad Frix May 16 '17 at 20:36
  • Hi conrad, I did that and then i Iterated over the list returned from `getExportedTypes()` - the assembly I have referenced is not in that list. What does that mean? – Iofacture May 16 '17 at 20:45
  • 1
    Why would you use GetExportedTypes on the ExecutingAssembly? That iterates on the publicly available types (which you already have direct access to). I would have expected `serviceAssembly = Assembly.GetExecutingAssembly() ;` then `serviceAssembly.GetReferencedAssemblies();` then use reflection to load your type. Unless you did that and I misunderstood – Conrad Frix May 16 '17 at 21:40
  • I'll tell you why Conrad. Because I literally have *no idea what im doing*. The DLL concept is so completely different from a jar file in java (the land from which I come) that I am utterly and completely lost. Doing it this way, getting the referenced assemblies, only shows me things i already have in my "usings". I have to repeat. I will not explicitly know what namespace.class.method combination I am getting into this function so I cannot create a using statement for it - I must load the assembly by name and then invoke it. So far this methodology is not working. – Iofacture May 16 '17 at 22:14

1 Answers1

2

You can get the path of the executable and set current directory to it. Here is example how to do it:

String path = System.Reflection.Assembly.GetExecutingAssembly().Location;
path = System.IO.Path.GetDirectoryName(path);
Directory.SetCurrentDirectory(path);

Store Dll's in the installation folder of the executable of your Service and your Relative path's will work after this steps.

Samvel Petrosov
  • 7,580
  • 2
  • 22
  • 46
  • I have used the Visual Studio 2015 Installer component in order to install my service - So there is some default location being used. It looks like I will need to first learn about how to specify an installation folder before I can try utilizing this method. I will check back with this once I figure that out. – Iofacture May 16 '17 at 20:48
  • Hello Mr. Petrosov - when I try this, and simply call Assembly.Load on the assemblyname.dll - I get an exception "System.ArgumentException: Absolute path information is required. at System.Security.Util.StringExpressionSet.CreateListFromExpressions(String[] str, Boolean needFullPath)" – Iofacture May 16 '17 at 22:29
  • See my second edit - I ended up using this method. It feels wrong since I'm already referencing the dll in my service, but I guess this way I don't have to do that anymore - I think. – Iofacture May 16 '17 at 23:27