I am struggling to get dynamic loading working with my plugin. I'm loading the plugin into an appdomain but I get the following exception when my plugin tries to execute any mySQL code:
MySql.Data.MySqlClient.MySqlException
was unhandled
Message=Access denied
for user ''#'localhost' (using
password: NO)
Here is the code on Plugins.cs:
public class Plugins {
private static List<PluginContainer> PluginList;
public static bool PluginsAreLoaded {
get {
if(PluginList==null) {
return false;
}
else {
return true;
}
}
}
public static void LoadAllPlugins(Form host) {
//add assemblies in OD folder to appDomain
Assembly[] list = AppDomain.CurrentDomain.GetAssemblies();
AppDomain appDomainAnesthesia AppDomain.CreateDomain("appDomainAnesthesia");
for(int i=0;i<ProgramC.Listt.Count;i++) {
string dllPathWithVersion = String.Empty;
if(!ProgramC.Listt[i].Enabled) {
continue;
}
if(ProgramC.Listt[i].PluginDllName=="") {
continue;
}
string dllPath=ODFileUtils.CombinePaths(Application.StartupPath,ProgramC.Listt[i].PluginDllName);
if(dllPath.Contains("[VersionMajMin]")) {
Version vers=new Version(Application.ProductVersion);
dllPathWithVersion=dllPath.Replace("[VersionMajMin]",vers.Major.ToString()+"."+vers.Minor.ToString());
dllPath=dllPath.Replace("[VersionMajMin]","");//now stripped clean
if(File.Exists(dllPathWithVersion)){
File.Copy(dllPathWithVersion,dllPath,true);
}
}
if(!File.Exists(dllPath)) {
continue;
}
//add our plugin to the appDomain
try {
string typeName = Path.GetFileNameWithoutExtension(dllPath) + ".Plugin";
PluginBase plugin = (PluginBase)appDomainAnesthesia.CreateInstanceAndUnwrap(Path.GetFileNameWithoutExtension(dllPathWithVersion), typeName);
appDomainAnesthesia.Load(Path.GetFileNameWithoutExtension(dllPathWithVersion));
plugin.Host=host;
}
catch(Exception ex) {
MessageBox.Show(ex.Message);
continue;//don't add it to plugin list.
}
PluginContainer container=new PluginContainer();
container.Plugin=plugin;
container.ProgramNum=ProgramC.Listt[i].ProgramNum;
PluginList.Add(container);
}
}
Here is PluginBase:
public abstract class PluginBase : MarshalByRefObject {
private Form host;
///<summary>This will be a refrence to the main FormOpenDental so that it can be used by the plugin if needed. It is set once on startup, so it's a good place to put startup code.</summary>
public virtual Form Host {
get {
return host;
}
set {
host=value;
}
}
///<summary>These types of hooks are designed to completely replace the existing functionality of specific methods. They always belong at the top of a method.</summary>
public virtual bool HookMethod(object sender,string methodName,params object[] parameters) {
return false;//by default, no hooks are implemented.
}
///<summary>These types of hooks allow adding extra code in at some point without disturbing the existing code.</summary>
public virtual bool HookAddCode(object sender,string hookName,params object[] parameters) {
return false;
}
public virtual void LaunchToolbarButton(long patNum) {
}
}
PluginContainer:
class PluginContainer {
public PluginBase Plugin;
public long ProgramNum;
and finally, Plugin:
public class Plugin : PluginBase {
private Form host;
public AnestheticData AnesthDataCur;
public AnestheticRecord AnestheticRecordCur;
public Patient PatCur;
public string SourceDirectory;
public string DestDirectory;
public System.ComponentModel.IContainer components;
public long patNum;
public static Procedure procCur;
public static List<Procedure> ProcList;
public static ODtextBox textNotes;
public override Form Host {
get {
return host;
}
set {
host=value;
ConvertAnesthDatabase.Begin();//if this crashes, it will bubble up and result in the plugin not loading. }
}
It bombs at the
plugin.Host=host;
line on Plugins.cs which comes from the ConverAnesthDatabase.Begin() method as there are multiple mySQL statements there.
I have access to the mySQL database, so that is not the issue.
How do I resolve this error?
Try not to "extend" your PluginBase and use an Interface instead:
public interface IPlugin
{
Form Host {get; set; }
bool HookMethod(object sender,string methodName,params object[] parameters);
bool HookAddCode(object sender,string hookName,params object[] parameters);
void LaunchToolbarButton(long patNum);
}
and then
public class Plugin : IPlugin
{
...
}
Related
Is it possible to pass the generic type from one class to other class generic property.
For example:
Assembly Logger
namespace Logger
{
public class GenericLoger<T>
{
T _genericLog;
LogManager _logManager;
public GenericLoger(string logName)
{
_logManager = new LogManager(logName);
//Assigning the generic type to Log.GenerciLog, this is how I am
expecting or by some other possible way?.
Log.GenerciLog = _genericLog;
}
public static Write(string description)
{
_logManager.write(description);
}
}
public static class Log
{
LogManager _logManager;
static Log()
{
_logManager = new LogManager();
}
public static Write(string description)
{
_logManager.write(description);
}
//The generic type supplied in GenericLoger need to pass here,
//like this or by some other possible way?
public static T GenerciLog { get; internal set; }
//T is unrecognized here as type is available in GenericLoger
//I want to pass here from GenericLoger
}
}
Assembly Main Caller of Logger
using Logger;
namespace DataProcessor
{
internal class SpecialLogger
{
private static Lazy<GenericLog<SpecialLogger>> _passed;
public static GenericLog<SpecialLogger> Passed
{
get
{
if (_passed == null)
{
_passed = new Lazy<GenericLog<SpecialLogger>>(() => new GenericLog<SpecialLogger>("Passed"), true);
}
return _passed.Value;
}
}
private static Lazy<GenericLog<SpecialLogger>> _failed;
public static GenericLog<SpecialLogger> Failed
{
get
{
if (_failed == null)
{
_failed = new Lazy<GenericLog<SpecialLogger>>(() => new GenericLog<SpecialLogger>("Failed"), true);
}
return _failed.Value;
}
}
}
internal class Processor
{
public void ProcessRate()
{
var trans = dataManager.GetData();
//Will write the log in "Log.txt" file
Log.write(trans.Count + " transaction found");
foreach (var item in trans)
{
try
{
//transaction process code here
//This will write the text in "Passed.txt" file. 'Passed' property I want to access like this
Log.GenerciLog.Passed.Write(item);
}
catch (Exception ex)
{
//This will write the text in "Failed.txt" file. 'Failed' property I want to access like this
Log.GenerciLog.Failed.Write(item);
}
}
}
}
}
NOTE: In .NET you don't have a way for automatic type inference for use case like yours, also there is no automatic type substitution.
Not sure if this is what you are looking for
Your method definition should look like this
public static T GenerciLog<T> { get; internal set; }
and this is how to call it
try
{
//transaction process code here
//This will write the text in "Passed.txt" file. 'Passed' method I want to access like this
Log.GenerciLog<SpecialLogger>.Passed.Write(item);
}
catch (Exception ex)
{
//This will write the text in "Failed.txt" file. 'Failed' method I want to access like this
Log.GenerciLog<SpecialLogger>.Failed.Write(item);
}
This is a very simple log class. There is a lot more you could do with this sort of thing. Its all provided by log4net which I'd recommend using rather than trying to write your own logger. But the below is a start of how I'd implement a simple logger. It allows you to log to several different things at once. I appreciate the below doesn't answer exactly what you want but its an indication of how to start and you can adapt it to suit your needs.
public static class Logger
{
private static List<ILogger> _loggers = new List<ILogger>();
public static void Log(string message)
{
foreach (var logger in _loggers)
logger.Write(message);
}
public static void AddLogger(ILogger logger)
{
_loggers.Add(logger);
}
}
public interface ILogger
{
void Write(string message);
}
public class SpecialLogger : ILogger
{
public void Write(string message)
{
//special log code here eg
Console.WriteLine(message);
}
}
then somewhere do this
Logger.AddLogger(new SpecialLogger());
Logger.Log("A log message");
I've got a Xamarin Cross-Platform App and want to use Android's NetworkServiceDiscovery API.
I tried to implement it according to https://developer.android.com/training/connect-devices-wirelessly/nsd.html
Now, I'm not sure if I did everything right, for example: The android documentation wants you to create a RegistrationListener like this:
Android:
public void initializeRegistrationListener() {
mRegistrationListener = new NsdManager.RegistrationListener() {
#Override
public void onServiceRegistered(NsdServiceInfo NsdServiceInfo) {
// Save the service name. Android may have changed it in order to
// resolve a conflict, so update the name you initially requested
// with the name Android actually used.
mServiceName = NsdServiceInfo.getServiceName();
}
#Override
public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
// Registration failed! Put debugging code here to determine why.
}
#Override
public void onServiceUnregistered(NsdServiceInfo arg0) {
// Service has been unregistered. This only happens when you call
// NsdManager.unregisterService() and pass in this listener.
}
#Override
public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
// Unregistration failed. Put debugging code here to determine why.
}
};
}
And I rebuilt it like this in C#:
public class RegistrationListener : NsdManager.IRegistrationListener
{
public string serviceName;
public void Dispose()
{
throw new NotImplementedException();
}
public IntPtr Handle { get; }
public void OnRegistrationFailed(NsdServiceInfo serviceInfo, NsdFailure errorCode)
{
// Registration failed! Put debugging code here to determine why.
}
public void OnServiceRegistered(NsdServiceInfo serviceInfo)
{
// Save the service name. Android may have changed it in order to
// resolve a conflict, so update the name you initially requested
// with the name Android actually used.
serviceName = serviceInfo.ServiceName;
}
public void OnServiceUnregistered(NsdServiceInfo serviceInfo)
{
// Service has been unregistered. This only happens when you call
// NsdManager.unregisterService() and pass in this listener.
}
public void OnUnregistrationFailed(NsdServiceInfo serviceInfo, NsdFailure errorCode)
{
// Unregistration failed. Put debugging code here to determine why.
}
}
I implemented the ResolveListener and DiscoveryListener in the same way.
Then I made a Helper Class with the functions I want to call via DependencyServices:
public class NsdHelper
{
public static readonly string SERVICE_TYPE = "chatTest._tcp";
public DiscoveryListener discoveryListener;
public NsdManager nsdManager;
public NsdServiceInfo nsdServiceInfo;
public RegistrationListener registrationListener;
public ResolveListener resolveListener;
public string SERVICE_NAME { get; set; }
public void InitializeNsd()
{
resolveListener = new ResolveListener();
discoveryListener = new DiscoveryListener();
registrationListener = new RegistrationListener();
resolveListener.ServiceName = SERVICE_NAME;
resolveListener.ServiceInfo = nsdServiceInfo;
discoveryListener.resolveListener = resolveListener;
discoveryListener.nsdManager = nsdManager;
}
public void RegisterService(string sessionName)
{
SERVICE_NAME = sessionName;
// Create the NsdServiceInfo object, and populate it.
nsdServiceInfo = new NsdServiceInfo
{
ServiceName = sessionName,
ServiceType = SERVICE_TYPE,
Port = GenerateFreePort()
};
InitializeNsd();
// The name is subject to change based on conflicts
// with other services advertised on the same network.
nsdManager = (NsdManager) Application.Context.GetSystemService(Context.NsdService);
nsdManager.RegisterService(
nsdServiceInfo, NsdProtocol.DnsSd, registrationListener);
}
private int GenerateFreePort()
{
//setting the ServerSocket to 0 will generate the next free port
var serverSocket = new ServerSocket(0);
return serverSocket.LocalPort;
}
public void DiscoverServices()
{
nsdManager.DiscoverServices(
SERVICE_TYPE, NsdProtocol.DnsSd, discoveryListener);
}
public void StopDiscovery()
{
nsdManager.StopServiceDiscovery(discoveryListener);
}
public NsdServiceInfo GetChosenServiceInfo()
{
return nsdServiceInfo;
}
public void TearDown()
{
nsdManager.UnregisterService(registrationListener);
}
}
And now when I call RegisterService I get the following Error:
I don't know where exactly I've gone wrong! The errors I get while debugging Xamarin Apps also don't help much :(
Inherit your RegistrationListener subclass from Java.Lang.Object
Remove the Handle property and the Dispose methods as those are implemented in the Java.Lang.Object.
public class RegistrationListener : Java.Lang.Object, NsdManager.IRegistrationListener
{
~~~
}
Once you do that a ACW (Android Callable Wrapper) that will be generated to bind your C# implementation so it can be instanced from Java VM.
I am unable to catch an exception from a generic abstract class in the client app. The reason I want the exception to be thrown in the constructor is that the config file functionality needs to be the same in the derived classes and therefore I don't see a reason to implement this in every derived class. The exception should be handled in the overarching generic class AgentModule<T> however, this is not the case for a reason I am unaware.
I can catch the exception when I move the code to a method and invoke from the client class.
The abstract class:
public abstract class Importer
{
public abstract string Name { get; }
public abstract string Description { get; }
private System.Configuration.Configuration _customConfig;
public Importer()
{
string customConfigFile = this.GetType().Assembly.Location + ".config";
if (System.IO.File.Exists(customConfigFile))
{
System.Configuration.ExeConfigurationFileMap fileMap = new System.Configuration.ExeConfigurationFileMap();
fileMap.ExeConfigFilename = customConfigFile;
_customConfig = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(fileMap, System.Configuration.ConfigurationUserLevel.None);
}
else
{
throw new System.IO.FileNotFoundException("Could not load configuration file: " + customConfigFile);
}
}
public abstract void Load(ILogger logger);
}
The generic overarching class:
public class AgentModule<T> : ModuleBase where T : Importer, new()
{
private Importer _importer;
public override void Run()
{
try
{
_importer = (Importer)Activator.CreateInstance<T>();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
Derived class:
public class XLoad : Importer
{
public override string Name { get { return _name; } }
public override string Description { get { return _description; } }
private string _name;
private string _description;
public XLoad()
{
_name = "Load";
_description = "some desc";
}
public override void Load(ILogger logger)
{
Console.WriteLine("I am loading");
}
}
The Visual Studio debugger will not catch any exception for whatever happens within Activator.CreateInstance(). But if you execute the exe manually/programmatically, this exception will be handled. You can even get the exception thrown at the constructor of the Importer class from the InnerException at AgentModule.Run()
try
{
_importer = (Importer)Activator.CreateInstance<T>();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
if(e.InnerException != null)
Console.WriteLine(e.InnerException.Message );
}
I am trying to implement the Ninject.Extensions.Factory pattern and my program is telling me my bindings aren't right, but I can't figure out why. I keep getting an "Error activating IHashable. No matching bindings are available, and the type is not self-bindable" exception thrown. The relevant areas of my code are below:
public interface IHashable
{
FileInfo File { get; }
string ComputeHash();
}
public interface IHashableFactory
{
IHashable GetNew(FileInfo file);
}
public class MD5ChecksumProvider : IHashable
{
private FileInfo _file;
public FileInfo File
{
get { return _file; }
}
public MD5ChecksumProvider(FileInfo file)
{
if (file == null)
throw new ArgumentNullException("file");
_file = file;
}
public string ComputeHash()
{
// implementation
}
}
public class AppFileProvider : IAppFileProvider
{
private IHashableFactory _hashFactory;
public IHashableFactory HashProvider
{
get { return _hashFactory; }
}
public AppFileProvider(IHashableFactory hashProviderFactory)
{
_hashFactory = hashProviderFactory;
}
public string GetChecksum(FileInfo fileInfo)
{
var hasher = _hashFactory.GetNew(fileInfo);
return hasher.ComputeHash();
}
}
public class BindingProviders : NinjectModule
{
public override void Load()
{
Bind<IHashable>()
.To<MD5ChecksumProvider>();
}
}
public class BindingFactories : NinjectModule
{
public override void Load()
{
Bind<IHashableFactory>()
.ToFactory();
}
}
// my DI container
public sealed class Container : IDisposable
{
private bool _isDisposed;
private IKernel _kernel;
private BindingFactories _bindingFactories;
private BindingObjects _bindingObjects;
private BindingProviders _bindingProviders;
public Container()
{
_isDisposed = false;
_bindingFactories = new BindingFactories();
_bindingObjects = new BindingObjects();
_bindingProviders = new BindingProviders();
_kernel = new StandardKernel(_bindingObjects, _bindingProviders, _bindingFactories);
}
public T Get<T>()
{
return _kernel.Get<T>();
}
public void Dispose()
{
// nothing worth seeing
}
private void Dispose(bool disposing)
{
// nothing worth seeing
}
}
// the program (composition root)
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
using (var container = new Container())
{
var fileProvider = container.Get<IAppFileProvider>();
foreach (var file in files)
{
string hash = fileProvider.GetChecksum(storePath, file); // this line throws "Error activating IHashable. No matching bindings are available, and the type is not self-bindable.""
}
}
}
}
I feel like my bindings are setup correctly but I must be missing something obvious. Any ideas why I'm getting the exception from the above code?
This is caused by a feature of Ninject.Extensions.Factory.
It treats methods which start with Get differently from those which don't.
If you rename IHashableFactory.GetNew to Create or Make everything works fine.
The "Get" feature is described here:
The default instace provider of the extension has the convention that it tries to return an instance using a named binding whenever a method starts with “Get”. E.g. IFoo GetMySpecialFoo() is equal to
resolutionRoot.Get<IFoo>("MySpecialFoo");
Since i think this is not obvious to the user and the exception isn't helpful at all in this regard, i have filed an issue report here
I am getting a CommunicationException when trying to do a callback to a client, I know that the problem lies within this class, although I can't figure out what member is having trouble being serialized.
[DataContract]
public sealed class DirectoryInformation : FileSystemInfo
{
public override void Delete()
{
Directory.Delete(FullName);
}
[DataMember]
public override string Name
{
get { return Path.GetFileName(FullName); }
}
[DataMember]
public override bool Exists
{
get { return Directory.Exists(FullName); }
}
[DataMember]
public int Size { get; private set; }
#region Ctor
public DirectoryInformation(string directory)
{
FullPath = directory;
Refresh();
CalculateDirectorySize(directory);
}
public DirectoryInformation()
{
}
#endregion
#region Private Methods
private void CalculateDirectorySize(string directory)
{
if (!IsDirectoryAccessible(directory)) return;
Size = Size + (int) Directory.GetFiles(directory).Select(x => new FileInfo(x)).Sum(x => x.Length);
foreach (var dir in Directory.GetDirectories(directory))
CalculateDirectorySize(dir);
}
private static bool IsDirectoryAccessible(string directory)
{
try
{
Directory.GetFiles(directory);
return true;
}
catch (UnauthorizedAccessException)
{
return false;
}
}
#endregion
}
I have already checked and it is not one of the members of FileSystemInfo, since the problem persisted even when removing the inheritance. I already made sure I am assigning all the members with DataMember attribute and there is a standard contructor.
I think what is causing the problem is something I am not aware of the existence, but what could it be?