如何在Delphi Seattle中将内存中的PDF加载到TWebBrowser组件?
在TWebBrowser中直接显示内存中的PDF(无需保存到文件)
我之前也碰到过类似的问题,自定义协议处理PDF失效通常是因为响应头设置不全或者IE插件的兼容性问题。下面给你几个经过验证的可行方案:
方法1:利用IWebBrowser2直接加载IStream
这是兼容性最好的方式,核心是把内存中的PDF流包装成COM的IStream接口,再传给TWebBrowser的Navigate2方法:
uses ActiveX, SHDocVw, Classes; procedure LoadPDFFromStream(WebBrowser: TWebBrowser; PDFStream: TStream); var StreamAdapter: IStream; WebBrowserIntf: IWebBrowser2; EmptyVar: OleVariant; begin if not Assigned(PDFStream) or (PDFStream.Size = 0) then Exit; PDFStream.Position := 0; // 将Delphi流包装成COM标准IStream StreamAdapter := TStreamAdapter.Create(PDFStream, soReference); WebBrowserIntf := WebBrowser.DefaultInterface; // 先导航到空白页确保浏览器处于就绪状态 EmptyVar := EmptyParam; WebBrowserIntf.Navigate2('', @EmptyVar, @EmptyVar, @EmptyVar, @EmptyVar); while WebBrowserIntf.ReadyState <> READYSTATE_COMPLETE do Application.ProcessMessages; // 加载内存中的PDF流 WebBrowserIntf.Navigate2('', @EmptyVar, @EmptyVar, StreamAdapter, @EmptyVar); end;
注意事项:
- 确保系统安装了Adobe Reader或其他支持在IE中渲染PDF的插件;
- 如果是Delphi 10.4及以上版本,建议将TWebBrowser设置为使用Edge内核,兼容性和稳定性会大幅提升。
方法2:将PDF转为Base64嵌入HTML
这种方式无需依赖复杂的COM接口,直接通过HTML的embed标签加载Base64编码的PDF:
uses ActiveX, SHDocVw, Classes, MSHTML, System.NetEncoding; procedure LoadPDFAsBase64(WebBrowser: TWebBrowser; PDFStream: TStream); var Base64Content: string; HtmlDoc: IHTMLDocument2; HtmlContent: string; begin if not Assigned(PDFStream) or (PDFStream.Size = 0) then Exit; PDFStream.Position := 0; // 将PDF二进制流转为Base64字符串 Base64Content := TNetEncoding.Base64.EncodeBytesToString(TBytesStream(PDFStream).Bytes); // 先导航到空白页 WebBrowser.Navigate('about:blank'); while WebBrowser.ReadyState <> READYSTATE_COMPLETE do Application.ProcessMessages; // 获取文档对象并写入嵌入PDF的HTML HtmlDoc := WebBrowser.Document as IHTMLDocument2; HtmlContent := Format( '<html><body style="margin:0;"><embed type="application/pdf" src="data:application/pdf;base64,%s" width="100%%" height="100%%"></body></html>', [Base64Content] ); HtmlDoc.Write(HtmlContent); HtmlDoc.Close; end;
注意事项:
- 该方法对IE11及以上版本支持较好,旧版IE可能无法识别
data:URI方案; - 大体积PDF的Base64编码会额外占用约30%的内存,需注意内存开销。
方法3:修复自定义协议处理逻辑
如果你想继续使用自定义协议,需要确保协议处理程序正确返回响应头和PDF数据:
uses ActiveX, WinInet; function TCustomProtocolHandler.Start(const szUrl: PWideChar; IInternetSink: IInternetSink; dwFlags: DWORD; out dwResult: DWORD): HResult; var PDFStream: TMemoryStream; ResponseHeaders: string; BytesWritten: Cardinal; begin // 替换成你获取内存PDF流的逻辑 PDFStream := TMemoryStream.Create; try // 假设已将PDF数据加载到PDFStream中 PDFStream.Position := 0; // 必须设置完整的响应头:Content-Type和Content-Length缺一不可 ResponseHeaders := Format( 'Content-Type: application/pdf'#13#10 + 'Content-Length: %d'#13#10#13#10, [PDFStream.Size] ); // 发送响应头 IInternetSink.ReportData(BSCF_FIRSTDATANOTIFICATION or BSCF_LASTDATANOTIFICATION, Length(ResponseHeaders), 0); IInternetSink.Write(Pointer(ResponseHeaders), Length(ResponseHeaders), @BytesWritten); // 发送PDF二进制数据 IInternetSink.ReportData(BSCF_FIRSTDATANOTIFICATION or BSCF_LASTDATANOTIFICATION, PDFStream.Size, 0); IInternetSink.Write(PDFStream.Memory, PDFStream.Size, @BytesWritten); dwResult := 0; Result := S_OK; finally PDFStream.Free; end; end;
关键修复点:
- 必须返回
Content-Length头,否则IE可能无法正确解析完整的PDF数据; - 响应头和数据之间必须用一个空行(
#13#10#13#10)分隔; - 检查IE安全设置,确保自定义协议被允许加载ActiveX内容。
内容的提问来源于stack exchange,提问作者Konstantin Knyazev
相关产品推荐
相关产品推荐

