I'm building a COM Visible Object that needs to get called from a software written in Sybase Powerbuilder 11.5.
I'm using C# and .Net Framework 4.7.2.
I ususally build this kind of objects with no problem and register them using regasm /codebase because that's the way Powerbuilder likes them.
Now I'm trying to build an object that references the latest version of these nuget packages:
Microsoft.Graph
Microsoft.Graph.Auth
Microsoft.IdentityModel.Clients.ActiveDirectory
Graph.Community
I have my class library project with a com visible class.
I built a console application that references that library (it does not use COM server) to test the code and everything works fine on my development machine from the command line.
I can also run the console application from my target machin with no issues.
When I try to run the Powerbuilder application that uses the COM object, it throws an error:
Could not load file or assembly 'Microsoft.Graph.Core, Version=1.23.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies.
I have that dll in assembly's folder, but it is version 1.24.0.0.
I have two questions:
Why does the console application work while the COM object doesn't, given that they are using the same assemblies?
How I solve this versioning problem in the COM version of the object?
I found the solution to the problem but did not fully understand it.
It was a cross reference problem, where packages A and B were both dependent on package C, but required different versions.
I don't understand why this error shows up only when using COM Interface and not with the console application.
Here is what I did to solve:
// Define the AssemblyResolve event in the constructor of the COM Object.
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
// Get the path where COM Object's dll is
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
_dllPath = System.IO.Path.GetDirectoryName(path);
Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
// Get the name of the assembly that failed loading
var name = new AssemblyName(args.Name);
// Manage the assembly that I know will cause problems
string bindingAssembly = "Microsoft.Graph.Core";
if (name.Name == bindingAssembly)
{
// Load the assembly from the COM Object's folder, and use whatever version there is
return Assembly.LoadFrom(System.IO.Path.Combine(_dllPath, bindingAssembly + ".dll"));
}
// I should not get here. If I do I need to add another manual assembly load for the dll that failed loading
return null;
}
Related
I'm adding my own custom loader to AssemblyResolve to load some embedded resources in a .net library;
AppDomain.CurrentDomain.AssemblyResolve += (sender, e) => { return Domain.Assemblies.LoadResource(e.Name, System.Reflection.Assembly.GetExecutingAssembly()); };
public static System.Reflection.Assembly LoadResource(string fileName, System.Reflection.Assembly assembly)
{
fileName = assembly.GetName().Name + "." + fileName.Split(',')[0] + ".dll";
var resFilestream = assembly.GetManifestResourceStream(fileName);
byte[] ba = new byte[resFilestream.Length];
resFilestream.Read(ba, 0, ba.Length);
var byteArray = ba;
resFilestream.Close();
resFilestream.Dispose();
return System.Reflection.Assembly.Load(byteArray);
}
This works fine in many different environments(Winforms, Azure apps/webjobs/functions), but when I try to execute this code within an asp.net(4.7.2) MVC site, it breaks the anti-forgery? The issue seems to be related a DLL not being loaded correctly, and causes three seperate errors
TypeInitializationException: The type initializer for 'System.Web.Helpers.Claims.ClaimsIdentityConverter' threw an exception.
FileLoadException: Could not load file or assembly 'Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. Invalid pointer (Exception from HRESULT: 0x80004003 (E_POINTER))
NullReferenceException: Object reference not set to an instance of an object.
If I remove the object initialising the class within the assembly loading dll, the site works with no error. If I put it back in it breaks. I also tried moving the code from the global.asax to in-line c#, but as soon as you run it, the next load of a page with a forgery token will error in the same way.
UPDATE
Having run Fusion++ to check the DLL loading, I can see that the DLL that is erroring actually has the exact same load failure in both situations(A fairly generic "Could not find assembly"). But only when I load my DLL does it present itself as an exception.
The problem is not with your code, but this dependecy Microsoft.IdentityModel: to exist this dependency and possibilty to consume into Windows client environment, probally the dll and dependecies needs to be installed at GAC (Global Assembly Cache); if Microsoft.IdentityModel not exists into GAC Folder, the code returns the error.
It's possible to install the Windows Identity Foundation 3.5 with Visual Studio or from Windows Features install the Windows Identity Foundation 3.5.
MyApp is a C# .NET v4.5.2 WPF app. MyAssembly is a Matlab .NET assembly I created containing MyClass. MyAssembly requires MCR v9.1. MyApp performs various tasks; when one requires Matlab and it tries to instantiate MyClass then a popup window displays:
The procedure entry point H5Rdereference could not be located in dynamic link library C:\Program Files\MATLAB\MATLAB Runtime\v91\bin\win64\libmat.dll.
MyApp contains a version of hdf5.dll (obtained from Nuget package HDF.PInvoke v1.10.1), and I discovered that MCR 9.1 also contains a (different) version of hdf5.dll in C:\Program Files\MATLAB\MATLAB Runtime\v91\bin\win64. When I replaced MyApp's version of that dll with MCR 9.1's version, MyApp is able to instantiate MyClass, but then MyApp is no longer able to perform its HDF5 tasks; calls to H5.open() throw:
Unable to load DLL 'hdf5.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)'
MyClass doesn't require HDF5, so why is the MCR loading its copy of that DLL?
How do I get around this conflict?
Update 1: I moved all of MyApp's non-Matlab-related use of HDF5 into a separate app domain, but the problem persists. It is as if HDF5.dll is getting loaded into the primary app domain even though the code that loads it is executing in a separate app domain.
HDF5.dll is a native DLL and thus not bound by AppDomain boundaries.
I have a problem with MEF and using a plugins folder.
I have a main app that supports plugins via MEF. The main app does not reference the assemblies containing the .NET Task type for multithreading but one or more of the plugins do.
The plugins are located in a Plugins folder and I'm using a DirectoryCatalog.
I keep getting ReflectionTypeLoadException being thrown by MEF on
Unable to load one or more of the requested types. Retrieve the
LoaderExceptions property for more information.
The LoaderExceptions property contains a FileNotFoundException
"Could not load file or assembly 'System.Threading.Tasks,
Version=1.5.11.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or
one of its dependencies. The system cannot find the file
specified.":"System.Threading.Tasks, Version=1.5.11.0,
Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
The plugin is referencing System.Threading.Tasks via a Microsoft NuGet package reference.
This is my helper method:
public static void Compose(IEnumerable<string> searchFolders, params object[] parts)
{
// setup composition container
var catalog = new AggregateCatalog();
// check if folders were specified
if (searchFolders != null)
{
// add search folders
foreach (var folder in searchFolders.Where(System.IO.Directory.Exists))
{
catalog.Catalogs.Add(new DirectoryCatalog(folder, "*.dll"));
}
}
catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
// compose and create plug ins
var composer = new CompositionContainer(catalog);
composer.ComposeParts(parts);
}
public class MEFComposer
{
[ImportMany(typeof(IRepository))]
public List<IRepository> Repositories;
[ImportMany(typeof(ILogging))]
public List<ILogging> LoggingRepositories;
[ImportMany(typeof(IPlugin))]
public List<IPlugin> Plugins;
}
This is the code I'm using to invoke MEF and load the plugins.
public void Compose()
{
// try to connect with MEF types
try
{
var parts = new MEFComposer();
MEFHelpers.Compose(new[] { Path.Combine(Application.StartupPath, "Plugins") }, parts);
RepositoryFactory.Instance.Repository = parts.Repositories.FirstOrDefault();
Logging.Repositories.AddRange(parts.LoggingRepositories);
foreach (var plugin in parts.Plugins)
{
this.applicationApi.Plugins.Add(plugin);
plugin.Connect(this.applicationApi);
}
}
catch
{
// ERR: handle error
}
}
Why is MEF not able to load the plugins even though the Microsoft.Threading.Tasks.dll and related assembly files are present in the Plugins folder, but not the main application bin folder? And is there any way of telling MEF to search the Plugins folder for assembly dependencies?
Having a plugin model means I can't anticipate what assemblies a plugin may be referencing so I cannot include them in the main bin folder for the app, which is why I want all related plugins and plugins dependencies to be in the plugins folder.
You have run into an essential problem when supporting 3rd party plugins. Your problem is that when you are loading the plugin, the runtime will search for its references when needed only in the specified folders your AppDomain knowns of. That would be the WorkingDirectory of that process, then path etc.
Essentially, you are loading an Plugin that requires System.Threading.Tasks. That DLL lies within your /Plugin folder. As .net loads your plugin, it will search for that assembly but has no way in finding it as it is located within the /Plugin folder and fails.
There are a couple of solutions to that.
Do not use a Plugin folder
That would be the most simple solution, when all assemblies (including references of the 3rd party lib) are located in your WorkingDirectory, .net will have not trouble finding all references of that plugin.
Add the Plugin folder to your Probing path
This will extend the paths .net will search the 3rd party references:
https://learn.microsoft.com/en-us/dotnet/framework/deployment/how-the-runtime-locates-assemblies#locating-the-assembly-through-probing
Use AppDomains for each plugin.
An AppDomain is my choice of go here as it allows you to load the assembly not only in its "own" container, but also can simulate the Working directory only for the plugin. That can come handy if on of the Plugins uses the same framework as your application but in a different version for example.
Load the dependencies by yourself
That would be the "straight forward" method of solving this. You can load every assembly as ReflectionOnly, Determinate all dependencies and then load these. That will almost Garantie to work.
4.1. AssemblyResolve event
This is just another way how to "redirect" .net to load assemblies from the PluginFolder
https://learn.microsoft.com/en-us/dotnet/api/system.appdomain.assemblyresolve?view=netframework-4.8
Edit:
There is also a certain problem with the AssemblyCatalog, it uses Assembly.Load instead of Assembly.LoadFrom to load the given assembly. That is an essential part of your problem, as LoadFrom would probe the path where the assembly originates from for its dependencies where Load does not.
https://github.com/JPVenson/MSEF/blob/master/JPB.Shell/JPB.Shell.MEF/Model/StrongNameCatalog.cs
You could use an Catalog like this one that is using LoadFrom instead.
Disclaimer: I am the creator of that project.
I have added third party reference (Json newtonsoft) dll in my script component (using edit script option), but when i run the package, I am getting an error
Could not load file or assembly 'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The system cannot find the file specified.
Any suggestions?
I will not be able to add the dll in GAC.
I am using SQL Server 2008.
By "Running," I assume running from agent/command-line is failing? It should work from within BIDS/SSDT. The short answer is the DLL must be registered with the GAC or you can download the source code and add that project into the script task and then reference said project.
Looking at the project, it should be a strongly signed DLL (based on presences of Dynamic.snk) and thus capable of being added to the GAC. Oh, but you state you will not be able to add it into the GAC, implying it's a permission not a capability issue.
If that's the case, either compile the project in with the source or surround it with a web service wrapper and then reference the service.
I also saw this answer, seems you can try loading the references dynamically.
Automated deployment of mixed SSIS / DLL solution
You can using Reflection to load dll at runtime from file system without needing to install in GAC . This is helpful if permission to install in GAC is not availaible .
//Add a Static Constructor which is guaranteed to be called exactly once
// “before the first instance is created or any static members are referenced.”,
// so therefore before the dependent assemblies are loaded.
static ScriptMain()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
//Provide path to dll stored in folder on file system
static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string path = #"D:\DLL\";
return System.Reflection.Assembly.LoadFile(System.IO.Path.Combine(path, "Newtonsoft.dll"));
}
Ofcourse you need to also Add Reference to dll in script task .
I have added third party reference (Json newtonsoft) dll in my script component (using edit script option), but when i run the package, I am getting an error
Could not load file or assembly 'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The system cannot find the file specified.
Any suggestions?
I will not be able to add the dll in GAC.
I am using SQL Server 2008.
By "Running," I assume running from agent/command-line is failing? It should work from within BIDS/SSDT. The short answer is the DLL must be registered with the GAC or you can download the source code and add that project into the script task and then reference said project.
Looking at the project, it should be a strongly signed DLL (based on presences of Dynamic.snk) and thus capable of being added to the GAC. Oh, but you state you will not be able to add it into the GAC, implying it's a permission not a capability issue.
If that's the case, either compile the project in with the source or surround it with a web service wrapper and then reference the service.
I also saw this answer, seems you can try loading the references dynamically.
Automated deployment of mixed SSIS / DLL solution
You can using Reflection to load dll at runtime from file system without needing to install in GAC . This is helpful if permission to install in GAC is not availaible .
//Add a Static Constructor which is guaranteed to be called exactly once
// “before the first instance is created or any static members are referenced.”,
// so therefore before the dependent assemblies are loaded.
static ScriptMain()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
//Provide path to dll stored in folder on file system
static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string path = #"D:\DLL\";
return System.Reflection.Assembly.LoadFile(System.IO.Path.Combine(path, "Newtonsoft.dll"));
}
Ofcourse you need to also Add Reference to dll in script task .