1. A new communication method
Idispatch provides another communication mode for customers and components. With idispatch, the COM component can provide the services it supports through a standard interface, there is no need to provide multiple specific and service interfaces.
1.1 old communication methods
Communication between a customer and a component is completed through an interface, which has an array composed of function pointers. The Customer Code must contain a header file describing the interface in the form of abstract base classes. The compiler will read this header file and then assign an index to each member function in the abstract base class. This index is actually the index of the corresponding function pointer in the function pointer array.
PIX-> fxstringout (MSG );
It will be interpreted:
(* (Pix-> pvtbl [indexoffxstringout]) (pix, MSG );
Pvtbl is the pointer to the vtbl of the corresponding class, while indexoffxstringout is the index of the fxstringout pointer in the function pointer table.
In macro language development, if the macro language may obtain the index of the pointer of the function in vtbl so that it can call them? When a macro language calls a function in the COM component, three types of information can be used: the proid, function name, and parameters passed to the function. The macro runtime system must provide a simple method to execute functions with the old name of the function. This method is the idispatch interface.
1.2idispatch Interface
In short, idispatch will receive a function name and execute it. IDL description of idispatch
Interfaceidispatch: iunknown
{
Typedef [unique] idispatch * lpdispatch;
Hresult gettypeinfocount (
[Out] uint * pctinfo
);
Hresult gettypeinfo (
[In] uint itinfo,
[In] lcid,
[Out] itypeinfo ** pptinfo
);
Hresult getidsofnames (
[In] refiid riid,
[IN, size_is (cnames)] lpolestr * rgsznames,
[In] uint cnames,
[In] lcid,
[Out, size_is (cnames)] dispid * rgdispid
);
[Local]
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
);
[Call_as (invoke)]
Hresult remoteinvoke (
[In] dispid dispidmember,
[In] refiid riid,
[In] lcid,
[In] DWORD dwflags,
[In] dispparams * pdispparams,
[Out] variant * pvarresult,
[Out] partition info * p1_info,
[Out] uint * pargerr,
[In] uint cvarref,
[IN, size_is (cvarref)] uint * rgvarrefidx,
[In, out, size_is (cvarref)] variantarg * rgvarref
);
The most interesting about idispatch is that getidsofnames and invoke. getidsofnames read the name of a function and return its scheduling ID (dispid ). Dispid is a long integer that identifies a function. The automatic control program will pass the dispid to the invoke member function. Invoke can use dispid as the index of the function pointer array.
Idispatch: The implementation of invoke is similar to that of vtbl. Both of them can define interfaces. Idispatch: An Implementation of invoke is called a scheduling interface in the function set. The COM interface is a pointer to a function pointer array. The first three elements of this array are QueryInterface, addref, and release. The following figure shows the scheduling interface:
On the left is a traditional COM interface idispatch, which is implemented in vtbl mode, and on the right is a scheduling interface. Invoke can recognize the dispid, which is vital to the scheduling interface. Possible implementation is given. getidsofnames obtains its dispid through the function name, and invoke finds the corresponding function pointer through dispid to execute the function.
You can also use a COM interface to implement idispatch: invoke:
It is not the only method to implement the scheduling interface using COM components. You can also use the following method. Let the COM component implementing idispatch: invoke inherit idispatch instead of iunknown. This is a method called a double interface. functions that can be accessed through invoke can also be accessed directly through vtbl.
2. Use of idispatch
Consider the following vbprogram:
Dim cmpnt as object
Set cmpnt = Createobject ("insidecom. cmpnt1 ")
Cmpnt. FX
Call FX through the idispatch interface implemented by the COM component. Let's take a look at how C ++ achieves this.
Hresult hR = oleinitialize (null );
Wchar_tprogid [] = l "insidecom. cmpnt1 ";
CLSID;
Clsidfromprogid (progid, & CLSID );
Idispatch * pdispach = NULL;
Cocreateinstance (CLSID,
Null, clsctx_inproc_server,
Iid_idispatch, (void **) & pdispach );
Dispid;
Olechar * name = l "FX ";
Pdispach-> getidsofnames (
Iid_null, & name,
1, getuserdefaultlcid (), & dispid );
Dispparams dispparamsnoargs = {
Null, null, 0, 0
}
Pdispach-> invoke (dispid,
Iid_null,
Getuserdefaultlcid (),
Dispatch_method,
& Dispparamsnoargs,
Null, null, null );
Return an idispatch pointer through cocreateinstance. You can use idispatch: getidsofnames to convert the function name into a dispid and call the corresponding function through idispatch: invoke.
2.1invoke function parameters
The first parameter is the dispid that controls the function to be called by the program, and the second parameter is reserved and must be iid_null. The third is the saved location information. Others are discussed below.
All the Members in the COM interface are functions, and the "set" and "get" class functions are used to simulate the access to the variable of the year-old member. For example, setvisible can be used to set the visibility of a window. Getvisible can obtain this attribute.
If (piwindows-> getvisible () = false)
Piwindows-> setvisible (true );
It is not ideal for VB to use set and get. In VB, the following calls
If window. Visible = false then
Window. Visible = true
End if
In IDL, The propget kernel propput attribute can be interpreted as a property of the COM function.
Idispatch: The parameter passed to the called function when the Fifth parameter of invoke contains. The structure is as follows:
Typedef
Struct tagdispparams
{
Variantarg * rgvarg;
Dispid * rgdispidnamedargs;
Uint cargs;
Uint cnamedargs;
} Dispparams;
The first parameter of the structure is a parameter array, and the cargs member is the number of elements in the array. Each parameter type is of the variantarg type. Variant is a large combination of many different types.
The sixth parameter pvarresult points to a Variant Structure pointer. Save the result of the function or propget executed by invoke.
Idispatch: the second-to-last parameter of invoke is the pointer to the response info result. If an exception occurs when the invoke executes a function or attribute, the structure is filled with information about the exception.
Typedef
Struct tagdetailinfo {
Word wcode;
Word wreserved;
BSTR bstrsource;
BSTR bstrdescription;
BSTR bstrhelpfile;
DWORD dwhelpcontext;
Pvoid pvreserved;
Hresult (_ stdcall * pfndeferredfillin) (struct tag1_info *);
Scode;
} Raise info, * lp1_info;
The error code (wcode) or return value (scode) must contain an identified value. The other is 0;
Raise info raise Info;
Hresult hR = pdispach-> invoke (..., & cmdinfo );
If (failed (HR ))
{
If (hR = disp_e_exception)
{
If (Response info. pfndeferredfillin (! = NULL ))
(* (Response info. pfndeferredfillin) (& Response info );
Cout <"exception information:" <Endl
<"Source:" <cmdinfo. bstrsource <Endl
<"Description:" <strong info. bstrdescription <ends;
}
}
3. Type Library
The Type Library provides type information about components, interfaces, methods, attributes, parameters, and structures. The Type Library is a binary file.
3.1 create a database of the type
The automated library function createtypelib can create a Type Library. This function returns an icreatetypelib interface that can fill the type library with the corresponding information. You can use IDL to generate the proxy/stub DLL code.
The key to creating a Type Library Using IDL is the library statement. The content of square brackets after the library keyword is edge to the Type Library.
[
Object,
UUID (7aaae05f-283e-4d79-b98c-5d3dcbe733bb ),
// Iid_ipoint
Helpstring ("ipointinterface "),
Oleautomation,
Dual
]
Interface ipoint: idispatch
{
[Propget,
Helpstring ("returns and sets X coordinate")]
Hresultx ([out, retval] int * retval );
[Propput,
Helpstring ("returns and sets X coordinate")]
Hresultx ([in] intvalue );
[Propget,
Helpstring ("returns and sets y coordinate")]
Hresulty ([out, retval] int * retval );
[Propput,
Helpstring ("returns and sets y coordinate")]
Hresulty ([in] intvalue );
[Helpstring ("displaythe point.")]
Hresultdisplay ();
}
[
UUID (13ba7d38-8f44-4e80-9dae-6e01776bee16), // libid_point
Helpstring ("pointcomponent Type Library "),
Version (1.0)
]
Library point
{
Importlib ("stdole32.tlb ");
Interface ipoint;
[
UUID (0615ea9a-0ed9-458d-9f0d-a9e36000038ab), // clsid_point
Helpstring ("pointclass "),
Appobject
]
Coclass point
{
[Default]
Dispinterfaceipoint;
}
}
- Distribution of Type Libraries
After a Type Library is generated, it can be released as a separate file, or it can be included as a resource in EXE or DLL.
3.2 use of database types
The first step to using a Type Library is to load it. loadregtypelib will try to load the specified type library from the Windows registry. If it fails, you can use loadtypelib to load the specified type library from the disk or loadtypelibfromresource to load the specified type library from the EXE/DLL.
Hresulthr;
Lptypelibptypelib = NULL;
HR = loadregtypelib (libid_point, 1, 0, 0, & ptypelib );
If (failed (HR ))
{
HR = loadtypelib (olestr ("point. TLB"), & ptypelib );
}
If (failed (HR ))
Return hr;
HR = ptypelib-> gettypeinfoofguid (iid_ipoint, & s_ptypeinfo );
Ptypelib-> release ();
If (failed (HR ))
Return hr;
S_ptypeinfo-> addref ();
Return hr;
3.3 Type Library in the Registry
Libid class table under the keyword hkey_class_root \ typelib.
4. Implementation of Type Libraries
Stdmethodimpcpoint: gettypeinfocount (_ RPC _ out uint * pctinfo)
{
* Pctinfo = 1;
Return s_ OK;
}
Stdmethodimp cpoint: gettypeinfo (uintitinfo, lcid, itypeinfo ** pptinfo)
{
Hresulthr;
If (itinfo! = 0)
Return type_e_elementnotfound;
If (pptinfo = NULL)
Return e_pointer;
If (s_ptypeinfo)
{
S_ptypeinfo-> addref ();
HR = s_ OK;
}
Else
{
HR = loadmytypelib ();
}
If (! HR)
* Pptinfo = s_ptypeinfo;
Return hr;
}
Stdmethodimp cpoint: getidsofnames (refiidriid, lpolestr * rgszname, uint cnames, lcid, dispid * rgdispid)
{
Hresulthr;
If (isequaliid (riid, iid_null ))
Return disp_e_unknowninterface;
If (! S_ptypeinfo)
{
HR = loadmytypelib ();
If (failed (HR ))
Return hr;
}
HR = s_ptypeinfo-> getidsofnames (rgszname, cnames, rgdispid );
Return hr;
}
Stdmethodimp cpoint: invoke (dispiddispidmember, refiid riid, lcid, word wflags, dispparams * pdispparams, variant * pvarresult, interval info * p1_info, uint * puargerr)
{
Hresulthr;
If (isequaliid (riid, iid_null ))
Return disp_e_unknowninterface;
If (! S_ptypeinfo)
{
HR = loadmytypelib ();
If (failed (HR ))
Return hr;
}
HR = s_ptypeinfo-> invoke (this, dispidmember, wflags, pdispparams, pvarresult, p1_info, puargerr );
Return hr;
}
Hresult cpoint: loadmytypelib ()
{
Hresulthr;
Lptypelibptypelib = NULL;
HR = loadregtypelib (libid_point, 1, 0, 0, & ptypelib );
If (failed (HR ))
{
HR = loadtypelib (olestr ("point. TLB"), & ptypelib );
}
If (failed (HR ))
Return hr;
HR = ptypelib-> gettypeinfoofguid (iid_ipoint, & s_ptypeinfo );
Ptypelib-> release ();
If (failed (HR ))
Return hr;
S_ptypeinfo-> addref ();
Return hr;
}