Compile at runtime with referenced assemblies - c#

I have created a function to compile C# code in a string in memory and use it at runtime. It works very well.
Next I created a small class library (lets call it mynew.dll) and placed it at c:\mylibraries\mynew.dll.
In my code I can add a referenced assembly. I do it like this:
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters
{
GenerateExecutable = false,
GenerateInMemory = true
};
parameters.ReferencedAssemblies.Add(#"c:\mylibraries\mynew.dll");
CompilerResults results = provider.CompileAssemblyFromSource(parameters, mycode);
Now, in my C# code-string (mycode) is a function called "Execute". When I declare the namespace and class from the mynew.dll in this function I get this error:
Could not load file or assembly 'MyNew, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null' or one of its dependencies. The
system cannot find the file specified.
When I copy the dll in the bin/debug folder of the main application is suddenly works, but I don't want this. I want the code, that is compiled at runtime, to use it, not the main application.
This I did before posting here:
Search Stackoverflow
Used Google
Tried to compile the dll to the disk and not in memory. This works... Problem now is that the dll is locked in IIS and I cannot delete it without restarting the IIS server; no option since the code can change any moment and I don't want to restart the server when people are on the website.
Is there a way to fix this?

Use Directory.GetFiles to find your assembly in the file system, use System.Reflection.Assembly.LoadFile to load the assembly, and then find the types required. Use interface that will be implemented by your concrete classes to avoid having to know the concrete types.
This thread should help: Finding objects that implement interface from loaded assembly -how to compare types?

Related

C# load a dll from file, then update that dll and then re-load the dll from file

tldr; I need to be able to reference a dll, update the dll and then reference the new dll
We have a service that takes weeks to update on our production system, and one use case that will require an update cycle of less than a day, our solution to this is to have the system load .dll files directly into itself and then use the classes/methods from those assemblies. The problem is that I can only seem to reference the first dll, any attempt at using a new one gets ignored.
The Functional code I have is
Assembly myAssembly1 = Assembly.LoadFrom("path.dll");
Type myType = myAssembly1.GetType("ClassName");
var myObject = Activator.CreateInstance(myType);
dynamic test = myType.InvokeMember("methodName", BindingFlags.InvokeMethod, null, myObject, null);
await test;
This method get's referenced a few times, and apparently the the first 'LoadFrom' is the only one that actually changes the app domain.
I understand creating a new App domain each time will solve the issue, but I can't seem to get it to work;
AppDomain domain = AppDomain.CreateDomain("MyDomain");
Assembly myAssembly1 = domain.Load(AssemblyName.GetAssemblyName(path));
//or = domain.Load(path);
//or = domain.Load("assemblyname");
Type myType = myAssembly1.GetType("ClassName");
var myObject = Activator.CreateInstance(myType);
dynamic test = myType.InvokeMember("methodName", BindingFlags.InvokeMethod, null, myObject, null);
await test;
AppDomain.Unload(domain);
So far any attempt at loading the .dll file into my app domain has resulted in the error 'Error: Could not load file or assembly 'assemblyName, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.', I'm assuming this is either me not putting the .dll file in the right location, or not handling the dependencies properly.
Any help would be greatly appreciated, I keep trying different things and seem to keep running into walls
thanks in advance
Cuan
Edit:
windows 10, .net standard 2.0
Difference between LoadFile and LoadFrom with .NET Assemblies? this does partially answer my question (thanks Hasan)
Slight caveat of this method - once either LoadFile or LoadFrom have been used -windows views the file as being in use by the application, and so you can't delete the dll file, you have to rename it and then add a new file
Another solution found was: changing the assembly name of the dll each time (versioning it) will work, as LoadFrom no longer sees the dll libraries as being the same. The benefit here is saving you from worrying about dependencies, as the assembly name is different each time you can use LoadFrom repeatedly. With this method you still need to rename old dll files though, or have a DB script that updates the file locations with each version.

CodeDom Reference ActiveX Resource

Very Similar Unanswered Question Here: CodeDom Reference VB6 dll
I've been searching google for the last few days regarding an issue referencing an ActiveX/COM assembly using an in-memory assembly compiled using CodeDom (Visual Basic Provider).
I'll try my best to explain what I'm trying to do and, sorry ahead of time if the technical lingo is off!
Visual Studio Process - Working!
Open Visual Studio.
Create new Windows Forms project (C#).
Right-click References -> Add Reference...
Select the Executable I want (Executable may be important here?)
The operation generates two references.
These classes, methods, etc. become available for us as I want.
CodeDom - Not working!
Pre-Work Steps Taken!
Used TibImp.exe on the Executable to generate the two .dll files (Interop).
Moved the generated .dll files to an accessible location (Desktop!)
CodeDom Code Example
string sCodeLocation = #"C:\Interop\Code.vb";
string[] sCodeContents = { File.ReadAllText(sCodeLocation) };
List<string> ReferenceList = new List<string>();
ReferenceList.Add("System.dll");
ReferenceList.Add(#"C:\Users\xxxxx\Desktop\Interops\CustomActiveX.dll");
string[] References = ReferenceList.ToArray();
CompilerParameters CompilerParams = new CompilerParameters();
CompilerParams.GenerateInMemory = true;
CompilerParams.TreatWarningsAsErrors = false;
CompilerParams.CompilerOptions = "/optimize /platform:x86";
CompilerParams.ReferencedAssemblies.AddRange(References);
VBCodeProvider Provider = new VBCodeProvider();
CompilerResults CompileResult = Provider.CompileAssemblyFromSource(CompilerParams, sCodeContents);
The Exception I'm Receiving
System.IO.FileNotFoundException: 'Could not load file or assembly 'CustomActiveX, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.'
List Of Attempted Resolutions (Based on Google Searches)
Ensure platform is set across the board (x86 in this case).
Use TibImp.exe to create Interop DLL's.
Tried referencing with CompilerParameters.EmbeddedResources (But don't understand it)
Tried referencing CompilerParameters.LinkedResources (But don't understand it)
Possible Workaround (Not tested -- not preferred)
As a workaround, I thought about maybe having my CodeDom referenced snippet of code load the assembly dynamically on its end. However, if Visual Studio can reference the COM / ActiveX object and use it, I want to make CodeDom do the same.

Exposing internals to a dynamically generated assembly

I have an assembly which generates a helper assembly. And I want to expose internals of that assembly to the generated one.
But the generated assembly is in-memory assembly so it has random name everytime it is generated.
I do understand that I cannot use InternalsVisibleTo() in that case.
So I see two options:
There is another InternalsVisibleTo thing that I can use but I don't know about it's existance.
There is a way to delete assembly from HDD at least at app exit (I'll survive if it stay after exception) if I use OutputAssembly of CompilerParameters class to generate assembly with specific name. I think it cannot be done due the file is beeing used by the app.
May be some one can help me with that situation.

Issue with AppDomain.CreateInstanceFromAndUnwrap

I'm using AppDomain.CreateInstanceFromAndUnwrap() to create an object in a different AppDomain. I couldn't get it to work because it kept throwing the following error at me:
Could not load file or assembly 'COMon, Version=2.0.4960.27874, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The module was expected to contain an assembly manifest.
However, I found that it is because it tries to load my DLL (which has the same name as my .NET assembly).
This is how I call the method:
_script = (Script)_appDomain.CreateInstanceFromAndUnwrap(Assembly.GetExecutingAssembly().Location, "COMon.Scripting.Script");
It works fine as long as there isn't a native DLL file with the same name as my .NET assembly. Why does this happen when I'm passing it the full path and filename of my .NET assembly?
when I'm passing it the full path and filename of my .NET assembly?
That's not how the method works. The first argument is the display name of the assembly. It is not a file name. The MSDN article recommends that you take a look at Assembly.FullName to learn more about display names.
So the normal CLR search rules will be in effect for finding the assembly. It will look in the GAC first, then in the probing path for the AppDomain. With a quirk that you didn't count on, the CLR does not pay attention to the filename extension for a file. The display name for an assembly doesn't specify it. So it considers an EXE and a DLL equivalent. Something you can see back in the trace for Fuslogvw.exe, the utility you always want to use when you have trouble like this. And in other places, adding a reference to an EXE works fine for example.
So it finds COMon.exe and that's a kaboom, it is not a managed assembly.
It isn't that clear what the proper workaround might be in your case, other than simply renaming the assembly. When you tinker with AppDomains then you typically also want to use AppDomainSetup and set the ApplicationBase or PrivateBinPath property.

Dynamically loading assemblies in asp.net

I'm currently trying to dynamically load an assembly from within a asp.net httphandler. I have a dll that is built as part of a seperate library and my project contains a reference to said DLL and is deployed along with the service with CopyLocal true. I create a throwaway object to get the assembly path and I have confirmed the existence of the dll within the Temporary ASP.NET Files folder, but calling GetTypes() throws an exception.
I do something like:
string assemblyPath = new SomeClassInAssembly().GetType().Assembly.Location;
Type[] types = System.Reflection.Assembly.LoadFrom(assemblyPath).GetTypes();
I cannot add the assembly to the GAC since that would defeat what I am trying to do with the service (think sandbox service that loads assemblies when necessary) and I cannot find anything that has been able to fix my problem thus far.
For reference I'm using VS 2008.
Since you compile your web application with a reference to the assembly i don't see your need to load it using Assembly.LoadFrom. The GetTypes should be available using:
Type[] types = typeof(SomeClassInAssembly).Assembly.GetTypes();
Doh, I finally hooked up to the exception and looked at the LoaderMessage and I was missing a referenced assembly.

Categories

Resources