显示Microsoft Access“OLE对象”在我的申请 [英] Display Microsoft Access "OLE Object" in my application
问题描述
主要目标是将OLE对象字段移动到存储在blob字段。
我已经找到一些解析blob的示例代码,并尝试提取底层文件。我真的在寻找使用OLE对象的东西,而不是试图解决它们。
stackoverflow有两个类似的问题:
转换OLE图像对象从MS Access在.NET中使用
从Access DB提取OLE对象(pdf)
我打开这个问题主要是发布我当前的Delphi代码,看看是否有比我当前的代码更好的方法,如果没有,帮助别人。
我使用的是Delphi 2007,但是使用任何语言的代码都是有帮助的。
以下是我正在使用的解决方案。其起点来自 Ms Ole Fields on Ms about.com网站。
几个注释:
- 访问似乎使用OLE1存储对象标准不是OLE2。除了上述的帖子以外,我找不到任何确认。
- 访问在OLE1字段前添加自己的标题。再次,我没有发现除帖子之外的任何文档。
- 我找不到直接使用OLE1对象的方法,我需要将其转换为OLE2对象。为了做到这一点,我需要和OLE1流似乎没有在VCL或Win API中实现任何地方。实现是直接从帖子中获取的,我不能说我理解它正在做的一切。
一旦你有一个IOLEObject如果您可以使用OleDraw函数来绘制自己的画布。在这一点上,你有一个图像,并且关闭并运行。如果您只想显示对象,您可以使用TOLEContainer组件,并处理图形。
这是使用该单元的代码。我有一个ADO表,其类型为TBlobField的字段对象名为AdoTable1Photo。
procedure TForm2.ADOTable1AfterScroll(DataSet:TDataSet);
var
Bmp:TBitmap;
Jpg:TJpegImage;
OleObject:IOleObject;
DataObject:IDataObject;
CreateInfo:TCreateInfo;
begin
如果AdoTable1Photo.BlobSize = 0然后
exit;
OleObject:= OleFieldToObject(AdoTable1Photo);
//如果要保存图像文件绘制,则将ole对象绘制到
//一个位图画布,然后保存结果。可用于将
//一个字段转换为真实图像Blob而不是OLE对象类型。
Bmp:= TBitmap.Create();
Jpg:= TJpegImage.Create();
try
DrawOleOnBmp(OleObject,Bmp);
Jpg.Assign(Bmp);
Jpg.SaveToFile('C:\temp\test.jpg');
finally
Bmp.Free;
Jpg.Free;
结束
//如果只是尝试显示没有转换的结果,可以将
// OleObject附加到将处理图形的OleContainer组件。
//我找不到一个简单的方法来直接做到这一点。我需要使用
// IDataObject interace与CreateInfo记录。
如果成功(OleObject.QueryInterface(IDataObject,DataObject))然后
begin
//使用IDataObject
加载OLE容器控件CreateInfo.CreateType:= ctFromData;
CreateInfo.ShowAsIcon:= false;
CreateInfo.DataObject:= DataObject;
OleContainer.CreateObjectFromInfo(CreateInfo);
结束
OleObject:= nil;
结束
这里是将字段转换为IOleObject的单位。很难的部分是提取分流头并将其转换为OLE2对象的流。一旦完成,我真的只使用两个函数:OLELoad和OLEDraw。
单元MSAccessOleObject;
接口
使用ActiveX,Windows,Classes,ComObj,DB,Graphics;
//这个文件是这里发布的源代码的修改版本:
// http://forums.about.com/ab-delphi/messages?lgnF=y&msg = 1865.1
// -------------------------------------- ---------------------------------------
//从Ole.h转换
//
//从OleConvertOLESTREAMToIStorage OLE中使用OLE函数
//到目前为止,我知道Access转换OLE1对象中的OLE2对象
//
// So为了阅读这种类型的字段,我们必须将OLE1格式
//转换为OLE2格式,因此我们需要OleConvertOLESTREAMToIStorage
//并将OLE对象写入Field,我们需要
// OleConvertIStorageToOLESTREAM OLE功能。
//这里的代码仅用于阅读Ole Object字段,但它也适用于
//来编写它们。
//
// OLE.h定义一个使用vtable和回调函数的OLE流。 I
//在VCL中找不到实现OLE v1 Stream的类。
type
POleStreamVtbl = ^ TOleStreamVtbl;
TOleStreamVtbl = record
获取:指针;
放:指针;
结束
POle1Stream = ^ TOle1Stream;
TOle1Stream = record
pvt:POleStreamVtbl;
lpData:指针; //链接到.MDB文件中的数据
dwSize:Integer; // OLE流长度(相对于位置)
end;
POleStream = ^ TPOleStream;
TPOleStream = record
lpstbl:POleStreamVtbl;
结束
// ---------------------------------------- -------------------------------------
// Microsoft Access Field Header
//
// Access将头信息添加到实际的OLE流之前。
//我们需要读取它来获取大小,以便找到
//实际OLE流的开始。
type
TKind = record
case
0的整数:(oot:DWord); // OLE对象类型代码(OT_LINK,OT_EMBEDDED,OT_STATIC)
1:(lobjTyp:LongInt); //在我们的例子中:OT_EMBEDDED
end;
PAccessOleObjectHeader = ^ TAccessOleObjectHeader;
TAccessOleObjectHeader = record
typ:WORD; //类型签名(0x1C15)
cbHdr:WORD; // sizeof(struct OLEOBJECTHEADER)+ cchName + cchClass
lobjType:TKind; // OLE对象类型代码(OT_STATIC,OT_LINKED,OT_EMBEDDED)
cchName:WORD; //对象名称中的字符数计数(CchSz(szName)+ 1))
cchClass:WORD; //类名中的字符数(CchSz(szClss)+ 1))
ibName:WORD; //结构中的对象名称的偏移量(sizeof(OLEOBJECTHEADER)
ibClass:WORD; //结构中类名的偏移量(ibName + cchName)
ptSize:TSmallPoint; // Object的原始大小(MM_HIMETRIC )
end;
函数CreateOle1Stream(pStm:IStream; dwSize:Integer):POle1Stream;
procedure DeleteOle1Stream(var Ole1Stream:POle1Stream);
// OLE1 Stream的回调函数
函数Get(OleStream:POLESTREAM; Pb:Pointer; cb:Integer):Integer; stdcall;
函数Put(OleStream:POLESTREAM; const Pb:Pointer ; cb:Integer):Integer; stdcall;
函数OleFieldToObject(AdoField:TBlobField):IOleObject;
程序DrawOleOnBmp(Ole:IOleObject; Bmp:TBitmap);
实现
使用Sysutils;
{
CreateOle1Stream
------------------ -------------------------------------------------- -------
}
函数CreateOle1Stream(pStm:IStream; dwSize:Integer):POLE 1流
var
cb:Int64;
begin
结果:= new(POle1Stream);
Result.pvt:= new(POleStreamVtbl);
Result.pvt.Get:= @Get;
Result.pvt.Put:= @Put;
Result.dwSize:= dwSize;
Result.lpData:=指针(pStm);
//寻求流的开始
IStream(Result.lpData).Seek(0,STREAM_SEEK_SET,cb);
结束
{
DeleteOle1Stream
---------------------------- -----------------------------------------------
}
procedure DeleteOle1Stream(var Ole1Stream:POle1Stream); // Dispose then OLE1 Stream
begin
if Ole1Stream = Nil then
exit;
Dispose(Ole1Stream ^ .pvt);
Dispose(Ole1Stream);
结束
{
放
-------------------------------- -------------------------------------------
回调函数Ole1Stream
}
函数Put(OleStream:POLESTREAM; const Pb:Pointer; cb:Integer):Integer;标准
Var
pStream:POle1Stream;
ulBytesWritten:longInt;
begin
pStream:= POle1Stream(OleStream);
if(pStream = Nil)或(pStream ^ .lpData = Nil)或(pb = Nil)然后
begin
结果:= 0;
退出;
结束
ulBytesWritten:= 0;
如果IStream(pStream ^ .lpData).Write(Pb,cb,@ ulBytesWritten)< S_OK然后
begin
结果:= 0;
退出;
结束
pStream ^ .dwSize:= pStream ^ .dwSize + ulBytesWritten;
结果:= cb;
结束
{
获取
-------------------------------- -------------------------------------------
回调函数Ole1Stream
}
function Get(OleStream:POLESTREAM; Pb:Pointer; cb:Integer):Integer;标准
Var
pStream:POle1Stream;
ulBytesRead:LongInt;
begin
pStream:= POle1Stream(OleStream);
if(pStream = Nil)或(pStream ^ .lpData = Nil)或(pStream ^ .dwSize< cb)
然后
begin
结果:= 0;
退出;
结束
ulBytesRead:= 0;
如果IStream(pStream ^ .lpData).Read(pb,cb,@ulBytesRead)<> S_OK然后
begin
结果:= 0;
退出;
结束
pStream ^ .dwSize:= pStream ^ .dwSize-ulBytesRead;
结果:= cb;
end;
{
OleFieldToObject
-------------------------------- -------------------------------------------
通过ADO字段的OLE对象类型,并获取IOleObject
接口。然后,您可以将该对象附加到OleContanier,
让它绘制自己或使用DrawOleOnBmp函数来获取您自己的位图
。
}
函数OleFieldToObject(AdoField:TBlobField):IOleObject;
var
AccessHeader:TAccessOleObjectHeader;
FullAdoStream:TMemoryStream;
ObjectStream:TMemoryStream;
StreamAdapter:TStreamAdapter;
StreamInterface:IStream;
Ole1Stream:POle1Stream;
OleBytes:ILockBytes;
OleStorage:IStorage;
OleObject:IOleObject;
begin
FullAdoStream:= nil;
ObjectStream:= nil;
StreamAdapter:= nil;
StreamInterface:= nil;
Ole1Stream:= nil;
try
//我们需要一个IStorage接口,用于加载OLEObject
OleCheck(CreateILockBytesOnHGlobal(0,true,OleBytes));
OleCheck(StgCreateDocfileOnILockBytes(OleBytes,
STGM_Create或STGM_READWRITE或STGM_SHARE_EXCLUSIVE或STGM_DIRECT,0,OleStorage));
//我们需要从字段中获取数据。 Microsoft Access存储一个OLE1字段
//而不是当前的OLE2格式。它还将它自己的头添加到对象上。
//我们需要 - 提取流,删除头,将流包装在
// IStreamInterface中,将其包含在内部和OLE1Stream对象中。
FullAdoStream:= TMemoryStream.Create();
AdoField.SaveToStream(FullAdoStream);
FullAdoStream.Seek(0,soBeginning);
FullAdoStream.ReadBuffer(AccessHeader,sizeof(TAccessOleObjectHeader));
//我们可以检查AccessHeader.typ = $ 1C15,但如果格式不是
//对,以后会出错。
//寻找标题,然后将流的其余部分复制到新的流。
FullAdoStream.Seek(AccessHeader.cbHdr,soBeginning);
ObjectStream:= TMemoryStream.Create();
ObjectStream.CopyFrom(FullAdoStream,FullAdoStream.Size - FullAdoStream.Position);
StreamAdapter:= TStreamAdapter.Create(ObjectStream,soReference);
StreamInterface:= StreamAdapter as IStream;
Ole1Stream:= CreateOle1Stream(StreamInterface,ObjectStream.Size);
//现在将OLE1流转换为OLE2(IStorage)并加载IOleObject
//这个函数似乎很慢,但是我找不到任何东西要更改
//或任何其他使用的功能。
OleCheck(OleConvertOLESTREAMToIStorage(Ole1Stream,OleStorage,Nil));
OleCheck(OleLoad(OleStorage,IOleObject,nil,OleObject));
finally
DeleteOle1Stream(Ole1Stream);
StreamInterface:= nil;
StreamAdapter:= nil;
ObjectStream.Free;
FullAdoStream.Free;
结束
result:= OleObject;
end;
{
DrawOleOnBmp
---------------------------- -----------------------------------------------
取OleObject并将其绘制到位图画布上。位图将被调整为
以匹配OLE对象的正常大小。
}
程序DrawOleOnBmp(Ole:IOleObject; Bmp:TBitmap);
var
ViewObject2:IViewObject2;
ViewSize:TPoint;
AdjustedSize:TPoint;
DC:HDC;
R:TRect;
begin
如果成功(Ole.QueryInterface(IViewObject2,ViewObject2))然后
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;
SetRect(R,0,0,Bmp.Width,Bmp.Height);
OleDraw(Ole,DVASPECT_CONTENT,Bmp.Canvas.Handle,R);
end
else
begin
raise Exception.Create('无法获取OleObject上的IViewObject2 interfact);
结束
end;
结束。
I have an an Access database that contains an OLE Object field. I need to extract the contents of this field as an image. It does not matter what type was originally placed in the OLE Field. I just need an image that represents what that object looks like.
The main goal of this is to move away from the OLE Object field to a more standard image stored in a blob field.
I have found some example code that parses the blob and tries to extract the underlying file. I am really looking for something that uses the OLE objects as they were intended instead of trying to work around them.
There are two similar questions on stackoverflow:
Converting an OLE Image Object from MS Access for use in .NET
Extract OLE Object (pdf) from Access DB
I'm opening this question mainly as a place to post my current Delphi code to see if there is a better way than my current code and to help others if not.
I am using Delphi 2007 but code in any language would be helpful.
Below is the solution that I am using. The starting point for this came from the Ms Access Ole Fields on the about.com site.
A few notes:
- Access seems to store the object using the OLE1 standard not the OLE2. I could not find any confirmation of this anywhere other than the post mentioned above.
- Access appends its own header in front of the OLE1 field. Again I did not find any documentation other than the post.
- I could not find a way to use the OLE1 object directly, I needed to convert it to an OLE2 object. In order to do that I needed and OLE1 stream which did not seem to be implemented in the VCL or Win API anywhere. The implementation is taken directly from the post, I can't say I understand everything it is doing.
Once you have a IOLEObject in had you can use the OleDraw function to draw to your own canvas. At that point you have an image and are off and running. If you just want to display the object you can use the TOLEContainer component and let it deal with the drawing.
Here is the code that uses the unit. I have an ADO table with a field object of type TBlobField named AdoTable1Photo.
procedure TForm2.ADOTable1AfterScroll(DataSet: TDataSet);
var
Bmp: TBitmap;
Jpg: TJpegImage;
OleObject: IOleObject;
DataObject: IDataObject;
CreateInfo: TCreateInfo;
begin
if AdoTable1Photo.BlobSize = 0 then
exit;
OleObject := OleFieldToObject(AdoTable1Photo);
// If you want to save out an image file draw let the ole object draw to
// a bitmap canvas and then save the results. Could be used for converting
// a field to a true image blob instead of a OLE Object type.
Bmp := TBitmap.Create();
Jpg := TJpegImage.Create();
try
DrawOleOnBmp(OleObject, Bmp);
Jpg.Assign(Bmp);
Jpg.SaveToFile('C:\temp\test.jpg');
finally
Bmp.Free;
Jpg.Free;
end;
// If just trying to display the results without converting you can attach
// the OleObject to a OleContainer component that will handle the drawing.
// I could not find an easy way to do this directly. I needed to use the
// IDataObject interace with the CreateInfo record.
if Succeeded(OleObject.QueryInterface(IDataObject, DataObject)) then
begin
// Load the OLE Container control by using the IDataObject
CreateInfo.CreateType := ctFromData;
CreateInfo.ShowAsIcon := false;
CreateInfo.DataObject := DataObject;
OleContainer.CreateObjectFromInfo(CreateInfo);
end;
OleObject := nil;
end;
And here is the unit that is converting the field to the IOleObject. The hard part is extracting the stream splitting out the header and converting it to an OLE2 object. Once that is done there are really only two functions I am using: OLELoad and OLEDraw.
unit MSAccessOleObject;
interface
uses ActiveX, Windows, Classes, ComObj, DB, Graphics;
// This file is a modified version of the source code posted here:
// http://forums.about.com/ab-delphi/messages?lgnF=y&msg=1865.1
//-----------------------------------------------------------------------------
// Converted from Ole.h
//
// Used inside from OleConvertOLESTREAMToIStorage OLE Function
// As far I know the Access Converts the OLE2 objects in OLE1 Objects
//
// So for read this Kind of field we must covert the OLE1 format
// to OLE2 format so we need the OleConvertOLESTREAMToIStorage
// and to write an OLE object to Field we need the
// OleConvertIStorageToOLESTREAM OLE function.
// The code here is only for reading "Ole Object" fields, but it coudld adapted
// to write them as well.
//
// OLE.h define a OLE stream that uses a vtable and callback functions. I
// could not find a class in the VCL that implemented the OLE v1 Stream.
type
POleStreamVtbl = ^TOleStreamVtbl;
TOleStreamVtbl = record
Get: Pointer;
Put: Pointer;
end;
POle1Stream = ^TOle1Stream;
TOle1Stream = record
pvt: POleStreamVtbl;
lpData: Pointer; // Link to Data in .MDB file
dwSize: Integer; // OLE Stream length (relative to position)
end;
POleStream = ^TPOleStream;
TPOleStream = record
lpstbl: POleStreamVtbl;
end;
//-----------------------------------------------------------------------------
// Microsoft Access Field Header
//
// Access adds header information in front of the actual OLE stream.
// We need to read it to get the size in order to find the start of
// the actual OLE stream.
type
TKind=record
case Integer of
0: (oot: DWord); // OLE Object type code (OT_LINK, OT_EMBEDDED, OT_STATIC)
1: (lobjTyp: LongInt); // in our case: OT_EMBEDDED
end;
PAccessOleObjectHeader=^TAccessOleObjectHeader;
TAccessOleObjectHeader = record
typ: WORD; // Type signature (0x1C15)
cbHdr: WORD; // sizeof(struct OLEOBJECTHEADER) + cchName +cchClass
lobjType: TKind; // OLE Object Type Code (OT_STATIC, OT_LINKED,OT_EMBEDDED)
cchName: WORD; // Count of characters in object Name (CchSz(szName) + 1))
cchClass: WORD; // Count of characters in class Name (CchSz(szClss) + 1))
ibName: WORD; // Offset of object name in structure (sizeof(OLEOBJECTHEADER)
ibClass: WORD; // Offset of class name in structure (ibName +cchName)
ptSize: TSmallPoint; // Original size of Object (MM_HIMETRIC)
end;
function CreateOle1Stream(pStm: IStream; dwSize: Integer): POle1Stream;
procedure DeleteOle1Stream(var Ole1Stream: POle1Stream);
// Callback Functions for OLE1 Stream
function Get(OleStream: POLESTREAM; Pb:Pointer; cb:Integer): Integer; stdcall;
function Put(OleStream: POLESTREAM; const Pb:Pointer; cb:Integer): Integer; stdcall;
function OleFieldToObject(AdoField: TBlobField): IOleObject;
procedure DrawOleOnBmp(Ole: IOleObject; Bmp: TBitmap);
implementation
uses Sysutils;
{
CreateOle1Stream
---------------------------------------------------------------------------
}
function CreateOle1Stream(pStm:IStream; dwSize:Integer): POLE1Stream;
var
cb: Int64;
begin
Result := new(POle1Stream);
Result.pvt := new(POleStreamVtbl);
Result.pvt.Get := @Get;
Result.pvt.Put := @Put;
Result.dwSize := dwSize;
Result.lpData := Pointer(pStm);
// Seek to the start of the stream
IStream(Result.lpData).Seek(0,STREAM_SEEK_SET,cb);
end;
{
DeleteOle1Stream
---------------------------------------------------------------------------
}
procedure DeleteOle1Stream(var Ole1Stream: POle1Stream); // Dispose then OLE1 Stream
begin
if Ole1Stream = Nil then
exit;
Dispose(Ole1Stream^.pvt);
Dispose(Ole1Stream);
end;
{
Put
---------------------------------------------------------------------------
Callback function for Ole1Stream
}
function Put(OleStream: POLESTREAM; const Pb:Pointer; cb:Integer): Integer; stdcall;
Var
pStream: POle1Stream;
ulBytesWritten: longInt;
begin
pStream:=POle1Stream(OleStream);
if (pStream = Nil) or (pStream^.lpData=Nil) or (pb=Nil) then
begin
Result:=0;
exit;
end;
ulBytesWritten:=0;
if IStream(pStream^.lpData).Write(Pb,cb,@ulBytesWritten) <> S_OK then
begin
Result:=0;
exit;
end;
pStream^.dwSize:=pStream^.dwSize+ulBytesWritten;
Result:=cb;
end;
{
Get
---------------------------------------------------------------------------
Callback function for Ole1Stream
}
function Get(OleStream: POLESTREAM; Pb: Pointer; cb: Integer): Integer; stdcall;
Var
pStream: POle1Stream;
ulBytesRead: LongInt;
begin
pStream := POle1Stream(OleStream);
if (pStream=Nil) or (pStream^.lpData=Nil) or (pStream^.dwSize < cb)
then
begin
Result := 0;
exit;
end;
ulBytesRead := 0;
if IStream(pStream^.lpData).Read(pb, cb, @ulBytesRead) <> S_OK then
begin
Result := 0;
exit;
end;
pStream^.dwSize := pStream^.dwSize-ulBytesRead;
Result := cb;
end;
{
OleFieldToObject
---------------------------------------------------------------------------
Pass in the ADO field of the "OLE Object" type and get the IOleObject
interface back. You can then attached that object to a OleContanier and
let it draw itself or use the DrawOleOnBmp function to get your own bitmap
that can be used by itself.
}
function OleFieldToObject(AdoField: TBlobField): IOleObject;
var
AccessHeader: TAccessOleObjectHeader;
FullAdoStream: TMemoryStream;
ObjectStream: TMemoryStream;
StreamAdapter: TStreamAdapter;
StreamInterface: IStream;
Ole1Stream: POle1Stream;
OleBytes: ILockBytes;
OleStorage: IStorage;
OleObject: IOleObject;
begin
FullAdoStream := nil;
ObjectStream := nil;
StreamAdapter := nil;
StreamInterface := nil;
Ole1Stream := nil;
try
// We need a IStorage Interface that will be used to load the OLEObject
OleCheck( CreateILockBytesOnHGlobal(0, true, OleBytes) );
OleCheck( StgCreateDocfileOnILockBytes(OleBytes,
STGM_Create or STGM_READWRITE or STGM_SHARE_EXCLUSIVE or STGM_DIRECT, 0, OleStorage) );
// We need to get the data out of the field. Microsoft Access stores a OLE1 field
// and not the current OLE2 format. It also adds it's own header on the object.
// We need to - extract the stream, remove the header, wrap the stream in an
// IStreamInterface, wrap that inside and OLE1Stream object.
FullAdoStream := TMemoryStream.Create();
AdoField.SaveToStream(FullAdoStream);
FullAdoStream.Seek(0, soBeginning);
FullAdoStream.ReadBuffer(AccessHeader, sizeof(TAccessOleObjectHeader));
// We could check if AccessHeader.typ = $1C15 but if the format is not
// right something will go wrong later.
// Seek past the header and then copy the rest of the stream to a new stream.
FullAdoStream.Seek(AccessHeader.cbHdr, soBeginning);
ObjectStream := TMemoryStream.Create();
ObjectStream.CopyFrom(FullAdoStream, FullAdoStream.Size - FullAdoStream.Position);
StreamAdapter := TStreamAdapter.Create(ObjectStream, soReference);
StreamInterface := StreamAdapter as IStream;
Ole1Stream := CreateOle1Stream(StreamInterface, ObjectStream.Size);
// Now convert the OLE1 stream to OLE2 (IStorage) and load the IOleObject
// This function seems to be slow, but I can't find anything to change
// or any other function to use.
OleCheck( OleConvertOLESTREAMToIStorage(Ole1Stream, OleStorage, Nil) );
OleCheck( OleLoad(OleStorage, IOleObject, nil, OleObject) );
finally
DeleteOle1Stream(Ole1Stream);
StreamInterface := nil;
StreamAdapter := nil;
ObjectStream.Free;
FullAdoStream.Free;
end;
result := OleObject;
end;
{
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: TBitmap);
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;
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;
end.
这篇关于显示Microsoft Access“OLE对象”在我的申请的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!