All, I call a .NET DLL containing a WinForm at run-time from a WinForm C# application. To do this I use the following:
DLL = Assembly.LoadFrom(strDllPath);
classType = DLL.GetType(String.Format("{0}.{1}", strNamespaceName, strClassName));
if (classType != null)
{
if (bDllIsWinForm)
{
classInst = Activator.CreateInstance(classType);
Form dllWinForm = (Form)classInst;
dllWinForm.Show();
// Invoke required method.
MethodInfo methodInfo = classType.GetMethod(strMethodName);
if (methodInfo != null)
{
object result = null;
result = methodInfo.Invoke(classInst, new object[] { dllParams });
return result == null ? String.Empty : result.ToString();
}
}
}
This is invoking the WinForm DLL and the required method fine for serial methods within the DLL. However, I am now invoking a multi-threaded DLL, and calling the following method:
public async void ExecuteTest(object[] args)
{
Result result = new Result();
if (!BuildParameterObjects(args[0].ToString(), args[1].ToString()))
return;
IProgress<ProgressInfo> progressIndicator = new Progress<ProgressInfo>(ReportProgress);
List<Enum> enumList = new List<Enum>()
{
Method.TestSqlConnection,
Method.ImportReferenceTables
};
Task task = Task.Factory.StartNew(() =>
{
foreach (Method method in enumList)
{
result = Process.ProcessStrategyFactory.Execute(Parameters.Instance, progressIndicator,
Process.ProcessStrategyFactory.GetProcessType(method));
if (!result.Succeeded)
{
// Display error.
return;
}
}
});
await task;
Utilities.InfoMsg("VCDC run executed successfully.");
}
But this is returning control to the caller right away due to the await
(which is expected). However, the return causes the calling method to exit which closes the DLL WinForm.
What is the best method to keep the DLL WinForm active/open?
Thanks for your time.
Edit. Following the suggestion by Stephen below I have decided to turn my DLL intery method type to Task<object>
and set up a continuation as follows
if (classType != null)
{
if (bDllIsWinForm)
{
// To pass object array to constructor use the following.
// classInst = Activator.CreateInstance(classType, new object[] {dllParams});
classInst = Activator.CreateInstance(classType);
dllWinForm = (Form)classInst;
dllWinForm.Show();
// Invoke required method.
MethodInfo methodInfo = classType.GetMethod(strMethodName);
if (methodInfo != null)
{
object result = null;
result = methodInfo.Invoke(classInst, new object[] { dllParams });
if (result != null)
{
if (result.GetType() == typeof(Task<object>))
{
Task<object> task = (Task<object>)result;
task.ContinueWith(ant =>
{
object innerResult = task.Result;
return innerResult == null ? String.Empty : innerResult.ToString();
});
}
return result.ToString();
}
return String.Empty;
}
}
}
I decided to set up the continuation instead of the await
to avoid the chaining that would occur with the await
keyword - that is making the calling method (that calling the DLL of type Task<String>
etc. up the call stack.
The DLL entry method now becomes:
public Task<object> ExecuteTest(object[] args)
{
Task<object> task = null;
Result result = new Result();
if (!BuildParameterObjects(args[0].ToString(), args[1].ToString()))
return task;
IProgress<ProgressInfo> progressIndicator = new Progress<ProgressInfo>(ReportProgress);
List<Enum> enumList = new List<Enum>()
{
Method.TestSqlConnection,
Method.ImportReferenceTables
};
task = Task.Factory.StartNew<object>(() =>
{
foreach (Method method in enumList)
{
result = Process.ProcessStrategyFactory.Execute(Parameters.Instance, progressIndicator,
Process.ProcessStrategyFactory.GetProcessType(method));
if (!result.Succeeded)
{
// Display error.
}
task.Wait(5000); // Wait to prevent the method returning too quickly for testing only.
}
return null;
});
return task;
}
But this causes the DLL WinForm to be show for a split second and then disapear. i even attempted to make the Form dllWinForm
global to keep the refernce to the object active, but this also has not worked. I want to note that the call to the DLL (N.B. the calling method is already running on a background thread-pool thread).
Any further helps is appreciated.
It's difficult to guess what you have in dll but eventually your dll code + exposed in your question:
should eventually have been after juxtaposing:
Probably, you should change
dllWinForm.Show();
bydllWinForm.ShowDialog().Start()
;ShowDialog()
, in contrast withShow()
, starts its own message pumping and returns only when explicitly closed.Update (in respnse to comment):
It is not absolutely necessary to launch a form from UI.
Since you are on .NET 4.5, it is probably simpler to use WPF (instead of Windows Form) form.
Here is the code for WPF form which, in order to adjust for Windows Form, you should change
Dispatcher
parts by initializing WindowsFormsSynchronizationContextThough, IMO, WinForms code will be much more complex.
Change the return type of
Execute
toTask
andawait
it.