Design of ActiveX Control Using MFC-Step 2

Source: Internet
Author: User
When we suddenly find that the persistence control attribute can be implemented in a very simple way, it is not very comfortable, because it means that the previous efforts are done in vain. Fortunately, this simple method is still flawed. It can only be used for the implementation of the IPersistStream interface, but not for the implementation of the IPersistPropertyBag interface, and it is a little troublesome for the persistence of interface objects.
In any case, this article will talk about this simple Serialize method.

Start with source code analysis:
The source code for implementing the IPersistStream interface is as follows:
STDMETHODIMP COleControl: XPersistStreamInit: Load (LPSTREAM pStm)
{
METHOD_PROLOGUE_EX (COleControl, PersistStreamInit)
Return pThis-> LoadState (pStm );
}

STDMETHODIMP COleControl: XPersistStreamInit: Save (LPSTREAM pStm,
BOOL fClearDirty)
{
METHOD_PROLOGUE_EX (COleControl, PersistStreamInit)

// Delegate to SaveState.
HRESULT hr = pThis-> SaveState (pStm );

// Bookkeeping: Clear the dirty flag, if requested.
If (fClearDirty)
PThis-> m_bModified = FALSE;

Return hr;
}

We can see that the two functions of COleControl are used: LoadState and SaveState.

HRESULT COleControl: SaveState (IStream * pstm)
{
HRESULT hr = S_ OK;

TRY
{
// Delegate to the Serialize method.
COleStreamFile file (pstm );
CArchive ar (& file, CArchive: store );
Serialize (ar );
}
CATCH_ALL (e)
{
Hr = E_FAIL;
DELETE_EXCEPTION (e );
}
END_CATCH_ALL

Return hr;
}

HRESULT COleControl: LoadState (IStream * pstm)
{
HRESULT hr = S_ OK;

TRY
{
// Delegate to the Serialize method.
COleStreamFile file (pstm );
CArchive ar (& file, CArchive: load );
Serialize (ar );
}
CATCH_ALL (e)
{
// The load failed. Delete any partially-initialized state.
OnResetState ();
M_bInitialized = TRUE;
Hr = E_FAIL;
DELETE_EXCEPTION (e );
}
END_CATCH_ALL

// Clear the modified flag.
M_bModified = FALSE;

// Unless IOleObject: SetClientSite is called after this, we can
// Count on ambient properties being available while loading.
M_bCountOnAmbients = TRUE;

// Properties have been initialized
M_bInitialized = TRUE;

// Uncache cached ambient properties
_ AfxAmbientCache-> Cache (NULL );

Return hr;
}

These two functions are finally classified as Serialize, and at most an OnResetState is added. Let's take a look at the two functions.

Void COleControl: Serialize (CArchive & ar)
{
CArchivePropExchange px (ar );
DoPropExchange (& px );
If (ar. IsLoading ())
{
BoundPropertyChanged (DISPID_UNKNOWN );
InvalidateControl ();
}
}

Void COleControl: OnResetState ()
{
CResetPropExchange px;
DoPropExchange (& px );
InvalidateControl ();
}

These two functions finally come to the familiar DoPropExchange function. Now I think we should be familiar with the intermediate process. When the framework is generated, we usually see DoPropExchange and OnResetState, we need to reload them. We often used to confuse them. It is clear that the properties have been initialized when DoPropExchange uses the PX_xxx function. Why do we need to use OnResetState? What is the relationship between them? Now I understand, the complementary relationship. Some properties are initialized in DoPropExchange. In OnResetState, other data that is not initialized in DoPropExchange can be initialized.

After figuring out these relationships, we will also be clear about what we will do next. We know that the IPersistStream interface will use the Serialize of COleControl at the end, so this so-called is simpler and more optimized, the more flexible operation lies in the heavy-load Serialize function. Note that you do not need to call COleControl: Serialize (ar. Of course, it is not added during wizard generation :)

But before starting the example, let's take a look at COleControl: DoPropExchange

Void COleControl: DoPropExchange (CPropExchange * pPX)
{
ASSERT_POINTER (pPX, CPropExchange );

ExchangeExtent (pPX );
ExchangeStockProps (pPX );
}
We can see that Extent and some Stock attributes have been persisted in the COleControl. This sentence is also available when the framework is generated.
Void CToppCtrl: DoPropExchange (CPropExchange * pPX)
{
ExchangeVersion (pPX, MAKELONG (_ wVerMinor, _ wVerMajor ));
COleControl: DoPropExchange (pPX );
}

So if we want to implement persistence with Serialize, we need to persist these things first. Fortunately, COleControl provides us with SerializeExtent, SerializeStopProps, SerializeVersion, and ResetStockProps for initialization, resetVersion.

Back to the example topp we often use when talking about the property page

1. Reload void Serialize (CArchive & ar)

Void CToppCtrl: Serialize (CArchive & ar)
{
// COleControl: Serialize (ar );
// Return;
DWORD dwVersion =
SerializeVersion (ar, MAKELONG (_ wVerMinor, _ wVerMajor ));
SerializeExtent (ar );
SerializeStockProps (ar );

If (ar. IsStoring ())
{// Storing code
Ar <m_color;
}
Else
{// Loading code
Ar> m_color;
}
SerializePicture (ar );
SerializeBox (ar );
SerializeItems (ar );
}

The first few lines of code here are basically standard code and do not need to be modified.
Because we only have one common attribute Color, the simple ar <m_color and ar> m_color is used. The other three functions are used for Serialize image attributes Picture, LPDISPATCH attribute Box and variable m_saItems.

2. Add a Serialize interface function SerializeUnknown (CArchive & ar, LPUNKNOWN * ppUnk, REFIID iid ). Here, it is basically copied from the COleControl's ExchangePersistentProp code, just to avoid confusion, removing a BYTE-type flag indicating the default Interface. If this BYTE flag is added, it can be used with controls that are persisted in the standard way. That is to say, the controls that are saved in the standard way can be read in the way provided in this Article.

Void CToppCtrl: SerializeUnknown (CArchive & ar, LPUNKNOWN * ppUnk, REFIID iid)
{
ASSERT_POINTER (ppUnk, LPUNKNOWN );

BOOL bResult = FALSE;
CArchiveStream stm (& ar );

If (ar. IsLoading ())
{
If (* ppUnk ){
(* PpUnk)-> Release ();
}
* PpUnk = NULL;

// Read the CLSID
CLSID clsid;
Ar> clsid. Data1;
Ar> clsid. Data2;
Ar> clsid. Data3;
Ar. Read (& clsid. Data4 [0], sizeof clsid. Data4 );

// Check for GUID_NULL first and skip if found
If (IsEqualCLSID (clsid, GUID_NULL ))
BResult = TRUE;
Else
{
// Otherwise will need a stream
LPSTREAM pstm = & stm; // _ AfxGetArchiveStream (m_ar, stm );
If (IsEqualCLSID (clsid, CLSID_StdPicture) |
IsEqualCLSID (clsid, _ afx_CLSID_StdPicture2_V1 ))
{
// Special case for pictures
BResult = SUCCEEDED (: OleLoadPicture (pstm, 0, FALSE, iid,
(Void **) ppUnk ));
}
Else
{
// Otherwise, seek back to the CLSID
LARGE_INTEGER li;
Li. LowPart = (DWORD) (-(long) sizeof (CLSID ));
Li. HighPart =-1;
VERIFY (SUCCEEDED (pstm-> Seek (li, STREAM_SEEK_CUR, NULL )));

// And load the object normally

CLSID clsid;
If (SUCCEEDED (: ReadClassStm (pstm, & clsid ))&&
(SUCCEEDED (: CoCreateInstance (clsid, NULL,
CLSCTX_SERVER | CLSCTX_REMOTE_SERVER,
Iid, (void **) ppUnk) |
SUCCEEDED (: CoCreateInstance (clsid, NULL,
CLSCTX_SERVER &~ CLSCTX_REMOTE_SERVER,
Iid, (void **) ppUnk ))))
{
LPPERSISTSTREAM pps = NULL;
If (SUCCEEDED (* ppUnk)-> QueryInterface (
IID_IPersistStream, (void **) & pps) |
SUCCEEDED (* ppUnk)-> QueryInterface (
IID_IPersistStreamInit, (void **) & pps )))
{
ASSERT_POINTER (pps, IPersistStream );
BResult = SUCCEEDED (pps-> Load (pstm ));
Pps-> Release ();
}

If (! BResult)
{
(* PpUnk)-> Release ();
* PpUnk = NULL;
}
}
}
}
}
Else
{
ASSERT_NULL_OR_POINTER (* ppUnk, IUnknown );

// Check if * ppUnk and pUnkDefault are the same thing. If so, don't
// Bother saving the object; just write a special flag instead.

If (* ppUnk! = NULL)
{
LPPERSISTSTREAM pps = NULL;
If (SUCCEEDED (* ppUnk)-> QueryInterface (
IID_IPersistStream, (void **) & pps) |
SUCCEEDED (* ppUnk)-> QueryInterface (
IID_IPersistStreamInit, (void **) & pps )))
{
ASSERT_POINTER (pps, IPersistStream );
LPSTREAM pstm = & stm; // _ AfxGetArchiveStream (ar, stm );
BResult = SUCCEEDED (: OleSaveToStream (pps, pstm ));
Pps-> Release ();
}
}
Else
{
// If no object, write null class ID.
Ar. Write (& GUID_NULL, sizeof (GUID ));
}
}

// Throw exception in case of unthrown errors
If (! BResult)
AfxThrowArchiveException (CArchiveException: generic );
}

3. Implement the three SerializeXXX functions mentioned above

Void CToppCtrl: SerializePicture (CArchive & ar)
{
ASSERT_POINTER (& m_pic, CPictureHolder );

LPUNKNOWN & pUnk = (LPUNKNOWN &) m_pic.m_pPict;

SerializeUnknown (ar, & pUnk, IID_IPicture );
}

Void CToppCtrl: SerializeBox (CArchive & ar)
{
LPUNKNOWN & pUnk = (LPUNKNOWN &) m_pboxdisp;
SerializeUnknown (ar, & pUnk, IID_IDispatch );
}

Void CToppCtrl: SerializeItems (CArchive & ar)
{
If (ar. IsLoading ())
{
Int n = 0;
Ar> n;
For (int I = 0; I <n; I ++ ){
CString str;
Ar> str;
M_saItems.Add (str );
}
}
Else {
Int n = m_saItems.GetSize ();
Ar <n;
For (int I = 0; I <n; I ++ ){
CString str = m_saItems [I];
Ar <str;
}
}
}
It is worth noting that SerializeItms can be seen that the persistence of custom information can be greatly simplified in the way provided in this Article.

4. Add Initialization

Void CToppCtrl: OnResetState ()
{
// COleControl: OnResetState (); // Resets defaults found in DoPropExchange
// Return;
// TODO: Reset any other control state here.
ResetVersion (MAKELONG (_ wVerMinor, _ wVerMajor ));
ResetStockProps ();

CToppBox * pbox = new CToppBox;
M_pboxdisp = pbox-> GetIDispatch (FALSE );
}

In the previous two lines of code, ResetVersion and ResetStockProps are also basically standard configurations and do not need to be modified. As needed, only m_pboxdisp Initialization is added.

Note: The Code in DoPropExchange does not need to be deleted. In addition, in DoPropExchange and OnResetState, you can find that the previous two lines of code have been commented out, removing the comment, then you can restore to the original method for persistence.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.