【问题标题】:Extract bitmap from TOleContainer?从 TOleContainer 中提取位图?
【发布时间】:2026-01-30 21:20:08
【问题描述】:

我正在尝试使用 IDataObject 的 GetData 方法从 TOleContainer 中提取位图。

 OleContainer1.CreateObject('Paint.Picture', false);
 OleContainer1.OleObjectInterface.QueryInterface(IDataObject, DataObject);

在 DataObject 上带有 DATADIR_GET 的 EnumFormatEtc 返回以下内容:

 cfFormat, ptd, dwAspect, lIndex, tymed

 CF_METAFILEPICT, nil, DVASPECT_CONTENT, -1, TYMED_MFPICT
 CF_DIB, nil, DVASPECT_CONTENT, -1, TYMED_HGLOBAL or TYMED_ISTREAM
 CF_BITMAP, nil, DVASPECT_CONTENT, -1, TYMED_HGLOBAL

但是当我这样做时:

FormatEtc.cfFormat := CF_BITMAP;
FormatEtc.ptd := nil;
FormatEtc.dwAspect := DVASPECT_CONTENT;
FormatEtc.lIndex := -1;
FormatEtc.tymed := TYMED_HGLOBAL;

OleCheck(DataObject.GetData(FormatEtc, StorageMedium));

我收到 无效的 FORMATETC 结构 错误。我做错了什么?

【问题讨论】:

标签: delphi bitmap containers extract ole


【解决方案1】:

我通过使用here 找到的代码来做与您尝试做的事情相同的事情。就我而言,我发现最好执行以下操作,在提供的链接中使用 DrawOleOnBmp()

oleMain.UpdateObject;
if oleMain.OleObjectInterface = nil then
  raise Exception.Create('OLE Container is empty.');
DrawOleOnBmp(oleMain.OleObjectInterface, imgMain.Bitmap);
imgMain.Bitmap.SaveToFile('Filename.bmp');

其中oleMainTOleContainerimgMainTImage32。两者都在表单上可见...

为方便起见,这里是链接中的方法,由@MarkElder 编写:

{
  DrawOleOnBmp
  ---------------------------------------------------------------------------
  Take a OleObject and draw it to a bitmap canvas.  The bitmap will be sized
  to match the normal size of the OLE Object.
}
procedure DrawOleOnBmp(Ole: IOleObject; Bmp: TBitmap32);
var
  ViewObject2: IViewObject2;
  ViewSize: TPoint;
  AdjustedSize: TPoint;
  DC: HDC;
  R: TRect;
begin
  if Succeeded(Ole.QueryInterface(IViewObject2, ViewObject2)) then
  begin
    ViewObject2.GetExtent(DVASPECT_CONTENT, -1, nil, ViewSize);

    DC := GetDC(0);
    AdjustedSize.X := MulDiv(ViewSize.X, GetDeviceCaps(DC, LOGPIXELSX), 2540);
    AdjustedSize.Y := MulDiv(ViewSize.Y, GetDeviceCaps(DC, LOGPIXELSY), 2540);
    ReleaseDC(0, DC);

    Bmp.Height := AdjustedSize.Y;
    Bmp.Width := AdjustedSize.X;

    Bmp.FillRect(0, 0, Bmp.Width, Bmp.Height, clWhite);

    SetRect(R, 0, 0, Bmp.Width, Bmp.Height);

    OleDraw(Ole, DVASPECT_CONTENT, Bmp.Canvas.Handle, R);
  end
  else
  begin
    raise Exception.Create('Could not get the IViewObject2 interfact on the OleObject');
  end;
end;

【讨论】:

  • 感谢您的意见。我想这会起作用,但一方面,您是否知道GetData 失败的原因是什么?似乎它与TYMED_HGLOBAL 有关,因为当我将CF_BITMAP 更改为CF_DIB 并将TYMED_HGLOBAL 更改为TYMED_ISTREAM 时,我就能够成功检索数据。另一方面,我也想写回数据,这只有IDataObject接口才有可能,不是吗?
  • TOleContainer 玩的不多,所以IDataObject 界面的使用我不是专家。但去年 7 月,我试图将 RTF 文档转换为另一种格式。其中许多包含 OLE 对象。当我的计算机上没有为其中一个 OLE 对象安装 OLE 服务器时,我只看到了“无效的 FORMATETC 结构”。如果您的错误是由同一问题引起的(缺少 OLE 服务器),那么您无法使用代码来消除错误。 OLE 服务器必须存在才能解析 OLE 数据并呈现它。