Keep a Form running after COM visible DLL terminates - c#

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!

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.

Running an exe converted to a dll from another exe

I have a completely functioning application that I have converted to a Dll.
I need to be able to run that application (now dll) out of an exe . Ive added the dll as a reference to the new exe and have tried to just instantiate a frmMain object from the namespace used in the dll and show it like I would in any other application.
However, I get a bunch of null reference exceptions in several of the references that I had in the exe that I converted to a dll.
I've also tried the solution here Launch Dll using C# program
and tried jumping to the Program.cs Main() function at which point I get a "SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application." error.
Here is the dll's program.cs
namespace VTRS
{
class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new frmMain());
}
//had to add this because exe can't find Main
public void invokeMain()
{
Main();
}
//you have to have a constructor for the exe to instantiate this class
public Program()
{
}
}
}
and here is the new applications code
private void button1_Click(object sender, EventArgs e)
{
try
{
System.Reflection.Assembly dll1 = System.Reflection.Assembly.LoadFile(Application.StartupPath + "\\VTRSTCACodeMaintenance.dll");
if (dll1 != null)
{
object obj = dll1.CreateInstance("VTRS.Program");
if (obj != null)
{
System.Reflection.MethodInfo mi = obj.GetType().GetMethod("invokeMain");
mi.Invoke(obj, new object[0]);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Can anyone explain to me why this doesn't work or point me in the right direction? I'm super confused and am not doing this by choice (I'm being told to by the boss)
You do not need reflection, nor do you need to execute the Main() method of your original .exe file (indeed, you should probably just remove it, if that project is intended to just be a plain DLL).
You don't need reflection because, once you've added the assembly as a reference in your new project, all of the public types and members are accessibly in your new project. Just make sure you've declared frmMain as public (this is the default in Visual Studio, so it may already be public). Then instead of all that reflection rigmarole, all you need to do is what you'd normally do to show a form:
frmMain form = new frmMain();
form.Show();
or even just:
new frmMain().Show();
You don't need to execute the Main() method, because all it does is work that your new project already does:
The [STAThread] sets the entry point thread (which becomes the main UI thread) to the STA apartment model. But since your new project is also (it seems) a Winforms program, you already have an entry point and a main UI thread that has been correctly configured. It's this thread in which the Click event for button1 is raised, and so you are executing in that thread when the button1_Click() method is called.
Likewise, the calls to set the visual styles and text rendering. You don't need to call these again, because your new project called them when it started.
Finally, the call to Application.Run(new frmMain()). This call has three effects: it creates an instance of frmMain() (which you can do yourself as I noted above), it then passes that instance to the Application.Run() method so that method can call the Show() method on the form (again, you can do that yourself), and then the Run() method sits in a loop dispatching window messages. You also don't need to do that because, again, you are already in a Winforms program, which has already called Application.Run(), and is already dispatching window messages (which is how the Click event got raised in the first place).
So, stop trying to invoke the Main() method, just make sure the form class you want to use is public so it's visible from projects that reference the assembly, and just create and show the frmMain object the way you would if it were declared in the same project.
I think it's because of the access modifier of the class program just change it to public.
also if you don't load a changeable dll (dynamic) why you don't just reference it to the project as you would normally do with other dll's and call the main from the program class (you still need program class to be public)
In Solution Explorer, in your project view right click on References --> Add New Reference --> Browse --> Find VTRSTCACodeMaintenance.dll(it should be in your \bin\debug folder) --> double click it --> click Ok.
That is basically.

Can a C# Form application run without Application.Run?

I have a library that I originally created in Java, but I want to rewrite it (and update it) in C#. I've found IKVM, and I might have to go that route. But my first choice would be to rewrite the library in C#.
The library is a teaching tool reminiscent of turtle graphics or Karel the Robot. So I need to be able to open a non-modal window while still sending commands from the Main method.
To mimic the original, I would like to use code similar to what is shown below to start the program since that would reinforce the typical way that objects are declared and instantiated. As the name implies, MyCustomForm would be a subclass of Form.
MyCustomForm myform = new MyCustomForm(...);
I've searched through the site, but haven't found any other question similar to mine.
Is this possible in C#?
Yes, there are several ways. The easiest would be to call Application.DoEvents periodically. Here's a short example of how you might write it.
static class Program {
static bool formClosed = false;
[STAThread]
static void Main() {
MyCustomForm form = new MyCustomForm();
form.Show();
form.FormClosed += Form_FormClosed;
while(!formClosed) {
Application.DoEvents();
Thread.Sleep(10);
}
}
private static void Form_FormClosed(object sender, FormClosedEventArgs e) {
formClosed = true; }
}
There exists much better solutions than this example, though. The term you want to Google for is "C# game loop" despite that your application is not a game.
No, it is not possible.
Application.Run() starts the event pump which allows your application to respond to events. This includes events that come from the user (e.g. clicks, drags, maximize, close) and events that come from the O/S or your own program (such a request to repaint the window, respond to timers, shut down when the O/S is being shut own). All windows forms applications require this sort of event pump in order to provide typical functionality.
Yes, just use ShowDialog() on your form, simple like that, then when your form closes call Application.Exit(). There is no need for Application.DoEvents();
In my case I'm using this to allow user to login again with another user.
So I can close my main form e then show user login form again

How do I prevent two instances of my application from loading after updating using ClickOnce?

I have an application that I am deploying using ClickOnce. I am using the default InstallUpdateUpdateSyncWithInfo() method provided here. I made two changes though; I made the method public and static as I am calling it from a static class. I know bad practices. This is some lazy code just to try out ClickOnce.
Everytime the application updates it loads two instances, the old one and the new one.
Other than that though I am calling the method in my app.xaml.cs like this:
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
MainWindow window = new MainWindow();
CheckForUpdates.InstallUpdateSyncWithInfo();
window.Show();
}
}
I thought if I call Window.Show() after checking for an Update it would call the Application.Restart() method in InstallUpdateUpdateSyncWithInfo() before the old version could load, but this is not the case.
Does anyone know how I can prevent two instances of my application from loading after the application is updated?
There was another post on Stack Overflow which from the title, I thought would directly address this question, but I did not see how the poster modified his code to prevent two instances from loading.
There's no need to write the auto-update code yourself. First, I would remove your update code.
Next right-click on your C# project and select Properties. Then go to Publish and click Updates.... Tick the checkbox so your application checks for updates and ClickOnce will handle the rest.

Windows Forms GUI hangs when calling OpenFileDialog.ShowDialog()

my project a three tier architecture project talking to a WCF service in the backend. When the backend is able to fetch data from the service, it notifies the business layer using publish-subscribe, which in return notifies the GUI layer.
I have added an OpenFileDialog to my UI design using Visual Studios designer. A button event handler calls the ShowDialog message. However, once I click the button, the whole UI hangs.
Having googled around a bit, I found out that using delegates is the preferred way to handle tasks like this. However, with nor without delegate the problem persists.
Currently my code looks like this:
private void bOpen_Click(object sender, EventArgs e)
{
Func<Image> del = delegate
{
OpenFileDialog d = new OpenFileDialog();
if (d.ShowDialog() == DialogResult.OK)
{
return Image.FromFile(d.FileName);
}
return null;
};
Invoke(del);
}
I'm coming from the Java world, so I'm not really familiar with the intricacies of C# UI programming.
Anything I'm missing here?
openFileDialog1->ShowHelp = true;
I put this line in my code then the problem was solved.
I seem to have solved the problem adding the [STAThread] Attribute to the main method. I was told to do so once I ran the program in a debugger - which I hadn't done before because I ran the service from Visual Studio and the client regularly from Windows.
[STAThread]
public static void Main(string[] args)
{
GUI gui = new GUI();
gui.ShowDialog();
}
Can anybody explain what exactly is going on though
This tends to be an environmental problem, when you use OpenFileDialog a lot of shell extensions get loaded into your process. A misbehaving one can easily screw up your program. There are a lot of bad ones out there.
Debugging this is difficult, you need an unmanaged debugger since these shell extensions are unmanaged code. You might be able to tell something from the call stack when you break in after the deadlock. Windows debugging symbols required, enable the Microsoft symbol server. But the most effective approach is to use SysInternals' AutoRuns utility. Start by disabling all of the shell extensions that were not produced by Microsoft. Then start re-enabling the ones you cannot live without one by one.
And, as you found out, these shell extension expect to run on an STA thread and fail miserably when they don't get it. The UI thread of a program must always be STA, also to support the clipboard and drag-and-drop and various kinds of controls like WebBrowser. Normally always taken care of automatically by the [STAThread] attribute on the Main() method, put there by the project template. And the Application.Run() call, required to implement the STA contract. Deadlock when you don't.
I believe the "delegate" prefered way actually refers to using a separate thread.
I'm gonna give you an example using BackgroundWorker.
It would look like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
m_Worker.DoWork += new DoWorkEventHandler(m_Worker_DoWork);
m_Worker.ProgressChanged += new ProgressChangedEventHandler(m_Worker_ProgressChanged);
m_Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(m_Worker_RunWorkerCompleted);
}
void m_Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//Usually, used to update a progress bar
}
void m_Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Usually, used to add some code to notify the user that the job is done.
}
void m_Worker_DoWork(object sender, DoWorkEventArgs e)
{
//e.Argument.ToString() contains the path to the file
//Do what you want with the file returned.
}
private void bOpen_Click(object sender, EventArgs e)
{
OpenFileDialog d = new OpenFileDialog();
if (d.ShowDialog() == DialogResult.OK)
{
m_Worker.RunWorkerAsync(d.FileName);
}
}
BackgroundWorker m_Worker = new BackgroundWorker();
}
Now, as for the reason your UI "hangs", it's because by default, your operation runs on the UI thread, so if you run something heavy the UI won't respond.
I also met this problem. And I tried all the solution here and none can solve it. Then I change the target framework from .Net Framework 4.7 to 4.6.2, the problem solved...
I think my problem is different, as none of the above solutions worked for me.
I wrote temporary code to set the OpenFileDialog.FileName property to something not null or empty string (it was empty string when the hang up occured), and I restarted my computer. When I started up Visual Studio again, and ran it, it worked again without hanging up.

Categories

Resources