Compile C ++ code with better reusability -- band object and comtoys (7)

Source: Internet
Author: User

Compile/Zhao Xiangning

Original: Paul dilascia

MSJ November 1999 & December 1999

Keywords: bands object, desk bands, info/Comm bands, explorer bar, and tool bands.

This document assumes that you are familiar with C ++, COM, and IE.

Download the source code of this article: mybands.zip (KB)
Testeditsrch.zip (75kb)

Part 1: Introduction to band objects
Part 2: bandobj class hierarchy and mybands service program Registration
Part 3: go deep into the band and unveil the band.
Part 4: problems encountered in using the band object
Part 5: build your own COM programming platform comtoys
Part 6: design and construct comtoys

Part 7 Implementation

Hybrid Implementation of Classes
So far, I have introduced a method to use multi-inheritance instead of Nested classes to implement COM objects in MFC. The basic idea is to ignore the MFC macro and interface ing, and call getinterfacehook to return the interface pointer. Obviously, this has made programming much easier, but where is reusability reflected? The main purpose of developing comtoys is to make it reusable to implement common com interfaces. Therefore, comtoys uses a mixed class.
A mixed class is designed to mix multiple inheritance features with other classes. Generally, the relationship between the mixed class and the main class is directly handed in, that is to say, it provides the independence with the class level to avoid the diamond derived tree (please forgive me for the conflicting modifier), so there is no doubt, cbandobj is more complex than I have discussed in the previous four sections. The new version of the Code is as follows:

// Update cbandobj /////////////////////////////////// /////// cbandobj -- a typical multi-interface com class // header file. h: Class cbandobj: Public cwnd, // interfaces // public iolewindow, // inherit from ideskband // public idockingwindow, // inherit from ideskband public ideskband, public iobjectwithsite, public iinputobject, // public ipersist, // inherit public ipersiststream and public icontextmenu from ipersiststream, // use comtoys to implement public ctolewin Dow, public ctdockingwindow, public ctpersist, public ctpersiststream, public ctinputobject, public ctinputobjectsite, public ctcontextmenu {public: cbandobj (refclsid CLSID); Virtual ~ Cbandobj (); // rewrite to implement QueryInterface virtual lpunknown getinterfacehook (const void * IID); // use comtoys to implement interface trim (); declare_iolewindow (); declare_idockingwindow (); iterator (); declare_ipersist (); declare_ipersiststream (); declare_icontextmenu (); iterator (); // ideskband stdmethod (getbandinfo) (DWORD, DWORD, deskbandinfo *);}; // implementation file. CPP: cbandobj (refclsid CLSID): // Initialize all implementation classes ctmfccomobj (this), ctolewindow (this), ctdockingwindow (this), ctpersist (CLSID ), ctpersiststream (), ctinputobject (this), ctcontextmenu (this, ctmfccomobjfactory: getfactory (CLSID)-> getresourceid ()){......} /// // these macros use comtoys all interfaces are implemented // implement_iunknown (cbandobj, values); values (cbandobj, ctolewindow); values (cbandobj, ctdockingwindow); values (cbandobj, ctpersist); values (cbandobj, ctcontextmenu); values (cbandobj, ctpersiststream ); implement_iinputobject (cbandobj, ctinputobject ); //////////////////////////////////////// ///// // After rewriting, bypass the MFC in cve-target:: interface ing in internalqueryinterface // lpunknown cbandobj: getinterfacehook (const void * piid) {refiid IID = * (IID *) piid); If (IID = iid_iunknown) return (ideskband *) This; # define if_interface (IID, iface)/If (IID = IID _ # iface)/Return (iface *) This;/if_interface (IID, iolewindow); if_interface (IID, idockingwindow); if_interface (IID, iobjectwithsite); if_interface (IID, identifier); if_interface (IID, ipersist); if_interface (IID, ipersiststream ); if_interface (IID, icontextmenu); if_interface (IID, ideskband); return NULL ;}

At first glance, these classes are so similar to ATL! See the following analysis.
Cbandobj inherits iolewindow and idockingwindow from ideskband, so they must be implemented. For iunknown, The comtoys macro declaration method, declare_iolewindow and declare_idockingwindow only declare the majority of derived methods, rather than the inherited methods (I will understand why immediately). These two macros are indispensable. The declare_ideskband macro does not exist because comtoys does not have classes to implement it. ideskband is implemented by cbandobj. For iunknown, comtoys has more macros to implement this interface.
The new macro uses ctolewindow to implement iolewindow and ctdockingwindow to implement idockingwindow. Implement_iwhatsit macro links an interface definition (abstract class) to an interface implementation (specific class) by means of methods that generate call class implementation.

// Hresult cbandobj: getwindow (hwnd * phwnd) {export targentrytr (_ T ("cbandobj (% P): iolewindow: getwindow/N") generated by implement_iolewindow "), this); Return ctolewindow: getwindow (phwnd );}

Once you declare and implement these interfaces, you can write code in getinterfacehook to let MFC know about them:

// In cbandobj: getinterfacehook, if (IID = iid_iolewindow) Return (iolewindow *) This; If (IID = iid_idockingwindow) Return (idockingwindow *) This;

If you understand how they work in iolewindow, you will understand that most of the code of cbandobj is repeated in the same thing. For each iwhatsit interface implemented by the new COM class, it must be derived from two classes: the COM interface itself (iwhatsit) and the CT class implementing ctwhatsit. The execution class can be a class in comtoys or its own class. Both interfaces and implementations are separated. This is more professional, because it is also the basic principle of COM. Now let's assume that you already have ctwhatsit to implement some interfaces. To use it in your own com class, you must do four things:

  • Derived iwhatsit and ctwhatsit;
  • Use declare_iwhatsit to declare the method;
  • Use implement_iwhatsit (cmycomclass, ctwhatsit) to implement these methods;
  • Add a few lines of corresponding code in cmycomclass: getinterfacehook. You can see that there is only five lines of code in total, which is so simple.

Implementation class

The previous discussion assumes that the ctwhatsit that implements the interface already exists! At least we know how to localize the interface implementation in a C ++ class -- in other words, we know how to reuse the interface implementation. To provide more convenient applications, comtoys has many built-in ready-made interface implementations. Ctpersist, ctpersiststream, ctolewindow, ctdockingwindow, ctinputobject, and so on. How do they work? The first strange thing about execution classes is that they are not derived from their corresponding interfaces! But only a class with the same name and signature method.

// Do not derive from iolewindow! Class ctolewindow {public: // same macro as the main class declare_iolewindow ();};

There is no real connection between interfaces and implementations; the so-called connection is completely vocabulary. This is another reason for using declare_ifoo: it ensures that the correct name and signature are obtained. The Orchestration in ctolewindow does not produce errors. Simply define a new function. Another thing to note about the mixed class is that the method implementation is only for the derived interface, rather than the inherited interface. Idockingwindow implements showdw, closedw, and resizeborderdw -- instead of getwindow or contextsensitivehelp, which are inherited from iolewindow. To implement idockingwindow, you must mix ctdockingwindow and ctolewindow. Similarly, ctpersiststream does not implement getclassid, which is inherited from ipersist. Why am I doing this? First, it blends and matches different implementation classes. Second, it is optional. For example, if I derived ctpersistfile and ctpersiststream from ctpersist, they all have the getclassid function (which I initially did ). Now, any class using ctpersistfile and ctpersiststream has two copies of getclassid, resulting in ambiguous multi-inheritance. In Figure 17, the multi-inheritance rule (MAGIC mi) is only used for pure virtual functions, rather than for specific functions. Therefore, all the multi-inheritance interfaces obtain the same specific functions. As you can imagine, although you want ipersistfile and ipersiststream to have different getclassid implementations, 99% of the time is not enough. In ctpersistfile and ctpersiststream, only the majority of derivative methods are implemented. comtoys can be performed once and only once to implement the desired ctpersist. After writing the following code:

 IMPLEMENT_IPersist(CMyComClass, CTPersist);      

The getclassid method is used to replace getclassid in ipersiststream and ipersistfile. Finally, I had to write some code for the language specification in these methods. Fortunately, it is not too troublesome. Only the ctxxx class corresponding to the bandobj conversion cost text previously implemented by Nested classes is converted into necessary variables and initialized in the constructor. For example, the ctpersist code is as follows:

 class CTPersist { public:   const CLSID& m_clsid;   CTPersist(const CLSID& clsid) : m_clsid(clsid) { }   STDMETHODIMP GetClassID(LPCLSID pClassID) {     return pClassID ?        (*pClassID=m_clsid, S_OK) : E_UNEXPECTED;   } };      

Ctpersist saves the reference of the class ID, not the class ID itself. This is the general principle of comtoys: The execution class does not store actual data, but only pointers or references to external objects. For ctpersist, it does not matter, because once the object class ID in the running changes, it will be at a loss. But in general, data can be changed, so it is wise to let the parent class own data, so that it can freely process the data. Both ctpersistfile and ctpersiststream have a m_bmodified modification flag, but it is a reference to the bool type, not the bool type. If the main class changes the actual flag, the execution class automatically changes and does not need to call functions such as setmodified. As a general programming rule, it is best to perform State operations in the On-demand mode (demand-pull) instead of the first-in method (supply-push ), in addition, each state variable of the entire system should have only one copy from start to end. Ctmfccontextmenu is similar. It stores references to only one cmenu, and the main class must be provided during construction.

// In comtoys. in the H file, class setting {public: c0000target * m_pcmdtarget; cmenu & m_ctxmenu; ctmfccontextmenu (c0000target * ptarg, cmenu & menu): m_pcmdtarget (ptarg), m_ctxmenu (menu ){}...... }; // In the bandobj. cpp file, cbandobj: cbandobj (refclsid CLSID): ctmfccontextmenu (this, m_menu ),...{...... }

Because ctmfccontextmenu: m_ctxmenu is a reference, cbandobj can change the menu in any way without explicitly notifying ctmfccontextmenu. Cbandobj depends on this feature because when you right-click a band, it generates its floating menu. 12 ). The implementation of ctmfccontextmenu basically copies the corresponding part of the code above.


Figure 12 cbandobj context menu

When the container calls icontextmenu: querycontextmenu to obtain the menu item, ctmfccontextmenu uses the expected cmenu item to fill in the menu, but not before the ccmdui object is created and sent through c1_target, therefore, the on_update_command_ui processor can mount the menu. Similarly, when the container calls invokecommand, ctmfccontextmenu sends this command to the on_command processor. Ctmfccontextmenu can even process the prompt string by searching for the string resource. In short, ctmfccontextmenu converts the com language to the MFC language. The rest is to give it a menu and Command target (usually the com class itself ). The processing of all commands is the same as that in the MFC application. You do not need to implement icontextmenu again-you can use comtoys to perform compaction. (To be continued)

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.