允许一个ActiveX控件嵌入到容器中,这项技术已经很长时间了。OLE文档标准(当时称为复合文档,Compound Documents)于1991年创建,其中包含了OLE的主要内容。实际上OLE是Object Linking and Embedding(对象链接与嵌入)的首字母缩写。在ActiveX控件出现以后,OLE文档标准也得到了增强,它允许文档服务器向外公开programmatic功能。
现在,如果你所开发的控件需要提供一个可视化界面,并需要通过鼠标点击或类似的方式与用户实现交互,则它必须支持基本的OLE文档接口:IOleObject、IOleInPlaceObject、IOleInPlaceActiveObject、IDataObject以及IViewObject2
接口 |
说明 |
IOleObject |
IOleObject提供了OLE文档构架的基本内容。通过该接口,容器和组件相互协商被嵌入对象的大小 |
IOleInPlaceObject |
一个控件必须实现IOleInPlaceObject来支持在容器中实现在位激活和失效。该接口还提供了一个方法,用于通知控件,他的大小已经改变或已经在容器中删除 |
IOleInPlace ActiveObject |
一个控件必须实现IOleInPlaceActiveObject,以便对在控件中使用和转换加速键提供支持。IOleInPlaceActiveObject的大部分方法对于ActiveX控件是不需要的 |
IOleControl |
IOleControl是为支持ActiveX控件而加入的一个新的接口。它所提供的方法使得与控件容器的交互得到了加强。IOleControl的主要功能是允许控件和容器在处理键盘输入时能够很好的协调工作 |
IDataObject |
控件实现该接口主要用于为容器提供图形绘制功能。IDataObject还提供了一个属性,用于对它的属性持续性进行设置 |
IViewObject2 |
当一个控件提供一个可视的外观时需要提供IViewObject2。该接口为容器提供各种方法,允许容器通过这些方法请求控件在容器的客户区重绘控件本身 |
IPersistFile |
IPersistFile接口比较简单,客户程序调用Load函数使永久对象装载状态数据,执行初始化操作,永久对象可以使文件保持打开状态。在Save函数中,如果参数lpszFileName为NULL,那么永久对象把状态数据写到文件中,同时清除修改标记并关闭文件;如果lpszFileName不为NULL,那么根据参数fRemember的不同值,或者执行Save As操作,或者执行Save Copy As,前者情况下也要清除修改标记并关闭文件。客户程序调用SaveCompleted函数,通知永久对象保存操作已经完成,对象可以再打开文件。GetCurFile函数返回永久对象所使用的文件名字 |
IPersistStorage |
IPersistStorage接口并不复杂,它不仅可实现在事物方式下用存储对象保存永久状态数据,而且也支持零内存保存特性。客户程序通过SaveCompleted函数通知永久对象,它已经完成了所有的保存操作,根据参数的值,客户控制永久对象进入相应的状态。客户程序利用HandsOffStorage函数指示永久对象释放存储对象的所有接口指针,包括所有的子对象,以后由客户程序完全控制存储对象 |
来个例子吧,仅仅是实现了一个IPersistStorage接口:
1、我打算从d://test.bmp得到bmp的数据,然后StgCreateStorageEx,通过IPersistStorage的Save函数保存到Zeng.stg中(createStream了一个流“ZengStream”)
2、通过读取d://a.stg然后在Load函数中OpenStream流“ZengStream”,从里面得到刚才保存的数据,再写到本地文件1.bmp中
// CImpIPersist.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include "windows.h" #include "fstream" #include "iostream" using namespace std; #include <initguid.h> #include "assert.h" DEFINE_GUID(CLSID_CImpIPersistStorage, 0x00021108, 0, 0, 0xB0,0,0,0,0,0,0,0x47); class ImpIPersistStorage : public IPersistStorage { protected: ULONG refence_count_; LPSTORAGE pIStorage_; LPSTREAM pIStream_; public: ImpIPersistStorage(); ~ImpIPersistStorage(); STDMETHODIMP QueryInterface(/* [in] */ REFIID riid, /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR *__RPC_FAR *ppvObject); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); STDMETHODIMP GetClassID(LPCLSID); STDMETHODIMP IsDirty(void); STDMETHODIMP InitNew(LPSTORAGE); STDMETHODIMP Load(LPSTORAGE); STDMETHODIMP Save(LPSTORAGE, BOOL); STDMETHODIMP SaveCompleted(LPSTORAGE); STDMETHODIMP HandsOffStorage(void); }; ImpIPersistStorage::ImpIPersistStorage() { refence_count_ = 0; pIStorage_ = NULL; pIStream_ = NULL; } ImpIPersistStorage::~ImpIPersistStorage() { if (0 == refence_count_) { // delete this; } } ////////////////////////////////////////////////////////////////////////// // IUnknown ////////////////////////////////////////////////////////////////////////// STDMETHODIMP ImpIPersistStorage::QueryInterface(/* [in] */ REFIID riid, /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR *__RPC_FAR *ppvObject) { *ppvObject = NULL; if (IsEqualIID(riid, IID_IUnknown)) *ppvObject=(LPVOID)this; if (IsEqualIID(riid, IID_IPersist) || IsEqualIID(riid, IID_IPersistStorage)) *ppvObject=(LPVOID)this; //AddRef any interface we'll return. if (NULL!=*ppvObject) { ((LPUNKNOWN)*ppvObject)->AddRef(); return NOERROR; } return ResultFromScode(E_NOINTERFACE); } STDMETHODIMP_(ULONG) ImpIPersistStorage::AddRef(void) { ++refence_count_; return (ULONG)refence_count_; } STDMETHODIMP_(ULONG) ImpIPersistStorage::Release(void) { --refence_count_; if (0 == refence_count_) { delete this; return 0; } return (ULONG)refence_count_; } ////////////////////////////////////////////////////////////////////////// // IPersistStorage ////////////////////////////////////////////////////////////////////////// STDMETHODIMP ImpIPersistStorage::GetClassID(LPCLSID pClsID) { *pClsID = CLSID_CImpIPersistStorage; return NOERROR; } STDMETHODIMP ImpIPersistStorage::IsDirty(void) { return ResultFromScode(S_OK); } STDMETHODIMP ImpIPersistStorage::InitNew(LPSTORAGE pIStorage) { if (NULL != pIStorage_) { return ResultFromScode(E_UNEXPECTED); } HRESULT hr = pIStorage->CreateStream( L"ZengStream", STGM_DIRECT | STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pIStream_); if (FAILED(hr)) { return hr; } WriteClassStg(pIStorage, CLSID_CImpIPersistStorage); WriteFmtUserTypeStg(pIStorage, 0, 0); pIStorage->AddRef(); pIStorage_ = pIStorage; return NOERROR; } STDMETHODIMP ImpIPersistStorage::Load(LPSTORAGE pIStorage) { HRESULT hr; ULONG cb = -1; ULONG fileLength = 0; LPSTREAM pIStream; ofstream fout("d://1.bmp", ios::binary); if ( NULL != pIStorage_ ) return ResultFromScode(E_UNEXPECTED); if ( NULL == pIStorage ) return ResultFromScode( STG_E_INVALIDPOINTER ); hr = pIStorage->OpenStream( L"ZengStream", 0, STGM_DIRECT | STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pIStream ); if ( FAILED(hr) ) return ResultFromScode( STG_E_READFAULT ); // begin read the file length info from IStream hr = pIStream->Read( (LPVOID)&fileLength, sizeof(ULONG), &cb ); LARGE_INTEGER lInt; lInt.QuadPart = sizeof(ULONG); pIStream->Seek( lInt,STREAM_SEEK_SET, 0 ); if ( FAILED(hr) || ( sizeof(ULONG) != cb ) ) { pIStream->Release(); return ResultFromScode( STG_E_READFAULT ); } cout << "read the file length is :" << fileLength << endl; void *readFileData = new char[ fileLength ]; hr = pIStream->Read( readFileData, fileLength, &cb ); if ( FAILED(hr) || ( fileLength != cb ) ) { pIStream->Release(); return ResultFromScode( STG_E_READFAULT ); } //已经读取到了bmp的数据 fout.write( (char *)readFileData, fileLength ); pIStream_ = pIStream; pIStorage->AddRef(); pIStorage_ = pIStorage; fout.close(); return NOERROR; } STDMETHODIMP ImpIPersistStorage::Save( LPSTORAGE pIStorage, BOOL fSameAsLoad ) { HRESULT hr; LPSTREAM pIStream; ULONG totalWriteBytes = 0; if (NULL==pIStorage) return ResultFromScode(STG_E_INVALIDPOINTER); // fSameAsLoad is true, mean create a new stream if ( fSameAsLoad ) { LARGE_INTEGER li; pIStream = pIStream_; LISet32( li, 0 ); pIStream->Seek( li, STREAM_SEEK_SET, NULL ); pIStream->AddRef(); } else { // hr = pIStorage->CreateStream( L"zengraoli", STGM_DIRECT // | STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pIStream ); hr = pIStorage->CreateStream( // 建立流 L"ZengStream", // 流名称 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0,0, &pIStream ); // 取得流接口指针 if (FAILED(hr)) { return hr; } WriteFmtUserTypeStg( pIStorage, 0, 0 ); } if ( NULL == pIStream ) { return ResultFromScode( STG_E_WRITEFAULT ); } // read bitmap from local file ifstream fin("d://test.bmp", ios::binary); if (NULL == fin) { printf("the file is open fail..."); return 0; } fin.seekg(0, ios::end); ULONG fileLength = fin.tellg(); fin.seekg(0, 0); int writeLength = fileLength + sizeof(ULONG); void *fileData = new char[writeLength + 1]; *((ULONG*)fileData) = fileLength; // write the bitmap date length to fileDate while (!fin.eof()) { fin.read((char *)fileData + sizeof(ULONG), fileLength); } // write fileData to pIStream hr = pIStream->Write(fileData, writeLength, &totalWriteBytes); pIStream->Release(); if (FAILED(hr) || totalWriteBytes != writeLength) { return ResultFromScode(STG_E_WRITEFAULT); } return NOERROR; } STDMETHODIMP ImpIPersistStorage::SaveCompleted(LPSTORAGE pIStorage) { HRESULT hr; LPSTREAM pIStream; if (NULL != pIStorage) { hr = pIStorage->OpenStream( L"ZengStream", 0, STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pIStream ); if (FAILED(hr)) { return hr; } if (NULL != pIStream_) { pIStream_->Release(); } pIStream_ = pIStream; if (NULL != pIStorage_) { pIStorage_->Release(); } pIStorage_ = pIStorage; pIStorage_->AddRef(); } return NOERROR; } STDMETHODIMP ImpIPersistStorage::HandsOffStorage(void) { //Release held pointers if (NULL != pIStream_) { pIStream_->Release(); pIStream_ = NULL; } if (NULL != pIStorage_) { pIStorage_->Release(); pIStorage_ = NULL; } return NOERROR; } int _tmain(int argc, _TCHAR* argv[]) { ImpIPersistStorage test; HRESULT hr; IStorage *pStg = NULL; // 根存储指针 IStorage *pSub = NULL; // 子存储接口指针 IStream *pStm = NULL; // 流接口指针 ::CoInitialize(NULL); // COM 初始化 ////////////////////////////////////////////////////////////////////////// // test the Save method ////////////////////////////////////////////////////////////////////////// // hr = ::StgCreateStorageEx( L"d://Zeng.stg", // STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, STGFMT_STORAGE, // 0, NULL, NULL, IID_IStorage, (void **)&pStg ); // // hr = pStg->CreateStorage( // 建立子存储 // L"ZengStorage", // 子存储名称 // STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, // 0,0, // &pSub ); // 取得子存储接口指针 // // assert( SUCCEEDED(hr) ); // // test.InitNew(pSub); // test.Save(pSub, false); // test.SaveCompleted(pStg); ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // test the Load method ////////////////////////////////////////////////////////////////////////// hr = ::StgOpenStorage( L"d://Zeng.stg", NULL, STGM_DIRECT | STGM_READ | STGM_SHARE_EXCLUSIVE | STGFMT_STORAGE, 0, 0, &pStg); assert( SUCCEEDED(hr) ); hr = pStg->OpenStorage( L"ZengStorage", 0, STGM_READ | STGM_SHARE_EXCLUSIVE | STGFMT_STORAGE, 0, 0, &pSub ); // hr = pSub->OpenStream( L"ZengStream", 0, // STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStm); assert( SUCCEEDED(hr) ); test.Load(pSub); ////////////////////////////////////////////////////////////////////////// if( pStm ) pStm->Release(); // 释放流指针 if( pSub ) pSub->Release(); // 释放子存储指针 if( pStg ) pStg->Release(); // 释放根存储指针 ::CoUninitialize(); // COM 释放 return 0; }
测试完之后,在我的D盘出现1.bmp和Zeng