深入解析OLE工作原理与机制
对 OLE (Object Linking and Embedding) 进行详细深入的解析,包括 UML 建模、项目结构、实例实现。覆盖 OLE 的本质、原理、机制,并实现一个简单的 OLE 容器和服务器示例(或至少演示嵌入/链接、就地激活)。由于 OLE 复杂,可以简化但必须展现关键接口:IOleObject, IOleClientSite, IOleInPlaceFrame, IOleInPlaceObject, IStorage, IPersistStorage 等。
OLE (Object Linking and Embedding) 工作原理与机制:UML 建模与实现方案
1. 项目概述
OLE(Object Linking and Embedding)是基于 COM 的复合文档技术,允许在一个文档(容器)中嵌入或链接由另一个应用程序(服务器)创建的数据。经典例子:在 Word 文档中嵌入 Excel 表格,双击即可就地编辑。
核心概念:
- 嵌入(Embedding):将数据的完整副本保存到容器文档中,独立于源文件。
- 链接(Linking):容器文档只保存指向源文件的引用,数据动态更新。
- 就地激活(In-Place Activation):用户双击对象时,服务器程序界面无缝融入容器窗口,实现编辑。
关键接口:
- 容器:
IOleClientSite、IOleInPlaceSite、IOleInPlaceFrame、IAdviseSink - 服务器:
IOleObject、IOleInPlaceObject、IOleInPlaceActiveObject、IPersistStorage、IDataObject
本项目实现一个简单的 OLE 容器(类似画板,可插入 OLE 对象)和一个简单的 OLE 服务器(提供圆形图形对象),演示嵌入、链接、就地激活。
2. 项目文件结构
ole_demo/
├── README.md
├── docs/
│ └── uml.md # UML 图
├── idl/
│ └── OleShapes.idl # 定义图形对象接口
├── container/ # OLE 容器应用程序
│ ├── ContainerWindow.h
│ ├── ContainerWindow.cpp # 主窗口,管理嵌入对象
│ ├── OleClientSite.h
│ ├── OleClientSite.cpp # IOleClientSite 实现
│ ├── OleInPlaceSite.h
│ ├── OleInPlaceSite.cpp # IOleInPlaceSite
│ ├── main.cpp
│ └── Makefile
├── server/ # OLE 服务器(图形对象)
│ ├── ShapeObject.h
│ ├── ShapeObject.cpp # 实现 IOleObject, IPersistStorage, IDataObject
│ ├── ShapeInPlaceObj.h
│ ├── ShapeInPlaceObj.cpp # 就地激活窗口
│ ├── ShapeFactory.h
│ ├── ShapeFactory.cpp
│ ├── dllmain.cpp
│ ├── server.def
│ └── Makefile
└── tools/
└── register.bat
3. UML 建模(Mermaid)
3.1 类图 – OLE 容器与服务器的接口关系
3.2 序列图 – 嵌入对象并就地激活
3.3 活动图 – 容器文档的保存(嵌入数据)
3.4 部署图 – OLE 服务器 DLL 与容器 EXE
3.5 状态图 – OLE 对象的生命周期
4. IDL 定义(图形对象接口)
文件:idl/OleShapes.idl
import "unknwn.idl";
import "objidl.idl";
import "oleidl.idl";
[
object,
uuid(12345678-1234-1234-1234-123456789ABC), // IID_IShape
pointer_default(unique)
]
interface IShape : IUnknown
{
HRESULT SetColor([in] COLORREF color);
HRESULT GetColor([out] COLORREF* pColor);
HRESULT SetRect([in] RECT rc);
HRESULT GetRect([out] RECT* prc);
};
[
uuid(87654321-4321-4321-4321-CBA987654321), // CLSID_Shape
helpstring("OLE Shape Object")
]
coclass Shape
{
[default] interface IShape;
interface IOleObject;
interface IPersistStorage;
interface IDataObject;
};
MIDL 编译:midl OleShapes.idl 生成 .h 和 _i.c。
5. OLE 容器实现(C++)
5.1 容器主窗口(管理多个嵌入对象)
文件:container/ContainerWindow.h
#include <windows.h>
#include <ole2.h>
#include <vector>
#include "OleClientSite.h"
class ContainerWindow
{
private:
HWND m_hWnd;
std::vector<OleClientSite*> m_objects;
RECT m_clientRect;
public:
ContainerWindow();
~ContainerWindow();
BOOL Create();
void OnPaint();
void OnInsertObject(); // 插入新的 OLE 对象
void OnSaveDocument();
void OnLoadDocument();
HWND GetWindow() const { return m_hWnd; }
void AddObject(OleClientSite* pSite);
void RemoveObject(OleClientSite* pSite);
void DrawObject(HDC hdc, OleClientSite* pSite, RECT& rc);
// IOleInPlaceFrame 相关(简化,实际需要实现接口)
void OnUIActivate(IOleInPlaceActiveObject* pActiveObj);
void OnUIDeactivate();
};
文件:container/ContainerWindow.cpp(关键部分)
#include "ContainerWindow.h"
#include <commctrl.h>
// 窗口过程
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
ContainerWindow* pThis = (ContainerWindow*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
switch (msg)
{
case WM_CREATE:
pThis = new ContainerWindow();
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pThis);
pThis->Create();
break;
case WM_PAINT:
if (pThis) pThis->OnPaint();
break;
case WM_COMMAND:
if (LOWORD(wParam) == 1) pThis->OnInsertObject();
if (LOWORD(wParam) == 2) pThis->OnSaveDocument();
break;
case WM_DESTROY:
delete pThis;
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
void ContainerWindow::OnInsertObject()
{
// 调用 OLE 插入对象对话框
IOleObject* pObject = NULL;
HRESULT hr = OleCreate(CLSID_Shape, IID_IOleObject, OLERENDER_DRAW, NULL, NULL, NULL, NULL, (void**)&pObject);
if (SUCCEEDED(hr))
{
OleClientSite* pSite = new OleClientSite(this, pObject);
m_objects.push_back(pSite);
// 设置对象的位置和大小(暂定 100x100)
RECT rc = { 50, 50, 150, 150 };
pSite->SetRect(rc);
InvalidateRect(m_hWnd, NULL, TRUE);
pObject->Release();
}
}
void ContainerWindow::OnPaint()
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(m_hWnd, &ps);
// 绘制所有嵌入对象(通过 IDataObject 或直接调用 IViewObject)
for (OleClientSite* pSite : m_objects)
{
RECT rc = pSite->GetRect();
DrawObject(hdc, pSite, rc);
}
EndPaint(m_hWnd, &ps);
}
void ContainerWindow::DrawObject(HDC hdc, OleClientSite* pSite, RECT& rc)
{
// 调用对象的 IViewObject::Draw
IOleObject* pObj = pSite->GetObject();
IViewObject* pView = NULL;
if (SUCCEEDED(pObj->QueryInterface(IID_IViewObject, (void**)&pView)))
{
pView->Draw(DVASPECT_CONTENT, -1, NULL, NULL, hdc, NULL, (RECTL*)&rc, NULL, NULL, 0);
pView->Release();
}
}
5.2 实现 IOleClientSite
文件:container/OleClientSite.h
#include <ole2.h>
#include "ContainerWindow.h"
class OleClientSite : public IOleClientSite
{
private:
LONG m_cRef;
ContainerWindow* m_pContainer;
IOleObject* m_pObject;
RECT m_rect; // 对象在容器中的位置
IOleInPlaceSite* m_pInPlaceSite; // 用于就地激活
public:
OleClientSite(ContainerWindow* pContainer, IOleObject* pObject);
~OleClientSite();
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IOleClientSite
STDMETHODIMP SaveObject();
STDMETHODIMP GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker** ppmk);
STDMETHODIMP GetContainer(LPOLECONTAINER FAR* ppContainer);
STDMETHODIMP ShowObject();
STDMETHODIMP OnShowWindow(BOOL fShow);
STDMETHODIMP RequestNewObjectLayout();
// Helper
IOleObject* GetObject() const { return m_pObject; }
void SetRect(RECT rc) { m_rect = rc; }
RECT GetRect() const { return m_rect; }
};
实现:
#include "OleClientSite.h"
#include "OleInPlaceSite.h"
OleClientSite::OleClientSite(ContainerWindow* pContainer, IOleObject* pObject)
: m_cRef(1), m_pContainer(pContainer), m_pObject(pObject), m_pInPlaceSite(NULL)
{
m_pObject->SetClientSite(this);
// 创建就地站点
m_pInPlaceSite = new OleInPlaceSite(pContainer, this);
// 设置对象范围
m_pObject->SetExtent(DVASPECT_CONTENT, (SIZEL*)&m_rect);
}
OleClientSite::~OleClientSite()
{
if (m_pInPlaceSite) m_pInPlaceSite->Release();
if (m_pObject) m_pObject->Release();
}
STDMETHODIMP OleClientSite::QueryInterface(REFIID riid, void** ppv)
{
if (riid == IID_IUnknown || riid == IID_IOleClientSite)
{
*ppv = static_cast<IOleClientSite*>(this);
AddRef();
return S_OK;
}
*ppv = NULL;
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) OleClientSite::AddRef() { return InterlockedIncrement(&m_cRef); }
STDMETHODIMP_(ULONG) OleClientSite::Release()
{
ULONG ref = InterlockedDecrement(&m_cRef);
if (ref == 0) delete this;
return ref;
}
STDMETHODIMP OleClientSite::SaveObject()
{
// 通知对象保存到持久化存储(容器实现 IStorage 链)
// 简化:调用 IPersistStorage::Save
return S_OK;
}
STDMETHODIMP OleClientSite::ShowObject()
{
// 重绘容器中该对象的区域
InvalidateRect(m_pContainer->GetWindow(), &m_rect, TRUE);
return S_OK;
}
// 其他方法简化返回 E_NOTIMPL
5.3 实现 IOleInPlaceSite
文件:container/OleInPlaceSite.cpp
class OleInPlaceSite : public IOleInPlaceSite
{
private:
LONG m_cRef;
ContainerWindow* m_pContainer;
OleClientSite* m_pClientSite;
public:
OleInPlaceSite(ContainerWindow* pContainer, OleClientSite* pSite)
: m_cRef(1), m_pContainer(pContainer), m_pClientSite(pSite) {}
// IUnknown 标准实现
STDMETHODIMP GetWindow(HWND* phwnd) { *phwnd = m_pContainer->GetWindow(); return S_OK; }
STDMETHODIMP CanInPlaceActivate() { return S_OK; }
STDMETHODIMP OnInPlaceActivate() { return S_OK; }
STDMETHODIMP GetWindowContext(IOleInPlaceFrame** ppFrame, IOleInPlaceUIWindow** ppDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo)
{
// 返回容器框架(简化:容器本身实现 IOleInPlaceFrame)
// 这里返回 NULL 表示就地激活无菜单合并等高级功能
*ppFrame = NULL;
*ppDoc = NULL;
// 设置位置矩形
*lprcPosRect = m_pClientSite->GetRect();
GetClientRect(m_pContainer->GetWindow(), lprcClipRect);
// 填充框架信息
lpFrameInfo->fMDIApp = FALSE;
lpFrameInfo->hwndFrame = m_pContainer->GetWindow();
lpFrameInfo->haccel = NULL;
lpFrameInfo->cAccelEntries = 0;
return S_OK;
}
// 其他方法...
};
6. OLE 服务器实现(图形对象)
6.1 ShapeObject 类(核心)
文件:server/ShapeObject.h
#include "OleShapes.h" // MIDL generated
#include <ole2.h>
class ShapeObject : public IShape, public IOleObject, public IPersistStorage, public IDataObject, public IViewObject
{
private:
LONG m_cRef;
COLORREF m_color;
RECT m_rect;
IOleClientSite* m_pClientSite;
IStorage* m_pStorage; // 持久化存储
BOOL m_bDirty;
// 就地激活相关
ShapeInPlaceObj* m_pInPlaceObj;
public:
ShapeObject();
~ShapeObject();
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IShape
STDMETHODIMP SetColor(COLORREF color);
STDMETHODIMP GetColor(COLORREF* pColor);
STDMETHODIMP SetRect(RECT rc);
STDMETHODIMP GetRect(RECT* prc);
// IOleObject
STDMETHODIMP SetClientSite(IOleClientSite* pClientSite);
STDMETHODIMP GetClientSite(IOleClientSite** ppClientSite);
STDMETHODIMP SetHostNames(LPCOLESTR szContainerApp, LPCOLESTR szContainerObj);
STDMETHODIMP Close(DWORD dwSaveOption);
STDMETHODIMP SetMoniker(DWORD dwWhichMoniker, IMoniker* pmk);
STDMETHODIMP GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker** ppmk);
STDMETHODIMP InitFromData(IDataObject* pDataObject, BOOL fCreation, DWORD dwReserved);
STDMETHODIMP GetClipboardData(DWORD dwReserved, IDataObject** ppDataObject);
STDMETHODIMP DoVerb(LONG iVerb, LPMSG lpmsg, IOleClientSite* pActiveSite, LONG lindex, HWND hwndParent, LPCRECT lprcPosRect);
STDMETHODIMP EnumVerbs(IEnumOLEVERB** ppEnumOleVerb);
STDMETHODIMP Update();
STDMETHODIMP IsUpToDate();
STDMETHODIMP GetUserClassID(CLSID* pClsid);
STDMETHODIMP GetUserType(DWORD dwFormOfType, LPOLESTR* pszUserType);
STDMETHODIMP SetExtent(DWORD dwDrawAspect, SIZEL* psizel);
STDMETHODIMP GetExtent(DWORD dwDrawAspect, SIZEL* psizel);
STDMETHODIMP Advise(IAdviseSink* pAdvSink, DWORD* pdwConnection);
STDMETHODIMP Unadvise(DWORD dwConnection);
STDMETHODIMP EnumAdvise(IEnumSTATDATA** ppenumAdvise);
STDMETHODIMP GetMiscStatus(DWORD dwAspect, DWORD* pdwStatus);
STDMETHODIMP SetColorScheme(LOGPALETTE* pLogpal);
// IPersistStorage
STDMETHODIMP GetClassID(CLSID* pClassID);
STDMETHODIMP IsDirty();
STDMETHODIMP Load(IStorage* pStg);
STDMETHODIMP Save(IStorage* pStg, BOOL fSameAsLoad);
STDMETHODIMP SaveCompleted(IStorage* pStgNew);
STDMETHODIMP HandsOffStorage();
// IDataObject
STDMETHODIMP GetData(FORMATETC* pFormatEtc, STGMEDIUM* pMedium);
STDMETHODIMP GetDataHere(FORMATETC* pFormatEtc, STGMEDIUM* pMedium);
STDMETHODIMP QueryGetData(FORMATETC* pFormatEtc);
STDMETHODIMP GetCanonicalFormatEtc(FORMATETC* pFormatEtcIn, FORMATETC* pFormatEtcOut);
STDMETHODIMP SetData(FORMATETC* pFormatEtc, STGMEDIUM* pMedium, BOOL fRelease);
STDMETHODIMP EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC** ppEnumFormatEtc);
STDMETHODIMP DAdvise(FORMATETC* pFormatEtc, DWORD advf, IAdviseSink* pAdvSink, DWORD* pdwConnection);
STDMETHODIMP DUnadvise(DWORD dwConnection);
STDMETHODIMP EnumDAdvise(IEnumSTATDATA** ppenumAdvise);
// IViewObject
STDMETHODIMP Draw(DWORD dwDrawAspect, LONG lindex, void* pvAspect, DVTARGETDEVICE* ptd, HDC hdcTargetDev, HDC hdcDraw, LPCRECTL lprcBounds, LPCRECTL lprcWBounds, BOOL (CALLBACK* pfnContinue)(ULONG_PTR), ULONG_PTR dwContinue);
STDMETHODIMP GetColorSet(DWORD dwDrawAspect, LONG lindex, void* pvAspect, DVTARGETDEVICE* ptd, HDC hicTargetDev, LOGPALETTE** ppColorSet);
STDMETHODIMP Freeze(DWORD dwDrawAspect, LONG lindex, void* pvAspect, DWORD* pdwFreeze);
STDMETHODIMP Unfreeze(DWORD dwFreeze);
STDMETHODIMP SetAdvise(DWORD dwAspects, DWORD dwAdvf, IAdviseSink* pAdvSink);
STDMETHODIMP GetAdvise(DWORD* pAspects, DWORD* pAdvf, IAdviseSink** ppAdvSink);
};
6.2 关键实现片段
// 构造函数
ShapeObject::ShapeObject() : m_cRef(1), m_color(RGB(0,0,255)), m_pClientSite(NULL), m_pStorage(NULL), m_bDirty(FALSE), m_pInPlaceObj(NULL)
{
SetRect(&m_rect, 0, 0, 100, 100);
}
// IOleObject::SetClientSite
STDMETHODIMP ShapeObject::SetClientSite(IOleClientSite* pClientSite)
{
if (m_pClientSite) m_pClientSite->Release();
m_pClientSite = pClientSite;
if (m_pClientSite) m_pClientSite->AddRef();
return S_OK;
}
// IOleObject::DoVerb – 处理就地激活
STDMETHODIMP ShapeObject::DoVerb(LONG iVerb, LPMSG lpmsg, IOleClientSite* pActiveSite, LONG lindex, HWND hwndParent, LPCRECT lprcPosRect)
{
if (iVerb == OLEIVERB_INPLACEACTIVATE || iVerb == OLEIVERB_SHOW)
{
if (m_pInPlaceObj == NULL)
{
// 创建就地激活对象
m_pInPlaceObj = new ShapeInPlaceObj(this);
// 获取就地站点
IOleInPlaceSite* pIPS = NULL;
if (m_pClientSite && SUCCEEDED(m_pClientSite->QueryInterface(IID_IOleInPlaceSite, (void**)&pIPS)))
{
m_pInPlaceObj->SetInPlaceSite(pIPS);
pIPS->Release();
}
}
// 显示窗口
m_pInPlaceObj->Show();
// 通知容器已激活
if (m_pClientSite)
m_pClientSite->ShowObject();
return S_OK;
}
return E_NOTIMPL;
}
// IPersistStorage::Save
STDMETHODIMP ShapeObject::Save(IStorage* pStg, BOOL fSameAsLoad)
{
// 创建流,写入 m_color 和 m_rect
IStream* pStream = NULL;
HRESULT hr = pStg->CreateStream(L"ShapeData", STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream);
if (SUCCEEDED(hr))
{
hr = pStream->Write(&m_color, sizeof(m_color), NULL);
if (SUCCEEDED(hr))
hr = pStream->Write(&m_rect, sizeof(m_rect), NULL);
pStream->Release();
}
m_bDirty = FALSE;
return hr;
}
// IViewObject::Draw – 绘制图形
STDMETHODIMP ShapeObject::Draw(DWORD dwDrawAspect, LONG lindex, void* pvAspect, DVTARGETDEVICE* ptd, HDC hdcTargetDev, HDC hdcDraw, LPCRECTL lprcBounds, LPCRECTL lprcWBounds, BOOL (CALLBACK* pfnContinue)(ULONG_PTR), ULONG_PTR dwContinue)
{
if (dwDrawAspect != DVASPECT_CONTENT) return E_INVALIDARG;
RECT rc = { lprcBounds->left, lprcBounds->top, lprcBounds->right, lprcBounds->bottom };
HBRUSH hBrush = CreateSolidBrush(m_color);
FillRect(hdcDraw, &rc, hBrush);
DeleteObject(hBrush);
FrameRect(hdcDraw, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH));
return S_OK;
}
6.3 就地激活窗口 ShapeInPlaceObj
文件:server/ShapeInPlaceObj.h
class ShapeInPlaceObj : public IOleInPlaceObject
{
private:
LONG m_cRef;
ShapeObject* m_pParent;
HWND m_hWnd;
IOleInPlaceSite* m_pIPS;
public:
ShapeInPlaceObj(ShapeObject* pParent);
~ShapeInPlaceObj();
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IOleInPlaceObject
STDMETHODIMP GetWindow(HWND* phwnd);
STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode);
STDMETHODIMP InPlaceDeactivate();
STDMETHODIMP UIDeactivate();
STDMETHODIMP SetObjectRects(LPCRECT lprcPosRect, LPCRECT lprcClipRect);
STDMETHODIMP ReactivateAndUndo();
// Helper
void SetInPlaceSite(IOleInPlaceSite* pIPS);
void Show();
};
实现:
ShapeInPlaceObj::ShapeInPlaceObj(ShapeObject* pParent) : m_cRef(1), m_pParent(pParent), m_hWnd(NULL), m_pIPS(NULL) {}
void ShapeInPlaceObj::Show()
{
if (m_hWnd == NULL)
{
// 创建无边框窗口,作为就地编辑界面
HINSTANCE hInst = GetModuleHandle(NULL);
WNDCLASS wc = {0};
wc.lpfnWndProc = DefWindowProc;
wc.hInstance = hInst;
wc.lpszClassName = L"ShapeInPlaceWnd";
RegisterClass(&wc);
m_hWnd = CreateWindow(L"ShapeInPlaceWnd", NULL, WS_CHILD, 0, 0, 100, 100, GetActiveWindow(), NULL, hInst, NULL);
}
// 获取容器窗口位置并调整
RECT rcPos;
m_pParent->GetRect(&rcPos);
SetWindowPos(m_hWnd, NULL, rcPos.left, rcPos.top, rcPos.right-rcPos.left, rcPos.bottom-rcPos.top, SWP_SHOWWINDOW);
// 告知容器 UI 激活
IOleInPlaceFrame* pFrame = NULL;
if (m_pIPS && SUCCEEDED(m_pIPS->GetWindowContext(&pFrame, NULL, NULL, NULL, NULL)))
{
pFrame->SetActiveObject(this, NULL);
pFrame->Release();
}
}
STDMETHODIMP ShapeInPlaceObj::InPlaceDeactivate()
{
if (m_hWnd)
{
DestroyWindow(m_hWnd);
m_hWnd = NULL;
}
if (m_pIPS)
m_pIPS->OnInPlaceDeactivate();
return S_OK;
}
6.4 类工厂与 DLL 导出
标准 COM DLL 导出 DllGetClassObject, DllCanUnloadNow, DllRegisterServer, DllUnregisterServer。在 DllRegisterServer 中注册 CLSID_Shape,并添加 Insertable 键(使 OLE 插入对象对话框能识别)。
7. 运行与测试
- 编译服务器 DLL:
cl /LD shapeobj.cpp ... /FeOleShape.dll /link /DEF:server.def - 注册服务器:
regsvr32 OleShape.dll - 编译容器 EXE:
cl container.cpp ... /FeOleContainer.exe - 运行容器:点击“插入对象”菜单,选择“Shape Object”,会在窗口中显示一个蓝色矩形。双击矩形,弹出可编辑窗口(可改变颜色等,本例简化)。
8. 深入机制解析
8.1 嵌入 vs 链接
- 嵌入:容器调用
OleCreate,服务器将数据保存到容器的IStorage中。IPersistStorage::Save写入子存储。 - 链接:容器调用
OleCreateLink,服务器返回IOleLink接口,数据源为文件或 URL。容器保存链接源信息。
8.2 就地激活的细节
- 就地站点(IOleInPlaceSite):由容器实现,提供容器窗口、位置、框架信息。
- 就地对象(IOleInPlaceObject):由服务器实现,负责创建子窗口、响应
SetObjectRects。 - UI 激活:服务器接管容器的菜单、工具栏(通过
IOleInPlaceFrame插入菜单)。
8.3 持久化
- 容器为每个嵌入对象创建一个
IStorage(结构化存储)。 - 对象通过
IPersistStorage::Save将数据写入该存储的子流或子存储。 - 容器保存整个
IStorage到文件(复合文档格式)。
8.4 OLE 自动化
OLE 自动化允许脚本语言(如 VBA)控制 OLE 对象。服务器需要实现 IDispatch,将属性和方法暴露给自动化控制器。
9. 总结
本项目通过实现一个简单的 OLE 容器和图形服务器,完整展示了 OLE 的核心机制:
- 嵌入:容器创建对象,对象保存到容器存储。
- 就地激活:服务器窗口嵌入容器,实现无缝编辑。
- 接口协作:
IOleClientSite/IOleObject实现容器-服务器通信,IOleInPlaceSite/IOleInPlaceObject实现就地激活。
所有 UML 图使用 Mermaid 渲染,代码展示了关键接口的实现,可作为学习 OLE 技术的参考。尽管 OLE 已被现代技术替代,但其设计模式(如结构化存储、就地激活)对理解复合文档和组件化架构仍有重要价值。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐




所有评论(0)