[Tips] the first part of COM programming-What is com and how to use com

Source: Internet
Author: User

Since: http://www.vckbase.com/document/viewdoc? Id = 212


The purpose of this article is to provide a programming guide for programmers who have just come into contact with COM and help them understand the basic concepts of COM. The content includes the com standard introduction, important com terms, and how to reuse existing COM components. This article does not include how to compile your own COM Object and interface.
Com is the component object model. It is the abbreviation of the first three letters of the Component Object Model. These three letters are everywhere in today's Windows world. A large number of new technologies emerging at any time are based on COM. Various documents are also filled with terms such as COM objects, interfaces, and servers. Therefore, a programmer should not only master the method of using COM, but also be completely familiar with everything about com.
This article describes the internal operating mechanism of COM in a simple way and teaches you how to use the COM Object provided by a third party (taking the Windows Shell component shell as an example ). After reading this article, you will be able to learn how to use the built-in components in the Windows operating system and the COM objects provided by third parties.
This document assumes that you are proficient in the C ++ language. In the example code, we use a bit of MFC and ATL. If you are not familiar with MFC and ATL, this article will thoroughly explain these codes. This article includes the following parts:

  • Com -- what is it? -- Main Points of the COM standard, which is designed to solve what problems
  • Definition of basic elements-com terms and their meanings
  • Use and process COM objects-how to create, use, and destroy COM objects
  • Basic interface-describe the basic iunknown interface and its method
  • Understanding how to process strings-how to process strings in COM code
  • Use COM technology-Example code to illustrate all concepts discussed in this article
  • Handle HRESULT--HRESULT type descriptions, how to monitor errors and successful code

Com -- what is it?

In short, COM is a way to share binary code across applications and languages. Unlike C ++, it advocates source code reuse. ATL is a good example. Code-Level Reuse is good, but can only be used in C ++. It also brings about the possibility of name conflicts, not to mention the constant copying and reuse of code, resulting in Project expansion and bloated.
Windows uses DLLs to share code at the binary level. This is also the key to running Windows programs-reusing kernel32.dll and user32.dll. However, DLLs is written for C interfaces and can only be used by C or a language that understands C call specifications. The programming language is responsible for implementing shared code, rather than the DLLs itself. In this case, the use of DLLs is restricted.
MFC introduces another MFC extension DLLs binary sharing mechanism. However, its use is still limited-it can only be used in the MFC program.
Com solves these problems by defining binary standards. That is, com explicitly states that the binary modules (DLLs and exes) must be compiled to match the specified structure. This standard also specifies exactly how to organize COM objects in the memory. The binary standards defined by COM must also be independent of any programming language (such as naming modification in C ++ ). Once these conditions are met, these modules can be easily accessed from any programming language. The binary code generated by the compiler is compatible with the standard. This makes it easier for later users to use the binary code.
In memory, this standard form of COM objects is occasionally used in C ++ virtual functions, so this is why many com codes use C ++. But remember, the language used to write the module is irrelevant because the result binary code is available in all languages.
In addition, COM is not unique to Win32. Theoretically, it can be transplanted to Unix or other operating systems. However, it seems that I have never heard of COM outside of windows.

Definition of basic elements

Let's look at it from the bottom up. An interface is just a group of functions. These functions are called methods. The interface name starts with an uppercase I, for example, ishelllink in C ++. The interface is designed as an abstract base class with only pure virtual functions.
Interfaces can be inherited from other interfaces. The principle of inheritance mentioned here is like single inheritance in C ++. Interfaces cannot be inherited.
Coclass (Component Object Class for short-Component Object Class) is included in DLL or EXE and contains the code of one or more interfaces. The Component Object Class (coclasss) implements these interfaces. COM objects are represented as an instance of the Component Object Class (coclasss) in the memory. Note that the COM "class" and C ++ "class" are different, although the com class is often implemented as a C ++ class.

The COM server is a binary (DLL or EXE) that contains one or more coclasses ).

Registry is a process of creating a registry portal, which tells the Windows operating system where the COM server is located. Unregistry is the opposite-delete these registration entries from the registry.

GUID (the homophone is "fluid", which indicates that the unique identifier-globally unique identifier) is a 128-bit number. It is an identifier independent of the COM programming language. Each interface and coclass have a guid. Because every GUID is globally unique, it avoids name conflicts (as long as you use com APIs to create them ). Sometimes you may encounter another term UUID (which is also the world's Unique Identifier-universally unique identifier ). Uuids and guids have the same purpose in actual use.

The class ID or CLSID is the guid named coclass. The Interface ID or IID is the guid of the named interface.

There are two reasons for the widespread use of guid in COM:

  1. Guids is just a simple number and can be processed by any programming language;
  2. Guids can be created by anyone on any machine. Once it is created, it is unique. Therefore, com developers can create their own unique guids instead of conflicting with the guids created by other developers. This eliminates the need for centralized authorization to publish guids.

Hresult is an integer used by com to return errors and successful code. In addition, it does not mean that, although prefixed with h, there is no handle. We will discuss it more here.
Finally, the com Library is part of the operating system that interacts with you when you use Com. It often refers to com itself. However, they are described separately to avoid confusion.

Use and process COM objects

Each language has its own way of processing objects. For example, C ++ creates an object in the stack or uses new to dynamically allocate objects. Because COM must be independent of the language, the com Library provides its own object management routines. The following is a comparison between COM Object Management and C ++ object management:

Create a new object

In C ++, use the new operator or create an object in the stack.

Delete object

In C ++, use the delete operator or kick the stack object out.
Com, all objects keep their own reference count. The caller must notify the object when it is used up. When the reference count is zero, the COM object will be released from the memory.
It can be seen that two phases of object processing are indispensable: creation and destruction. When creating a COM Object, notify the com library of the interface used. If this object is successfully created, the com library returns the pointer to the requested interface. Then, call the method using this pointer, just like using a conventional C ++ object pointer.

Create a COM Object

To create a COM Object and obtain an interface from this object, you must call the API function of the COM library, cocreateinstance (). The prototype is as follows:

HRESULT CoCreateInstance (    REFCLSID  rclsid,    LPUNKNOWN pUnkOuter,    DWORD     dwClsContext,    REFIID    riid,    LPVOID*   ppv );

The following describes the parameters:

Rclsid: CLSID of coclass. For example, you can pass clsid_shelllink to create a COM object to create a shortcut. Punkouter: this parameter is only used for the aggregation of COM objects. It is used to add a new method to the existing coclass. If the parameter value is null, aggregation is not used. Dwclscontext: indicates the type of the COM server used. This article uses the simplest COM server, an in-process DLL, so the passed parameter value is clsctx_inproc_server. Note that do not use clsctx_all here (in ATL, It is the default value), because failure may occur on Windows 95 without DCOM installed. Riid: The iid of the request interface. For example, you can pass iid_ishelllink to obtain the ishelllink interface pointer. BPPV: the address of the interface pointer. Com library returns the requested interface through this parameter.

When you call cocreateinstance (), it is responsible for finding the location of the COM server in the registry, loading the server to the memory, and creating the coclass instance you requested. The following is a call example: Create an instance of the clsid_shelllink object and request a pointer to the ishelllink interface of this object.

Hresult hr; ishelllink * pisl; HR = cocreateinstance (clsid_shelllink, // coclass CLSID null, // It is not used to aggregate clsctx_inproc_server, // The server type iid_ishelllink, // api iid (void **) & pisl); // If (succeeded (HR )) {// call method with pisl} else {// COM object cannot be created, HR is error code}

First, declare an hresult and ishelllink pointer that accept the return value of cocreateinstance. Call cocreateinstance () to create a new COM object. If HR receives a successful code, the succeeded macro returns true; otherwise, false. Failed is a macro corresponding to succeeded to check the failure code.

Delete COM Object

As mentioned above, you don't need to release COM objects, just tell them that you have used up objects. Iunknown is a required interface for every COM object. It has a method called release (). Call this method to notify the COM object that you no longer need the object. Once this method is called, you cannot use this interface again, because the COM object may disappear from the memory.
If your application uses many different COM objects, it is very important to call release () after an interface is used up. If you do not release the interface, the COM Object (DLLs containing code) will be kept in the memory, which will increase unnecessary overhead. If your application is running for a long time, you should call the cofreeunusedlibraries () API when the application is idle. This API will Uninstall any COM server without obvious reference, which also reduces the memory overhead used by the application.
Continue to use the following example to illustrate how to use release ():

// Create a COM object as above, and then, if (succeeded (HR )) {// call the method with pisl // notify the COM Object not to use it pisl-> release ();}

Next we will discuss in detail the iunknown interface.

Basic interface-iunknown

Each COM interface is derived from iunknown. This name is a bit misleading and does not mean an unknown interface. Its original intention is that if there is an iunknown pointer pointing to a COM object, you do not need to know what the potential object is, because each COM Object implements iunknown.

Iunknown has three methods:

  • Addref () -- notifies the COM object to add its reference count. If you make a copy of the interface pointer, you must call this method once and use both the original value and the copied value. The addref () method is not used in the example in this article;
  • Release () -- notifies the COM object to reduce its reference count. See the previous release () sample code snippet;
  • QueryInterface () -- Request an interface pointer from a COM object. This method is required when coclass implements more than one interface;

We have seen the use of release (), but how to use QueryInterface? When you use cocreateinstance () to create an object, you get a returned interface pointer. If the COM Object implements more than one interface (excluding iunknown), you must use the QueryInterface () method to obtain any additional interface pointers you need. The prototype of QueryInterface () is as follows:

HRESULT IUnknown::QueryInterface (    REFIID iid,    void** ppv );

The following describes the parameters:

IID: The IID of the requested API. BPPV: the address of the interface pointer. QueryInterface () returns this interface when it succeeds.

Let's continue with the shell link example. It implements the ishelllink and ipersistfile interfaces. If you already have an ishelllink pointer, pisl, you can request the ipersistfile interface from the COM Object:

HRESULT hr;IPersistFile* pIPF;hr = pISL->QueryInterface ( IID_IPersistFile, (void**) &pIPF );

Then use the succeeded macro to check the HR value to determine the call status of QueryInterface (). If the call succeeds, you can use the new interface pointer and pipf as you would with other interface pointers. But remember to call pipf-> release () to notify com that the interface has been used up.

Handle strings carefully

This part will take some time to discuss how to process strings in COM code. If you are familiar with Unicode and ANSI and know how to convert them, you can skip this part; otherwise, you can read this part.
At any time, as long as the com method returns a string, this string is a unicode string (this refers to all the methods written to the com standard ). Unicode is a character delimiter set, which is similar to ASCII, but represents a character in two bytes. If you want to better control or operate the string, convert it to a tchar string.
Tchar and functions starting with _ T (such as _ tcscpy () are designed to allow you to process Unicode and ANSI strings with the same source code. In most cases, the code is used to process ANSI strings and ANSI windowsapis. Therefore, unless otherwise stated, all the characters/strings I mentioned refer to the tchar type. You should be familiar with the tchar type, especially when you read the code written by others, pay special attention to the tchar type.
When you return a unicode string from a com method, you can convert it to a char string using one of the following methods:

  1. Call the widechartomultibyte () API;
  2. Call the CRT function wcstombs ();
  3. Use the cstring constructor or assign values (only for MFC );
  4. Use ATL String Conversion macro;

You can use widechartomultibyte () to convert a unicode string into an ANSI string. The prototype of this function is as follows:

int WideCharToMultiByte (    UINT    CodePage,    DWORD   dwFlags,    LPCWSTR lpWideCharStr,    int     cchWideChar,    LPSTR   lpMultiByteStr,    int     cbMultiByte,    LPCSTR  lpDefaultChar,    LPBOOL  lpUsedDefaultChar );

The following describes the parameters:

  • CodePage: The code page to which Unicode characters are converted. You can pass cp_acp to use the current ANSI code page. The code page contains 256 character sets. The character 0--127 is the same as the ANSI encoding. The 128--255 character is different from the ANSI character. It can contain graphical characters or pronunciation symbols. Each language or region has its own code page, so using the correct code page is important for correctly displaying accent characters.
  • Dwflags: Dwflags determines how Windows processes the "composite" UNICODE character. It is a character with a pronunciation symbol.
    RUE is a composite character. If these characters are in the code page specified by the codePage parameter, nothing happens.
    Otherwise, it must be converted in windows. Pass wc_compositecheck so that this API checks non- ing compound characters.
    Passing wc_sepchars enables Windows to divide the characters into two segments, that is, the character plus the pronunciation, such as E '.
    Passing wc_discardns causes windows to discard the pronunciation symbol.
    Passing wc_defaultchar replaces the composite character with the default characters described in the lpdefaultchar parameter in windows.
    The default behavior is wc_sepchars.
  • LpwidecharstrUnicode string to be converted.
  • CchwidecharThe length of lpwidecharstr in Unicode characters. Generally, the value-1 indicates that the string ends with 0x00.
  • LpmultibytestrThe Byte size of the Character Buffer cbmultibyte lpmultibytestr for the string to be converted.
  • LpdefaultcharOptional -- a single-character ANSI string passed when dwflags contains wc_compositecheck | wc_defaultchar and a Unicode Character cannot be mapped to the same ANSI string. It contains the inserted "default" character. Null can be passed to allow the API to use the system default character (a question mark ).
  • LpuseddefaultcharOptional -- a pointer to the bool type to indicate whether the default character has been inserted with an ANSI string. Null can be passed to ignore this parameter.

I got dizzy myself ......!, Everything is hard at the beginning ......, If you do not understand these things, it is difficult to understand the com string processing. What's more, the list in the document is much more complex than the actual application. The following example shows how to use this API:

// Assume that you already have a unicode string wszsomestring... char szansistring [max_path]; widechartomultibyte (cp_acp, // ANSI code page wc_compositecheck, // check the accent character wszsomestring, // The original Unicode string-1, //-1 indicates that the string ends with 0x00 with szansistring, // The destination char string sizeof (szansistring), // the buffer size is null, // The default fat string is null ); // ignore this parameter

After this function is called, szansistring will contain the ANSI version of the Unicode string. After this function is called, szansistring will contain the ANSI version of the Unicode string.


This CRT function wcstombs () is a simplified version, but it ends the call of widechartomultibyte (), so the final result is the same. The prototype is as follows:

size_t wcstombs (    char*          mbstr,    const wchar_t* wcstr,    size_t         count );

The following describes the parameters:

Mbstr: receives the character (char) buffer of the ANSI string. Wcstr: Unicode string to be converted. Count: the buffer size specified by the mbstr parameter.

Wcstombs () uses the wc_compositecheck | wc_sepchars flag in its call to widechartomultibyte. Use wcstombs () to convert the Unicode string in the preceding example. The result is the same:

wcstombs ( szANSIString, wszSomeString, sizeof(szANSIString) );

The cstring in MFC contains constructors and values assigned to Unicode strings. Therefore, you can use cstring for conversion. For example:

// Assume that there is a unicode string wszsomestring... cstring str1 (wszsomestring); // use the constrtor to convert cstring str2; str2 = wszsomestring; // use the value assignment operation to convert the string.
ATL macro

ATL has a set of convenient macros for String Conversion. W2a () is used to convert a unicode string to an ANSI string (the memory is "wide to ANSI"-width to ANSI ). In fact, ole2a () is more accurate. "Ole" indicates a com or OLE string. The following is an example of using these macros:

# Include <atlconv. h> // assume that there is a unicode string wszsomestring... {char szansistring [max_path]; uses_conversion; // declare the local variable lstrcpy to be used by this macro (szansistring, ole2a (wszsomestring ));}

The ole2a () macro "returns" the pointer to the converted string, but the converted string is stored in a temporary stack variable. Therefore, you must use lstrcpy () to obtain your own copy. The other macros are w2t () (Unicode to tchar) and w2ct () (Unicode to constant tchar string ).
A macro is ole2ca () (Unicode to a constant char string). In the preceding example, ole2ca () is actually a more positive macro, because lstrcpy () the second parameter of is a constant char *. This topic will be discussed in detail later.
On the other hand, if you do not want to perform the preceding complex string processing, even if you want to keep it as a unicode string, if you are writing a console application, the full-course variable STD: wcout should be used to output/display Unicode strings, for example:

wcout << wszSomeString;

But remember, STD: wcout only recognizes Unicode, so if you are a "normal" string, you must use STD: cout to output/display it. For the number of Unicode string texts, use the prefix L, such:

wcout << L"The Oracle says..." << endl << wszOracleResponse;

If the persistence string is Unicode, there are two restrictions for programming:

  • Wcsxxx () Unicode string processing functions must be used, such as wcslen ();
  • In Windows 9x, Unicode strings cannot be transmitted in Windows APIs. To write applications that can run on both 9x and NT, The tchar type must be used. For details, refer to msdn;

Summarize the above content using the example code

The following two examples demonstrate the concept of COM in this article. The Code also contains the example project in this article.

Use a single interface COM Object

The first example shows a single interface COM object. This may be the simplest example. It uses the Active Desktop component object class (clsid_activedesktop) in the shell to obtain the file name of the current desktop wallpaper. Make sure that active desktop is installed in the system ). The procedure is as follows:

  • Initialize the com library. (Initialize );
  • Create a COM object that interacts with the active desktop and obtain the iactivedesktop interface;
  • Call the getwallpaper () method of the COM object;
  • If getwallpaper () is successful, the wallpaper file name is output/displayed;
  • Release Interface (release ());
  • Reclaim the com Library (uninitialize );
Wchar wszwallpaper [max_path]; cstring strpath; hresult hr; iactivedesktop * PIAD; // 1. initialize the com Library (for Windows to load DLLs ). It is usually called in the initinstance () of the program // coinitialize (null) or other startup code. The MFC program uses afxoleinit (). Coinitialize (null); // 2. Create a COM Object using the Active Desktop component object class provided by the shell. // The fourth parameter Notifies COM of the interface required (iactivedesktop ). hR = cocreateinstance (clsid_activedesktop, null, clsctx_inproc_server, iid_iactivedesktop, (void **) & PIAD); If (succeeded (HR) {// 3. if the COM object is successfully created, the getwallpaper () method of the object is called. HR = PIAD-> getwallpaper (wszwallpaper, max_path, 0); If (succeeded (HR) {// 4. If getwallpaper () succeeds, output the name of the file it returns. // Note that wcout is used to display the Unicode string wszwallpaper. wcout is // Unicode-specific and has the same function as cout. Wcout <L "wallpaper path is: \ n" <wszwallpaper <Endl;} else {cout <_ T ("getwallpaper () failed. ") <Endl;} // 5. release API. PIAD-> release ();} else {cout <_ T ("cocreateinstance () failed.") <Endl;}/6. Reclaim the com library. This step is not required for the MFC program. It is automatically completed. Couninitialize ();

In this example, the output/display Unicode string wszwallpaper uses STD: wcout.

COM Object using multiple interfaces

The second example shows how to use a single-interface COM Object QueryInterface () function. The Code uses the shell link component object class of the shell to create the shortcut of the wallpaper file we obtained in the first example. The procedure is as follows:

  • Initialize the com library;
  • Create a COM Object for creating shortcuts and obtain the ishelllink interface;
  • Call the setpath () method of the ishelllink interface;
  • Call the QueryInterface () function of the object and obtain the ipersistfile interface;
  • Call the SAVE () method of the ipersistfile interface;
  • Release interface;
  • Reclaim the com library;
Cstring swallpaper = wszwallpaper; // convert the wallpaper path to ansiishelllink * pisl; ipersistfile * pipf; // 1. initialize the com Library (for Windows to load DLLs ). generally, // coinitialize (null) or other startup code is called in initinstance. The MFC program uses afxoleinit (). Coinitialize (null); // 2. Use the shell link component object class provided by the shell to create a COM object .. // The fourth parameter Notifies COM of the interface required (ishelllink ). HR = cocreateinstance (clsid_shelllink, null, clsctx_inproc_server, iid_ishelllink, (void **) & pisl); If (succeeded (HR) {// 3. set the path of the shortcut target (wallpaper file. HR = pisl-> setpath (swallpaper); If (succeeded (HR) {// 4. Obtain the Second Interface (ipersistfile) of this object ). HR = pisl-> QueryInterface (iid_ipersistfile, (void **) & pipf); If (succeeded (HR) {// 5. call the SAVE () method to save a file. The first parameter is a // Unicode string. HR = pipf-> Save (L "C: \ wallpaper. lnk", false); // 6a. Release the ipersistfile interface. Pipf-> release () ;}// 6. Release the ishelllink interface. Pisl-> release ();} // The output error message is omitted here. // 7. Reclaim the com library. This step is not required for the MFC program. It is automatically completed. Couninitialize ();

Processing hresult

This part is intended to use succeeded and failed macros for some simple error handling. It mainly studies the hresult returned from the com method in depth to achieve full understanding and skillful application.
Hresult is a 32-bit signed integer. Its non-negative value indicates success, and the negative value indicates failure. Hresult has three fields: Level (indicating success or failure), function code, and status code. The function code indicates the component or program from which the hresult comes from. Microsoft assigns function codes to different components, such as COM and task scheduling programs. The function code is a 16-bit value. It only has no other internal meaning. It is randomly associated between numbers and meanings. It is similar to the value returned by getlasterror.
If you are in winerror. h. Find the error code in the header file and you will see a lot of hresult values listed according to the [function] _ [degree] _ [description] naming conventions. The general hresult returned by the component is similar to e_outofmemory) there is no function code in the name. For example:

Function Code = regdb, which refers to "registry database )";
Degree = e indicates an error );
Description = readregdb is a description of the error (meaning the registry database cannot be read ). S_ OK: no function code -- General (generic)
Degree = s; success );
"OK" indicates that everything is fine (everything's OK ).

Fortunately, there is a way easier to determine the meaning of hresult than viewing winerror. H files. You can use the error lookup tool provided by VC to easily find the built-in function code for hresult. For example, assume that you forgot to call coinitialize () before cocreateinstance (). The value returned by cocreateinstance () is 0x800401f0. You only need to enter this value to the error search tool and press the "look up" button to see the error message description "coinitialize not called", as shown in:

Another method to find the description of hresult is in the debugger. Assume that the hresult variable is hres. Enter "hres, HR" in the Left box of the Watch window to indicate the value to be viewed. "HR" will notify VC to display the value described by hresult. As shown in:

Through the above discussion, you must have a preliminary understanding of COM programming. The second part of this article will discuss the internal mechanism of COM. Teaches you how to write your own interfaces in C ++.

(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.