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
Summary:
Reusable classes are like small com blocks. People can assemble these com blocks in different ways to create more refined objects on different occasions. But how to create these com? This article uses the mybands and bandobj frameworks as the experimental platform to develop a reusable library: comtoys ....... Comtoys provides a method or path to compile COM in C ++. These methods and methods apply to any type of COM Object, whether you use MFC. Or something else. Comtoys is an attitude-It tells people that it is not difficult to write COM components in C ++, and that is indeed the case!
We have discussed a program named mybands. One of the functions of this program is to put the edit box control in the Windows taskbar. In addition, mybands implements three windows band objects, including the web search box, as shown in Figure 1:
Figure 1 web search box in the taskbar
To implement the mybands program, I have compiled a mini-band object framework: bandobj. Its implementation classes are cbandobjdll, cbandobjfactory, and cbandobj. This framework provides support for compiling various band objects. With this band object framework, you don't have to do too much work to develop band applications that meet your needs.
Cbandobj has many different interfaces for code implementation, such as ideskband, iolewindow, and icontextmenu. However, only one interface is related to the processing of band objects, that is, the ideskband interface. Other code has nothing to do with this. The implementation of icontextmenu in cbandobj does not involve anything about the band object; it only needs a menu. Iolewindow only requires a window handle. Other implementations are also the same. The cbandobj framework abstracts these interfaces as much as possible and encapsulates them in reusable classes-like small com blocks, you can assemble the COM in different ways on different occasions to create more refined objects. How can we create these small com blocks?
This article will show you how to use the mybands and bandobj frameworks as a test platform to develop a library. The source code of this library is the example program in this article: comtoys. Comtoys is not integrated with the system like ATL or MFC. It is a collection of more special macros, functions, classes, and things that I think can easily write bandobj. However, comtoys provides a method or method to compile com using C ++. It applies to any type of COM Object, whether you use MFC. Or something else. Comtoys is an attitude-It tells people that it is not difficult to write COM components in C ++, and that is indeed the case!
COM: C ++ programming dilemma
With COM, I have to sympathize with C ++ programmers who use Java and Visual Basic. You will see the proud smile of the programmer who wrote "form. color = Red", and the C ++ hackers are anxiously issuing commands-QueryInterface... Get ()... Set () -- and remind yourself not to forget to check hresult at any time! If you haven't called release for every addref, ask God to help you! Haha ...... In Visual Basic, you do not have to remember to enter a semicolon. Compared with Visual Basic and Java, C ++ is more powerful and more inclined. However, when com is involved, its complexity seems hopeless. How ironic is that com is designed for C ++! After all, the COM object is only a C ++ virtual table (vtbl ).
The problem is neither C ++ nor COM. But most C ++ programmers do not have enough knowledge about them. They cut and paste code from the SDK example-these long public code is only used to demonstrate the most primitive and unmasked programming methods, and never considered how to design the system. Those are not product code! I wonder why you call them examples? C ++ enables you to write heavyweight programs. But no one can endure that kind of programming abuse every day. If you want to make it easy, you can make it easy. As long as you spend some time building some tools, these tools will multiply your reward in the future. The reason why Visual Basic and Java are easy to use is that the infrastructure is built into the language system. But you can also write your own infrastructure to make C ++ easy to use.
The mybands program discussed earlier has two layers of structure, the bottom layer is the framework bandobj, and the above is the application of mybands. These two structures are created for demonstration purposes. In actual programming, I use a three-tier structure to build the bandobj framework, as shown in sixteen. The basis of this structure is provided by comtoys. We can see that only after removing comtoys can we focus on the band object. Now let's reveal this system.
Figure 16 architecture of mybands
When designing mybands/bandobj/comtoys, I must make a decision that many programmers have to face today: Which programming system is used to compile com? Is ATL used ?, MFC ?, Com ++ ?, Or none of the above? Although I often like to show off myself, I never refuse to use what others do, as long as it can work and be easy to use. So I consider using both ATL and MFC.
ATL is well-known and easy to use, but who can understand all terms about it and those sharp brackets? Do you really know how much code does ATL generate? If the code is so suitable for use in the template, why not replace it with a simple class or subroutine? That's all right. Templates Can parameterize many things, which is of course great. But I only want to open a grocery store, and there is no need or need to use a shuttle. More importantly, ATL lacks support for graphical user interfaces, and shell extensions such as band Objects Require GUI support. Then again, the ease of use of ATL, the temptation to inherit more models and smart pointers makes people unable to refuse, just like the ATL register used in the previous article, it makes me put it easy.
What about MFC? The temptation of its GUI is irresistible. Even if you don't need a document/view structure, it also has command processing routines and the on_command_update_ui processor-this is the root of any UI object. However, as long as COM is involved, MFC is inferior to many. Most people are worried about its elephant-sized DLL, but in my opinion, it is nothing remarkable. Good, mfc42.dll is indeed very fat, but without it, can windows be converted? It is a part of the OS, and its shadow is everywhere in windows. (As a matter of fact, you can see from the Windows 98 cab file that the bulky stuff is hidden in the win98_62.cab file, so it is part of windows .) Since the advent of COM, MFC has become increasingly problematic: the use of its Nested classes completely loses its personality. The problem will be described in detail later.
So what exactly is used? In the end, I used various directors to establish my own systems and use both MFC and ATL. This completes my work and makes full use of the advantages of MFC and ATL. The effect of mutual learning between MFC and ATL is achieved. Comtoys uses the MFC class factory and iunknown, but bypasses Nested classes. At the same time, we also used the smart pointers, registrars, and multi-inheritance of ATL to filter out heavy templates and confusing object models. This hybrid method is not only practical, but also fever (with your own code, you can always better understand the code ).
I am not saying how good comtoys is. I just want to provide a new idea and hope to inspire others. The main purpose is to demonstrate that you can compile your own COM Object creation platform without using MFC or ATL. It shows you how to build a basic structure framework and then use C ++ to easily program com-even easier than Visual Basic!
Rough Nested classes
This is an expert's comment. Let's look at the code. To explain how comtoys works, the first thing I need to do is to explain one of the problems that comtoys is designed to solve: avoiding Nested classes used by MFC. For this reason, let's briefly review the basic concepts of MFC/COM programming.
To write a com Using MFC, You Need To derive your own class from csf-target and use a macro to implement your own interface. For example, if your class implements ipersistfile, the following code must be written:
// In the header file, class mycomclass: Public c1_target {begin_interface_part (persistfile, ipersistfile) stdmethodimp getclassid (lpclsid pclsid); stdmethodimp isdirty (void );...... End_interface_part (persistfile )};
These macros declare a nested class: mycomclass: xpersistfile in the main class and declare an instance: m_xpersistfile. Mycomclass: xpersistfile implements ipersistfile methods, including methods inherited from iunknown. To let MFC know this interface, an interface ing must be created:
// In the. cpp file, begin_interface_map (cmycomclass, c0000target) interface_part (cmycomclass, iid_ipersistfile, persistfile )...... End_interface_map ()
This macro generates a detail table for each COM interface in your class. The IID of the table item storage interface of each detail table and the offset in the main class that implements its nesting
{ &IID_IPersistFile, offsetof(CMyComClass, m_xPersistFile) }
Once you declare and implement this interface ing, you must implement their methods, including addref, release, and QueryInterface. Because this class is nested, you must write iunknown for every interface supported by the COM object, even if the implementation is the same. For example:
// The codes of adref and release are the same. stdmethodimp cmycomclass: xpersistfile: QueryInterface (...) {method_prologue (cmycomclass, persistfile) return pthis-> externalqueryinterface (...);}
Method_prologue is essential in MFC to obtain the relationship between them (afx_manage_state is indispensable at any DLL entry point) and set pthis, it points to the parent class cmycomclass ("this" pointer address minus the nested offset ). Csf-target: externalqueryinterface searches for your interface ing to find iid entries that match the requested interface. If yes, the offset is added to the pointer and the result is returned. If everything is normal, it points to the nested object implementing the interface. This method works, but it is extremely troublesome. First, it is awkward to implement the iunknown of each interface. It opens the door to errors and becomes bloated due to copying unnecessary code. Second, you cannot directly access your class members from the interface methods; instead, you must use pthis to access them-this is simply an illusion; in concept, the interface methods belong to external classes, so why cannot I access members like other Members? Finally, the most annoying point is that the nested class method cannot overload interface methods in the derived class. Assume that you derive a new class, cmycomclass2, and you only want to reload ipersistfile: savecompleted to set a flag. The on_update_command_ui processor checks this flag and displays "Storage .... "Until the storage is completed. You don't have to do anything. Cmycomclass does not have the savecompleted function that can be reloaded. The savecompleted class is nested in it, cmycomclass: xpersistfile, there is no way to reload it. In addition to reloading the savecompleted, you must re-implement the entire ipersistfile interface in the derived class to create another nested class, which contains methods that do nothing. Just call the basic class method, cmycomclass: m_xpersistfile. I encountered this problem in the code in the previous article. When the container sets the scene, in order to let the cbandobj derived class do something, I have to provide a new virtual function, cbandobj: onsetsite, and call it from cbandobj: xbankband: setsite. What about other interface methods? Do I want to introduce something like onxxx for every interface method implemented by cbandobj? I don't want this! For these reasons, many c ++ programmers-including ATL code writers-use multi-inheritance for COM programming. Derive multiple com classes from each implemented interface.
class CMyComClass : public IPersistFile, public IContextMenu, ...{ // IUnknown STDMETHOD_(ULONG, AddRef)();…… // IPersistFile STDMETHODIMP GetClassID(LPCLSID pClsID);…… // IContextMenu STDMETHOD (QueryContextMenu)(...);……};
All methods belong to the primary class, so you can reload them in the usual way, and all methods can directly access class members; pthis is not required. Then, with the magic of C ++, you only need to implement addref, release, and QueryInterface once, and the same implementation applies to all iunknown instances. This is precisely because of the C ++ rule that "pure virtual functions are always redefined by implementing any of its subclasses. "All virtual tables (vtbs) will have an iunknown location, pointing to the same physical function at this location. This is also true for other interfaces that may be inherited by multiple elements. For example, ipersist inherits from ipersistfile and ipersiststream. As shown in figure 17, I'm going to call it the magic mi law.
Figure 17 multi-Inheritance
Since the inheritance is so good, why does MFC not need it? This is because confusion is caused when multiple inheritance occurs for a specific class, which has real functions and data. If you write x = m_foo, Will m_foo be inherited from A or B? Besides, MFC derives all its classes from cobject. Using multiple inheritance causes a terrible diamond hierarchy. Although the virtual base class can be used to overcome this deficiency, the problem will be worse. So the buddy who created the MFC cleverly decided to avoid multi-inheritance. (To be continued)