A Free Trial That Lets You Build Big!
Start building with 50+ products and up to 12 months usage for Elastic Compute Service
Http://www.allaboutprogram.com opened a new layout: Class Library discussion, hope to be able to explore the popular class library, not only can get the use of experience, but also for their future design class library to specify the direction. ATL (Active Template Library) is also one of the objects discussed on this page. It is a set of support libraries developed by Microsoft for com (Component Object Model, Component Object Model. Generally, it is of little significance to discuss the class library from the supported objects. I will write a simple article to introduce it. It is also a stepping stone for anyone who wants to learn COM, I just don't want to hit me. (Since everyone in this forum is familiar with C ++, I will make a lot of comparisons with C ++ and COM. That is to say, the readers I expect are at least familiar with C ++. Besides, although the differences between objects and interfaces in COM and the meanings in C ++ are different, I may mix them without causing confusion. In addition, I do not differentiate the versions of COM. That is to say, I use com to name COM and DCOM (Distributed COM) and COM + (an improved version of COM supported since Windows 2000 ).)
1. binary object standards
A. c ++ object Layout
As we all know, in C ++, a class can have member data and member functions. If I have a class and a function:
Myclass (INT, name );
String getname () const;
Void print (const myclass & OBJ)
Cout <obj. getname () <Endl;
ExperiencedProgramEveryone knows that if this class and function are compiled by VC, you should not try to use it in Borland C ++ builder. Let alone the Lib file format is different. Even if it is the same, can you ensure that the string definitions under the two compilers are the same? Can you ensure that the alignment mode during VC compilation is the same as your current alignment mode? Can you ensure that the heap used in VC is the same as that in BCB? Any subtle difference may cause problems, and it is Crash. Therefore, the object standard of C ++ is not binary,Source codeLevel. Even different C ++ compilers do not follow a standard, let alone interact with other languages (VB, Pascal.
B. Only vtable classes
In the C ++ class, a function is called a virtual function. The call of a virtual function is "dynamically bound", that is, it is determined during running. For exampleCode:
Virtual int GETID () const = 0;
Myinterface * P = getsomeobject ();
Cout <p-> GETID () <Endl;
The call of p-> GETID () in this Code may be similar to the following pseudo code:
Function_pointer * vtbl = (function_pointer *) P;
P-> vtbl ;
That is to say, when we call a function, we use an integer (subscript of the virtual function table) to uniquely determine this function. Besides, because we have no data members, this class may have only one pointer. The offset of this pointer does not need to be calculated, that is, 0; or, if the vtbl layout is the same and the call specifications for calling a function are the same, then we can share this object pointer between different compilers. To unify the layout and calling methods of vtbl, it is much easier to unify the layout of different compiler classes, the implementation of standard libraries, and various compilation parameters.
Com is a binary object standard proposed by the MS. It is the same vtbl layout we mentioned in B. Any language that can call functions through indirect pointers can implement or use COM objects. These languages include C/C ++, VB, Pascal, Java ,..., DHTML. In COM, class users see one "interface" after another, that is, only one vtbl class. The call method of each function is _ stdcall.
2. iunknown-life cycle, discovering new content and versions
A. Object Lifecycle
In OO systems, object ownership and life cycle are an eternal topic: this is because the relationship between objects is very complex in complicated oo systems, we do not want to see that an object does not exist when it is referenced, nor do we want a useless object to continue occupying system resources in the memory. C ++ proposes auto_ptr, shared_ptr,..., all of which aims to facilitate object lifecycle management. In systems without garbage collection, one of the common management methods is reference counting. When you need to use an object, add one to its reference count. When it is used up, subtract one from its reference count. Objects can maintain a counter internally or ignore the information (for objects on static/stack). That is, we use an object reference count to provide the same interface for managing different object lifecycles. The iunknown interface has two methods:
Ulong addref ();
Ulong release ();
The former can add one to the reference count of the interface, and the latter can reduce the reference count of the interface by one (please note that it is the reference count of the interface! This is different from the reference count of an object)
At the same time, using reference counting can also avoid a problem, that is, the creation form is different. For example, if you have a function in a DLL: string * getname ();. You call this function in EXE to obtain a pointer. After using this pointer, you may delete it, because in DLL, this pointer is created through new. However, this will probably crash, because your DLL and your EXE may use different heaps. In this way, the EXE Delete will often fail to find this pointer in its own heap.
B. Discover new content
In C ++, if we have a base class pointer, we can obtain the pointer of the derived class through forced type conversion to obtain more functions:
Class derived: public Base
Virtual void anothercall ();
Base * B = getbase ();
Derived * D = (derived *) B;
However, this is generally considered a very dangerous operation, because if you cannot confirm that this B actually points to a D, the subsequent use of D may bring about a program crash. C ++ provides a mechanism to add type information to classes and use dynamic_cast to obtain valid pointers. For various reasons (early compilers had poor support for dynamic_cast, or the addition of type information was often global, with a high overhead ,...) some class libraries, such as MFC, implement similar mechanisms. The implementation of such a mechanism is often based on maintaining a table within the class to know what "oneself" is and what classes are related to "oneself ,...; at the same time, it provides an interface for the outside world to query such information. To do this, you must solve two problems: How can I obtain this public interface and how can I identify a class. In standard C ++, we use the typeid operator to obtain a const type_info &. In MFC, because all classes that support dynamic type information are inherited from cobject and its subclass, we provide these methods in cobject. In this way, the first problem is solved for both of them. In the standard C ++, the dynamic information of a class is the type_info. You can compare it, determine it, or obtain a name with no definite meaning, but you cannot use it to do more. In MFC, a string is used to identify a class. You can use a string to dynamically obtain the type information of the class it represents. Because simple strings are very prone to conflicts, com uses a 128-bit random integer GUID (Global unique identifier). Such random integers are considered unlikely to conflict. Another method in iunknown is:
Hresult QueryInterface (refiid IID, void ** Interface );
This refiid is the unique identifier of an interface, and we also become the interface ID. You call QueryInterface for an interface pointer to ask for another interface. If it supports this interface, a pointer is returned, or an error is returned.
D. Some conventions
-Any COM interface must be derived from the iuknown interface
-the QueryInterface result of any interface must support self-inverse, pass-through, reversible, and persistent. That is to say:
to query an interface, it must be successful.
If You Want to query interface type B for interface a of type A and return the pointer B pointing to interface B, then you can ask B about a again.
if you ask interface type B for interface a of type A, and return the pointer B pointing to interface B, and the pointer C of Interface C from interface B is returned, then you must be able to successfully query the pointer from A to interface C. Note that the pointer to this C is not necessarily the same as the pointer to the previous C. (That is to say, this Convention only guarantees that the query is successful, but does not guarantee that the returned pointer is the same)
if you have successfully asked interface B for interface, in the future, each query will be successful.
-if you want to upload an interface to a function, you must ensure that this interface is valid before the function returns. If a function returns an interface pointer, it must be addref before the return. Similarly, if you get an interface pointer from a function, you do not need to add it again, it should be release.
-in particular, according to the previous one, after you call QueryInterface to an interface, this interface has been previously returned by addref.
-to query another interface multiple times with the same interface pointer, the pointer returned each time is not necessarily the same. However, if the interface ID you query is iid_iunknown, the pointer returned each time is the same. This statement is equivalent to that the pointer to iunknown is a COM Object Identifier. That is to say, the only common method to compare whether two interfaces belong to the same object is to compare the iunknown interface from the query. In addition, although each COM interface is derived from iunknown, you cannot simply assign an interface pointer to an iunknown pointer to obtain a class ID, although this is legal in C ++.
-The reference count is based on the interface, so this Code may be faulty. iinterface1 * P1 = getobj ();
Iinterface2 * P2;
P1-> QueryInterface (iid_interface2, (lpvoid *) & p2); // assume the operation is successful.
P1-> release ();
P1-> func (); // at this time, P1 may be invalid !! Although this object is valid
P2-> release ();
IDL is the interface definition language ). As we mentioned earlier, all functions of COM are presented through interfaces. As a binary standard, we need a common method to describe interfaces. Although C ++ is powerful, it is not suitable for this purpose. First, it is too complicated. Second, many of its data types are not necessarily supported by other languages. We need a neutral language to define the data exchange of the entire interface (we don't need it to define the interface function, because it depends on the specific implementation .) This is the language of IDL. You can use IDL to describe the attributes of a COM object or a COM interface: What parameters are accepted by this function, the flow direction of each parameter ,.... Another advantage of IDL over C ++ is that it is relatively simple and can be processed by tools. The generated binary information can be used to fully describe the external features of your interface.
In fact, in other orb systems, IDL is usually used as the original definition language of the interface. For example, in CORBA, you first use IDL to describe the interface, then use some programs to convert it into a C ++ definition, and continue to implement the interface function in this c ++ definition, this is called IDL ing from IDL to C ++ ). The MS's midl compiler can do the same, but not conceptually. In COM, you directly implement an interface using C ++ without IDL, which is a legal action and a common action at the beginning. In CORBA, you write a C ++ ing by yourself, it is considered a clever behavior (of course, people familiar with it can do this ).
For specific definitions and usage of IDL, see msdn.
4. Location independence
In the com book I have read, I always put this part in a later explanation. But I tend to advance it for two reasons: Not difficult (for experienced C ++ programmers), useful (for understanding the content below ).
A. a c ++ Model
Suppose I have a C ++ class, which is probably like this: class local
Virtual void encrypt (char * Str) = 0; // encrypt a string
My customers use it like this:
Local * P = getglobalencryptor ();
P-> encrypt (password );
This code is very simple. If my code and Customer Code are in the same module, there is almost no problem. However, if we need to put the encryption method on a server one day (the encryption code is put on the client, it is difficult to ensure that there is no assembler master to read the code ), then this program will be modified. Of course, we hope that the program will be modified as little as possible. The general method is as follows:
First, we need to write another EXE, which runs on the server. It loads the DLL and listens to a specific TCP port. If there is a connection, it first reads the length of a four-byte string, then reads the entire string, then calls the function in the DLL, and then returns the result in the form of four-Byte Length + content, send it back through this connection.
Second, we do some tricks on getglobalencryptor. We return a "fake" object. After receiving a string parameter, this parameter is sent to the specific port on the server using the method described above. In this way, our Customer Code does not need to be changed, and the same functions can be implemented. People familiar with the design pattern may think of it as a proxy pattern.
The key to obtaining "location" independence in this way is that we use two additional objects, an interface required to "simulate" on the client, A real interface is called by using the information sent from the client on the server side. It is not very important to know how to pass the call Information. If the situation is simple, that is, our implementation code and Customer Code are in two processes on the same machine, then we can complete this communication in other ways, such as Windows messages; we can even use named pipes, NETBIOS, and even files to exchange this information.
Proxy and stub in B. com
Com is a distributed object system that spans processes and networks. Therefore, objects and its users cannot be restricted in the same process space. Therefore, com provides a mechanism called proxy/stub. We call this simulated object on the client side a proxy and call the handler on the server side a stub. You can implement these two programs by yourself, but the more direct method is that if the parameters and return values of all our functions can be recognized by the Basic COM Service, it can automatically create this proxy based on this information. You can use midl to compile your IDL file. Midl can create the source code of these proxies/stub, And you can compile the corresponding DLL. You can also directly create a binary type description file: Type Library, com can read this file to generate proxy/stub. In COM, the mechanism for connecting remote objects through proxy and stub is called scheduling ).
If you implement your own marshal code, you can implement the imarshal interface on your own objects. When you create an object remotely, the stub code will ask your object for this interface and call the marshalinterface method for each required interface. This method will pass in an istream interface pointer, you can set interface-related data (for example, server name, listener port ,...) when this stream is written, the COM Service will pass the content of this stream to the client. The stub code will also call getunexternalclass to obtain a CLSID, which will be used to create a COM Object locally, and the COM object must implement the imarshal interface. The content of the stream will be passed to this local interface to create a proxy interface.
Because the DLL itself cannot be executed independently, COM + provides a service called surrogate, which is an executable file that can load your DLL and use the objects in it. In this way, you can not only access the objects implemented in the DLL on a remote machine, but also put the local DLL in the proxy process for better security.
-The marshal support for basic com interfaces has been implemented, so you do not have to provide your own implementations.
-Com not only uses proxy/stub for remote independence: proxy/stub is also used for thread independence, this will be discussed in detail later on the com thread mode.
-The proxy/stub code is based on interfaces rather than objects.
-The return value of the COM method that requires remote execution usually needs to be hresult (in fact, it is recommended that any COM method use this as the return value ). This is because when communication between the client and the service machine fails, the proxy object can tell you this fact by returning some hresults that identify remote service failure. Otherwise, if your return value is void, there is no way for the proxy object to notify you of problems on the remote server.
5. Create or find an object
The so-called OO system cannot be separated from the creation of objects. How do we create objects in C ++? Below are some common methods:
Class derived: public Base
Virtual derived * clone () const;
Base * b1 = new derived (); // explicitly create an object
Base * b2 = B1-> clone (); // returns a pointer to an object through a member function (either created or obtained in another way)
Base * B3 = Createobject ("base"); // returns a pointer to an object through a global function, which may require some information.
To create an object explicitly, you need to know the Class Identifier. If you call a function to create an object, you may not need to provide the identifier. In COM, it is usually difficult to use the concept of object creation, because we usually care about: How do I obtain the first interface pointer of an object, because after obtaining this pointer, I can use QueryInterface to obtain other interfaces of interest. COM does not provide a method similar to new to create an object. Generally, we use a factory object to explicitly create a new object. (Note that although we call it "create object" here ", but what you actually get is a valid interface. Although most of the time you use iclassfactory, some objects are created, but in some cases, no objects are created .).
What is an object factory? In a broad sense, an object factory refers to a class. One or more methods of this class can create an object based on your needs (or return a valid object pointer to you, in a narrow sense, in the com world, we usually refer to an object that implements the iclassfactory interface. Generally, we call a function to obtain the pointer of the object Factory: cogetclassobject. Its prototype is as follows:
Stdapi cogetclassobject (refclsid rclsid, DWORD dwclscontext, coserverinfo * pserverinfo, refiid riid, lpvoid * bp );
Like the interface ID mentioned above, rclsid is a guid, which is called a class ID ). Here, the reason for using a guid is the same as that for using a guid for interface identifiers. It is also because the string names are prone to conflicts. We don't worry about dwclscontext and pserverinfo at present. Riid is an interface ID. Because the object you create must be returned to you in the form of an interface pointer, we usually use iid_ifactory to obtain an object factory. Then, the created pointer is identified by the BPPV.
Why is this function called cogetclassobject instead of cogetclassfactory? Classobject refers to the static part of a class in the OO field. For example, if you have some static methods in a C ++ class, you can put these static methods and static data into an independent object, this object is the class object of this class. Com, so classobject is used. Iclassfactory is an interface that is usually implemented by classobject, but classobject does not support this interface. Of course, we usually implement (or even only implement) The iclassfactory interface when implementing our own classobject, because this is one of the standard class objects supported by com basic services.
After you get the pointer to iclassfactory, you can call its createinstance to create an object you want.
In COM, many other methods are provided to obtain an interface pointer. For example, calling a global function, such as cogetmalloc, or calling an interface pointer to obtain another interface pointer, the typical one is QueryInterface. Among them, moniker is the most important. (Expand ?)
COM Service obtains information about CLSID to the object implementation location through information in the registry. When you pass a CLSID to cogetclassobject, the COM Service searches for the corresponding key under hkey_classes_rootclsid. If this key defines the inprocserver32 sub-key, the object is defined in a DLL. The default value of this key is the complete file name of the DLL. If localserver32 is defined, the object is defined in an EXE. Each class in Com also has a "readable" name, called progid. You can find many keys similar to XXXXX. yyyyy. N under hkey_classes_root. These keys have a CLSID sub-key that uniquely represents the corresponding clsid. You can use clsidfromprogid and progidfromclsid to convert the two.
After the com service loads a DLL, it will call the dllgetclassobject exported by the DLL to obtain the class object corresponding to the CLSID. When the com service loads an EXE, it will use the/embedding parameter to run this exe. This EXE should call coregisterclassobject to register classobject, of course, in this case, the final iclassfactory pointer obtained by the customer is often replaced by Marshal.
6. handle errors
In any system, the return value is always a common form of error handling. The return value of many functions in COM is an hresult. Com is a cross-platform, cross-language architecture that requires more meanings of the returned values and can be easily defined by users. Hresult has a total of 32 bits. The highest bits are severity, indicating whether the operation is successful. The next two bits are retained, and the next 13 BITs are facility indicating that the status (not just an error) is generated. The last 16 digits are the status code. For example, hresult_from_win32 converts a Win32 error value to hresult. Therefore, when the value is 0, hresult is 0 (the highest bit is 0, indicating success ). Otherwise, it sets the highest value to 1 and facility to facility_win32, and then sets the status value to (error code & 0 xFFFF ).
If you want to return some error messages to your interface methods, you can use facility_itf. ITF indicates the interface, that is, the error message customized by the interface. Other Facility options include facility_null, facility_rpc ,...
In many cases, a simple return value seems pale. For example, opening a file fails to return a value, but which file fails to be opened? A 32-bit integer cannot express such information. In C ++, we can use an exception class to provide more error information. The similar mechanism in COM is ierrorinfo. For the customer, it can query isupporterrorinfo for an interface and call the interfacesupporterrorinfo method of isupporterrorinfo to determine whether an interface of this object supports ierrorinfo. If an interface supports ierrorinfo, in addition, if you call the method to return an error hresult, you can call geterrorinfo to obtain an ierrorinfo, and call the method of this interface pointer to obtain detailed error information. For the server side, it needs to implement isupporterrorinfo. In case of an error, it calls createerrorinfo to create an icreateerrorinfo interface pointer and complete the relevant settings.
Multithreading will always cause extra troubles to your program. Windows, as a multithreading system, naturally cannot refuse to use multithreading in COM. Suppose we use a library function provided by someone else in C ++:
Int dosomecriticalthing (INT );
What should we do if our program is multi-threaded and every thread needs to call this function? First of all, check the document of this function to see if it is thread-safe. If it is thread-safe, you do not need to consider anything and call it directly. If it is not thread-safe, we need to make some synchronization when calling it. For example, we set a global critical section. Each time we call the forward-to-the-critical section, we call this function and return the result before exiting the critical section. Of course, another method is to use the existing message mechanism in windows. I create a message thread, which receives messages and calls this function according to the Message parameters, and return the result to the caller (also through the message, of course ). The caller who calls this function sends the parameter of this function to that thread through a message (is it a bit like Marshal ?) Even more extreme, the implementer of this function does not know that multithreading exists in the world, so the function he writes can only run in the main thread of the program. What should I do at this time? Only by turning the main thread into a message thread, other threads indirectly call this function by sending messages to the main thread.
Let's look at another situation. If our program is a single-threaded program and the function to be called supports multithreading, can it be called directly? Not always! This function can be used, but not necessarily for the following function.
Typedef int (* callback) (INT );
Int dosomecriticalthingandcallmeback (INT, callback );
In this function, we must pass it a callback function. Our callback function may be thread insecure. Since this function is multi-threaded, it may implement a thread pool internally, and assign this job to other threads for execution. If it calls this callback function in that thread, it is difficult to ensure that the synchronization problem does not occur, so it also needs to be processed in the above special way.
Com, each object belongs to one apartment, and each suite can be composed of zero, one or more threads. Com supports four suite modes: STA (single threaded apartment, one thread), MTA (multiple threaded apartment, multiple threads), Na (neutral apartment, 0 threads ), main Sta. Sta is the single thread and message scheduling method we mentioned earlier. MTA is the multi-thread method. Neutral apartment is similar to the critical section method we mentioned earlier, main sta refers to the sta that must be run in the main thread. We can mark an object to tell the system how it supports multithreading. com supports five marking methods: STA, main STA, MTA, both, and Na, both means that an object can either survive in the sta or in the MTA. Each thread enters a suite when calling coinitializeex according to different parameters. A process can have multiple Stas, but only one MTA. Object calls between different suites may require marshal.
8. data storage and transmission
One difficulty of the Object System is persistence. Almost every OO framework implements its own persistence mechanism. In C ++, we usually use some specific class functions for persistence, such as operator <and operator>, but in multiple root systems, it is usually difficult to provide a global persistence mechanism. Com implements persistence through a series of ipersist * Mechanisms (ipersiststream, ipersistfile, ipersiststorage, ipersiststream. After you obtain an interface, you can query one of ipersist * based on your needs. If the query is successful, you can call the corresponding functions to write or read persistent data. By combining the istorage and istream interfaces above, we can implement an efficient and elegant persistence system.
In C ++, reuse is often an inheritance and combination. These reuse methods can also be used in COM programs, provided that you use C ++ for development and can access all source code. We call this type of reuse source code reuse. We can also use some binary reuse methods in COM: containment/delegation and aggregation ).
The so-called include is very easy to understand. It means that your object contains an interface pointer pointing to another object. You can call the method of that interface to implement some or all of your own functions. This is almost the same as the combination in C ++. The difference is that the combination in C ++ can completely include another object inside its own object, in COM, you can only include the pointer of another interface.
Aggregation is an interesting technique because it can completely use the implementation of another object to replace an interface that needs to be implemented. Assume that the object we want to implement is outer. The interface we need to implement is iPrint, and an object named inner happens to have this interface and can be used directly for us. To achieve this, we need the following implementation: first, when we create an outer, we call cocreateinstance to create an inner object and keep the object pointer in the data member; secondly, in our QueryInterface, whenever the customer needs the iPrint interface, we pass this request to the QueryInterface of the inner object. At first glance, users can easily obtain reuse in this way, but this violates several basic principles of COM. First, although I can get an iPrint pointer from the outer object, if I query the interface implemented by an outer object for this iPrint, it will return an error, because the inner object does not implement this interface. In addition, if I query iunknown on this iPrint interface, the pointer returned by this interface is different from the iunknown pointer that I directly query on an interface of the outer object. In this way, it violates the object marking principle of COM. The key to solving this problem is that we must let inner know that it is being aggregated by outer. com already provides us with this mechanism. Both cocreateinstance and createinstance methods of iclassfactory have a parameter called iunknownouter. When outer creates an inner, it passes in a non-null value (its own iunknown pointer) for this parameter, and the inner knows that it will be aggregated by this iunknownouter. At this time, the inner needs to save the iunknownouter pointer, and then return its real iunknown pointer to the outer. In the future, every time the outside world calls addref/release/QueryInterface to the iPrint interface, simply pass these requests to iunknownouter (if you are familiar with traditional opera, you should know that these are the dual springs that two objects sing together). This ensures the transmission of QueryInterface, reversible and the unique identifier of iunknown on an object. For outer, it retains the iunknowninner returned during the creation and passes the query on the iPrint interface to it. Note the following when implementing aggregation:
-After the inner obtains the outer iunknown pointer, do not addref it. Otherwise, the two objects will be referenced cyclically and never be released.
-When creating an inner for outer, only the iunknown pointer must be returned. Otherwise, the creation fails. Because if you get pointers from other interfaces, you will not be able to implement the functions we mentioned above.
-The QueryInterface of outer cannot transfer all queries to iunknowninner unconditionally, but can only pass the required interfaces. Otherwise, inner may provide some unpredictable interfaces to outer customers.
11. Registration Information
12. c ++ and COM
Some C ++ Programmers think that some mechanisms of COM are incompatible with C ++ and regard this as one of the reasons why COM is not good. Common complaints are: I am used to exceptions, and the error handling in COM is not as complex as exceptions. c ++ inheritance is more convenient. I reuse it in COM, you also need to write the aggregate code yourself ;.... In fact, when we use C ++ to implement or use COM objects, we should review our c ++ level, or review the C ++ language (if there is a function, no one can easily implement it ). Because COM is a technology, a standard, and C ++ is a common language, if none of us can correctly use the C ++ mechanism and OO idea to make a perfect description of COM, it would be a deficiency in the C ++ language. In fact, it is very convenient to use C ++ to create/use COM objects. For example, you can write an exception Conversion Function from com hresult and ierrorinfo to C ++. You can also write a function that uses try/catch to set ierrorinfo; you can implement a set of C ++ classes to easily create aggregated objects. You can also implement the smart PTR of the COM interface to ensure that every interface is released ;.... In fact, Visual C ++ provides ATL and some com support classes to implement most of these features, we can simplify the com development work by creating/extending/using such class libraries.
When I introduced com to some C ++ programmers, they always asked me one sentence: these functions are not difficult. Why not implement them by myself? In fact, I often ask myself this question when I read COM, especially when you see MTS, Object pool, DCOM, lce, and other so-called "advanced functions" of COM, you will think that the implementation and thinking behind each function are very basic. My answer to this question is: Since Microsoft provides an efficient, mature, and error-free basic service (relative to your own implementation), why don't we use it? C ++ programmers often lack security when compared with programmers who use other languages. If we do not know what the specific implementation behind a thing is, we may not use it. So for most C ++ programmers, we will do one thing before using a new class library: Read the source code, so that when we use the function of this class library, we can know exactly what each sentence we write has done. I am not making any comments on this kind of habit (I am also one of C ++ programmers), so I hope this article will let everyone know exactly what the basic COM Service is doing, have a rough understanding.
In fact, I think COM is divided into two parts: one is the com standard and the other is the basic service of COM. These two parts complement each other and bring many benefits to people who implement oo systems. I have met many experts who think that com is not worth mentioning. I have also met many beginners who feel that com is outdated. In any case, COM, as a basic OO system, is very meaningful to design its own OO system.
You are welcome to discuss the details in detail. If a friend who is familiar with COM thinks that I have missed important knowledge points, please let me know and try to add them to the next version.
Start building with 50+ products and up to 12 months usage for Elastic Compute Service