任务
我有数千个带有嵌入式 OLE 对象的 RTF 文档。需要提取 OLE 对象并以该TOleContainer.SaveToFile()
格式保存。
当前解决方案
将每个 RTF 文件加载到TJvRichEdit
控件中并循环通过其 OLE 对象。这些对象可以加载到一个TOleContainer
然后保存到磁盘。
问题
如果我的计算机上没有安装特定的 OLE 服务器,则代码将TOleContainer.CreateObjectFromInfo()
失败并显示错误“无效的 FORMATETC 结构”。
是否有另一种方法可以将 OLE 对象从TJvRichEdit
控件复制到TOleContainer
不需要安装 OLE 服务器的控件?
代码
uses ActiveX, JvRichEdit, RichEdit, ComObj;
----
{ used to iterate through OLE objects }
type
_ReObject = record
cbStruct: DWORD;
cp: ULONG;
clsid: TCLSID;
poleobj: IOleObject;
pstg: IStorage;
polesite: IOleClientSite;
sizel: TSize;
dvAspect: Longint;
dwFlags: DWORD;
dwUser: DWORD;
end;
TReObject = _ReObject;
IRichEditOle = interface(IUnknown)
['{00020d00-0000-0000-c000-000000000046}']
function GetClientSite(out clientSite: IOleClientSite): HResult; stdcall;
function GetObjectCount: HResult; stdcall;
function GetLinkCount: HResult; stdcall;
function GetObject(iob: Longint; out reobject: TReObject;
dwFlags: DWORD): HResult; stdcall;
function InsertObject(var reobject: TReObject): HResult; stdcall;
function ConvertObject(iob: Longint; rclsidNew: TIID;
lpstrUserTypeNew: LPCSTR): HResult; stdcall;
function ActivateAs(rclsid: TIID; rclsidAs: TIID): HResult; stdcall;
function SetHostNames(lpstrContainerApp: LPCSTR;
lpstrContainerObj: LPCSTR): HResult; stdcall;
function SetLinkAvailable(iob: Longint; fAvailable: BOOL): HResult; stdcall;
function SetDvaspect(iob: Longint; dvaspect: DWORD): HResult; stdcall;
function HandsOffStorage(iob: Longint): HResult; stdcall;
function SaveCompleted(iob: Longint; const stg: IStorage): HResult; stdcall;
function InPlaceDeactivate: HResult; stdcall;
function ContextSensitiveHelp(fEnterMode: BOOL): HResult; stdcall;
function GetClipboardData(var chrg: TCharRange; reco: DWORD;
out dataobj: IDataObject): HResult; stdcall;
function ImportDataObject(dataobj: IDataObject; cf: TClipFormat;
hMetaPict: HGLOBAL): HResult; stdcall;
end;
{ Note: 'ole' is a TOleContainer and 're' is a TJvRichEdit }
procedure TForm1.Button1Click(Sender: TObject);
var
reOle: IRichEditOle;
reObj: TReObject;
oData: IDataObject;
oInfo: TCreateInfo;
i, cnt: Integer;
begin
if dlgOpen.Execute then
begin
re.Clear;
re.Lines.LoadFromFile(dlgOpen.FileName);
if SendMessage(re.Handle, EM_GETOLEINTERFACE, 0, Longint(@reOle)) <> 0 then
try
if not Assigned(reOle) then
raise Exception.Create('Failed to retrieve IRichEditOle');
cnt := reOle.GetObjectCount;
// cycle through objects
for i := 0 to cnt - 1 do
begin
// initialize 'reObj' structure
FillChar(reObj, SizeOf(reObj), 0);
reObj.cbStruct := SizeOf(reObj);
// get OLE object
OleCheck(reOle.GetObject(i, reObj, 7));
try
// get the OLE object's data
reObj.poleobj.QueryInterface(IDataObject, oData);
if Assigned(oData) then
try
// needed for some OLE servers (like MSPaint)
OleRun(oData);
// initialize TCreateInfo object
oInfo.CreateType := ctFromData;
oInfo.ShowAsIcon := False;
oInfo.IconMetaPict := 0;
oInfo.DataObject := oData;
try
ole.DestroyObject;
ole.CreateObjectFromInfo(oInfo); // <- this is where it fails
ole.SaveToFile([a filename]);
finally
oInfo.DataObject := nil;
end;
finally
oData := nil;
end;
finally
reObj.poleobj := nil;
end;
end;
finally
reOle := nil;
end;
end;
end;