CEF4Delphi中TChromium.OnDownloadUpdated事件自定义窗体DFM流异常问题
CEF4Delphi自定义下载对话框DFM加载异常问题
我在基于CEF4Delphi的项目中,实现了下载完成后弹出自定义打开/保存对话框的功能,但偶尔会出现DFM流加载失败的情况:窗体创建后、OnShow事件触发前,按钮、标签等控件的Width、Left属性全部变为0,导致窗体显示异常。
事件代码实现
procedure TMainForm.ChromiumDownloadUpdated(Sender: TObject; const browser: ICefBrowser; const downloadItem: ICefDownloadItem; const callback: ICefDownloadItemCallback); var FileIcon: TIcon; Descripcion: string; originalFilename: string; openSaveForm: TOpenSaveForm; begin else if downloadItem.IsComplete then begin FileIcon := TIcon.Create; openSaveForm := TOpenSaveForm.Create(Self); try try openSaveForm.LabelFilename.Caption := OpenSaveFormCaptionMsg(originalFilename, downloadItem.ReceivedBytes); GetFileIconAndDescription(downloadItem.GetFullPath, FileIcon, Descripcion); openSaveForm.RadioButtonOpen.Caption := openSaveForm.RadioButtonOpen.Caption + ' ' + Descripcion; openSaveForm.FileIcon.Picture.Icon := FileIcon; if openSaveForm.ShowModal = mrOk then ChooseShowSaveDialog(downloadItem.GetFullPath, openSaveForm.RadioButtonSave.Checked); except on E: Exception do ShowMessage('Excepcion opensaveform: ' + E.Message); end; finally openSaveForm.Free; FileIcon.Free; end; end; end;
显示效果对比
正常显示状态:

异常显示状态(DFM加载失败):

问题根源
CEF4Delphi的ChromiumDownloadUpdated事件回调运行在非主线程(浏览器IO线程或渲染线程),而VCL控件的创建、属性修改必须在主线程中执行。直接在非主线程中创建窗体,会破坏DFM的加载流程,导致控件布局属性初始化失败。
解决方案:切换到主线程操作UI
将窗体创建和相关UI操作代码通过TThread.Synchronize或TThread.Queue切换到主线程执行,修改后的代码如下:
procedure TMainForm.ChromiumDownloadUpdated(Sender: TObject; const browser: ICefBrowser; const downloadItem: ICefDownloadItem; const callback: ICefDownloadItemCallback); var originalFilename: string; fullPath: string; receivedBytes: Int64; begin if downloadItem.IsComplete then begin // 先保存需要的参数,避免在同步闭包中直接引用downloadItem(可能存在生命周期问题) originalFilename := downloadItem.OriginalFileName; fullPath := downloadItem.GetFullPath; receivedBytes := downloadItem.ReceivedBytes; // 使用Synchronize切换到主线程执行UI操作 TThread.Synchronize(nil, procedure var FileIcon: TIcon; Descripcion: string; openSaveForm: TOpenSaveForm; begin FileIcon := TIcon.Create; openSaveForm := TOpenSaveForm.Create(Self); try try openSaveForm.LabelFilename.Caption := OpenSaveFormCaptionMsg(originalFilename, receivedBytes); GetFileIconAndDescription(fullPath, FileIcon, Descripcion); openSaveForm.RadioButtonOpen.Caption := openSaveForm.RadioButtonOpen.Caption + ' ' + Descripcion; openSaveForm.FileIcon.Picture.Icon := FileIcon; if openSaveForm.ShowModal = mrOk then ChooseShowSaveDialog(fullPath, openSaveForm.RadioButtonSave.Checked); except on E: Exception do ShowMessage('Excepcion opensaveform: ' + E.Message); end; finally openSaveForm.Free; FileIcon.Free; end; end); end; end;
补充说明
TThread.Synchronize会阻塞当前非主线程,直到主线程完成UI操作;如果不想阻塞线程,可以改用TThread.Queue,它会将任务放入主线程消息队列后立即返回。- 所有CEF事件回调中的UI操作都必须遵循主线程原则,否则可能引发各种UI异常或内存问题。
内容的提问来源于stack exchange,提问作者Héctor C.
相关产品推荐
相关产品推荐

