Download source code
I. Preface
Some time ago, due to busy work, I was not able to write in time. In the meantime, I received a letter from many netizens asking and encouraging me. I would like to express my gratitude to you here. Cough... I also need a job to support my family ......
The previous response introduced two methods to write component programs for the automated (idispatch) interface. One is to write the "pure" idispatch interface in the MFC mode; the second is to use the ATL method to compile the "dual interface" component.
Ii. idispatch and dual Interfaces
To call common COM component functions, you must load the Type Library File TLB (for example, # import in VC) of this component ). However, in a script program, because the script is interpreted and executed, you cannot use the method of loading the Type Library for pre-compilation. So how does the script interpreter use the COM component? This is where the automation (idispatch) component is amazing. The idispatch interface needs to implement four functions. The caller can call all functions of the automation component only through these four functions. The four functions are as follows:
Hresult gettypeinfocount ( [Out] uint * pctinfo) |
How many types of libraries are provided in the component? Of course, it is generally one. However, if you implement multiple idispatch interfaces in a component, it is not necessary (note 1) |
Hresult gettypeinfo ( [In] uint itinfo, [In] lcid, [Out] itypeinfo ** pptinfo) |
The caller uses this function to obtain the desired type library. Fortunately, in the case of 99%, we don't need to care about the implementation of these two functions, because both MFC and ATL help us complete the default implementation. If we complete the function code by ourselves, you can even directly return e_notimpl to indicate that no implementation is implemented. (Note 2) |
Hresult getidsofnames ( [In] refiid riid, [IN, size_is (cnames)] lpolestr * rgsznames, [In] uint cnames, [In] lcid, [Out, size_is (cnames)] dispid * rgdispid) |
Obtain the function serial number based on the function name to prepare for calling invoke. The so-called function serial number, you can observe the double interface IDL file and the odl file of MFC, each function and attribute will have a description like [ID (serial number. |
Hresult invoke ( [In] dispid dispidmember, [In] refiid riid, [In] lcid, [In] Word wflags, [In, out] dispparams * pdispparams, [Out] variant * pvarresult, [Out] partition info * p1_info, [Out] uint * puargerr) |
Execute the function according to the serial number. We do not need to care about the implementation of the component program written using MFC/ATL. If you write your own code, this function is similar to the following: Switch (dispidmember) { Case 1:...; break; Case 2:...; break; .... } In fact, branch calling is performed according to the serial number. (Note 3) |
From the implementation of the invoke () function, we can see that the execution efficiency of the program using the idispatch interface is relatively low. From the perspective of efficiency, ATL implements an interface mode called dual. Let's take a look at what a dual interface is:
Figure 1 dual INTERFACE STRUCTURE
It can be seen that the so-called double interface actually contains three interfaces in a virtual function table of a vtab (because any interface is derived from iunknown, so I will not emphasize iunknown, is called a double interface ). If we call QueryInterface () from any interface to obtain another interface pointer, the obtained pointer address is the same. What are the advantages of the double interface? A: Okay, how good, especially good ......
Usage |
Because |
So |
Script Language component |
The interpreter only recognizes the idispatch interface. |
Can be called, but the execution efficiency is the lowest |
Use components in compiled languages |
It recognizes the idispatch Interface |
Can be called, and execution efficiency is relatively low |
Use components in compiled languages |
After it loads the type library, it knows the ixxx interface. |
You can call the ixxx function directly, with the highest efficiency. |
Conclusion |
The dual interface not only meets the ease of use of the script language, but also meets the efficiency of the compilation language. So are all the COM component interfaces we write implemented using double interfaces? Error! No! No! If you do not explicitly want to support script calls, it is best not to use dual interfaces, because: |
If all functions are placed in a double interface, the hierarchy, structure, and category are unclear. |
If multiple dual interfaces are used, other problems may occur (note 4) |
The dual interface and idispatch interface only support Automatic Parameter types, which are restricted and inconvenient to use in some cases. |
There are still many disadvantages, but I cannot think of them now ...... |
Iii. Usage
If your development environment is vc6.0, use the simple6 component in the ninth return as an example to download it ......
If your development environment is vc.net 2003, use the simple8 component in the tenth return as an example to download it ......
Hey, it doesn't matter if you don't download it, because you only need to download this sample program, which contains the required components. But do not forget to register it before using it: regsvr32.exe simple6.dll or regsvr32.exe simple8.dll (do not forget to enter the installation directory of the component ). After successful registration, you can use the following methods:
Sample program |
Use of automated components |
Brief Description |
Example 0 |
Call in script |
In the ninth or tenth rounds, we have already introduced |
Example 1 |
API call |
Reveal the call principle of idispatch, but it will be exhausted if you use it like this. |
Example 2 |
Smart pointer packaging class using ccomdispatchdriver |
This is much easier than using APIs directly! |
Example 3 |
How to package an MFC loaded Library |
Simple! Easy to use! Common! However, it uses the idispatch interface in essence, so the execution efficiency is slightly lower. |
Example 4 |
Use the # import method to load the Type Library |
# Use components in import mode. Let's talk about them in the seventh session. Common! For dual-interface components, you can directly call user-defined interface functions without going through idispatch, so the execution efficiency is the highest. |
Example x |
VB, Java, C #, BCB, Delphi ....... |
I will not, but I will go to consult senior personnel. |
Example 1: How idispatch works
Void demo ()
{
: Coinitialize (null); // COM Initialization
CLSID; // obtain CLSID through progid
Hresult hR =: clsidfromprogid (L "simple8.dispsimple. 1", & CLSID );
Assert (succeeded (HR); // if a failure occurs, the component is not registered.
Idispatch * Pdisp = NULL; // The component is started by clsid and the idispatch pointer is obtained.
HR =: cocreateinstance (CLSID, null, clsctx_all, iid_idispatch, (lpvoid *) & Pdisp );
Assert (succeeded (HR); // if it fails, it indicates that com is not initialized.
Lpolestr pwfunname = l "add"; // prepare to obtain the dispid of the add function.
Dispid; // The obtained sequence number. Save it here.
HR = Pdisp-> getidsofnames (// function that obtains the serial number based on the function name
Iid_null,
& Pwfunname, // array of function names
1, // number of elements in the array of function names
Locale_system_default, // use the default language environment of the system
& Dispid); // Return Value
Assert (succeeded (HR); // if a failure occurs, the component does not have the Add function.
Variantarg V [2]; // parameters required to call the add (1, 2) Function
V [0]. Vt = vt_i4; V [0]. lval = 2; // second parameter, integer 2
V [1]. Vt = vt_i4; V [1]. lval = 1; // The first parameter, an integer of 1
Dispparams = {v, null, 2, 0}; // wrap the parameter in this structure
Variant vresult; // calculation result returned by the function
HR = Pdisp-> invoke (// call a function
Dispid, // The function is specified by dispid
Iid_null,
Locale_system_default, // use the default language environment of the system
Dispatch_method, // call a method, not an attribute
& Dispparams, // Parameter
& Vresult, // Return Value
Null, // ignore Exception Handling
Null); // ignore error handling
Assert (succeeded (HR); // if the request fails, the parameter is passed incorrectly.
Cstring STR; // display the result
Str. Format ("1 + 2 = % d", vresult. lval );
Afxmessagebox (STR );
Pdisp-> release (); // release interface pointer
: Couninitialize (); // release com
}
Example 2: How to Use the ccomdispatchdriver smart pointer packaging class
Void demo ()
{
// COM Initialization has been performed.
CLSID; // obtain the CLSID of the component through progid
Hresult hR =: clsidfromprogid (L "simple8.dispsimple. 1", & CLSID );
Assert (succeeded (HR); // if a failure occurs, the component is not registered.
Ccomptr <iunknown> spunk; // start the component by clsid and obtain the iunknown pointer.
HR =: cocreateinstance (CLSID, null, clsctx_all, iid_iunknown, (lpvoid *) & spunk );
Assert (succeeded (HR ));
Ccomdispatchdriver spdisp (spunk); // construct a pointer only
Ccomvariant V1 (1), V2 (2), vresult; // Parameter
HR = spdisp. invoke2 (// call a function with two parameters
L "add", // The function name is add
& V1, // The first parameter. The value is an integer of 1.
& V2, // The second parameter. The value is an integer of 2.
& Vresult); // Return Value
Assert (succeeded (HR); // if a failure occurs, it indicates that the Add function is absent or the parameter is incorrect.
Cstring STR; // display the result
Str. Format ("1 + 2 = % d", vresult. lval );
Afxmessagebox (STR );
}
The invoke2 () function is used in the example program. In fact, you can also use invoke0 (), invoke1 (), invoken (), putproperty () based on different functions (), getproperty ()...... and so on.
Example 3: load the Type Library and generate a packaging class to use
This method is simpler to use. If you observe the implementation of the packaging class that MFC helps you produce, you will find that it actually calls the idispatch interface function. To use vc6.0, follow these steps:
1. Create an MFC Application
2. Enable classwizard, execute add class, and select from a Type Library
Figure 2. Load Type Library
3. Find the component File simple6.dll you want to use (The TLB file can also be used), select the interface, and then confirm
Figure 3. Select the interface to be packaged in the Type Library
4. Enter the call code where appropriate
# Include "simple6.h" // header file of the packaging class
Void demo ()
{
// COM Initialization has been performed.
Idispsimple spdisp; // the object of the packaging class
Spdisp. createdispatch (_ T ("simple6.dispsimple. 1") // start the component
Spdisp. XXX (...); // call the Function
Spdisp. releasedispatch (); // Release Interface
}
To use vc.net, follow these steps:
1. Create an MFC Application
2. Execute the "Add/Add class" menu and select "Type Library in the MFC category"
Figure 4. Add the MFC class in the Type Library
3. Select the component File simple8.dll (or TLB file) and the interface to be packaged.
Figure 5. Select files and interfaces
4. Enter the call code at an appropriate location
# Include "cdispsimple. H" // header file of the packaging class
Void demo ()
{
// COM Initialization has been performed.
Cdispsimple spdisp; // the object of the packaging class
Spdisp. createdispatch (_ T ("simple8.dispsimple. 1") // start the component
Spdisp. XXX (...); // call the Function
Spdisp. releasedispatch (); // Release Interface
}
Example 4: Use # import to call Components
# The import method has been introduced in the seventh round, so it is not a long time. After downloading the sample program in this example, you can check it out. And you must master this method, because it runs at the fastest speed.
Iv. Summary
Leave homework. In all the component programs we previously implemented, only interface methods (functions) are added, but interface properties (variables) are not added. It's very easy to practice by yourself, then write a program to call it. In fact, for VC, there is not much difference between calling properties and calling methods (VC packs properties as getxxx ()/putxxx () or getxxx ()/putxxx () function methods ), however, in other languages (such as the scripting language), it is more convenient to set the attribute value to: object. property = variable or constant. Get the property value: Variable = object. attribute.
At this point, this book has been broken down. I have more knowledge about component design and usage, and I will try again to break it down ......
NOTE 1: The implementation of multiple automated interfaces will be discussed later.
NOTE 2: when you introduce itypelib: gettypeinfo () in the future, let's review idispatch: gettypeinfo.
NOTE 3: When we introduce "events" later, we will actually implement an idispatch: invoke () function.
Note 4: This issue will be discussed when multiple dual interfaces are introduced.