I created a Console application which searches for plugins ending with PlugIn.dll.
It loads the dll assembly and executes the write method of plugInClass in PlugIn.dll.
I created an interface called IWrite which includes the write method.
After executing the console app,it gives an error as given below:
Unable to cast object of type 'HPlugIn.plugInClass' to type 'ConsolePlugIn.IWrite'.
Here is my code for console app..[Main application]
using System;
using System.IO;
using System.Reflection;
namespace ConsolePlugIn
{
interface IWrite
{
void write();
}
class Program
{
static void Main(string[] args)
{
foreach (string s in Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*PlugIn.dll"))//getting plugins in base directory
{
Assembly aWrite = Assembly.LoadFrom(s);
Type tWrite = aWrite.GetType("HPlugIn.plugInClass");
IWrite click = (IWrite)Activator.CreateInstance(tWrite);//giving casting error
click.write();
}
}
}
}
here is my code for the plugIn dll file
using System;
namespace HPlugIn
{
interface IWrite
{
void write();
}
public class plugInClass : IWrite
{
public void write()
{
Console.Write("High from plugInClass");
}
}
}
Any idea for this casting error?
Thanks in advance!
The IWrite interfaces in the EXE and in the DLL are not the same, even though their structures are identical. You need to make a third dll with the interface, and share it among the DLLs and the EXE.
Common:
namespace Shared {
interface IWrite {
void write();
}
}
DLL:
using System;
using Shared;
namespace HPlugIn {
public class plugInClass : IWrite {
public void write() {
Console.Write("High from plugInClass");
}
}
}
EXE:
using System;
using System.IO;
using System.Reflection;
using Shared;
namespace ConsolePlugIn {
class Program {
...
}
}
You have defined the IWrite interface twice in 2 different assemblies. They are considered different types and you cannot cast from one to the other. The best way to achieve weaker coupling between the EXE and the assembly is to define this interface into a separate DLL. Then have the plugin and executable both reference this third assembly containing the contract (the IWrite interface).
There are two different IWrite interfaces. One in the console app and one in the dll. There are two ways to work around this.
Make the dll reference the console app and have plugInClass implement the ConsolePlugIn.IWrite interface.
Use the dynamic keyword to make interfaces looking the same match.
Related
I have created a unit test for a method in a class called game.cs. For some reason, when I reference the class, I am unable to create a new instance. How do I make this class accessible so I can test my code?
File Hierarchy and solution:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using BowlingKataTDD;
namespace BowlingKataTDDTest
{
[TestClass]
public class BowlingKataTDDUnitTests
{
[TestMethod]
public void DoesGameExist()
{
//arrange
BowlingKataTDD.
}
}
}
BowlingKataTDD Project:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BowlingKataTDD
{
class Game
{
static void Main(string[] args)
{
}
}
}
The reason you do not see the classes is that they are non-public (internal by default).
There are two solutions to this:
If you would like to make your classes visible to outside users, make them public
If you would rather not publish your classes, use InternalsVisibleTo attribute.
To use the second solution, open AssemblyInfo.cs and add the following line:
[assembly: InternalsVisibleTo("BowlingKataTDDTest")]
BowlingKataTDDTest is the name of your assembly, as defined in the project file.
Make the class public, as well as any members of that class which need to be invoked externally (such as by a unit test):
public class Game
{
public static void Main(string[] args)
{
}
}
This is a common mistake with calling libraries with new projects, and I was able to resolve it. Often easy to forget to change the modifier to public from the project in Visual Studio, because by default a new project template creates the class though without it being set to public. When changing the Game class to public, am able to instantiate the Game object.
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using BowlingKataTDD;
namespace BowlingKataTDDTest
{
[TestClass]
public class BowlingKataTDDUnitTests
{
[TestMethod]
public void DoesGameExist()
{
//arrange
BowlingKataTDD.Game game = new BowlingKataTDD.Game();
}
}
}
I have a hierarchy of namespaces like My.Namespace.MyObject in a library. My understanding is that if I include using My.Namespace; at the top of a source file that I should be able to use Object directly. Unfortunately, it only works if I type out the entire My.Namespace.MyObject, neither Namespace.MyObject nor MyObject alone will work. In trying to research this I've found that it can happen when classes and namespaces share names but this is not the case for me. It's really hard to Google for "using not working" so I haven't been able to find much else that might be relevant.
A full example is as follows. In one project I do:
namespace My {
namespace Namespace {
public struct MyObject {}
}
}
Then I build this which produces a dll file. In a second project I add the dll as a reference and then do:
using My.Namespace;
public class AnotherObject
{
public static void Main()
{
//results in a compilation error, while My.Namespace.MyObject doesn't
MyObject a;
}
}
Project A, compiled into LibraryTest.dll:
namespace FirstLevel
{
namespace SecondLevel
{
public class LibraryClass
{
public LibraryClass()
{
}
static void Main()
{
}
}
}
}
Project B, just a console app, using Project A library:
using FirstLevel.SecondLevel;
namespace ConsoleTest
{
class MainClass
{
public static void Main (string[] args)
{
LibraryClass test = new LibraryClass();
}
}
}
Important: Make sure your library is added to References in the other project
right click on References -> Edit References...
switch to .NET Assembly
click on Browse...
select your library
See also attached screenshot.
I'm trying to create what is essentially a plugin framework for a project. I'm trying to work out the pieces before full blown development and I've run into a problem. I am building a message processor. Where the message comes from determines how the message should be processed. As retrieving the message and sending the message will be the same no matter where the message comes from, I felt that a plugin framework would be a good way to implement this.
I built an interface that all Implementations could be built against.
IIPInterfaces.cs:
using System;
using System.Xml;
namespace IIPInterfaces
{
public interface IInterfaceProcessor
{
IIPResult ProcessRequest(XmlDocument xdoc, String processType);
}
public class IIPResult
{
public XmlDocument ResponseDocument { get; set;}
Boolean IsSuccessful { get; set; }
String Error { get; set; }
}
}
I created a implementation for the interface just to test it out.
PrototypeIIP
using IIPInterfaces;
using System;
using System.Xml;
namespace PrototypeIIP
{
public class IIPImplimentation : IInterfaceProcessor
{
public IIPResult ProcessRequest(XmlDocument xdoc, String requestType)
{
IIPResult result = new IIPResult();
Console.WriteLine("In interface {0}", requestType);
return result;
}
}
}
And then I created a test project to try to bind the implementation file at runtime and then use the interface.
Console Program
using IIPInterfaces;
using System;
using System.IO;
using System.Reflection;
using System.Xml;
namespace LateBindingPrototype
{
class Program
{
static void Main(string[] args)
{
String filePath = "C:\\XMLConfig\\PrototypeIIP.dll";
// Try to load a copy of PrototypeIIP
Assembly a = null;
try
{ a = Assembly.LoadFrom(filePath); }
catch(FileNotFoundException e)
{
Console.WriteLine(e.Message);
Console.ReadKey();
return;
}
if (a != null)
{ CreateUsingLateBinding(a); }
Console.ReadKey();
InvokeProcessMessage(a);
Console.ReadKey();
}
static void InvokeProcessMessage(Assembly asm)
{
try
{
Type processor = asm.GetType("PrototypeIIP.IIPImplimentation");
IInterfaceProcessor myProcessor = Activator.CreateInstance(processor) as IInterfaceProcessor;
XmlDocument xdoc = new XmlDocument();
myProcessor.ProcessRequest(xdoc, "test");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.ReadKey();
}
}
static void CreateUsingLateBinding(Assembly asm)
{
try
{
Type processor = asm.GetType("PrototypeIIP.IIPImplimentation");
object obj = Activator.CreateInstance(processor);
Console.WriteLine("Created a {0} using late finding!", obj);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.ReadKey();
}
}
}
}
The CreateUsingLateBinding Method works fine but when I try to create in instance of IInterfaceProcessor in the InvokeProcessMessage method the object is null.
Is what I am trying to do possible? I know that I could do this by bypassing the interface and calling the methods directly from the implementation dll but I was hoping to keep the code cleaner than that because others in our development group will need to support this and simpler is better when it comes to some of them.
Thanks!
The most obvious explanation for the problem you describe is that the interface type IInterfaceProcessor you use in your DLL when declaring the type that implements the interface is not the same interface type (also named IInterfaceProcessor) you use in your program when you try to create the instance.
I.e. the Activator.CreateInstance() actually returns a non-null reference (as it always will, unless an exception is thrown), but that type does not implement the interface you are trying to cast it to with the as operator.
The most common reason I've seen this happen is that a type is declared in some .cs file, but rather than compiling that .cs file into a single DLL and referencing the type via that DLL, the file is linked into two or more projects, compiled into each project separately. This results in a new type, one for each assembly, and of course the types are incompatible.
Granted, lacking a good, complete code example I can't say for sure that this is in fact the problem in your code. It's just an educated guess.
All that said, for the type of thing you seem to be trying to do, it is probably better to use the Managed Extensibility Framework. It provides a useful API on which to build exactly this kind of functionality.
Barring that, I assume that the hard-coded type names in your program are just for testing purposes. Of course, for a true plug-in architecture, you would want to simply enumerate all the types in an assembly, looking for those that implement the interface(s) you are interested in.
After Peter's response, I went back and tried being a bit more specific.
In PrototypeIIP.cs
public class IIPImplimentation : IInterfaceProcessor
to
public class IIPImplimentation : IIPInterfaces.IInterfaceProcessor
And in the Console program
IInterfaceProcessor myProcessor = Activator.CreateInstance(processor) as IInterfaceProcessor;
to
IIPInterfaces.IInterfaceProcessor myProcessor = Activator.CreateInstance(processor) as IIPInterfaces.IInterfaceProcessor;
It corrected the problem. I'm not sure why it is looking at these as two separate types but it is.
Thanks Peter.
I have a system that reads through a folder and saves the paths to an array list. this folder contains a bunch of class files ".cs" all of these classes implement an interface called BaseScript.
I am attempting to find the class name so that I can call class methods but you are supposed to be able to add class files to this folder on a regular basis, so I cannot hardcode the objects to be created.
e.g. I have a file called "Manufacturing.cs":
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LLS
{
class Manufacturing : BaseScript
{
public void init()
{
Console.WriteLine("Manufacturing...");
}
public void uninit() { }
public void recomp() { }
}
}
It implements "BaseScript":
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LLS
{
public interface BaseScript
{
void init();
void uninit();
void recomp();
}
}
I need to be able to use the path: "\Scripts\Manufacturing.cs" to call something like
BaseScript[] s = {new "Manufacturing class name"}
I know there must be some roundabout way of doing this.
how can I find the class name and then create an instance from that class?
If your class is in the same assembly then you can create an instance of the object by using Activator.CreateInstance. I didn't try your code on my side. So casting might not work. Double check this code:
BaseScript s = (BaseScipt)Activator.CreateInstance(null, "Namespace.TestClass");
Edit-I mistakenly though you had dlls and not cs files for some reason.
As others say you are not properly explaining the scenario. But from what I understand you are probably implementing a plugin type of pattern here.
In order to call methods on C# classes you may add later on, you need to iterate through the folder, get the *.cs files, compile them and then using reflection, load the assembly and create an instance of a class you know exists(you should know the fully qualified name of the class beforehand). Finally, you should find the method you want to call and then call it using reflection. Here is some code to get you started:
Microsoft.CSharp.CSharpCodeProvider provider = new Microsoft.CSharp.CSharpCodeProvider();
System.CodeDom.Compiler.CompilerParameters parameters = new System.CodeDom.Compiler.CompilerParameters();
parameters.GenerateInMemory = true;
//You should add all referenced assembiles needed for the cs file here
//parameters.ReferencedAssemblies.Add();
var files = new System.IO.DirectoryInfo("Scripts").GetFiles("*.cs");
foreach (System.IO.FileInfo file in files)
{
System.CodeDom.Compiler.CompilerResults result =
provider.CompileAssemblyFromSource(parameters, new[] { System.IO.File.ReadAllText(file.FullName) });
object instance = result.CompiledAssembly.CreateInstance(file.Name);
if (instance is BaseScript)
{
//Do processing on (BaseScript)instance
}
}
I have two separate projects (comclient and comserver), with some very simple code:
comserver/Program.cs
using System;
using System.Reflection;
using System.Runtime.InteropServices;
namespace comserver
{
class Program
{
static void Main(string[] args)
{
RegisterComObject();
}
public static void RegisterComObject()
{
Assembly asm = Assembly.GetExecutingAssembly();
RegistrationServices reg = new RegistrationServices();
bool f = reg.RegisterAssembly(asm, AssemblyRegistrationFlags.SetCodeBase);
Console.WriteLine("RegisterAssembly: {0}", f ? "ok" : "fail");
}
}
[ComVisible(true)]
[Guid("49752A5D-4CAD-495f-A220-07B60CDB6CE8")]
interface IComServerDemo
{
void SayHello(string name);
}
[ComVisible(true)]
[Guid("8FDB8319-6EC3-45b4-A384-1403D3993A07")]
public class ComServerDemo : IComServerDemo
{
public void SayHello(string name)
{
Console.WriteLine("Hello {0}!", name);
}
}
}
comclient/Program.cs
using System;
using System.Runtime.InteropServices;
namespace comclient
{
class Program
{
static void Main(string[] args)
{
ComServerDemo csdObj = new ComServerDemo();
IComServerDemo csd = (IComServerDemo)csdObj;
csd.SayHello("Bob");
}
}
[ComImport, Guid("8FDB8319-6EC3-45b4-A384-1403D3993A07")]
public class ComServerDemo
{
}
[ComImport, Guid("49752A5D-4CAD-495f-A220-07B60CDB6CE8")]
interface IComServerDemo
{
void SayHello(string name);
}
}
When I run comserver.exe, it registers the COM interface OK:
RegisterAssembly: ok
But when I try to run the COM client, I get this exception:
An unhandled exception of type
'System.InvalidCastException' occurred
in comclient.exe
Additional information: Unable to cast
object of type
'comserver.ComServerDemo' to type
'comclient.ComServerDemo'.
Any ideas why I'm getting this exception? The comclient project does not reference the comserver class. Also, I'm trying to create an out of proc COM server, but I have a feeling this might not do the trick - any ideas about this also?
You need a feature called 'Type equivalence', added to .NET 4.0. It is used to allow two distinct .NET types to be considered equivalent when they have the exact same GUID. This feature was used to implement the new 'Embed Interop Types' feature, making it unnecessary to deploy PIAs.
I don't know enough about it to judge whether you can actually use it to make this code work. Gut instinct says yes. Check out the docs for the [TypeIdentifier] attribute.
It doesn't appear that you have anything relating this section of code:
ComServerDemo csdObj = new ComServerDemo();
IComServerDemo csd = (IComServerDemo)csdObj;
In the server class you have ComServerDemo : IComServerDemo, but in the client you don't have this relationship. Attempting this cast should fail because the compiler won't see the connection.
Edit: In agreement with #Hans Passant on type equivalence, you have redefined (re: undefined) the relationship so the type equivalence no longer holds.
Well, you don't show us comclient, so I just have to guess, but I'll assume you define a class called ComServerDemo in comclient (defined identically to the one in comserver)
But identically definitions is not the same as being the same type.
UPDATE (now that you posted comclient)
Try putting your definitions of ComServerDemo & IComServerDemo in your comclient class, into the comserver namespace.
using System;
using System.Runtime.InteropServices;
namespace comclient
{
class Program
{
static void Main(string[] args)
{
ComServerDemo csdObj = new ComServerDemo();
IComServerDemo csd = (IComServerDemo)csdObj;
csd.SayHello("Bob");
}
}
}
namespace comserver
{
[ComImport, Guid("8FDB8319-6EC3-45b4-A384-1403D3993A07")]
public class ComServerDemo
{
}
[ComImport, Guid("49752A5D-4CAD-495f-A220-07B60CDB6CE8")]
interface IComServerDemo
{
void SayHello(string name);
}
}