Write c ++ code with better reusability -- band object and comtoys (9)

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: class implementation
Part 8: Category factories, registration, and US weaknesses

The ninth part shows more features of comtoys and comparison with MFC.

More features of comtoys

In addition to the main functions, comtoys has some other features.

Smart pointer
The smart pointer of ATL is something I have to praise. If you haven't used smart pointers so far, let's end this age! I mean, stop worrying about addref and release. No matter how many exit paths your function has, smart pointers can ensure that all reference counts are correct. So that you do not have to worry about it. Brothers who have had such experiences know that it is a terrible nightmare:


Fig 19 Aaaaaah

For those guys who are moving back when they see the template, comtoys has a macro that hides the sharp arc:

#define DECLARE_SMARTPTR(ifacename) /
typedef CComQIPtr SP##ifacename;

Now you can write:

DECLARE_SMARTPTR(IPersist);      

To define a new type of spipersist. This type can be used wherever ipersist occurs. Spiunknown is a specialized derivative because it requires ccomptr instead of ccomqiptr. (There is no need to call QueryInterface for iunknown-each interface has ). The following code snippet is a typical example of using COM code that does not use smart pointers. By comparison, I believe that more explanation is not needed:

//////////////////////////////////////// ///////////////////////////////
// The simplified smart pointer code:
// A typical com function that is not implemented using a smart pointer. This approach is terrible when multiple exit paths exist.
//
//
Stdmethodimp cexplorerbar: setsite (iunknown * punksite)
{
// If a site is being held, release it.
If (m_psite ){
M_psite-> release ();
M_psite = NULL;
}
// If punksite is not null, a new site is being set.
If (punksite ){

......

// Get and keep the iinputobjectsite pointer.
If (succeeded (punksite-> QueryInterface (iid_iinputobjectsite,
(Lpvoid *) & m_psite ))){
Return s_ OK;
}
Return e_fail;
}
Return s_ OK;
}

//////////////////////////////////////// //////////////////////////////////////// /////
// Use a smart pointer to implement the same com function, from 12 lines of code to 2 lines.
// Because m_psite is declared as ccomqiptr , There is no need to call QueryInterface (iid_iinputobjectsite ),
// There is no need to call addref to add a new pointer or call release to release the old pointer. All Smart pointers
//
Stdmethodimp cbandobj: setsite (iunknown * punksite)
{
If (m_psite = punksite ){
......
}
Return punksite &&! M_psite? E_fail: s_ OK;
}

For more in-depth discussions about ATL smart pointers, refer to another topic article: "using ATL to create lightweight COM objects ". This article also describes rare cases where smart pointers can cause troubles. Multi-threaded comtoys cannot implement all COM objects. Its application is limited to shell extension. The thread model is a single-threaded apartment (STA). In this case, the thread security of class members is automatic, and only the global volume needs to be protected. A comtoys class with a static member (global volume) also has a critical part, g_mydata, which must be locked before being accessed. Ctlockdata, a small class of comtoys, can easily complete this lock. The constructor locks the critical part and the Destructor locks the key part. Therefore, you only need to use the following code:

 { // protected block of code
CTLockData lock(g_mydata);
// do your worst
……
}

With smart pointers, the advantage of ctlockdata is that you don't have to remember to unlock it, even if a function or code block has multiple exits. C ++ ensures that the Destructor will be called and unlocked upon exit of control. Ctfactory uses ctlockdata to access its global factory list.

Debugging and Diagnosis
The best way to understand the operation behavior of any COM object is to output the diagnostic information as long as the interface method is called. Without such a diagnosis, it is impossible to figure out how the band object works. Comtoys has a built-in tracking mechanism. The Implement _ iwhatsit macro generates trace statements at the beginning of each function (top), so you only need to turn on the trace switch (comtoys: btrace = true) to see what the object is doing. Comtoys uses the ctracefn class.
Comtoys can generate readable interface names. This diagnostic system uses an overload function called dbgname to obtain the "Debug name" of different objects ". Dbgname (wm_destroy) returns wm_destroy; dbgname (scode) returns values such as s_ OK or e_outofmemory; while dbgname (cwnd *) returns the window name.
As expected, dbgname (refiid) returns a readable guid, such as: {EB0FE172-1A3A-11D0-89B3-00A0C90A90AC}, but how do you know that this long string represents ideskband? There is a way to comtoys:

 DEBUG_BEGIN_INTERFACE_NAMES()
DEBUG_INTERFACE_NAME(IDeskBand)
DEBUG_END_INTERFACE_NAMES()

Now dbgname (iid_ideskband) returns ideskband, rather than the obscure hexadecimal system. This macro generates a local table that is linked to a global table that can be searched by dbgname. For details, see the Debug. h and Debug. cpp files in the source code.

Comtoys dynamic link library DLL
After downloading the source code of comtoys, You can compile it in six ways: static library that is statically linked to MFC, static library that is dynamically linked to MFC ), alternatively, you can use the DLL that is dynamically linked to the MFC (Extended DLL) to compile them in debug and release modes to form six versions.

Problems
Which system will be okay? There are many areas to improve, including comtoys. I only wrote the part required by the band object. Therefore, ctpersiststream and ctpersistfile do nothing substantive; they have only one modification sign. Like Microsoft's tricks in the bug documentation: When you cannot solve a bug, you should regard it as a feature. It is said: "This is the design behavior of cloud ......"
Another annoying issue with comtoys is macro debugging-sometimes you cannot track it in the debugger. Fortunately, I don't like the debugger anywhere, but I use my own tracking method.

Comtoys and MFC
Before summing up, I would like to say a few more words about the relationship between comtoys and MFC. Comtoys seems very dependent on MFC. I don't agree with this because I like MFC and are always willing to use it. However, some people think that MFC is a burden and a burden. I don't think we should fall into the debate between MFC and ATL, but I must point out that, in essence, the methods that comtoys integrates interfaces into object classes are independent of MFC, that is to say, it does not rely on MFC at this point.
When writing comtoys, I carefully noticed where MFC is needed and where it is not needed. Therefore, ctolewindow, ctdockingwindow, and ctinputobject use the handle (hwnd, haccel) to replace the MFC objects such as cwnd *, because they do not need MFC to pack anything. On the other hand, ctmfccontextmenu requires cmenu and c1_target because it completes all command routines. Similarly, the ctmfcmodule calls MFC to implement the standard DLL entry. When designing a class related to MFC, I usually include "MFC" in the name ".
In addition to class implementation such as ctmfccontextmenu, comtoys only needs to be supported by MFC in three places. Implement_iunknownct suppose a csf-target is derived from the main class to implement iunknown. Ctfactory does not implement iclassfactory as it should, so you need a coleobjectfactory derived class factory to create an object. Ctmodule does not implement module locking or dllgetclassobject. Therefore, you need ctmfcmodule to obtain these important details. To completely isolate comtoys from MFC, you only need to fill these gaps.
For iunknown, a basic ctunknown must be used to implement addref, release, and QueryInterface. Addref/release can very easily perform ++/-- operations on the data member m_dwref, while the QueryInterface call is theoretically equivalent to getinterfacehook. getinterfacehook must be provided by external classes. You can derive another implementation (called ctunknownmta), which replaces ++/-- with interlockedincrement and interlockeddecrement /--. With these implementations, your com class can use implement _ iunknown (cmycomclass, ctunknown) to replace the implement_iunknownct macro currently designed for csf-target. This is a bit like ATL -- but does not involve templates.
As for the class factory, iclassfactory must be implemented for the ctfactory. This implementation can call a virtual function with a name similar to oncreateobject. Each com class does not have to provide this virtual function by returning "New cmycomclass. (Coleobjectfactory uses the MFC runtime system to create objects .)
To hook up a class factory, you must implement the ctmodule: ondllgetclassobject. It searches for the factory list of the Search Class to find who's CLSID matches the request, and then calls its createinstance method.
There are two more things to do: First, write some code in the ctmodule to lock the module (ctmodule: ondllcanunloadnow); second, when obtaining dll_process_attach/detach. initialize or terminate this module in dllmain.
After you finish the preceding operations, you can use comtoys to compile COM objects. You can use comtoys to compile COM objects that tend to be ATL, or Use ATL to compile ATL objects. The comtoys model has two layers: The base layer is independent of MFC, and the High-level class uses MFC. It is not unreasonable for me to perform this layer. It is indeed necessary. If I have time, I will write another article on this topic.

Anecdotes from black hole terror
No project is successfully completed without twists and turns and hardships. The same is true for comtoys. It occurs when I test the registration code. I noticed that iregistrar did not completely delete the deleted items when resourceunregister was called. I guess it does this for security reasons. So I decided to add a few lines of code to delete hkcr/CLSID while canceling the class. I found that there is a ready-made function in MFC to do this: afxoleunregisterclass. This function has two parameters: one is the ID of a class and the other is the progid. So I added this line of code:

AfxOleUnregisterClass(m_clsid, GetProgID());      

To test my code, run regsvr32.exe/u, Run regedit to view the registry, and press F5 to refresh. The result is 20. No! Hkey_classes_root has been expanded. My entire hkey_classes_root is missing, and there is only a blank CLSID key.


Figure 20! Where is my registry key?

How can this happen? Regedit must have made a mistake. I quit regedit and re-Run regedit from the "Start" menu. A small dialog box is displayed. "This program references a nonexistent lnk file ...... ", This information seems indifferent, but in fact I am facing a severe situation. Something on my desktop is missing. Resource Manager missing? Is ie gone? Each icon is replaced with an inexplicable icon. I cannot even start the MS-DOS window. I was so angry that I almost screwed up my display. You have to restart the machine. Unfortunately, I was cheated. MFC deleted the entire registry. I looked down at the screen and wondered when the last backup was ......
Fortunately, I used a program to automatically back up the registry every day. But how can I run all the lnk files? Finally, I can go to the "run" window from the "Start" menu, and then tap command.com/#.exeto open the ms-doswindow. Go to the configsafe directory and run it to restore the registry to the previous day's configuration. I just breathed a sigh of relief.
This caused the system to be near the verge of collapse. Later, band objects cannot be inserted, and they do not need progid. The registry string of mybands does not have progid, so the ctfactory: getprogid naturally returns an empty cstring. Note that empty is empty, not null. I uploaded this empty string to the afxoleunregisterclass function, which contains the following code lines:

if (pszProgID != NULL)
_AfxRecursiveRegDeleteKey(HKEY_CLASSES_ROOT,
(LPTSTR)pszProgID);

I don't need to explain it further. The word "recursive" in the function name illustrates everything. MFC is as happy as a fool to delete everything under hkey_classes_root. Will Microsoft fix this problem! This experience proves that you must pay attention to the following points when operating the registry:

1. You must be cautious about this.
2. to process the registry key in the program, add some additional security check code for null values and empty strings.
3. If you do not use a program to back up the registry every day, it is not too late to back up the registry.
4. Do not trust MFC. Don't trust anyone. And if you have heart disease, don't be a programmer (joke ).

Summary
Whether or not you decide to use comtoys in your own program, I want you to at least realize that using ATL, MFC or any other system inherited from ATL/mfc is not absolute. It is important not to fall into any strange circle of the system, but to use them in your own way. I would like to regard comtoys as an MFC with multi-inheritance, or an ATL without a template.
I also hope that you can understand that it takes some time to build your own programming platform or infrastructure (comtoys includes about 2400 lines of code in the execution class ,) in this way, you can greatly facilitate your c ++ COM programming. Macros, smart pointers, and tracking-these simple tools are also helpful for programming, making programming easier. The final source code of the comtoys library cannot be listed here. You can download all the source code if necessary. In short, it is very easy to use comtoys to compile bandobj objects. You only need to combine some pre-prepared items and add them to your new features. Its purpose is to be reusable. It can be used not only in bandobj, but also in other applications. In the future, you can further implement the ipersiststream interface to expand the functionality. Comtoys is not perfect, and no system can do it. However, comtoys works well in practice, and I use it to create a band object and a browser to quickly browse files. In the future, I will continue to improve comtoys, so please pay attention to the latest development trends and source code.

Finally, I wish you a pleasant programming!

(Full text)

Related Article

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.