Delphi12+TMS WebCore登出清理内存重启报错解决方案咨询
背景
我用 Delphi 12 结合 TMS WebCore 开发了一款 Web 应用,在登出场景(用户主动操作或闲置超时触发)下,出于以下需求需要清理内存:
- 安全考量
- 规避未发现的内存泄漏问题
- 允许浏览器下载应用新版本(若存在)
异常现象
但重启应用时持续抛出以下异常:
Error: Duplicate component name: "divHeading"
at Object.Create$1 (https://example.com.au/ops-test/ops_1_0_2109.js:3634:23)
at Object.CreateFmt (https://example.com.au/ops-test/ops_1_0_2109.js:3641:12)
at c.$create (https://example.com.au/ops-test/ops_1_0_2109.js:366:19)
at Object.ValidateRename (https://example.com.au/ops-test/ops_1_0_2109.js:15533:188)
at Object.SetName (https://example.com.au/ops-test/ops_1_0_2109.js:15513:21)
at Object.SetName (https://example.com.au/ops-test/ops_1_0_2109.js:33680:38)
at Object.LoadDFMValues (https://example.com.au/ops-test/ops_1_0_2109.js:127837:25)
at Object.CreateNewForm (https://example.com.au/ops-test/ops_1_0_2109.js:43375:13)
at Object.DoFormLoad (https://example.com.au/ops-test/ops_1_0_2109.js:43010:12)
at XMLHttpRequest.cb (https://example.com.au/ops-test/ops_1_0_2109.js:282:28)
需求
我希望实现以下任一目标:
- 阻止该异常发生(最优方案)
- 静默捕获异常并尝试再次重启
已尝试操作
试过多种方案,但主窗体构造函数仍持续抛出异常,说明上一会话的内存未被完全清理。
登出代码
procedure Tfrm_Ops_Main.Log_Out (AAuto : Boolean = False); begin TConsole.Log (msg_Logging_Out); if AAuto then Stop_Warning_User_At_Tab_Close; Free_Child_Form; Close; { Allowing for Form to Close } // THtml.Replace_Page; THtml.Reload_After_Delay (400); end;
尝试过的页面重载方法
Replace_PageReload_PageReload_Page_After_Delay
THtml 类代码
class procedure THtml.Replace_Page (AUrl : string = ''); var lUrl : string; lTime : Int64; begin { Add time in ms if same URL } if AUrl = '' then begin LTime := Trunc ((Now - UnixEpoch) * ToMS); LUrl := window.location.href + '?t=' + IntToStr (LTime); end else LUrl := AUrl; document.location.replace (LUrl); end; class procedure THtml.Reload_Page; begin document.location.reload (True); end; class procedure THtml.Reload_After_Delay (ADelayMS : Integer); var LTimer : TWebTimer; begin LTimer := TWebTimer.Create(nil); LTimer.Interval := ADelayMS; LTimer.Enabled := True; LTimer.OnTimer := Do_Reload; end; class procedure THtml.Do_Reload (Sender : Tobject); begin TWebTimer (Sender).Free; THtml.Reload_Page; end;
解决方案
方案一:彻底清理组件引用(阻止异常最优解)
TMS WebCore 页面重载时,若之前的窗体/组件未完全销毁,会导致 DOM 元素或组件实例残留,触发重名异常。需在登出时确保所有组件引用被彻底释放:
- 完善窗体销毁逻辑
在主窗体的OnClose事件中手动释放所有子组件、全局变量并解绑事件绑定:
procedure Tfrm_Ops_Main.FormClose(Sender: TObject; var Action: TCloseAction); var I: Integer; begin // 释放所有子窗体 for I := Screen.FormCount - 1 downto 0 do begin if Screen.Forms[I] <> Self then Screen.Forms[I].Free; end; // 释放全局组件/变量(按需添加) if Assigned(GlobalCustomComponent) then GlobalCustomComponent.Free; // 解绑JS事件监听(按需添加) asm if window.appEventListeners { window.appEventListeners.forEach(item => { document.removeEventListener(item.event, item.handler); }); window.appEventListeners = null; } end; Action := caFree; end;
- 窗体销毁后立即重载
延迟重载可能留下组件销毁的不确定窗口,建议在OnDestroy事件中直接触发重载:
procedure Tfrm_Ops_Main.FormDestroy(Sender: TObject); begin THtml.Reload_Page; end;
同时修改 Log_Out 方法,移除延迟逻辑:
procedure Tfrm_Ops_Main.Log_Out (AAuto : Boolean = False); begin TConsole.Log (msg_Logging_Out); if AAuto then Stop_Warning_User_At_Tab_Close; Free_Child_Form; Close; end;
- 优化缓存刷新逻辑
使用Replace_Page时,用 JS 时间戳确保彻底绕过缓存:
class procedure THtml.Replace_Page (AUrl : string = ''); var lUrl : string; lTime : string; begin if AUrl = '' then begin asm lTime = Date.now().toString(); end; LUrl := window.location.origin + window.location.pathname + '?t=' + lTime; end else LUrl := AUrl; document.location.replace(LUrl); end;
方案二:捕获异常并重试
若无法彻底清理残留,可在应用初始化阶段捕获异常并触发重载:
- 初始化时捕获重名异常
修改主窗体OnCreate事件,添加异常捕获逻辑:
procedure Tfrm_Ops_Main.FormCreate(Sender: TObject); begin try // 原有初始化逻辑 InitAppComponents; except on E: Exception do begin if Pos('Duplicate component name', E.Message) > 0 then begin TConsole.Log('检测到组件重名,正在重载...'); THtml.Reload_Page; end else raise; end; end; end;
- 限制重试次数避免死循环
通过 URL 参数记录重试次数,超过阈值则提示用户:
class procedure THtml.Reload_Page; var retryCount: Integer; newUrl: string; begin retryCount := StrToIntDef(TWebHttpRequest.QueryFields.Values['retry'], 0); if retryCount >= 3 then begin TWebMessageBox.Show('应用加载失败,请手动刷新页面或联系管理员'); Exit; end; newUrl := window.location.origin + window.location.pathname + '?retry=' + IntToStr(retryCount + 1); document.location.replace(newUrl); end;
内容的提问来源于stack exchange,提问作者Rohit Gupta

