Access windows forms controls from external DLLs in C# - 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.

Related

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.

One DLL with two implementations for two applications

I have a DLL with some classes and methods. And two applications using it.
One admin-application that needs almost every method and a client-application that only needs parts of the stuff. But big parts of it are used by both of them. Now I want make a DLL with the admin stuff and one with the client stuff.
Duplicating the DLL and edit things manually everytime is horrible.
Maybe conditional compiling helps me but I dont know how to compile the DLL twice with different coditions in one solution with the three projects.
Is there a better approach for this issue than having two different DLLs and manually editing on every change?
In general, you probably don't want admin code exposed on the client side. Since it's a DLL, that code is just waiting to be exploited, because those methods are, by necessity, public. Not to mention decompiling a .NET DLL is trivial and may expose inner-workings of your admin program you really don't want a non-administrator to see.
The best, though not necessarily the "easiest" thing to do, if you want to minimize code duplication, is to have 3 DLLs:
A common library that contains ONLY functions that BOTH applications use
A library that ONLY the admin application will use (or else compile it straight into the application if nothing else uses those functions at all)
A library that ONLY the client application will use (with same caveat as above)
A project that consists of a server, client, and admin client should likely have 3-4 libraries:
Common library, used by all 3
Client library, used by client and server
Admin library, used by server and admin client
Server library, used only by server (or else compile the methods directly into the application)
Have you considered using dependency injection on the common library, some form of constructor injection to determine the rules that need to be applied during execution.
Here's a very simple example:
public interface IWorkerRule
{
string FormatText(string input);
}
internal class AdminRules : IWorkerRule
{
public string FormatText(string input)
{
return input.Replace("!", "?");
}
}
internal class UserRules : IWorkerRule
{
public string FormatText(string input)
{
return input.Replace("!", ".");
}
}
public class Worker
{
private IWorkerRule Rule { get; set; }
public Worker(IWorkerRule rule)
{
Rule = rule;
}
public string FormatText(string text)
{
//generic shared formatting applied to any consumer
text = text.Replace("#", "*");
//here we apply the injected logic
text = Rule.FormatText(text);
return text;
}
}
class Program
{
//injecting admin functions
static void Main()
{
const string sampleText = "This message is #Important# please do something about it!";
//inject the admin rules.
var worker = new Worker(new AdminRules());
Console.WriteLine(worker.FormatText(sampleText));
//inject the user rules
worker = new Worker(new UserRules());
Console.WriteLine(worker.FormatText(sampleText));
Console.ReadLine();
}
}
When run you'll produce this output.
This message is *Important* please do something about it?
This message is *Important* please do something about it.

Keep a Form running after COM visible DLL terminates

I created a COM Visible DLL in C# that should show a Form after some inputs from the User in the host application (unmanaged). It works fine with ShowDialog(), but ideally the Form should keep running even after the DLL finishes. Because the Form need some Data a separated Project with Main(string[] args) is not an option.
How can I accomplish this? I tried something like that but it didn't worked.
public class FormManager : ApplicationContext
{
FormMain frmMain;
public FormManager()
:base(new FormMain())
{
frmMain = (FormMain)this.MainForm;
frmMain.Closed += new EventHandler(OnFormClosed);
}
public void SetData(object o1, object o2)
{
if (frmMain != null)
{
frmMain.SetData(o1, o2);
frmMain.Show();
}
}
private void OnFormClosed(object sender, EventArgs e)
{
ExitThread();
}
}
I have no idea where Application.Run should be inserted.
I, too, am a little unclear as to what exactly you are trying to do here... but normally, if you are placing the Application.Run somewhere in that code, it would be in place of this line:
frmMain.Show();
Of course, by using Application.Run you will be freezing this code (the thread that calls Application.Run) until the form in question closes... So maybe that doesn't really accomplish what you want (it is, indeed, unclear).
Edit After Clarification of Question
Here's the thing about COM in .NET that was not true about previous iterations of Microsoft languages. When you call an assembly in .NET via COM (OLE) the calling assembly subsumes the COM exposed code into its runtime. In other words, when you look in the Task Manager, you won't see both of your assemblies running! You'll only see the one that did the calling. Thus, when you close the main assembly, you close any running code attached to it, including your COM code.
There is one way around this, but it's not simple. In short, you would need to:
Launch your second process (you could, for instance, use a Process.Start())
Use the first process to look inside the ROT (Running Objects Table) and locate the second assembly
Communicate freely via COM (OLE) and pass your data
At this point, the two assemblies are running in separate runtimes, which will allow you to produce forms in the second assembly that will not close when the first assembly closes. That, as I understand it, is what you're looking for.
If you want to try this route, do a little Googling for the ROT and try some sample code. If you have questions about that let me know!

Task (or thread) requires Wait or Join to work

I am trying to make a plugin type system. I have made something along these lines in the past where all plugins execute in the main thread, which leads to problems if the plugins are taking a long time. So I thought I'd execute the appropriate methods in each plugin using Tasks.
I have a main program, which loads each plugin using Assembly.LoadFile, and then reacts to commands a user types. If one of these commands is handled by a plugin (the plugins report what commands they handle, the main program asks when it loads them), my program will start a method in the plugin in its own Task.
Task t = Task.Factory.StartNew(() => Plugin.ProcessCommand(cmd, Params, Context));
Each plugin also implements an event used when it has data to send to the main program for output. The main program attaches a handler to this event when it loads each plugin.
The plugin's ProcessCommand method does whatever work is needed, triggers the OnOutput event, and then ends.
This is a very simple plugin:
public override void ProcessCommand(PluginCommand Command, PluginParameters Parameters, PluginContext Context)
{
OnOutputGenerated(this,"Hello from Plugin A");
}
This worked fine with the first plugin I made. So I created another, using the exact same code, just changing "Hello from Plugin A" to "Hello from Plugin B."
Plugin A always works. If I issue the appropriate command in the main program, it runs and says Hello from Plugin A. Great.
The problem is: Plugin B executes maybe one in every 30 attempts. I've discovered, however, that if call the plugin in the following way, it works every time:
Task t = Task.Factory.StartNew(() => Plugin.ProcessCommand(cmd, Params, Context));
t.Wait(100);
Is there a technical reason why this might help? I've read through pretty much all of http://www.albahari.com/threading/ trying to figure things out, but I've not had any luck.
It's worth noting that I've also done this with threads, with the same problem.
Thread t = new Thread(() => Plugin.ProcessCommand(cmd, Params, Context));
t.Start();
Adding:
t.Join(100);
"fixed" it.
Updated
I've simplified everything. I've made a new project, which strips out all the code unrelated to the bugs.
foreach (string File in Directory.GetFiles(PluginDir, "*.dll")) {
try {
IPlugin Plugin = PluginManager.LoadPlugin(File);
Plugin.OnOutputGenerated += new PluginOutputEvent(Plugin_OnOutputGenerated);
} catch (Exception ex) {
}
}
// main loop
string Line = Console.ReadLine();
foreach (IPlugin Plugin in PluginManager.LoadedPlugins) {
foreach (PluginCommand cmd in Plugin.GetCommands()) {
if (cmd.Command.Equals(Line, StringComparison.InvariantCultureIgnoreCase)) {
PluginParameters Params = cmd.TryParseParameters(ParamString);
Task t = Task.Factory.StartNew(() => Plugin.ProcessCommand(cmd, Params, Context));
}
}
}
// output handler
static void Plugin_OnOutputGenerated(IPlugin Plugin, string OutputString) {
Console.WriteLine("Output: " + OutputString);
}
The main issue has changed. Previously, one of the plugins didn't work most of the time. Imagine two plugins.
Plugin A
* Has one command: CommandA
* Command triggers OnOutputGenerated event with the string "Hello from Plugin A"
Plugin B
* Has one command: CommandB
* Command triggers OnOutputGenerated event with the string "Hello from Plugin B"
If I run this new project I've made, and issue the command "CommandA", it will return "Hello from Plugin B". It continues doing this until I actually issue "CommandB". Once I've done that, it prints "Hello from Plugin B" (as it should). If I then issue "CommandA" again, it returns "Hello from Plugin A" (as it should have originally).
If I add
t.Wait(100);
it's fixed. It still seems to be related to the Task somehow, but I'm at a loss to explain how. It would appear that my logic otherwise is fine. I can't see how it would execute Plugin B when it should execute Plugin A, or vice-versa.
It sounds like without the Wait or Join, your main program simply exits before the requested Task code has a chance to run. If the Task logic used to be inline in the main thread, that would have implied the main thread would wait while the code executed. Now you have moved it to a separate thread, you have to add an explicit wait to allow every Task you start up to complete (maybe with a timeout, in case something goes wrong).
It's possible that even if you don't wait, a Task could occasionally finish up - that's going to be indeterminate, depending on the timing in any individual run.
Can you clarify what happens in the main thread without the Wait or Join?

How to make it so if one copy of a program is running another won't be able to open?

How to make it so if one copy of a program is running another won't be able to open?
Or better yet, how to make it so that if one copy is already running, then trying to run another copy will just act as if you maximized the original process?
Scott Hanselman wrote a post on doing this sort of thing
This article
True Single instance application - WinForms.NET
explains how to create a true single instance:
This article simply explains how you
can create a windows application with
control on the number of its instances
or run only single instance. This is
very typical need of a business
application. There are already lots of
other possible solutions to control
this.
e.g. Checking the process list with
the name of our application. But this
methods don't seems to be a good
approach to follow as everything is
decided just on the basis on the
application name which may or may not
be unique all across.
using System;
using Microsoft.VisualBasic.ApplicationServices;
namespace Owf
{
public class SingleInstanceController
: WindowsFormsApplicationBase
{
public SingleInstanceController()
{
// Set whether the application is single instance
this.IsSingleInstance = true;
this.StartupNextInstance += new
StartupNextInstanceEventHandler(this_StartupNextInstance);
}
void this_StartupNextInstance(object sender,
StartupNextInstanceEventArgs e)
{
// Here you get the control when any other instance is
// invoked apart from the first one.
// You have args here in e.CommandLine.
// You custom code which should be run on other instances
}
protected override void OnCreateMainForm()
{
// Instantiate your main application form
this.MainForm = new Form1();
}
}
}
Change you main function this way:
[STAThread]
static void Main()
{
string[] args = Environment.GetCommand
SingleInstanceController controller = new SingleInstanceController();
controller.Run(args);
}
Your best option is to use a named mutex. These articles explain the design pretty well and provide all the necessary code:
http://sanity-free.org/143/csharp_dotnet_single_instance_application.html
http://iridescence.no/post/CreatingaSingleInstanceApplicationinC.aspx
Extending this to maximise the main window of the running application should be a simple alteration to either of the examples provided.
You can use Mutex to make your app singleton. There are plenty of examples how to do it.
The Microsoft.VisualBasic.dll assembly contains a class 'WinformsFormsApplicationBase' which contains some functionality like the thing you want.
You can use this class in a C# application as well.
Just create a class which inherits from this class.
Set the SingleInstance property to true and override the necessary methods.
Offcourse, this means that you have a reference to the VisualBasic.dll assembly, which could be seen as a disadvantage, but, I think it is by far the most simple and easiest solution.
More info can be found here

Categories

Resources