Call function in Remotable class - c#

I have a remotable class derived from MarshalByRefObject.I have two public functions in it say TestMethod1 and TestMethod2. I am invoking these from an HttpHandler and it works fine. Now I have added a new function TestMethod3 that is similar to the two methods mentioned before. But, while calling this function from the same handler and getting an exception which says "undable to find the .exe for this application", where the exe is the process in which the remote object runs.
class HttpHandlerClass : IHttpHandler
{
RemotingConfiguration.RegisterWellKnownClientType(typeof(RemoteClass),"ipc://completeurl");
RemoteClass rc = new RemoteClass();
if (condition1)
rc.TestMethod1();
else
rc.TestMethod3();
}
class RemoteClass: MarshalByRefObject
{
public void TestMethod1(int a) { }
public void TestMethod2(string b) { }
public void TestMethod3(string c) { }
}
No clue why. Seems to me I need to make some changes(dont know where) so that the TestMethod3 is recognized.
Any ideas?
Output: Could not load file or assembly 'Project.exe, version=1.0.0.0, Culture-neutral, publickeytoken=blahblah' or one of its dependencies. The system cannot find the file specified."
Just noticed, it is looking for an older version of the Project.exe and not the one whihc is being executed.

Use ICE instead? I struggled for months in the same situation as you, then ended up solving everything within a week when I switched architectures.

This is just a wild guess, but I've run into this issue before with remoting.
Are you sure the .exe you're remoting to is up to date? It's possible you didn't copy a compiled file to the expected location, and then the old methods would still work, but the new one will break completely...

Related

Access windows forms controls from external DLLs in C#

This is my first Topic here and I didn't find any similar Topics so I try to describe my problem as good as I can:
I was ordered by my Company to create a modular C# program to assist our Software Developers with Background tasks. The Programm is composed of a Windows Forms application with a User Interface that calls external DLLs that do the actual work. All These DLLs are written by me aswell and follow certain rules to make them compatible to the Main App. That way I can easily add new funcions to the Programm just by putting the DLL into a predefined Folder. So to say Plug-and-Run
The main program contains a ListBox that shows all available PlugIns and if one get's selected and the "start" button is clicked, the Main program calls the corresponding DLL and Invokes the method "program" that starts the DLLs actual function. Furthermore the Main contains a method "Output" that is supposed to write the result of every PlugIn into a Tab of my TabControl. That way the results of every PlugIn running in separate threads can be viewed independently. The Access to the tab already has a delegate to make it threadsafe. The Information is gathered by invoke from the PlugIn's own "returnOutput" method that simply Returns a List of strings containing the results to the Main.
My Problem now is: How can i implement a Kind of a callback into my PlugIn DLLs so they can order the Main Program to gather the results at any time?
My first idea was to simply add the result as return values to the "program" method itself but that would make the Information only available at the end of the program and some of the Tasks require a "live update" during runtime.
My second idea was to use the delegate for the Control as Parameter and pass it to the PlugIn so the PlugIn DLL could Access the Control on it's own. This idea failed because the DLL doesn't "know" the Main program and can't Access it's Methods or the delegates instance so I am Always missing a reference.
Is there a way to solve my problem? If necessary I can provide Code snippets but the program has already around 800 lines of Code and each PlugIn adds a few hundred more..
Thanks in advance for every answer and sorry for my non-native english :D
Best Regards
Gerrit "Raketenmaulwurf" M.
Edit: I am using SharpDevelop 5.1
Code Snippet for the DLL call:
PlugIn = PlugIns.SelectedItem.ToString();
Assembly PlugInDLL = Assembly.LoadFile(#PlugInOrdner+"\\"+PlugIn+".dll");
Object Objekt = PlugInDLL.CreateInstance("DLL.PlugIn");
MethodInfo Info1 = Objekt.GetType().GetMethod("Programm");
Info1.Invoke(Objekt, new Object[]{Projekt, TIAInstanz});
it basically Looks for a DLL file that has the same Name as the highlighted item in the ListBox
There are many different ways to do this. Some of the suggestions in the comments are really good and implementing them would make a robust and extendable solution.
If you are looking for a quick and easy way to get messages from your plugins, though, then you can pass your callback directly to the plugin as an Action:
public class PluginRunner
{
public class PluginMessageEventArgs
{
public string Text { get; set; }
}
public event EventHandler<PluginMessageEventArgs> PluginMessage;
public void Run( string pluginPath )
{
Assembly PlugInDLL = Assembly.LoadFile(pluginPath);
Object Objekt = PlugInDLL.CreateInstance("DLL.PlugIn");
MethodInfo Info1 = Objekt.GetType().GetMethod("Programm");
Info1.Invoke(Objekt, new Object[] { Projekt, TIAInstanz, new Action<string>(Log) });
}
private void Log(string s)
{
PluginMessage?.Invoke(this, new PluginMessageEventArgs { Text = s });
}
}
so you can use it like:
var path =
Path.Combine(
Path.GetDirectoryName(Assembly.GetEntryAssembly().Location),
"Plugins",
"MyAwesomePlugin.dll");
var pr = new PluginRunner();
// be aware that your event delegate might be invoked on a plugin's thread, not the application's UI thread!
pr.PluginMessage += (s,e) => Console.WriteLine("LOG: " + e.Text);
pr.Run(path);
then your plugin's Programm method writes its logs:
public void Programm( ProjektClass p0, TIAClass p1, Action<string> log )
{
Task.Run(() =>
{
// do something
log.Invoke("here am I!");
// do something else
log.Invoke("here am I again!");
// do something more
});
}
I must admit, that this is not the ideal way to deal with messaging. There are far better (and, unfortunately, more complicated to implement) solutions out there. This one is fairly simple though. Just don't forget that you receive your message on the same thread that have sent it and avoid deadlocks.

System.MissingMethodException is thrown when calling a Method with ImmutableArray<> on an interface in a different dll

Problem
I have the below code in the two respective dlls. dll1 depends on dll2.
When DoWork() is called it will execute myInterface.MyListMethod(ImmutableList<byte>.Empty); fine. When it goes to execute myInterface.MyArrayMethod(ImmutableArray<byte>.Empty); the following exception is thrown:
Exception:System.MissingMethodException: Method not found: Namespace.MyArrayMethod(System.Collections.Immutable.ImmutableArray'1<Byte>)
dll1.dll
public class TestClass
{
public void DoWork()
{
IMyInterface myInterface = new MyInterface();
myInterface.MyListMethod(ImmutableList<byte>.Empty);
myInterface.MyArrayMethod(ImmutableArray<byte>.Empty);
}
}
dll2.dll
public class MyInterface : IMyInterface
{
public void MyArrayMethod(ImmutableArray<byte> byteArray)
{
// Do stuff
}
public void MyListMethod(ImmutableList<byte> byteList)
{
// Do stuff
}
}
public interface IMyInterface
{
void MyArrayMethod(ImmutableArray<byte> byteArray);
void MyListMethod(ImmutableList<byte> byteList);
}
Testing
From my point of view it seems that ImmutableArray<> is at fault as I've tried this with multiple types, including as you can see above, with other types in the Immutable namespace. It only happens with ImmutableArray<> as far as I've tried.
I've also made sure that both the dlls are the current dlls and there isn't some old cached version hanging around the gac. Updating the Interface to have another method then calling that before MyArrayMethod works as expected.
Visual Studios specifically doesn't pick up calling references to the method on the interface that contains the ImmutableArray<> param but it will for methods with the ImmutableList<>. It will also pick up references if you remove the ImmutableArray<> param from the method.
The solution builds fine however, it's only when it tries to JIT at run-time that this error is thrown.
If I add a project reference to 'System.Runtime' everything works fine. That’s it.
I ended up solving the problem when I was trying to repro it there was a Culture issue that VS solved by auto added a project reference to System.Runtime. This ended up fixing the larger problem. The references in VS are working fine now and the code runs without an issue.
When I pass null it will build without System.Runtime but when I call the method it will throw the exception. However when try and pass a default ImmutableArray it requires I add the System.Runtime. This resolves the issue.
I have no idea why this fixed my issue but it did.

Any downfall (or better alternative) to run initialization code (HTTPHandler - URL association) using a static constructor?

Summary :
I have a DLL that hosts a class library. The library is used by an ASP.NET website. I need some code (initialization) to be run when the library is used. I have placed the code on the static constructor of one of the classes, which most likely will be used. It runs right now, but I was wondering
is there a better place to put this code? Some sort of DLL init
method?
are there any downfalls? If the class is never used, will the code
run anyways?
Details:
I have a DLL that hosts a class library that implements ECommerce to be used on ASP.NET websites. It contains controls and logic objects specific to my client. As part of it, it contains an HTTPhandler that handles AJAX calls to the library. The url that is associated with the Handler has to be registered. I have done this on the static constructor of one of the classes.
using System.Web.Routing;
class CMyClass {
static CMyClass() {
RouteTable.Routes.Insert(0, new Route("myapi/{*pathinfo}", new CMyHTTPHandlerRouter()));
}
}
This works right now. The site that uses the DLL does not have to register the route, which is very convenient. I was wondering, though:
is there a better place to register routes from a DLL? Or a better
way to associate a handler with a URL, directly from the DLL, so it
is always registered when the DLL is used.
are there any downfalls? If CMyClass is never used, will the code run anyways?
I can answer your second question: the static constructor will only run if you somehow interact with CMyClass. In other words, it's run on demand, not eagerly when you e.g. access the DLL.
Routes are to be construed as "application code". Meaning once it is "compiled" you cannot make changes to it. This is by design. Application_Start is the place where routes are normally registered.
I would normally abide by this convention. But my reusable logic (i.e. inside any publicly exposed method in the dll) should ensure that the routes are registered, else throw up an error. This is how the end developers know that they aren't using your component right. And if "it" knows the routes are registered it can safely go and execute the actual stuff.
I'd use a static boolean variable to accomplish that.
public class MyMvcSolution
{
public static bool Registered {get; set; }
static MyMvcSolution(){ Registered = false; }
public static void DoSomethingImportant()
{
if(Registered)
{
//do important stuff
}
else
throw new InvalidOperationException("Whoa, routes are not registered!");
}
//this should be called in the Application_Start
public static void Init()
{
RouteTable.Routes.Insert(0, new Route("myapi/{*pathinfo}", new CMyHTTPHandlerRouter()));
Registered = true;
}
}
I believe the above solution will kind of do.
There is an alternative strategy. We want to add routes "dynamically". This talks about forcing the BuildManager to register routes you mention is a .cs file. This file isn't "compiled" as part of the application; there will be a *.cs file in your application somewhere. You will make an assembly out of it on-the-fly, and from that force the buildmanager to register. There is also a mechanism to "edit" the routes once that file changes too. I'll leave it to you to explore this. Deep but interesting stuff.

AssemblyResolve Does not fire

I have an asp.net application. I want to load some assemblies dynamically.
this is my code on application start
protected void Application_Start(Object sender, EventArgs e)
{
LoadPrivateAssemblies();
}
private static void LoadPrivateAssemblies()
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainAssemblyResolve;
Assembly.Load("MyDLL");
}
static Assembly CurrentDomainAssemblyResolve(object sender, ResolveEventArgs args)
{
//loads and returns assembly successfully.
}
this code works fine except when a nested c# code calls a class from my dynamic dll Inside an asp.net page (not code-behind)
sample :
<%if(MyDLL.TestObject.Value){%>white some ting<%}%>
what should I do now ?
I think If I know When a new AppDomain is created, it may solve my problem.
I really think you are barking up the wrong tree here.
Assembly.Load(byte[]) does "persist" the assembly in the app domain - otherwise, what else would it be doing?
To illustrate the fact it does, try this:
Create a solution with one console application, and one class library, named OtherAssembly.
In the class library, OtherAssembly, add a single class:
namespace OtherAssembly
{
public class Class1
{
public string HelloWorld()
{
return "Hello World";
}
}
}
In the console application, use this as your program:
public class Program
{
static void Main(string[] args)
{
try
{
using (var fs = new FileStream("OtherAssembly.dll", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
var buffer = new byte[fs.Length];
// load my assembly into a byte array from disk
fs.Read(buffer, 0, (int) fs.Length);
// load the assembly in the byte array into the current app domain
AppDomain.CurrentDomain.Load(buffer);
}
// get my type from the other assembly that we just loaded
var class1 = Type.GetType("OtherAssembly.Class1, OtherAssembly");
// create an instance of the type
var class1Instance = class1.GetConstructor(Type.EmptyTypes).Invoke(null);
// find and invoke the HelloWorld method.
var hellowWorldMethod = class1.GetMethod("HelloWorld");
Console.WriteLine(hellowWorldMethod.Invoke(class1Instance, null));
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
finally
{
Console.ReadLine();
}
}
}
Don't reference OtherAssembly from your main program assembly, instead, compile the solution and manually drop OtherAssembly.dll in the main program's bin folder.
Run the main program. It outputs "Hello World", which it can only have done if the assembly was loaded and retained in memory. You'll note I've been very careful not to give Visual Studio or C# any hint to load this OtherAssembly. No reference was added, the type is not explicitly referenced in C#.
You need to look again at your problem.
[EDIT: in response to you commenting on the fact this is not an ASP.NET application]
OK - I've moved my main program to an ASP.NET web page and tried accessing the assembly both from code behind and the markup - and it works in both situations. I'm sure you are missing something here - it just doesn't make sense that the behaviour of this method - whose job it is to load an assembly into the current app domain - is different in an ASP.NET scenario.
It seems to me there are at least two things to investigate:
If it really is a new app domain being created on each call, it would only be because of an unhandled exception in your application. The quickest way to diagnose this is to use SysInternals Process Explorer. Fire it up - add .Net -> Total AppDomains to the list of columns and watch the app domain count for your process (IIS worker process or web dev) as you run your app. If this increases on each call, you've got unhandled exceptions tearing down one AppDomain and forcing another to be created.
Are you sure this assembly is really being loaded? When you pass the byte array to Assembly.Load, try also writing it out to disk using FileStream.Write. Open the file in Reflector or some similar tool. Is this the assembly you expected it to be. Is it really being loaded at all, or is it an empty/corrupt array?
I'm not trying to be argumentative, but this really feels like you are looking in the wrong place for the cause of the problem.
I think If I know When a new AppDomain is created, it may solve my problem
You should use AssemblyLoad event. AssemblyResolved occurs when the resolution of assembly is fails
I found that the problem is because Assembly.Load(bytes); does not persist the assembly in appdomain. does any body know how to persist loaded assembly using Assembly.Load(bytes); in appdomain ?
finally I decided to switch to LoadFile method instead of load.
EDIT
Finally I switched from Assemly.Load(byte[]) to Assembly.LoadFile(string).
But it did not correct the problem itself. I have added <%#Assembly name="MyDLL"%> To All of my ASPX files that has markup C# code emebeded.
And This Solved my Problem.
Thanks to your answers. I have voted up answers that helped me, but I can not accept your solutions because no one is not complete enough.

Remote executing of Action<> or Func<> objects

I wanted to transfer (and execute) an Action or Func object from a C# client to a C# server application using WCF.
Here's my code:
[ServiceContract]
interface IRemoteExecuteServer
{
[OperationContract]
void Execute(Action action);
}
class RemoteExecuteServer : IRemoteExecuteServer
{
public void Execute(Action action)
{
action();
}
}
Servercode:
class Program
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(RemoteExecuteServer), new Uri("net.tcp://localhost:8000")))
{
host.AddServiceEndpoint(typeof(IRemoteExecuteServer), new NetTcpBinding(), "RES");
host.Open();
Console.WriteLine("Server is running!");
Console.WriteLine("Press any key to exit...");
Console.ReadKey(true);
host.Close();
}
}
}
Clientcode:
class Program
{
static void Main(string[] args)
{
IRemoteExecuteServer server = new ChannelFactory<IRemoteExecuteServer>(new NetTcpBinding(), new EndpointAddress("net.tcp://localhost:8000/RES")).CreateChannel();
server.Execute(delegate()
{
Console.WriteLine("Hello server!");
});
}
}
When executing the line "server.Execute" I get a CommunicationException.
Does anyone know how to fix this error?
Thanks for your help!
I would think of two solutions, both being pretty crazy in their nature. Mainly because your request of sending code to server for execution is nothing people do every day (and I think noone ever did something like that).
DLL solution:
Compile your code into separate DLL. Send this DLL as stream to server. Load some class with interface using reflection on server from this DLL. Then you can run code in created class.
Code solution:
Basicaly same as first one, but instead of sending precompiled DLL, you just send your code as string and then use programatic C# compiler to compile and run that code.
But you still cant extract your code from any function. And remember Action is nothing more than delegate (reference) for hard-coded function in your assembly.
I want to be able to use a lambda expression to specify a range of values to return over a wcf service
is a similar question i asked.
I link you too it as eric lippert showed how such a solution would be possible.
However its a decent amount of work.
There may be existing solutions out there, I know not. (There are some pretty nifty run-time byte-code injection/manipulation tools available, so...)
But given sufficient permissions (and level of trust! -- that's a big one) one can compile-on-the-fly and/or send various self-evaluation expressions across the network (this is different than sending byte-code, per-se, although that could theoretically be done if the byte-code or an equivalent can be extracted).
The approaches above wouldn't be sending an Action<...>, which would require byte-code extraction for serialization (including capturing the context, if any), but rather something like Compiling C# on the Fly which you can follow to an interesting discussion and finally find CodeDOM.
On the other hand, LINQ "gets about" the issue of byte-code extraction/de-compilation with the use of Expression trees. Note that it doesn't send over byte-code, but rather uses this method to allow remote execution of expression trees (a common one being an SQL query).

Categories

Resources