Delphi中如何实现外部进程向调用者应用的DC绘图?
跨进程DC绘图问题的解决办法
核心问题:跨进程GDI资源的隔离限制
Windows系统默认将每个进程的GDI资源(包括DC、画笔、画刷等)做了隔离处理,直接传递调用者进程的DC句柄给子进程,子进程无法正常访问该资源。同时Delphi的TControlCanvas依赖原控件的消息循环与内部状态,跨进程使用时无法关联到调用者面板的绘图上下文,自然不会触发绘制动作。正确实现思路:委托调用者进程完成绘图
不要直接传递DC句柄,而是让子进程将绘图指令传递给调用者,由调用者在自己的进程环境中完成面板绘制。具体步骤:
- 调用者注册自定义消息(如
WM_MY_CUSTOM_DRAW),在面板的消息处理函数中响应该消息,执行实际绘图逻辑。 - 启动子进程时,传递调用者面板的窗口句柄(而非DC句柄)作为命令行参数。
- 子进程需要绘图时,将绘图参数(如图形类型、坐标、颜色等)通过消息的
wParam/lParam传递,或用共享内存传递复杂数据,再调用SendMessage/PostMessage通知调用者执行绘图。
示例代码片段
- 调用者进程(主程序):
const WM_MY_CUSTOM_DRAW = WM_USER + 101; type TMainForm = class(TForm) Panel1: TPanel; procedure FormCreate(Sender: TObject); private procedure WMCustomDraw(var Msg: TMessage); message WM_MY_CUSTOM_DRAW; end; procedure TMainForm.FormCreate(Sender: TObject); begin // 启动子进程,传递Panel的窗口句柄 ShellExecute(0, 'open', 'ChildProcess.exe', PChar(IntToStr(Panel1.Handle)), nil, SW_SHOWNORMAL); end; procedure TMainForm.WMCustomDraw(var Msg: TMessage); var Canvas: TCanvas; begin Canvas := Panel1.Canvas; // 示例:绘制红色矩形 Canvas.Brush.Color := clRed; Canvas.FillRect(Rect(20, 20, 150, 150)); // 若需传递复杂参数,可从Msg.wParam/lParam解析,或读取共享内存 end; - 子进程(被调用程序):
var TargetHWND: HWND; begin if ParamCount < 1 then Exit; // 获取调用者传递的面板句柄 TargetHWND := StrToInt(ParamStr(1)); if IsWindow(TargetHWND) then begin // 发送绘图通知消息 SendMessage(TargetHWND, WM_MY_CUSTOM_DRAW, 0, 0); end; end;
- 调用者进程(主程序):
复杂绘图场景的扩展方案
如果需要传递图片、复杂路径等数据,可使用命名共享内存:
- 调用者创建命名共享内存区域,将内存句柄传递给子进程。
- 子进程打开该共享内存,写入序列化后的绘图数据(如记录类型、JSON字符串等)。
- 子进程发送消息通知调用者读取共享内存中的数据,解析后执行绘图。
内容的提问来源于stack exchange,提问作者SOLID Developper
相关产品推荐
相关产品推荐

