优化模块资源释放与异步预加载
This commit is contained in:
@@ -3245,8 +3245,11 @@ namespace Lskj.Main.Control
|
||||
if (Manager.ModuleForms.ContainsKey(guid))
|
||||
{
|
||||
DllModule m = Manager.ModuleForms[guid];
|
||||
Manager.ModuleCloseSnapshot closeSnapshot =
|
||||
Manager.PrepareModuleClose(m, item);
|
||||
tabMain.TabPages.Remove(item);
|
||||
Manager.ModuleForms.Remove(m.Tag + "");
|
||||
Manager.ModuleForms.Remove(guid);
|
||||
Manager.CompleteModuleClose(closeSnapshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3821,10 +3824,12 @@ namespace Lskj.Main.Control
|
||||
string guid = tabPage.Tag + "";
|
||||
if (!string.IsNullOrWhiteSpace(guid))
|
||||
{
|
||||
DllModule m = Manager.ModuleForms[guid];
|
||||
if (Manager.ModuleForms.ContainsKey(guid))
|
||||
DllModule m;
|
||||
if (Manager.ModuleForms.TryGetValue(guid, out m) && m != null)
|
||||
{
|
||||
Form form = m.ModuleForm as Form;
|
||||
Manager.ModuleCloseSnapshot closeSnapshot =
|
||||
Manager.PrepareModuleClose(m, tabPage);
|
||||
if (form != null)
|
||||
{
|
||||
form.Close();
|
||||
@@ -3850,7 +3855,8 @@ namespace Lskj.Main.Control
|
||||
{
|
||||
if (this.PreviousTab != null) this.tabMain.SelectedTabPage = this.PreviousTab;
|
||||
tabMain.TabPages.Remove(tabPage);
|
||||
Manager.ModuleForms.Remove(m.Tag + "");
|
||||
Manager.ModuleForms.Remove(guid);
|
||||
Manager.CompleteModuleClose(closeSnapshot);
|
||||
LogUtil.WriteDebug(m.Code, "关闭模块", m.Name, m.Name, m.Id);
|
||||
}
|
||||
|
||||
@@ -3956,10 +3962,12 @@ namespace Lskj.Main.Control
|
||||
string guid = page.Tag + "";
|
||||
if (!string.IsNullOrWhiteSpace(guid))
|
||||
{
|
||||
DllModule m = Manager.ModuleForms[guid];
|
||||
if (Manager.ModuleForms.ContainsKey(guid))
|
||||
DllModule m;
|
||||
if (Manager.ModuleForms.TryGetValue(guid, out m) && m != null)
|
||||
{
|
||||
Form form = m.ModuleForm as Form;
|
||||
Manager.ModuleCloseSnapshot closeSnapshot =
|
||||
Manager.PrepareModuleClose(m, page);
|
||||
if (form != null) form.Close();
|
||||
|
||||
bool isclose = true;
|
||||
@@ -3975,7 +3983,8 @@ namespace Lskj.Main.Control
|
||||
{
|
||||
if (this.PreviousTab != null) this.tabMain.SelectedTabPage = this.PreviousTab;
|
||||
tabMain.TabPages.Remove(page);
|
||||
Manager.ModuleForms.Remove(m.Tag + "");
|
||||
Manager.ModuleForms.Remove(guid);
|
||||
Manager.CompleteModuleClose(closeSnapshot);
|
||||
LogUtil.WriteDebug(m.Code, "关闭模块", m.Name, m.Name, m.Id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,17 +338,24 @@ namespace Lskj.Main
|
||||
int closeCount = 0;
|
||||
if (StaticControl.DogVerifyModuleForms.Count > 0)
|
||||
{
|
||||
foreach (DevExpress.XtraTab.XtraTabPage page in StaticControl.DogVerifyModuleForms)
|
||||
foreach (DevExpress.XtraTab.XtraTabPage page in
|
||||
new List<DevExpress.XtraTab.XtraTabPage>(StaticControl.DogVerifyModuleForms))
|
||||
{
|
||||
this.ActiveMainPanel.TabMain.Invoke(new Action(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
string moduleId = Convert.ToString(page.Tag);
|
||||
DllModule module;
|
||||
Manager.ModuleForms.TryGetValue(moduleId, out module);
|
||||
Manager.ModuleCloseSnapshot closeSnapshot =
|
||||
Manager.PrepareModuleClose(module, page);
|
||||
if (this.ActiveMainPanel.TabMain.TabPages.Contains(page))
|
||||
{
|
||||
this.ActiveMainPanel.TabMain.TabPages.Remove(page);
|
||||
page.Dispose();
|
||||
}
|
||||
Manager.ModuleForms.Remove(moduleId);
|
||||
Manager.CompleteModuleClose(closeSnapshot);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -360,7 +367,8 @@ namespace Lskj.Main
|
||||
}
|
||||
if (StaticControl.DogVerifyNoPageForms.Count > 0)
|
||||
{
|
||||
foreach (IForm iForm in StaticControl.DogVerifyNoPageForms)
|
||||
foreach (IForm iForm in
|
||||
new List<IForm>(StaticControl.DogVerifyNoPageForms))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -580,6 +588,7 @@ namespace Lskj.Main
|
||||
try
|
||||
{
|
||||
CloseWatermarkForm();
|
||||
Manager.CompleteAllModuleCloses();
|
||||
Manager.ModuleForms.Clear();
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -52,6 +52,170 @@ namespace Lskj.Main.Model
|
||||
DeferredForWpf
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WinForms-owned module lifetime token. Resources are registered while
|
||||
/// the module is opened and released from this scope on close.
|
||||
/// </summary>
|
||||
public sealed class ModuleCloseSnapshot
|
||||
{
|
||||
internal readonly string ModuleId;
|
||||
internal readonly XtraTabPage Page;
|
||||
internal readonly Form Form;
|
||||
internal readonly StaticControl.ModuleResourceScope Scope;
|
||||
|
||||
internal ModuleCloseSnapshot(
|
||||
string moduleId,
|
||||
XtraTabPage page,
|
||||
Form form,
|
||||
StaticControl.ModuleResourceScope scope)
|
||||
{
|
||||
ModuleId = moduleId ?? string.Empty;
|
||||
Page = page;
|
||||
Form = form;
|
||||
Scope = scope;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepares a close token without inspecting the hosted control tree.
|
||||
/// The module scope has already registered its resources during open;
|
||||
/// the fallback registration only covers legacy callers that create a
|
||||
/// page outside AddModuleToTab.
|
||||
/// </summary>
|
||||
public static ModuleCloseSnapshot PrepareModuleClose(
|
||||
DllModule module,
|
||||
XtraTabPage page)
|
||||
{
|
||||
Form form = module == null ? null : module.ModuleForm as Form;
|
||||
string moduleId = module == null
|
||||
? Convert.ToString(page == null ? null : page.Tag)
|
||||
: Convert.ToString(module.Tag);
|
||||
// Legacy callers historically set only XtraTabPage.Tag. Prefer the
|
||||
// page key when the module object has not been assigned its GUID so
|
||||
// each close operation resolves its own scope and dictionary entry.
|
||||
if (string.IsNullOrWhiteSpace(moduleId))
|
||||
moduleId = Convert.ToString(page == null ? null : page.Tag);
|
||||
moduleId = moduleId ?? string.Empty;
|
||||
if (module != null && !string.IsNullOrWhiteSpace(moduleId))
|
||||
module.Tag = moduleId;
|
||||
StaticControl.ModuleResourceScope scope;
|
||||
bool created = false;
|
||||
lock (ModuleScopesSync)
|
||||
{
|
||||
if (!ModuleScopes.TryGetValue(moduleId, out scope) || scope == null)
|
||||
{
|
||||
scope = StaticControl.FindAttachedModuleScope(form) ??
|
||||
StaticControl.FindAttachedModuleScope(page);
|
||||
if (scope == null)
|
||||
{
|
||||
scope = new StaticControl.ModuleResourceScope(moduleId);
|
||||
created = true;
|
||||
}
|
||||
ModuleScopes[moduleId] = scope;
|
||||
}
|
||||
}
|
||||
if (created)
|
||||
{
|
||||
StaticControl.AttachModuleScope(form, scope);
|
||||
StaticControl.AttachModuleScope(page, scope);
|
||||
RegisterOwnedModuleObjects(scope, form, page);
|
||||
}
|
||||
return new ModuleCloseSnapshot(moduleId, page, form, scope);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Completes a previously prepared module close by disposing its
|
||||
/// registration scope. No control-tree traversal is performed here.
|
||||
/// </summary>
|
||||
public static void CompleteModuleClose(ModuleCloseSnapshot snapshot)
|
||||
{
|
||||
if (snapshot == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (snapshot.Scope != null)
|
||||
snapshot.Scope.Dispose();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
LogHelper.Instance.WriteError(exception);
|
||||
}
|
||||
lock (ModuleScopesSync)
|
||||
{
|
||||
StaticControl.ModuleResourceScope current;
|
||||
if (ModuleScopes.TryGetValue(snapshot.ModuleId ?? string.Empty, out current) &&
|
||||
ReferenceEquals(current, snapshot.Scope))
|
||||
{
|
||||
ModuleScopes.Remove(snapshot.ModuleId ?? string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void CompleteAllModuleCloses()
|
||||
{
|
||||
List<StaticControl.ModuleResourceScope> scopes;
|
||||
lock (ModuleScopesSync)
|
||||
{
|
||||
scopes = ModuleScopes.Values.Where(scope => scope != null).Distinct().ToList();
|
||||
ModuleScopes.Clear();
|
||||
}
|
||||
foreach (StaticControl.ModuleResourceScope scope in scopes)
|
||||
{
|
||||
try
|
||||
{
|
||||
scope.Dispose();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
LogHelper.Instance.WriteError(exception);
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy page-open paths create scopes directly and therefore do
|
||||
// not enter ModuleScopes. Release those scopes during the global
|
||||
// shutdown pass as well.
|
||||
StaticControl.CompleteAllModuleResourceScopes();
|
||||
}
|
||||
|
||||
private static void RegisterOwnedModuleObjects(
|
||||
StaticControl.ModuleResourceScope scope,
|
||||
Form form,
|
||||
XtraTabPage page)
|
||||
{
|
||||
if (scope == null)
|
||||
return;
|
||||
|
||||
if (form != null)
|
||||
{
|
||||
scope.Register(form, () =>
|
||||
{
|
||||
if (!form.IsDisposed)
|
||||
form.Dispose();
|
||||
});
|
||||
}
|
||||
if (page != null)
|
||||
{
|
||||
scope.Register(page, () =>
|
||||
{
|
||||
if (!page.IsDisposed)
|
||||
page.Dispose();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the search control registered by the module during creation.
|
||||
/// The lookup is direct and does not inspect the WinForms control tree.
|
||||
/// </summary>
|
||||
public static MyControl FindModuleSearchControl(string moduleId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(moduleId))
|
||||
return null;
|
||||
|
||||
return StaticControl.FindModuleSearchControl(moduleId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 主程序
|
||||
/// </summary>
|
||||
@@ -91,6 +255,12 @@ namespace Lskj.Main.Model
|
||||
/// </summary>
|
||||
public static Dictionary<string, DllModule> ModuleForms = new Dictionary<string, DllModule>();
|
||||
/// <summary>
|
||||
/// 每个已打开模块的资源登记作用域。键与 ModuleForms 中的页签标识一致。
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, StaticControl.ModuleResourceScope> ModuleScopes =
|
||||
new Dictionary<string, StaticControl.ModuleResourceScope>(StringComparer.OrdinalIgnoreCase);
|
||||
private static readonly object ModuleScopesSync = new object();
|
||||
/// <summary>
|
||||
/// 记录F4配置快速菜单模块
|
||||
/// </summary>
|
||||
public static Form QuickMenuForm = null;
|
||||
@@ -513,7 +683,30 @@ namespace Lskj.Main.Model
|
||||
return false;
|
||||
|
||||
if (!form.Visible)
|
||||
form.Show();
|
||||
{
|
||||
StaticControl.ModuleResourceScope scope;
|
||||
lock (ModuleScopesSync)
|
||||
{
|
||||
ModuleScopes.TryGetValue(moduleId, out scope);
|
||||
}
|
||||
if (scope != null && scope.IsDisposed)
|
||||
return false;
|
||||
|
||||
// DeferredForWpf returns before Form.OnLoad. Re-establish the
|
||||
// module scope for activation so MyControl and its Timer are
|
||||
// registered even though the original open call has returned.
|
||||
if (scope == null)
|
||||
{
|
||||
form.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
using (StaticControl.PushModuleScope(scope))
|
||||
{
|
||||
form.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (form.IsDisposed)
|
||||
return false;
|
||||
@@ -601,6 +794,8 @@ namespace Lskj.Main.Model
|
||||
string guid = null;
|
||||
XtraTabPage tp = null;
|
||||
Form form = null;
|
||||
StaticControl.ModuleResourceScope moduleScope = null;
|
||||
IDisposable moduleScopeContext = null;
|
||||
bool trackDogVerifiedModule = false;
|
||||
try
|
||||
{
|
||||
@@ -701,10 +896,22 @@ namespace Lskj.Main.Model
|
||||
}
|
||||
|
||||
guid = Guid.NewGuid().ToString();
|
||||
moduleScope = new StaticControl.ModuleResourceScope(guid);
|
||||
lock (ModuleScopesSync)
|
||||
{
|
||||
ModuleScopes[guid] = moduleScope;
|
||||
}
|
||||
moduleScopeContext = StaticControl.PushModuleScope(moduleScope);
|
||||
tp = new XtraTabPage();
|
||||
StaticControl.AttachModuleScope(tp, moduleScope);
|
||||
moduleScope.Register(tp, () =>
|
||||
{
|
||||
if (!tp.IsDisposed)
|
||||
tp.Dispose();
|
||||
});
|
||||
if (trackDogVerifiedModule)
|
||||
{
|
||||
StaticControl.DogVerifyModuleForms.Add(tp);
|
||||
StaticControl.RegisterProtectedPage(tp);
|
||||
}
|
||||
|
||||
ERPInfo.Instance.selectFormModleId = module.Id;
|
||||
@@ -715,6 +922,12 @@ namespace Lskj.Main.Model
|
||||
//iform.SubForm.WindowState = _frmMain.WindowState;
|
||||
iform.SubForm.WindowState = FormWindowState.Normal; ;
|
||||
form = iform.SubForm; // 表示组成应用程序的用户界面的窗口或对话框。
|
||||
StaticControl.AttachModuleScope(form, moduleScope);
|
||||
moduleScope.Register(form, () =>
|
||||
{
|
||||
if (!form.IsDisposed)
|
||||
form.Dispose();
|
||||
});
|
||||
form.Tag = guid;
|
||||
// 这个必须有不然会提示:"不能向tabControl中添加顶级控件"
|
||||
form.TopLevel = isQuickLoad;
|
||||
@@ -792,12 +1005,22 @@ namespace Lskj.Main.Model
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (moduleScopeContext != null)
|
||||
{
|
||||
moduleScopeContext.Dispose();
|
||||
moduleScopeContext = null;
|
||||
}
|
||||
CleanupFailedModuleOpen(guid, tp, form);
|
||||
string Message = ErrorMessage.PromptErrorMessage(ex);
|
||||
MessageUtil.Show(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
|
||||
LogHelper.Instance.WriteError(ex);
|
||||
//LogUtil.WriteError("打开" + module.Name + "模块失败!", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (moduleScopeContext != null)
|
||||
moduleScopeContext.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static void CleanupFailedModuleOpen(
|
||||
@@ -812,19 +1035,31 @@ namespace Lskj.Main.Model
|
||||
ModuleForms.Remove(guid);
|
||||
}
|
||||
|
||||
StaticControl.ModuleResourceScope moduleScope = null;
|
||||
if (!string.IsNullOrWhiteSpace(guid))
|
||||
{
|
||||
lock (ModuleScopesSync)
|
||||
{
|
||||
if (ModuleScopes.TryGetValue(guid, out moduleScope))
|
||||
ModuleScopes.Remove(guid);
|
||||
}
|
||||
if (moduleScope != null)
|
||||
moduleScope.Dispose();
|
||||
}
|
||||
|
||||
if (tabPage != null)
|
||||
{
|
||||
StaticControl.DogVerifyModuleForms.Remove(tabPage);
|
||||
ERPInfo.Instance.RemoveModulePage(tabPage);
|
||||
}
|
||||
|
||||
if (form != null && !form.IsDisposed)
|
||||
if (moduleScope == null && form != null && !form.IsDisposed)
|
||||
{
|
||||
form.Close();
|
||||
form.Dispose();
|
||||
}
|
||||
|
||||
if (tabPage != null && !tabPage.IsDisposed)
|
||||
if (moduleScope == null && tabPage != null && !tabPage.IsDisposed)
|
||||
{
|
||||
tabPage.Dispose();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user