Developers Workshop to COM and ATL 3.0
| < Free Open Study > |
|
Now that we can create a single VARIANT using the COM library, we are ready to look at the DISPPARAMS structure. Using IDispatch from a C++ client can be difficult. The trouble comes from the need to package up any necessary parameters to the method in the form of an array of VARIANTS, which is represented by the DISPPARAMS structure. DISPPARAMS is defined in <oaidl.idl> as the following:
// The DISPPARAMS structure allows you to send over all required parameters to // a method using one data structure. typedef struct tagDISPPARAMS { [size_is(cArgs)] VARIANTARG * rgvarg; // Array of arguments. [size_is(cNamedArgs)] DISPID * rgdispidNamedArgs; // Array of named arguments. UINT cArgs; // # of items in array. UINT cNamedArgs; // # of named arguments. } DISPPARAMS;
Note | The VARIANTARG constant seen above is a simple typedef to the VARIANT structure. |
More often than not, you will only need to concern yourself with the first and third fields of the DISPPARAMS structure. As for the other fields, automation objects can support the idea of named arguments. The Visual Basic language allows you to pass parameters to a method in an arbitrary order. For example, the MsgBox() method of Visual Basic normally looks like the following:
' Here we have specified three of the five parameters in order. ' MsgBox "This is the prompt", vbOKOnly, "Title in box"
Using named arguments, the VB developer may call MsgBox() as:
' How much flexibility does a programmer really need... ' MsgBox Title:="Title in box", buttons:=vbOKOnly, prompt:="This is the prompt"
Why? Well, let's just say that VB tries to be as helpful as possible to the developers it services. Here's a rule to always keep firm in your mind: The more help VB provides VB programmers, the more miserable your life as a C++ programmer becomes. Given this bit of wisdom, you can now see that the second and fourth fields of the DISPPARAMS structure hold information about any named arguments which may be coming our way. CoSquiggle does not support any named arguments, so the values of these fields will be NULL. The other fields of the DISPPARAMS structure specify the upper bound of the array of VARIANT parameters, and the array itself. Given that none of the methods found in the dispinterface of CoSquiggle take parameters, a C++ client can assemble a DISPPARAMS structure quite easily:
// When you are calling a member of a dispinterface which does not require // any arguments at all (named or otherwise) set up your DISPPARAMS as so: DISPPARAMS params = {0, 0, 0, 0};
Most members of a dispinterface do take parameters, and thus we will be required to create some VARIANTS. For example, what if CoSquiggle defined a method that requires four longs, in order to draw the squiggle within some bounding rectangle:
// Here is a member of CoSquiggle's dispinterface which requires some parameters. void DrawSquiggleInABox(long top, long left, long bottom, long right);
As a C++ client, you may set up your DISPPARAMS structure as so:
// Create an array of 4 VARIANTs. VARIANT myVars[4]; VariantInit(&myVars [0]); // Top myVars [0].vt = VT_I4; myVars [0].lVal = 20; VariantInit(&myVars [1]); // Left myVars [1].vt = VT_I4; myVars [1].lVal = 20; VariantInit(&myVars [2]); // Bottom myVars [2].vt = VT_I4; myVars [2].lVal = 90; VariantInit(&myVars [3]); // Right myVars [3].vt = VT_I4; myVars [3].lVal = 200; // Package up the VARIANTS into DISPPARAMS DISPPARAMS myParams = { myVars, 0, 4, 0}; // Call DrawSquiggleInABox() using Invoke(). pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &myParams, NULL, NULL, NULL); ...
Again, as you can tell, working with IDispatch using straight C++ can become complex. Take heart, as chances are this will not be a very common task, especially as most coclasses implement a "dual interface" (which we examine soon). However, here again is how a C++ client can make use of CoSquiggle:
// A late bound C++ client int main(int argc, char* argv[]) { CoInitialize(NULL); IDispatch* pDisp = NULL; CLSID clsid; DISPID dispid; // Go look up the CLSID from the ProgID. CLSIDFromProgID(OLESTR("RawDisp.CoSquiggle"),&clsid); LPOLESTR str = OLESTR("DrawASquiggle"); // Create the CoSquiggle. CoCreateInstance(clsid, NULL, CLSCTX_SERVER, IID_IDispatch, (void**)&pDisp); // See if the object supports a method named 'DrawASquiggle' pDisp->GetIDsOfNames(IID_NULL, &str,1, LOCALE_SYSTEM_DEFAULT, &dispid); // Call Invoke. We have no parameters, but must still // supply an DISPPARAMS structure. DISPPARAMS params = {0, 0, 0, 0}; pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms, NULL, NULL, NULL); // See ya! pDisp->Release(); CoUninitialize(); return 0; }
So there you have it. IDispatch programming in C++: powerful, dynamic, and a real pain in the neck. Again, you will seldom need to access IDispatch from a C++ client, as most C++ clients will use early binding. Where IDispatch does make sense is from within the context of scripting clients. Again, here is some VBScript code that makes use of CoSquiggle from MS Internet Explorer:
' Some Web-enabled squiggling. ' <HTML> <BODY> <SCRIPT language=VBScript> dim o set o = CreateObject("RawDisp.CoSquiggle") o.DrawASquiggle o.FlipASquiggle o.EraseASquiggle </SCRIPT> </BODY> </HTML>
To sum up our examination of IDispatch thus far, a given coclass that supports late binding must implement IDispatch. Of the four methods, GetIDsOfNames() and Invoke() are the core items that allow a client to dynamically call methods in the object's dispinterface. As we have also seen, implementing these methods by hand can be a bother if your coclass has an extensive dispinterface supporting numerous methods and properties.
| < Free Open Study > |
|