【发布时间】:2017-04-04 19:27:41
【问题描述】:
我创建了一个使用插件的应用程序。插件包含我想添加到主 ToolStrip 容器面板(在 Form1 类中)的 ToolStrip。这很容易container.TopToolStripPanel.Controls.Add(plugin.PluginToolStrip;,但如果我想在单独的线程中运行插件代码,那就没那么容易了。 (我使用多线程来轻松卸载插件,我只需要杀死插件线程,并从主窗体中删除 ToolStrip)
我禁用了CheckForIllegalCrossThreadCalls = false; 以允许不使用 Invoke void。但是当我想从另一个线程运行container.TopToolStripPanel.Controls.Add(plugin.PluginToolStrip); 时,程序会抛出ArgumentException 并说我做不到。
那么,如何创建可以杀死插件线程的插件架构? (我想为用户提供简单的管理插件方式的可能性)
我反编译了 System.Windows.Forms.dll 以查看它在哪里引发了异常,我看到了:
/// <summary>Adds the specified control to the control collection.</summary>
/// <param name="value">The <see cref="T:System.Windows.Forms.Control" /> to add to the control collection. </param>
/// <exception cref="T:System.Exception">The specified control is a top-level control, or a circular control reference would result if this control were added to the control collection. </exception>
/// <exception cref="T:System.ArgumentException">The object assigned to the <paramref name="value" /> parameter is not a <see cref="T:System.Windows.Forms.Control" />. </exception>
public virtual void Add(Control value)
{
if (value == null)
{
return;
}
if (value.GetTopLevel())
{
throw new ArgumentException(SR.GetString("TopLevelControlAdd"));
}
if (this.owner.CreateThreadId != value.CreateThreadId)
{
throw new ArgumentException(SR.GetString("AddDifferentThreads")); //here!
}
/* [...] */
}
那么我认为如果我可以更改this.owner.CreateThreadId,那么我将能够通过这个if (if (this.owner.CreateThreadId != value.CreateThreadId)),并且程序不会抛出异常。在第 6315 行我看到了这段代码:
internal int CreateThreadId
{
get
{
if (this.IsHandleCreated)
{
int num;
return SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(this, this.Handle), out num);
}
return SafeNativeMethods.GetCurrentThreadId();
}
}
我们只有得到,它是内部的:(
我能做什么?你有什么建议吗?感谢和抱歉我的英语不好......
【问题讨论】:
-
简短的回答是否定的,从线程更新 UI,而不使用安全的跨线程机制,例如
SafeInvoke是 no no 并且会产生令人不快的故障和困难寻找错误。原则和规则不仅适用于 C#/Windows,也适用于 Java、Android、iOS 等。您应该考虑重新架构插件机制,以便能够以有利于 UI 流畅性的方式使用跨线程调用。跨度>
标签: c# multithreading exception plugins toolstrip