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.
Related
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.
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?
I have been investigating adding mod support to a Unity3D game. I am new to Unity (but not .NET), so if it seems like I am missing something obvious, please let me know because I probably am.
I would like to be able to export objects, including custom scripts, directly from the editor. It is easy enough to get the assembly associated with the script. Any loose scripts in the project seem to get compiled down to Assembly-CSharp.dll. The problem comes in when I try to load the assembly at runtime in another Unity project. Calling Assembly.Load(byte[]) returns to me the assembly of the current project, not the one represented by the byte array. I am assuming this is because the AssemblyName of the assembly from the mod project and the host project are both identical (Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null).
As a test, I updated my Visual Studio project for the mod project, renaming it from Assembly-CSharp to something different. This gets wiped out by Unity, but I can do a build before that happens and get the assembly I am looking for with a name other than Assembly-CSharp. Loading this via Assembly.Load(byte[]) seems to do the trick in a very simple 'import a script to rotate a cube' sort of scenario. While this appears to work, I am really looking for something more auto-magic single step from the Unity editor.
Now, on to the question...
If I understand the problem, what I am looking for is a programmatic way to change the AssemblyName of an assembly after compilation. All suggestions & advice welcome.
Some possible routes I have investigated or considered:
1) A magic API method to change AssemblyName and save it.
This would be ideal, but I have yet to stumble across anything useful here.
2) Edit raw bytes of an assembly to change the name? Not sure if this is possible, but if I could safely and reliably overwrite some of the raw bytes of an assembly to change its name, that would be swell.
3) Create a new dynamic assembly containing the types in the original assembly. My experience with dynamic assemblies is limited. I can create one, but what I am unsure how to do (if possible) is copy types defined in the original assembly to the new dynamic assembly.
4) Use the CSharpCodeProvider to manually compile the assembly. Assuming Unity supports it, then I assume this might work, seems like a pain though. I have not yet investigated what it would take to locate all scripts and references needed for compilation. I'd like to avoid it if possible.
5) *Some magic with ilasm/ildasm.* Assuming there is unity equivalents.. Is this something do-able with theses tools?
6) Programmatically update the csproj file and recompile. Seems pretty hacky and a pain. Guessing I would need to have code to support both Visual Studio and MonoDevelop.
I would prefer to avoid external tools and recompilation so one of the first three options would be ideal. Thoughts, ideas? Thanks!
Bonus Question:
My 'import a script to rotate a cube' test seemed to work, however in the log during the loading of the assembly, I see the two messages below. That is not the assembly name I used, not sure where it came from. Should I be concerned? Is this going to come back to bite me later?
> Non platform assembly: data-0B9D7940 (this message is harmless)
> Fallback handler could not load library C:/Dev/Test
> Projects/ModTest/Build/ModHost1/ModHost1_Data/Mono/data-0B9D7940.dll
The solution I ended up going with was using Mono.Cecil. Luckily, the Mono.Cecil library is available in the Unity editor, no need to deploy any extra libraries with my mod tools. Mono.Cecil worked great for renaming the assembly after it was compiled. Here is some code I'm using to make it happen:
// Have Mono.Cecil load the assembly
var assemblyDefinition = Mono.Cecil.AssemblyDefinition.ReadAssembly(assemblyFile.FullName);
// Tell Mono.Cecil to actually change the name
assemblyDefinition.Name.Name = newAssemblyNameNoExtension;
assemblyDefinition.MainModule.Name = newAssemblyNameNoExtension;
// We also need to rename any references to project assemblies (first pass assemblies)
foreach (var reference in assemblyDefinition.MainModule.AssemblyReferences)
{
if (Utilities.IsProjectAssembly(reference.Name))
{
reference.Name = Utilities.GetModAssemblyName(reference.Name, this._modName);
}
}
// Build the new assembly
byte[] bytes;
using (var ms = new MemoryStream())
{
assemblyDefinition.Write(ms, new Mono.Cecil.WriterParameters() { WriteSymbols = true });
bytes = ms.ToArray();
}
I have struggled with this for quite some time before finding the right approach.
While it is certainly possible to change the Assembly Name from the one generated by Unity (either by renaming it in the .csproj file before compilation, or after-compilation with Mono.Cecil (although I have not tried that approach myself)), be aware of the fact that if you want to load an asset bundle that has prefabs with scripts associated from the assembly in question, you will not be able to properly load that asset bundle, even if you have loaded your assembly first (Unity issues warnings of the sort: "the referenced script on this Behaviour is missing"). The reason is that Unity stores the Assembly Name with the script name as well, and requires that the type/script come from that specific Assembly (which makes sense).
What this boils down to is that you need to change the Assembly Name from within Unity. To do that, you can create an Assembly Definition asset in the scripts (root) folder and set the name to the one you wish to have for your Assembly Name. The technical details can be found at: https://docs.unity3d.com/Manual/ScriptCompilationAssemblyDefinitionFiles.html
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.
My c# application uses two different pre-packaged parsers, that each use a different strong version of Antlr.Runtime.dll.
The application throws a FileLoadException, apparently because it correctly loaded only one of the versions and could not find the other.
The <app>\bin\x86\Debug directory contains only one copy of the dll.
Note that I cannot use assembly aliases since my code doesn't directly call the versioned assemblies - they are only indirectly called via the parser assemblies.
Is this a matter of instructing Visual Studio to place the differently versioned dlls in separate directories since they have the same name? I'd like to avoid using the GAC as far as possible.
I've read many "side by side" and related articles, but none seem to address this question.
I'm in no way a SxS expert but at my company when this happens they create a different folder for each assembly and a manifest to load them.
Solved this problem by hooking AppDomain.AssemblyResolve:
AppDomain.CurrentDomain.AssemblyResolve += (s, e) =>
{
Assembly result = null;
if (e.Name ==
#"Antlr3.Runtime, Version=3.1.3.42154, Culture=neutral, PublicKeyToken=3a9cab8f8d22bfb7")
result = Assembly.LoadFile(
#"C:\src\NHibernate-3.0.0.GA\lib\net\3.5\Antlr3.Runtime.dll");
return result;
};