I have a c# library which provides some functionallity to upload data onto connected (android) devices. The dll itself gets exported via UnmangedExports to be used by an delphi application.
Here is the function which gets called by the delphi application:
[DllExport]
[return: MarshalAs(UnmanagedType.LPStr)]
public static string getDevices()
{
try
{
var devices = string.Empty;
var collection = new PortableDeviceCollection();
collection.Refresh();
foreach (var device in collection)
{
device.Connect();
if (devices != string.Empty)
{
devices += ";";
}
devices += device.FriendlyName;
device.Disconnect();
}
return devices;
}
catch (Exception e)
{
SomeClass.WriteErrorToLogFile(e);
return "ERROR";
}
}
Here is the class PortableDeviceCollection:
public class PortableDeviceCollection : Collection<PortableDevice>
{
private readonly PortableDeviceApiLib.PortableDeviceManagerClass _deviceManager;
public PortableDeviceCollection()
{
this._deviceManager = new PortableDeviceApiLib.PortableDeviceManagerClass();
}
public bool Refresh()
{
this._deviceManager.RefreshDeviceList();
// Determine how many WPD devices are connected
var deviceIds = new string[1];
uint count = 1;
this._deviceManager.GetDevices(null, ref count);
if (count > 0)
{
// Retrieve the device id for each connected device
deviceIds = new string[count];
this._deviceManager.GetDevices(deviceIds, ref count);
foreach (var deviceId in deviceIds)
{
Add(new PortableDevice(deviceId));
}
return true;
}
else
return false;
}
}
I can create the dll with visual studio and use this inside of the delphi application. When the delphi application calls the getDevices() function, i get an error on the instantiation of the PortableDeviceCollection class:
The file or assembly "Interop.PortableDeviceApiLib, Version = 1.0.0.0,
Culture = neutral, PublicKeyToken = null" or a dependency of it was
not found. The assembly is created by a runtime that is more recent
than the currently loaded runtime and can not be loaded.
ProductXY.PortableDeviceCollection..ctor()
ProductXY.ProductXYMain.getDevices()
The targetframework for the c# project is set to .Net Framework 4. Using any lower version i get an error when i try to compile the project:
The primary reference "Interop.PortableDeviceApiLib" could not be
resolved because it has an indirect dependency on the .NET Framework
assembly "mscorlib, version = 4.0.0.0, Culture = neutral,
PublicKeyToken = b77a5c561934e089", which is a higher version 4.0.0.0
than version 2.0.0.0 in the current target framework.
Please note. I have neither written the c# library nor the delphi application. Both have worked together for years. Now i have to add a functionallity to the c# library. I have not added any code to the project. I just tried to compile it again and use the dll. The only thing i did was updating the RGiesecke.DLLExport.Metadata via NuGet Packetmanager. Without updating i got an error
"Microsoft.Build.Utilities.ToolLocationHelper could not find
ildasm.exe"
I am aware of this Enumerating Windows Portable Devices in C# question. But my error is thrown before the code which is treaded by the question is reached. I still tried the solution to the question, but the action (deassamble, find and replace in the dll) which is described in the answere has already been done (otherwise my code would not have compiled).
The error message doesn't make sense to me. Interop.PortableDeviceApiLib is a COM-Lib which is not available for download in different framework-versions. I think I am missing something here.
Can anyone help me?
I was finally able to solve this problem. To be honest I don't know what finally solved this. For every one who stumbles up on this, here are the things i tried to fix this problem. They are in no specific order (since i tried everything multiple times):
Updating the RGiesecke.DLLExport packet
Changing the plattform in the Konfigurations-Manager to x86
Disassamble, edit and reassable the Interop.PortableDeviceApiLib like in this question (answeres of Christophe Geers and Bruno Klein)
Delete the reference to the Interop.PortableDeviceApiLib
Delete the reference to the Interop.PortableDeviceTypesLib
Readding the reference to the Interop.PortableDeviceApiLib
Readding the reference to the Interop.PortableDeviceTypesLib
Rebuild the project
Setting the Interoptyp embeddet on both to false (I found various statements to NOT do this, but the project was set up like this when i got it and it worked (be carefull)) on both Interop-Libs.
At least this helped me.
Related
I'm a bit confused here and haven't gotten much help from google. Here's what I'm trying to do:
public Boolean LoadModule(String moduleHandle)//name of module MUST match its .dll name. Name of AppDomain is the same as the Handle.
{
try
{
AppDomain moduleDomain = AppDomain.CreateDomain(moduleHandle);
String pathToDll = #"C:\IModules.dll"; //Full path to dll you want to load
Type moduleType = typeof(IModule);
IModule loadedModule = (IModule)moduleDomain.CreateInstanceFromAndUnwrap(pathToDll, moduleType.FullName);
ModuleList.Add(loadedModule, moduleDomain);
Broadcast("Module loaded: " + moduleHandle, ModuleManagerHandle);
return true;
}
catch (Exception e)
{
//console writeline the error? probably cant
OutputBox.AppendText(e.ToString() + Environment.NewLine);
return false;
}
}
I thought I finally had this figured out but when I try to instantiate the IModule (ConsoleModule, in this case), I get the following error:
System.MissingMethodException: Constructor on type 'IModules.IModule' not found.
I take this to mean that I need to have a constructor, as if this were a class object instantiating itself on this function call, but I cannot make an interface have a constructor.
I have seen other threads suggesting ways to solve this problem, but they use assembly instead of appdomain, which will mess up the ability to unload modules. I'm concerned that without the ability to unload modules the application will suffer memory bloating over time.
The end goal is to be able to write a module, leave the program running and load/unload the modules during runtime without any changes to the core program, and add functionality on the go.
Anyone know of a workaround or maybe a better way to deal with dynamic module loading and unloading?
This is fixed with .NET 5.0 AssemblyLoadContext:
var basePath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
AssemblyLoadContext moduleAssemblyLoadContext = new AssemblyLoadContext(moduleHandle, true);
Assembly moduleAssembly = moduleAssemblyLoadContext.LoadFromAssemblyPath($"{basePath}\\{moduleHandle}.dll");
Type[] types = moduleAssembly.GetTypes();
foreach (Type type in types)
{
// Does this class support the transport interface?
Type typeModule = type.GetInterface("IModule");
if (typeModule == null)
{
// Not supported.
continue;
}
// This class supports the interface. Instantiate it.
IModule loadedModule = moduleAssembly.CreateInstance(type.FullName) as IModule;
if (loadedModule != null)
{
loadedModule.LoadedModule(this);
ModuleList.Add(loadedModule, moduleAssemblyLoadContext);
Broadcast("Module loaded: " + moduleHandle, ModuleManagerHandle);
OutputTextBox.AppendText(moduleHandle + " was loaded." + Environment.NewLine);
// Successfully created the interface. We are done.
return true;
}
}
return false;
Can't find the source anymore but found it looking for a related problem (you can find it on MSDN anyways). This successfully loads and unloads assemblies into their context. User must set the isCollectible value to TRUE to enable full unloading.
Only issue I had is that .NET 5.0 is not compatible -with itself- yet and libraries loaded as .NET 5.0 into .NET 5.0 programs will give a BadImageFormatException when trying to load the assembly. To fix, set the LIBRARY to the next most recent target framework (in my case, .NET Core 3.1) and move the newly compiled dll to wherever it goes and the application should run using the new dll.
The error tells you that there is no default (empty) constructor found for the type IModule. Since IModule is an interface, the message seems to make some sense.
Resulotion: Instantiate a class that implements IModule. An interface can never be intantiated on its own.
To instantiate the class, just one line neds to be changed:
Type moduleType = typeof(ClassThatImplementIModule);
You can still cast the instance to IModule
I am trying to use a service created by somebody else. I have only the service dll. But the instructions provided to me are not complete. This is an example how to use the service but they missed the instantiation of the variable criteria. How can I figure out what type is the variable? . If possible, I prefer to solve the issue without decompiling the dll.
public static void GetData()
{
//ServiceReference1.ServicesClient client = null;
try
{
criteria.Settings = new CheckCall.CriteriaSettings();
CheckCall.Criteria criteria1 = new CheckCall.Criteria();
criteria1.Settings = new CheckCall.CriteriaSettings();
criteria1.AsgnType = CheckcallAssignTypeEnum.Driver;
criteria1.TractorNumber = "Tractor1";
criteria.Expressions = new List<CheckCall.CriteriaExpression>();
criteria.Expressions.Add(new CheckCall.CriteriaExpression
{
Conjuction = CriteriaSetting.ConjuctionEnum.OrOp,
Criteria = criteria1
});
}
catch(Exception ex)
{
Console.Write(ex);
}
}
You could use JetBrains .Net decompiler - dotPeek.(https://www.jetbrains.com/decompiler/)
Decompile the dll, then add the decompiled project to your solution and debug it. The only problem is wether you will receive a project that can be built after decompilation
If the decompilation is not an option/project has errors after decompilation - you can see the contents of the .dll via Object Browser. Find the .dll in the References, right click on it and choose "View in Object Browser". You will see the namespaces, classes and their methods of the .dll. Enter any of the property names of criteria object (Settings or Expressions) and Object Browser will filter everything out
I'm currently at a point where I really need some advice. In our company we mix many languages that are unmanaged like PowerBuilder or pure C++. For now we need a lot of code out of .NET. So my first purpose was why not just make a plugin system via COM.
This is what I'm trying to achieve at the moment. Everything works fine, the plugin system can load plugins. But as soon as I expose my plugin system to COM and try to load a plugin via, as an example VBS, I always get the the following error:
Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information
From my research I found that this problem occurs when, during Assembly.GetTypes(), the type couldn't be loaded. Which is really strange as I have put my interface into it's own assembly and reference it by my plugin system and the plugin, so that the interface is always the same.
But as I debugged a bit more I found that actually the problem seems to not be my interface. The load problem actually happens when he tries to load the type of my class of the plugin which inherits of the interface. Maybe it's the interface or something else.
But to point out for now, as long as I use the plugin system via managed code directly there is no problem. As soon as I do it via COM I receive this error. So I assume that I'm missing or messing something up with COM.
At the moment I really need a solution but not just the solution an explanation of the solution would also be really nice because I actually want to understand what I messed up.
Here is the download link to the solution. Maybe you find someting.
Solution
[ComVisible(true)]
public bool Initialize(string dllPlugin)
{
try
{
string dllPluginPath = Assembly.GetExecutingAssembly().Location.Replace("IncoPluginSystem.dll", "") + "plugins\\";
string completePluginPath = dllPluginPath + dllPlugin + ".dll";
if (!File.Exists(completePluginPath))
{
pluginError = "The plugin could not be found in the plugins directory.";
return false;
}
plugin = Assembly.LoadFile(completePluginPath);
if (plugin == null)
{
pluginError = "No plugin loaded. Pls initialize first";
return false;
}
foreach (Type t in plugin.GetTypes())
{
if (t.GetInterface("IPlugin") != null)
{
pluginInstance = Activator.CreateInstance(t) as IPlugin;
}
}
return true;
}
catch (Exception e)
{
pluginError = e.Message;
return false;
}
}
The Problem happens in the foreach loop where u access the Types of the assembly.
I added a more detailed Exception handling. This is the Error when i expose it to COM
Could not load file or assembly 'IncoPluginSystemInterface, Version-1.0.0.0, Culture=neutral, PuplicKeyToken=497bca4abf979e3e' or one of its dependencies. The system cannot finde the file specified.
Fusion Log:
WRN: Assembly binding logging is turned OFF.
Note: There is some performance penalty associated with assembly bind failure logging.
The references of the DLL are just the standard ones of creating a .net library. Nothing added.
The structure of the plugin system is the following
IncoPluginSystem.dll
IncoPluginSystemInterface.dll
plugins
->IncoPluginSystemInterface.dll
->TestPlugin.dll
So the missing dll is actually at the 2 spots where needed. Maybe it's because the IncoPluginSystem.dll is loaded as a COM object so maybe the path is incorrect but I'm not sure. I tested and changed all I can imagine. So it definitly has to be something I messed up with COM.
I was able to locate the problem. It's actually really that my DLL is in the wrong place. As soon as i put the DLL into the GAC it works. So for now my question is - how can i determine where to put the dll where it was missing. In my exception i only see what dll is missing but not the path. Does anyone know how to determine this?
Ok I was able to answer it myself. To Explain what was wrong. What I did to may help\improve others work in future.
I turned on the FusionLog in the Registry and on the LoaderException I checked for the FusionLog
catch (ReflectionTypeLoadException ex)
{
StringBuilder sb = new StringBuilder();
foreach (Exception exSub in ex.LoaderExceptions)
{
sb.AppendLine(exSub.Message);
FileNotFoundException exFileNotFound = exSub as FileNotFoundException;
if (exFileNotFound != null)
{
if (!string.IsNullOrEmpty(exFileNotFound.FusionLog))
{
sb.AppendLine("Fusion Log:");
sb.AppendLine(exFileNotFound.FusionLog);
}
}
sb.AppendLine();
}
pluginError = sb.ToString();
}
After enabling the FisonLog on the Registry (HKLM\SOFTWARE\Microsoft\Fusion!EnableLog (DWORD 1) I was able to see that the missing DLL is actually needed at the location of the executing program that call's the OLEObject to create an instance of the system via COM.
I wasn't aware of this, but yeah in some way it's rather logic. Luckily i didn't messed up anything on the code it was just in the wrong place.
I'm currently trying to load and use the Gephi Toolkit from within a .Net 4 C# website.
I have a version of the toolkit jar file compiled against the IKVM virtual machine, which works as expected from a command line application using the following code:
var controller = (ProjectController)Lookup.getDefault().lookup(typeof(ProjectController));
controller.closeCurrentProject();
controller.newProject();
var project = controller.getCurrentProject();
var workspace = controller.getCurrentWorkspace();
The three instances are correctly instantiated in a form similar to org.gephi.project.impl.ProjectControllerImpl#8ddb93.
If however I run the exact same code, with the exact same using statements & references, the very first line loading the ProjectController instance returns null.
I have tried a couple of solutions
Firstly, I have tried ignoring the Lookup.getDefault().lookup(type) call, instead trying to create my own instances:
var controller = new ProjectControllerImpl();
controller.closeCurrentProject();
controller.newProject();
var project = controller.getCurrentProject();
var workspace = controller.getCurrentWorkspace();
This fails at the line controller.newProject();, I think because internally (using reflector) the same Lookup.getDefault().lookup(type) is used in a constructor, returns null and then throws an exception.
Secondly, from here: Lookup in Jython (and Gephi) I have tried to set the %CLASSPATH% to the location of both the toolkit JAR and DLL files.
Is there a reason why the Lookup.getDefault().lookup(type) would not work in a web environment? I'm not a Java developer, so I am a bit out of my depth with the Java side of this.
I would have thought it possible to create all of the instances myself, but haven't been able to find a way to do so.
I also cannot find a way of seeing why the ProjectController load returned null. No exception is thrown, and unless I'm being very dumb, there doesn't appear to be a method to see the result of the attempted load.
Update - Answer
Based on the answer from Jeroen Frijters, I resolved the issue like this:
public class Global : System.Web.HttpApplication
{
public Global()
{
var assembly = Assembly.LoadFrom(Path.Combine(root, "gephi-toolkit.dll"));
var acl = new AssemblyClassLoader(assembly);
java.lang.Thread.currentThread().setContextClassLoader(new MySystemClassLoader(acl));
}
}
internal class MySystemClassLoader : ClassLoader
{
public MySystemClassLoader(ClassLoader parent)
: base(new AppDomainAssemblyClassLoader(typeof(MySystemClassLoader).Assembly))
{ }
}
The code ikvm.runtime.Startup.addBootClassPathAssemby() didn't seem to work for me, but from the provided link, I was able to find a solution that seems to work in all instances.
This is a Java class loader issue. In a command line app your main executable functions as the system class loader and knows how to load assembly dependencies, but in a web process there is no main executable so that system class loader doesn't know how to load anything useful.
One of the solutions is to call ikvm.runtime.Startup.addBootClassPathAssemby() to add the relevant assemblies to the boot class loader.
For more on IKVM class loading issues see http://sourceforge.net/apps/mediawiki/ikvm/index.php?title=ClassLoader
I have created a fairly simple Activity in Mono for Android project:
[Activity(Label = "AndroidApplication1", MainLauncher = true, Icon = "#drawable/icon")]
public class Activity1 : Activity
{
private string value = "intitial";
[Export]
public string GetString()
{
return value;
}
[Export]
public void SetString(string newValue)
{
value = newValue;
}
}
The activity should only serve as a proof-of-concept, hence its simplicity. Now, I'd like to be able to call the method GetString() from the "normal", Java-based Android application.
In Xamarin's docs, I've read about Java to Managed interop, which seems to be exactly what I'm looking for. If I understand it correctly, when Mono for Android app compiles, it generates Java classes that are referred to as Android Callable Wrappers (ACW). I should be then able to call methods on these ACWs from Java-based Android application.
The question is, how exactly do I reference compiled Mono for Android application (the apk file) from the Java-based Android app?
This is where I'm now stuck and was unable to find any concrete examples. There are similar questions here on SO (this one and this one) and some blogposts, but they just boil down to "use ACWs". But how exactly? Maybe I am missing something obvious here, being no Android guy.
What I've tried is to dynamically load the dex file that I yanked from my Mono for Android apk. I've simply put it on the storage card and then tried to use DexClassLoader from Java-based Android app to load it (I've followed this blog post). The ACW class was found, but when I tried to create its instance, I got the following error:
No implementation found for native Lmno/android/Runtime;.register (Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;)
I suppose that I have to somehow include Mono for Android runtime to the Java-based app, but I have no idea how.
EDIT:
This is the code I am trying to load the dex with:
DexClassLoader cl = new DexClassLoader(dexInternalStoragePath.getAbsolutePath(),
optimizedDexOutputPath.getAbsolutePath(),
null,
getClassLoader());
try {
Class<?> classActivity1 = cl.loadClass("androidapplication1.Activity1");
// the following line throws the exception
Object a = classActivity1.newInstance();
Method getStringMethod = classActivity1.getMethod("GetString");
Object result = getStringMethod.invoke(angel);
result = null;
} catch (Exception e) {
e.printStackTrace();
}
EDIT2:
I am now reading here that it should be possible to directly start activities written in Mono for Android from Java. It is still not clear to me how to reference the Mono for Android from Java and Googling yields no relevant hits. Really stumped now.
If I'm understanding correctly what you're trying to do, this isn't really possible. As the error message you got implies, an Activity within a Mono for Android application relies on the Mono runtime in order to function properly. The callable wrapper isn't useful on its own in this case, since it's just a thin Java wrapper class that calls into the Mono runtime. You can actually see the generated callable wrappers yourself if you look in the obj/Debug/android/src folder after you build your project. For example:
package androidapplication9;
public class Activity1
extends android.app.Activity
implements
mono.android.IGCUserPeer
{
static final String __md_methods;
static {
__md_methods =
"n_onCreate:(Landroid/os/Bundle;)V:GetOnCreate_Landroid_os_Bundle_Handler\n" +
"";
mono.android.Runtime.register ("AndroidApplication9.Activity1, AndroidApplication9, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", Activity1.class, __md_methods);
}
public Activity1 ()
{
super ();
if (getClass () == Activity1.class)
mono.android.TypeManager.Activate ("AndroidApplication9.Activity1, AndroidApplication9, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "", this, new java.lang.Object[] { });
}
public void onCreate (android.os.Bundle p0)
{
n_onCreate (p0);
}
private native void n_onCreate (android.os.Bundle p0);
java.util.ArrayList refList;
public void monodroidAddReference (java.lang.Object obj)
{
if (refList == null)
refList = new java.util.ArrayList ();
refList.add (obj);
}
public void monodroidClearReferences ()
{
if (refList != null)
refList.clear ();
}
}
That said, due to the way Android works, you could have a Java application start an activity that is defined in a Mono for Android application in the same way you'd start an external Java activity. This relies on both applications being installed, of course, but would result in the Mono for Android application and Mono runtime actually starting up to run that activity.
Edit
Updating to answer the questions you posed in your comment. The ExportAttribute basically just gives you some more control in how the ACW gets generated, allowing you to specify that a particular method or field should be present in the ACW and what name it should have. This can be useful when you want to use things like an android:onClick attribute in a layout, for example, where by default the ACW wouldn't contain the method you want to reference.
You can't get much use out of an ACW outside of the context of a Mono for Android application since the Mono runtime wouldn't be present. Code written in C# is executed on top of the Mono runtime, and not translated into Java behind the scenes during compilation or anything like that. At runtime there are then two runtimes going side by side, Dalvik (Android's runtime) and Mono, and the callable wrappers are there to allow the two to communicate back and forth. Because of that, even a Mono for Android class library would still depend on the Mono runtime, so you cannot use it independently of that runtime.
This diagram shows what the architecture looks like, and how the runtimes relate to each other:
Hope this helps clear things up!