Original: http://vckbase.com/index.php/wv/1236.html
First, preface
Some time ago, because the work is more busy, not able to write in time. In the meantime received a lot of netizens ' letters to ask and encourage, in this together express thanks. Cough...... I also need a job to feed my family ...
The last book introduces two methods to write the component program of the automation (IDispatch) interface, one is to write the "pure" IDispatch interface with MFC, and the other is to write the "dual interface" component in ATL mode.
Second,IDispatch interface and dual interface
To invoke normal COM component functionality, the user must load the type library file TLB for this component ( For example, using #import in VC). However, in a script, because the script is interpreted, it is not possible to precompile using the way the type library is loaded. So how does the script interpreter use COM components? This is where the automation (IDispatch) component is doing its part. The IDispatch interface needs to implement 4 functions that the caller can implement to invoke all functions in the automation component only through these 4 functions. These 4 functions function as follows:
HRESULT GetTypeInfoCount ( [out] UINT * pctinfo) |
How many types of libraries are available in the component? Of course, it's all one. But if you implement multiple IDispatch interfaces in one component , that's not necessarily true (Note 1) |
hresult gettypeinfo ( [in] UINT itinfo, [in] lcid lcid, [out] ITypeInfo * * pptinfo) |
The caller uses the function to get the type library that he wants. Fortunately, in the case of 99%, we do not care about the implementation of these two functions, because mfc/atl all help us to complete the default implementation, if you do the function code, even if you can return directly to the E_NOTIMPL expression is not implemented. (note 2) |
hresult getidsofnames ( [in] REFIID riid, Span lang= "ZH-CN" > [in,size_is (cnames)] LPOLESTR * Rgsznames, [in] UINT cnames, [in] lcid lcid, [out,size_is (cnames)] DispID * rgdispid) |
. The so-called function ordinal, everyone to observe the dual interface IDL file and MFC ODL file, each function and property will have [id ( serial number) .... this description. |
hresult Invoke ( [in] dispid Dispidmember, [in] refiid riid, [in] lcid lcid, [in] WORD wflags, & nbsp; [In,out] dispparams * pdispparams, [out] VARIANT * pvarresult, [out] excepinfo * pexcepinfo, [out] UINT * puargerr) |
Executes the function according to the ordinal. The uses Mfc/atl to write the component program, we do not have to care about the implementation of this function. If you are writing your own code, the function is similar to the following: switch (dispidmember) { case 1: ...; break; Case 2: ...; Break .... } Note 3) |
From the implementation of the Invoke () function, it can be seen that the use of the IDispatch interface program, its execution efficiency is relatively low. ATL has implemented an interface pattern called "dual Interface (dual)", which is based on efficiency. Let's take a look at what the two interfaces are:
Figure one, dual interface (dual) structure
As can be seen, the so-called dual interface, in fact, in a vtab virtual function table accommodates three interfaces (because any interface is derived from IUnknown , so it does not emphasize IUnknown , called Dual interface). If we call QueryInterface () from any one of the ports to get another interface pointer, we actually get the same pointer address. What are the benefits of a dual interface? A: Yes, how nice, especially good ...
How to use |
Because |
So |
Scripting languages using Components |
The interpreter knows only the IDispatch interface |
Can be called, but the execution is least efficient |
Compiled language uses components |
It knows the IDispatch interface |
Can be called, execution is less efficient |
Compiled language uses components |
When it loads the type library, it recognizes the Ixxx interface |
Can call the Ixxx function directly, the most efficient |
Conclusion |
Dual interface, not only satisfies the use of scripting language, but also satisfies the use efficiency of the compiled language. So, all of the COM component interfaces that we write are implemented with a dual interface? Wrong! Whether! No! If you do not explicitly want to support scripting calls, it is best not to use a dual interface because: |
If all functions are placed in a dual interface, then the hierarchy, structure, classification is not clear |
If multiple dual interfaces are used, other problems arise (Note 4) |
Dual interface, IDispatch interface only support Automation parameter type, use is limited, in some cases it is inconvenient |
There are many ills, but now I can't remember ... |
Iii. Methods of Use
If your development environment is vc6.0, then we use the nineth back of the Simple6 component as an example, download it now ...
If your development environment is Vc.net 2003, then use the tenth back of the Simple8 component as an example, download it now ...
Hey, actually does not download also has no relationship, because you just download this back of the sample program, which already contains the required components. But don't forget to register before use: Regsvr32.exe simple6.dll or regsvr32.exe simple8.dll (be careful not to forget to enter the component's installation directory). After the registration is successful, it can be used, using the following methods:
Sample Program |
How automation components are used |
Brief description |
Example 0 |
Called in the script |
In the Nineth and tenth back, an introduction has been made |
Example 1 |
Invoke using API mode |
Revealing the principle of IDispatch's invocation, but the fool is going to use that, it's exhausting. |
Example 2 |
Smart pointer wrapper class using CComDispatchDriver |
It's much easier than just using the API way, this is good! |
Example 3 |
How to use MFC to load a type library wrapper |
Simple! Good! Common! But it is essentially using the IDispatch interface, so the execution is slightly less efficient |
Example 4 |
To load a type library using #import method |
#import Way to use the component, we talked about it in the seventh. Common! For a two-interface component, the direct invocation of a custom interface function is no longer IDispatch, so the execution is most efficient. |
Example X |
VB, Java, C #, BCB, Delphi .... |
Anyway, I'm not going to ask the man to go:-( |
Example one, IDispatch call principle
void Demo () {:: CoInitialize (NULL);//COM Initialize CLSID clsid;//get Clsidhresult hr by ProgID =:: CLSIDFromProgID (L "Simple8.di Spsimple.1 ", &clsid); ASSERT (SUCCEEDED (HR));//If it fails, the component is not registered IDispatch * pdisp = null;//Starts the component by the CLSID and gets the IDispatch pointer hr =:: CoCreateInstance (CLSID, NULL, Clsctx_all, IID_IDispatch, (LPVOID *) &pdisp); ASSERT (SUCCEEDED (HR));//If it fails, the description is not initialized comlpolestr Pwfunname = L "add";//prepare to get the ordinal of the ADD function Dispiddispid dispid;//Get the ordinal, prepare Save here hr = Pdisp->getidsofnames (//Based on function name, get ordinal function iid_null,&pwfunname,//function name array 1,//the number of elements in the array of function names Locale_system_ default,//uses the system default locale &dispid);//return value assert (SUCCEEDED (HR));//If it fails, the component simply does not have an Add function variantarg v[2];//call add The parameters required by the function v[0].vt = Vt_i4;v[0].lval = 2;//The second argument, integer 2v[1].vt = vt_i4;v[1].lval = 1;//first parameter, integer 1DISPPARAMS dispparams = {V, NULL, 2, 0};//wrap parameters in this structure the variant vresult;//function returns the calculated result of hr = Pdisp->invoke (//Call function dispid,//function specified by DispID Iid_null,locale _system_default,//uses the system default locale dispatch_method,//call is a method, not a property &Amp;dispparams,//parameter &vresult,//return value null,//do not consider exception handling null),//Do not consider error handling assert (SUCCEEDED (HR));//failure to indicate parameter pass error CString str;//shows the result str. Format ("1 + 2 =%d", vresult.lval); AfxMessageBox (str);pD isp->release ();//Release Interface pointers:: CoUninitialize ();//Release COM}
Example two, how to use CComDispatchDriver Smart pointer wrapper class
void Demo () {///has been done with COM initialization clsid clsid;//Clsidhresult hr =:: CLSIDFromProgID (L "simple8.dispsimple.1") through ProgID &CLSID); ASSERT (SUCCEEDED (HR));//If it fails, the description does not register the component CComPtr < IUnknown > spunk;//starts the component by the CLSID and obtains the IUnknown pointer hr =:: Cocreatei Nstance (CLSID, NULL, Clsctx_all, IID_IUnknown, (LPVOID *) &spunk); ASSERT (SUCCEEDED (HR)); CComDispatchDriver Spdisp (spunk);//construct can only pointer CComVariant v1 (1), V2 (2), vresult;//parameter hr = SPDISP.INVOKE2 (//Call 2 arguments for function L "ADD" ,//The function name is add&v1,//the first parameter, the value is an integer 1&v2,//the second parameter, the value is an integer 2&vresult);//return value assert (SUCCEEDED (HR));//If failure, description or no ADD letter Number, or parameter error CString str;//shows the result str. Format ("1 + 2 =%d", vresult.lval); AfxMessageBox (str);}
The Invoke2 () function is used in the sample program , but you can also use Invoke0(),Invoke1(),Invoken (),PutProperty (), and the other functions depending on the function. GetProperty () ... And so on, it is very convenient indeed.
Example three, load type library, generate wrapper class to use
This method is simpler to use, and if you look at the implementation of the wrapper class that MFC helped you create, you will find that it is actually calling the IDispatch interface function. to use vc6.0 friends, the steps are as follows:
1. Building an MFC application
2. Open ClassWizard, execute Add Class, select from a type library
Figure II, loading type library
3. Then find the component file you want to use Simple6.dll (TLB file is also available ), select interface to confirm
Figure three, select the interface that needs to be wrapped in the type library
4. Enter the calling code where appropriate
#include "simple6.h"//wrapper class header file void Demo () {////has already done the COM initialization idispsimple spdisp;//wrapper class object Spdisp.createdispatch (_t (" Simple6.dispsimple.1 "))//Start Component spdisp.xxx (...); /Call function Spdisp.releasedispatch ();//Release Interface}
to use Vc.net friends, the steps are as follows:
1. Building an MFC application
2. Execute the menu "Add \ Add Class" and select "MFC Class in Type library" in MFC classification
Figure Iv. Adding MFC classes in a type library
3. Select the component file Simple8.dll (or tlb file ) and select the interface to be wrapped
Figure v. Selecting Files and Interfaces
4. Enter the calling code in the appropriate location
#include "CDispSimple.h"//wrapper class header file void Demo () {////has already done the COM initialization cdispsimple spdisp;//wrapper class object Spdisp.createdispatch (_t (" Simple8.dispsimple.1 "))//Start Component spdisp.xxx (...); /Call function Spdisp.releasedispatch ();//Release Interface}
Example four, invoking a component using the #import method
#import Way has been introduced in the seventh time, there is no more wordy. After you download the sample program, go to see it yourself. And be sure to master this method, because it is operating efficiency is the fastest ah.
Iv. Summary
Leave your homework. In all the component programs we implemented before, only the interface method (function) was added, not the interface properties (variables), you practice it, very simple, and then write a program call to see. In fact, for the VC, the call property and call method is not much different (VC Wrap property as GetXXX ()/putxxx () or GetXXX ()/putxxx () function mode ), In other languages, such as the scripting language, it is more convenient to set the property value: Object . property = Variable or constant, Get property value is: variable = Object . property.
This get books back to make a break, more component design and use of knowledge, and listen to tell ...
Note 1: The implementation of multiple automation interfaces, we will say later.
Note 2: In the future introduction of Itypelib::gettypeinfo () , we re-aftertaste IDispatch::GetTypeInfo () it.
Note 3: When we introduce "events" later, we will actually implement a idispatch::invoke () function.
Note 4: When introducing multiple two-interface implementations, talk to this question.
"Reprint" COM Component design and application (11)--idispatch and dual-interface calls