Basically, from what I've understood of the little I've managed to search up on the internet, threads can pass between AppDomains. Now, I've written the following code:
const string ChildAppDomain = "BlahBlah";
static void Main()
{
if (AppDomain.CurrentDomain.FriendlyName != ChildAppDomain)
{
bool done = false;
while (!done)
{
AppDomain mainApp = AppDomain.CreateDomain(ChildAppDomain, null, AppDomain.CurrentDomain.SetupInformation);
try
{
mainApp.ExecuteAssembly(Path.GetFileName(Application.ExecutablePath));
}
catch (Exception ex)
{
// [snip]
}
AppDomain.Unload(mainApp);
}
}
else
{
// [snip] Rest of the program goes here
}
}
This works fine and everything is clicking into place... The main thread passes through into the new version of my program and starts running through the main application body. My question is, how would I go about getting it to go back out to the parent AppDomain? Is this possible? What I'm trying to achieve is sharing an instance of a class between the two domains.
You cannot share instances of classes directly between AppDomains. To do so, you should derive the class from MarshalByRefObject and use remoting to access the instance from the other AppDomain.
An object in .Net can only exist in one AppDomain. It is not possible for it to exist in 2 AppDomains at the same time.
However you can use .Net Remoting to push a proxy of a .Net object into several AppDomains at once time. This will give your object the appearance of being in multiple domains. I believe this is what you are looking for.
There are many tutorials available online. Google for ".Net Remoting Tutorial" and that will put you on teh right track.
http://www.beansoftware.com/NET-Tutorials/NET-Remoting-Tutorial.aspx
http://msdn.microsoft.com/en-us/library/72x4h507(VS.80).aspx
Related
I have an Console Application started as [STAThread].
That application should open a seperate Wpf UI for entering some settings.
The functions for that:
private static void openUI()
{
var application = new System.Windows.Application();
//referenced project in the same solution
var ui = new ManagerUI.MainWindow();
//blocks execution
application.Run(ui);
application.Shutdown();
}
Opening the UI for the first time works as expected.
The problem occurs when opening the UI for the second time.
I get an System.InvalidOperationException, saying that I cannot run more than one Application-Instance in the same AppDomain.
For saving ram, it must be closed between the operations.
I also tried to create the System.Windows.Application in the constructor.
But as soon as I run the application the second time, I get a very similiar exception.
The InitializeComponents() method of the UI throws an System.InvalidOperationException, saying that the Object is going to be terminated.
The StackTraces shows that the error appears when the xaml is parsed, so I conclude it cannot open it, because it is still opened by the first execution.
Neither calling ui.Close() nor calling application.Shutdown() solves the problem (Environment.Exit() closes everything, including my Console Application).
The ram profiler indicates, not everything was closed correctly because it shows an higher use after the Window was closed, than before it was opened in the firts place.
How do I properly close the Application instance, or how do I re-use it to run an Wpf Application multiple times?
Having looked at the source code for the Application class, it doesn't look like you will be able to work around this, as various static fields are initialized by the class constructor:
public Application()
{
...
lock(_globalLock)
{
if (_appCreatedInThisAppDomain == false)
{
...
_appInstance = this;
...
_appCreatedInThisAppDomain = true;
}
else
{
throw new InvalidOperationException(...);
}
}
}
...
static private object _globalLock;
static private bool _appCreatedInThisAppDomain;
static private Application _appInstance;
...
Basically the constructor sets _appCreatedInThisAppDomain to true, and as that field is private you have no way of setting it back*.
I think the only way of achieving something similar to what you want is to write a separate WPF application, then use the Process class to launch that from your console application. Alternatively, you could theoretically create a separate AppDomain to host your WPF stuff but that would be a lot more complicated.
[*] other than using Reflection, but let's not go there!
You may create a class that derives from MarshalByRefObject:
public class AppDomainWrapper : MarshalByRefObject
{
public void openUI()
{
var application = new System.Windows.Application();
var ui = new Window();
application.Run(ui);
application.Shutdown();
}
}
...and execute its openUI() method in its own application domain:
[STAThread]
static void Main(string[] args)
{
const int n = 2;
for (int i = 0; i < n; ++i)
{
AppDomain appDomain = AppDomain.CreateDomain("AppDomain");
AppDomainWrapper application = appDomain.CreateInstanceAndUnwrap(typeof(AppDomainWrapper).Assembly.FullName, typeof(AppDomainWrapper).FullName) as AppDomainWrapper;
application.openUI();
AppDomain.Unload(appDomain);
}
}
Have a look at this question:Does a WPF Application Actually Need Application.Run?.
Basically it says, that you can open windows using window.ShowDialog() method without Application instance
The think is that Application.Run does not do anything important but run Dispatcher loop. ShowDialog have its own Dispatcher. You can create Application singleton instance however, since it contains some shared resources.
Hack(run it after application.Shutdown()). I use this in tests:
var field = typeof(Application).GetField(
"_appCreatedInThisAppDomain",
BindingFlags.Static | BindingFlags.NonPublic) ??
throw new InvalidOperationException(
"Field is not found: _appCreatedInThisAppDomain.");
field.SetValue(null, false);
Steven Rands shows the problem.
I have the same problem in an external add-in. But I need an application object for xaml resources and a valid Application.Current.
In my eyes this is a bug. If you call Shutdown() this member should also be reset to false.
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.
Update 2
The queueing problem was probably solved already, as we've been able to run multiple requests concurrently and the lib nicely reported progress for each operation. Other issues we're still facing about concurrency were likely the reason for this apparent behaviour, but that's a design matter. To solve this however, it'd be helpful to have some knowledge of the inner workings of classes, modules and variables as used in VB6. A question arise: would encapsulating everything (connections, components etc.) in classes ensure that every created object does not share any data with other instances?
Update 1
We've refactored the application a bit more to cope with resource disposing, especially when dealing with OCXs. Apparently that solved the out of memory issue. What still bothers me is that I don't understand what is happening beneath the surface. In this regard, is there a way to see what objects are currently in memory and how many references they have? I know the reference counting model is different from garbage collector-based systems. Still I would suppose the RCW wrapping our com objects would keep things clean for us. In the model given, is that a safe assumption or there's something we're missing?
So, I've probably read the most variegated kind of articles and docs about the topic of COM multithreading, but I still cannot get how that's supposed to work exactly, especially when interacting with .Net technologies such as ASP.Net MVC. That could be considered a simple fancy of mine, except for the fact that we've got this quite critical project and we're experiencing severe issues in trying to tie everything up. We're getting out of memory errors (in VB6) and apparently we got wrong how objects are created and data shared between these in COM. Continue reading to know how the story goes...
How things came to be
Not much to say here. We have a legacy VB6 Desktop application made up of a number of ActiveX DLLs. These are configured to use Apartment as the threading model, and all classes are set as MultiUse. All worked well and nice until the time came when we was requested to transpose the app on the mighty web :O
The problem we faced and how we (thought we) solved it
Since we haven't got the resources to design and develop a solution from scratch, we used a third party java(script)-based framework to quickly build a web app. However, much of the real work is done by the legacy library, so we needed a way to interface these two components. The easiest way we could think of was to build a very basic (w/o auth and w/o UI) Asp.Net MVC website to use as the middle layer. This would receive requests from the web app and translate them for the COM lib to crunch data.
To this end, and since the libs were never meant to be used as a server, we tried to refactor the whole thing a bit so that most classes can now be used in a standalone manner: this included separating logic from the UI and eliminating all module and public vars where possible; unfortunately, some of the former are still present, in particular some ComponentOne OCXs to handle reports and prints. All in all, this seemed to work just fine, until we had to deal with the COM threading model :O
Making sense of nonsense
Long story short, after a lot of digging and headaches we devised the current solution, which is outlined below:
we install the legacy app as usual, so that it register its dlls in the registry;
in our MVC solution, we use System.Threading.Tasks, one per every request, to start the requested operation in an asynchronous manner. We assign the operation an id and return this id to the client. To start the task we call this method:
protected Task<TReturn> StartSTATask<TReturn>(Func<TReturn> function)
{
var task = Task.Factory.StartNew(
function,
System.Threading.CancellationToken.None,
TaskCreationOptions.None,
STATaskScheduler // property to store the scheduler instance
);
return task;
}
the task is run using the STATaskScheduler. We modified it so that it spawns a new thread if the number of threads in the pool is set to 0.
/// <summary>Initializes a new instance of the StaTaskScheduler class with the specified concurrency level.</summary>
/// <param name="numberOfThreads">The number of threads that should be created and used by this scheduler.</param>
public StaTaskScheduler(int numberOfThreads)
{
// Validate arguments
//if (numberOfThreads < 1) throw new ArgumentOutOfRangeException("concurrencyLevel");
// Initialize the tasks collection
_tasks = new BlockingCollection<Task>();
if (numberOfThreads > 0)
{
// Create the threads to be used by this scheduler
_threads = Enumerable.Range(0, numberOfThreads).Select(i =>
{
var thread = new Thread(() =>
{
// Continually get the next task and try to execute it.
// This will continue until the scheduler is disposed and no more tasks remain.
foreach (var t in _tasks.GetConsumingEnumerable())
{
TryExecuteTask(t);
}
});
thread.Name = "sta_thread_" + i;
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
return thread;
}).ToList();
// Start all of the threads
_threads.ForEach(t => t.Start());
}
}
/// <summary>Queues a Task to be executed by this scheduler.</summary>
/// <param name="task">The task to be executed.</param>
protected override void QueueTask(Task task)
{
if (_threads != null)
// Push it into the blocking collection of tasks
_tasks.Add(task);
else
{
var thread = new Thread(() => TryExecuteTask(task));
thread.Name = "sta_thread_task_" + task.Id;
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
}
And in our base controller's OnActionExecuting method we initiliaze it so
STATaskScheduler = HttpContext.Application["STATaskScheduler"] as TaskScheduler;
if (null == STATaskScheduler)
{
STATaskScheduler = new StaTaskScheduler(0);
HttpContext.Application["STATaskScheduler"] = STATaskScheduler;
}
we use a thin wrapper to instantiate and call our COM libs through reflection:
// Libraries is a Dictionary containing the names of the registered dlls
protected object InitCom(Libraries lib)
{
return InitCom(lib, true);
}
protected virtual object InitCom(Libraries lib, bool setOperation)
{
var comObj = GetComInstance(lib);
var success = SetUpConnection(comObj);
if (!success)
throw new LeafOperationException(lib, "Errore durante la connessione: {1}".Printf(connectionString));
if(setOperation)
return InitOperation(comObj);
return comObj;
}
protected object GetComInstance(Libraries lib)
{
var comType = Type.GetTypeFromProgID(MALib[lib]);
var comObj = Activator.CreateInstance(comType);
return comObj;
}
protected virtual bool DisposeCom(object comObj)
{
var success = CloseConnection(comObj);
if(!success)
throw new LeafOperationException("Errore durante la chiusura della connessione: {1}".Printf(connectionString));
//Marshal.FinalReleaseComObject(comObj);
//comObj = null;
return success;
}
protected bool SetUpConnection(object comObj)
{
var serverName = connectionString.ServerName();
var catalogName = connectionString.CatalogName();
return Convert.ToBoolean(comObj.InvokeMethod("Set_ConnectionWeb", serverName, catalogName));
}
protected bool CloseConnection(object comObj)
{
return Convert.ToBoolean(comObj.InvokeMethod("Close_ConnectionWeb"));
}
protected object InitOperation(object comObj)
{
comObj.GetType().InvokeMember("OperationID", BindingFlags.SetProperty, null, comObj, new object[] { OperationId });
comObj.GetType().InvokeMember("OperationHash", BindingFlags.SetProperty, null, comObj, new object[] { OperationHash });
return comObj;
}
The rationale behind this is that we create a new instance of the class with each request, eventually releasing it when done. Read here to know why we commented out the ReleaseComObject part. Basically, we were trading out of memory for a lot of COM object that has been separated from its underlying RCW cannot be used exceptions.
The object is then used like this within methods of various classes:
public bool ChiusuraMese()
{
try
{
PulisciMessaggi();
var comObj = InitCom(Libraries.Chiusura);
var byRefArgs = new int[] { 2 };
var oReturn = comObj.InvokeMethodByRef("ChiusuraMese", byRefArgs, IdDitta, PeriodoGiornaliera, IdDipendenti.PadLeft(), IdGruppoInstallazione, CodGruppoGestione);
DisposeCom(comObj);
return Convert.ToInt32(oReturn) == 0;
}
catch (Exception ex)
{
using (ErrorLog Log = new ErrorLog(System.Reflection.Assembly.GetExecutingAssembly().FullName, ex)) { }
aErrorMessage = ex.Message;
return false;
}
}
where InvokeMethodByRef is an extension method defined this way:
public static object InvokeMethodByRef(this object comObj, string methodName, int[] byRefArgs, params object[] args)
{
var modifiers = new ParameterModifier(args.Length);
byRefArgs.ToList().ForEach(index => { modifiers[index] = true; });
return comObj.GetType().InvokeMember(methodName, BindingFlags.InvokeMethod, null, comObj, args, new ParameterModifier[] { modifiers }, null, null);
}
Left out of the apartment
For what I understood, this whole apartment stuff is really hard to get right, with its cross-thread marshalling, message loop, yadda yadda whatnot. Add to that we're using and old, unsupported technology used to develop an application that was not architected for the purpose we're forcing it into. All that said, and taken for grant that the .Net side of things is working correctly, a couple of thoughts still wander in our minds. In particular:
is this the correct way to get advantage of multithreading with COM? Sometimes, multiple requests for the same object get stuck as if queued. This makes us wonder whether COM is actually sharing some instances between threads;
are we really creating and disposing of objects with each request, or under the hood COM handles things differently? Apparently, we're getting public vars overwritten, so there's probably some resource contention and reentering somewhere we wouldn't expect;
is the setup correct? Are there alternatives which are easier to maintain and debug? Please keep in mind we don't have neither the time nor the resources to rewrite anything in great extent. We could probably try something like creating an exe ActiveX, but I wouldn't count on that.
what's the "least worse" way to use OCXs in a project of this kind (not using them is not an option at the moment)? Should we dispose of them in some particular way? We already checked we set them to nothing when finished, but maybe some other thread is still using them;
should we be aware of any particular COM limit related to our out of memory issue? We encountered the problem before when the form had more than 256 unique controls displayed. Maybe the same is happening here somehow? The error seems to be especially related to classes using UI components.
Things I've already read (and probably did not understand)
Before you point to resources online I should read, I add here some topics I've encountered, in random order:
About SingleUse/MultiUse
http://www.vb-helper.com/howto_activex_dll.html
https://msdn.microsoft.com/en-us/library/aa242108(v=vs.60).aspx
Not really much choice here, if we want to stick with ActiveX DLLs with forms.
About (apartment) threading
https://msdn.microsoft.com/en-us/library/aa716297(v=vs.60).aspx
https://msdn.microsoft.com/en-us/library/aa716228(v=vs.60).aspx. By the way, this one probably hints that calls to objects are being serialized for access by other threads.
https://msdn.microsoft.com/en-us/library/windows/desktop/ms680112%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
About debugging
https://msdn.microsoft.com/en-us/library/aa241684(v=vs.60).aspx
https://msdn.microsoft.com/en-us/library/aa716193%28v=vs.60%29.aspx?f=255&MSPPError=-2147217396
Could a stack dump be of any help when we face the error? I don't even know how to use WinDbg, so I'd like at least to know if that would be a total waste of time :D
We're kinda stuck here, as we've got no clue as to where or what to look for, so any kind of help would be really appreciated.
Comments
So I've been pointed out I should read more about COM's threading model. I kind of expected that. Anyhow, to elaborate further, let me write some comments.
First, I don't have any control over CoInitialize or whatever, I'm just instantiating some VB6 dlls. I guess COM is doing such and such under the hood. Fact is, I could not find anywhere what that is (edit - apparently, .Net is already taking care of that for me, see the answer to this question: Do i need to call CoInitialize before interacting with COM in .NET?).
To recap:
I'm using STA threads from the client app
I'm using Activator.CreateInstance supposing it is actually creating a new object every time it is called. The call is done within a new STA thread.
Let's set aside for a moment questions about thread-safety in the actual DLLs. What I'm mainly interested in understanding here is if the described solution is a correct way (possibly not the best way, I'm aware of that) to exploit multithreading with COM libraries.
To cite some sources, to the best of my current knowledge I should be in the situation depicted in Figure 8.5 here: https://msdn.microsoft.com/en-us/library/aa716228(v=vs.60).aspx
I can't find any reason why this should not work, since as I said I'm supposing each object resides in its own apartment and has its own variables, plus a copy of global vars (see here: https://msdn.microsoft.com/en-us/library/aa261361(v=vs.60).aspx).
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!
A quote from MSDN: http://msdn.microsoft.com/en-us/library/6kac2kdh.aspx
One or more managed threads
(represented by
System.Threading.Thread) can run in
one or any number of application
domains within the same managed
process. Although each application
domain is started with a single
thread, code in that application
domain can create additional
application domains and additional
threads. The result is that a managed
thread can move freely between
application domains inside the same
managed process; you might have only
one thread moving among several
application domains.
I tried to write code with 2 application domains that share one thread. But i gave up. I have really no idea how this is possible. Could you give me a code sample for this?
This can be done by simply creating an object which is MarshalByRef in a separate AppDomain and then calling a method on that object.
Take for example the following class definition.
public interface IFoo
{
void SomeMethod();
}
public class Foo : MarshalByRefObject, IFoo
{
public Foo()
{
}
public void SomeMethod()
{
Console.WriteLine("In Other AppDomain");
}
}
You can then use this definition to call into a separate AppDomain from the current one. At the point the call writes to the Console you will have 1 thread in 2 AppDomains (at 2 different points in the call stack). Here is the sample code for that.
public static void CallIntoOtherAppDomain()
{
var domain = AppDomain.CreateDomain("Other Domain");
var obj = domain.CreateInstanceAndUnwrap(typeof(Foo).Assembly.FullName, typeof(Foo).FullName);
var foo = (IFoo)obj;
foo.SomeMethod();
}
Call a method on an object of the other app domain.