xref: /reactos/dll/win32/oleaut32/dispatch.c (revision 84ccccab)
1 /**
2  * Dispatch API functions
3  *
4  * Copyright 2000  Francois Jacques, Macadamian Technologies Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  */
21 
22 #include "precomp.h"
23 
24 WINE_DEFAULT_DEBUG_CHANNEL(ole);
25 
26 /******************************************************************************
27  *		DispInvoke (OLEAUT32.30)
28  *
29  * Call an object method using the information from its type library.
30  *
31  * RETURNS
32  *  Success: S_OK.
33  *  Failure: Returns DISP_E_EXCEPTION and updates pexcepinfo if an exception occurs.
34  *           DISP_E_BADPARAMCOUNT if the number of parameters is incorrect.
35  *           DISP_E_MEMBERNOTFOUND if the method does not exist.
36  *           puArgErr is updated if a parameter error (see notes) occurs.
37  *           Otherwise, returns the result of calling ITypeInfo_Invoke().
38  *
39  * NOTES
40  *  Parameter errors include the following:
41  *| DISP_E_BADVARTYPE
42  *| E_INVALIDARG            An argument was invalid
43  *| DISP_E_TYPEMISMATCH,
44  *| DISP_E_OVERFLOW         An argument was valid but could not be coerced
45  *| DISP_E_PARAMNOTOPTIONAL A non optional parameter was not passed
46  *| DISP_E_PARAMNOTFOUND    A parameter was passed that was not expected by the method
47  *  This call defers to ITypeInfo_Invoke().
48  */
49 HRESULT WINAPI DispInvoke(
50 	VOID       *_this,        /* [in] Object to call method on */
51 	ITypeInfo  *ptinfo,       /* [in] Object type info */
52 	DISPID      dispidMember, /* [in] DISPID of the member (e.g. from GetIDsOfNames()) */
53 	USHORT      wFlags,       /* [in] Kind of method call (DISPATCH_ flags from "oaidl.h") */
54 	DISPPARAMS *pparams,      /* [in] Array of method arguments */
55 	VARIANT    *pvarResult,   /* [out] Destination for the result of the call */
56 	EXCEPINFO  *pexcepinfo,   /* [out] Destination for exception information */
57 	UINT       *puArgErr)     /* [out] Destination for bad argument */
58 {
59     TRACE("\n");
60 
61     return ITypeInfo_Invoke(ptinfo, _this, dispidMember, wFlags,
62                             pparams, pvarResult, pexcepinfo, puArgErr);
63 }
64 
65 /******************************************************************************
66  *		DispGetIDsOfNames (OLEAUT32.29)
67  *
68  * Convert a set of parameter names to DISPIDs for DispInvoke().
69  *
70  * RETURNS
71  *  Success: S_OK.
72  *  Failure: An HRESULT error code.
73  *
74  * NOTES
75  *  This call defers to ITypeInfo_GetIDsOfNames(). The ITypeInfo interface passed
76  *  as ptinfo contains the information to map names to DISPIDs.
77  */
78 HRESULT WINAPI DispGetIDsOfNames(
79 	ITypeInfo  *ptinfo,    /* [in] Object's type info */
80 	OLECHAR   **rgszNames, /* [in] Array of names to get DISPIDs for */
81 	UINT        cNames,    /* [in] Number of names in rgszNames */
82 	DISPID     *rgdispid)  /* [out] Destination for converted DISPIDs */
83 {
84     return ITypeInfo_GetIDsOfNames(ptinfo, rgszNames, cNames, rgdispid);
85 }
86 
87 /******************************************************************************
88  *		DispGetParam (OLEAUT32.28)
89  *
90  * Retrieve a parameter from a DISPPARAMS structure and coerce it to the
91  * specified variant type.
92  *
93  * NOTES
94  *  Coercion is done using system (0) locale.
95  *
96  * RETURNS
97  *  Success: S_OK.
98  *  Failure: DISP_E_PARAMNOTFOUND, if position is invalid. or
99  *           DISP_E_TYPEMISMATCH, if the coercion failed. puArgErr is
100  *           set to the index of the argument in pdispparams.
101  */
102 HRESULT WINAPI DispGetParam(
103 	DISPPARAMS *pdispparams, /* [in] Parameter list */
104 	UINT        position,    /* [in] Position of parameter to coerce in pdispparams */
105 	VARTYPE     vtTarg,      /* [in] Type of value to coerce to */
106 	VARIANT    *pvarResult,  /* [out] Destination for resulting variant */
107 	UINT       *puArgErr)    /* [out] Destination for error code */
108 {
109     /* position is counted backwards */
110     UINT pos;
111     HRESULT hr;
112 
113     TRACE("position=%d, cArgs=%d, cNamedArgs=%d\n",
114           position, pdispparams->cArgs, pdispparams->cNamedArgs);
115 
116     if (position < pdispparams->cArgs)
117     {
118       /* positional arg? */
119       pos = pdispparams->cArgs - position - 1;
120     }
121     else
122     {
123       /* FIXME: is this how to handle named args? */
124       for (pos=0; pos<pdispparams->cNamedArgs; pos++)
125         if (pdispparams->rgdispidNamedArgs[pos] == position) break;
126 
127       if (pos==pdispparams->cNamedArgs)
128         return DISP_E_PARAMNOTFOUND;
129     }
130 
131     if (pdispparams->cArgs > 0 && !pdispparams->rgvarg)
132     {
133         hr = E_INVALIDARG;
134         goto done;
135     }
136 
137     if (!pvarResult)
138     {
139         hr = E_INVALIDARG;
140         goto done;
141     }
142 
143     hr = VariantChangeType(pvarResult,
144                            &pdispparams->rgvarg[pos],
145                            0, vtTarg);
146 
147 done:
148     if (FAILED(hr))
149         *puArgErr = pos;
150 
151     return hr;
152 }
153 
154 
155 /******************************************************************************
156  * IDispatch {OLEAUT32}
157  *
158  * NOTES
159  *  The IDispatch interface provides a single interface to dispatch method calls,
160  *  regardless of whether the object to be called is in or out of process,
161  *  local or remote (e.g. being called over a network). This interface is late-bound
162  *  (linked at run-time), as opposed to early-bound (linked at compile time).
163  *
164  *  The interface is used by objects that wish to called by scripting
165  *  languages such as VBA, in order to minimise the amount of COM and C/C++
166  *  knowledge required, or by objects that wish to live out of process from code
167  *  that will call their methods.
168  *
169  *  Method, property and parameter names can be localised. The details required to
170  *  map names to methods and parameters are collected in a type library, usually
171  *  output by an IDL compiler using the objects IDL description. This information is
172  *  accessible programmatically through the ITypeLib interface (for a type library),
173  *  and the ITypeInfo interface (for an object within the type library). Type information
174  *  can also be created at run-time using CreateDispTypeInfo().
175  *
176  * WRAPPERS
177  *  Instead of using IDispatch directly, there are several wrapper functions available
178  *  to simplify the process of calling an objects methods through IDispatch.
179  *
180  *  A standard implementation of an IDispatch object is created by calling
181  *  CreateStdDispatch(). Numeric Id values for the parameters and methods (DISPIDs)
182  *  of an object of interest are retrieved by calling DispGetIDsOfNames(). DispGetParam()
183  *  retrieves information about a particular parameter. Finally the DispInvoke()
184  *  function is responsible for actually calling methods on an object.
185  *
186  * METHODS
187  */
188 
189 typedef struct
190 {
191     IDispatch IDispatch_iface;
192     void * pvThis;
193     ITypeInfo * pTypeInfo;
194     LONG ref;
195 } StdDispatch;
196 
197 static inline StdDispatch *impl_from_IDispatch(IDispatch *iface)
198 {
199     return CONTAINING_RECORD(iface, StdDispatch, IDispatch_iface);
200 }
201 
202 /******************************************************************************
203  * IDispatch_QueryInterface {OLEAUT32}
204  *
205  * See IUnknown_QueryInterface.
206  */
207 static HRESULT WINAPI StdDispatch_QueryInterface(
208   LPDISPATCH iface,
209   REFIID riid,
210   void** ppvObject)
211 {
212     StdDispatch *This = impl_from_IDispatch(iface);
213     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppvObject);
214 
215     *ppvObject = NULL;
216 
217     if (IsEqualIID(riid, &IID_IDispatch) ||
218         IsEqualIID(riid, &IID_IUnknown))
219     {
220         *ppvObject = iface;
221         IDispatch_AddRef(iface);
222         return S_OK;
223     }
224     return E_NOINTERFACE;
225 }
226 
227 /******************************************************************************
228  * IDispatch_AddRef {OLEAUT32}
229  *
230  * See IUnknown_AddRef.
231  */
232 static ULONG WINAPI StdDispatch_AddRef(LPDISPATCH iface)
233 {
234     StdDispatch *This = impl_from_IDispatch(iface);
235     ULONG refCount = InterlockedIncrement(&This->ref);
236 
237     TRACE("(%p)->(ref before=%u)\n",This, refCount - 1);
238 
239     return refCount;
240 }
241 
242 /******************************************************************************
243  * IDispatch_Release {OLEAUT32}
244  *
245  * See IUnknown_Release.
246  */
247 static ULONG WINAPI StdDispatch_Release(LPDISPATCH iface)
248 {
249     StdDispatch *This = impl_from_IDispatch(iface);
250     ULONG refCount = InterlockedDecrement(&This->ref);
251 
252     TRACE("(%p)->(ref before=%u)\n", This, refCount + 1);
253 
254     if (!refCount)
255     {
256         ITypeInfo_Release(This->pTypeInfo);
257         CoTaskMemFree(This);
258     }
259 
260     return refCount;
261 }
262 
263 /******************************************************************************
264  * IDispatch_GetTypeInfoCount {OLEAUT32}
265  *
266  * Get the count of type information in an IDispatch interface.
267  *
268  * PARAMS
269  *  iface   [I] IDispatch interface
270  *  pctinfo [O] Destination for the count
271  *
272  * RETURNS
273  *  Success: S_OK. pctinfo is updated with the count. This is always 1 if
274  *           the object provides type information, and 0 if it does not.
275  *  Failure: E_NOTIMPL. The object does not provide type information.
276  *
277  * NOTES
278  *  See IDispatch() and IDispatch_GetTypeInfo().
279  */
280 static HRESULT WINAPI StdDispatch_GetTypeInfoCount(LPDISPATCH iface, UINT * pctinfo)
281 {
282     TRACE("(%p)\n", pctinfo);
283     *pctinfo = 1;
284     return S_OK;
285 }
286 
287 /******************************************************************************
288  * IDispatch_GetTypeInfo {OLEAUT32}
289  *
290  * Get type information from an IDispatch interface.
291  *
292  * PARAMS
293  *  iface   [I] IDispatch interface
294  *  iTInfo  [I] Index of type information.
295  *  lcid    [I] Locale of the type information to get
296  *  ppTInfo [O] Destination for the ITypeInfo object
297  *
298  * RETURNS
299  *  Success: S_OK. ppTInfo is updated with the objects type information
300  *  Failure: DISP_E_BADINDEX, if iTInfo is any value other than 0.
301  *
302  * NOTES
303  *  See IDispatch.
304  */
305 static HRESULT WINAPI StdDispatch_GetTypeInfo(LPDISPATCH iface, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
306 {
307     StdDispatch *This = impl_from_IDispatch(iface);
308     TRACE("(%d, %x, %p)\n", iTInfo, lcid, ppTInfo);
309 
310     *ppTInfo = NULL;
311     if (iTInfo != 0)
312         return DISP_E_BADINDEX;
313 
314     *ppTInfo = This->pTypeInfo;
315     ITypeInfo_AddRef(*ppTInfo);
316 
317     return S_OK;
318 }
319 
320 /******************************************************************************
321  * IDispatch_GetIDsOfNames {OLEAUT32}
322  *
323  * Convert a methods name and an optional set of parameter names into DISPIDs
324  * for passing to IDispatch_Invoke().
325  *
326  * PARAMS
327  *  iface     [I] IDispatch interface
328  *  riid      [I] Reserved, set to IID_NULL
329  *  rgszNames [I] Name to convert
330  *  cNames    [I] Number of names in rgszNames
331  *  lcid      [I] Locale of the type information to convert from
332  *  rgDispId  [O] Destination for converted DISPIDs.
333  *
334  * RETURNS
335  *  Success: S_OK.
336  *  Failure: DISP_E_UNKNOWNNAME, if any of the names is invalid.
337  *           DISP_E_UNKNOWNLCID if lcid is invalid.
338  *           Otherwise, an HRESULT error code.
339  *
340  * NOTES
341  *  This call defers to ITypeInfo_GetIDsOfNames(), using the ITypeInfo object
342  *  contained within the IDispatch object.
343  *  The first member of the names list must be a method name. The names following
344  *  the method name are the parameters for that method.
345  */
346 static HRESULT WINAPI StdDispatch_GetIDsOfNames(LPDISPATCH iface, REFIID riid, LPOLESTR * rgszNames, UINT cNames, LCID lcid, DISPID * rgDispId)
347 {
348     StdDispatch *This = impl_from_IDispatch(iface);
349     TRACE("(%s, %p, %d, 0x%x, %p)\n", debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId);
350 
351     if (!IsEqualGUID(riid, &IID_NULL))
352     {
353         FIXME(" expected riid == IID_NULL\n");
354         return E_INVALIDARG;
355     }
356     return DispGetIDsOfNames(This->pTypeInfo, rgszNames, cNames, rgDispId);
357 }
358 
359 /******************************************************************************
360  * IDispatch_Invoke {OLEAUT32}
361  *
362  * Call an object method.
363  *
364  * PARAMS
365  *  iface        [I] IDispatch interface
366  *  dispIdMember [I] DISPID of the method (from GetIDsOfNames())
367  *  riid         [I] Reserved, set to IID_NULL
368  *  lcid         [I] Locale of the type information to convert parameters with
369  *  wFlags,      [I] Kind of method call (DISPATCH_ flags from "oaidl.h")
370  *  pDispParams  [I] Array of method arguments
371  *  pVarResult   [O] Destination for the result of the call
372  *  pExcepInfo   [O] Destination for exception information
373  *  puArgErr     [O] Destination for bad argument
374  *
375  * RETURNS
376  *  Success: S_OK.
377  *  Failure: See DispInvoke() for failure cases.
378  *
379  * NOTES
380  *  See DispInvoke() and IDispatch().
381  */
382 static HRESULT WINAPI StdDispatch_Invoke(LPDISPATCH iface, DISPID dispIdMember, REFIID riid, LCID lcid,
383                                          WORD wFlags, DISPPARAMS * pDispParams, VARIANT * pVarResult,
384                                          EXCEPINFO * pExcepInfo, UINT * puArgErr)
385 {
386     StdDispatch *This = impl_from_IDispatch(iface);
387     TRACE("(%d, %s, 0x%x, 0x%x, %p, %p, %p, %p)\n", dispIdMember, debugstr_guid(riid), lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
388 
389     if (!IsEqualGUID(riid, &IID_NULL))
390     {
391         FIXME(" expected riid == IID_NULL\n");
392         return E_INVALIDARG;
393     }
394     return DispInvoke(This->pvThis, This->pTypeInfo, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
395 }
396 
397 static const IDispatchVtbl StdDispatch_VTable =
398 {
399   StdDispatch_QueryInterface,
400   StdDispatch_AddRef,
401   StdDispatch_Release,
402   StdDispatch_GetTypeInfoCount,
403   StdDispatch_GetTypeInfo,
404   StdDispatch_GetIDsOfNames,
405   StdDispatch_Invoke
406 };
407 
408 /******************************************************************************
409  * CreateStdDispatch [OLEAUT32.32]
410  *
411  * Create and return a standard IDispatch object.
412  *
413  * RETURNS
414  *  Success: S_OK. ppunkStdDisp contains the new object.
415  *  Failure: An HRESULT error code.
416  *
417  * NOTES
418  *  Outer unknown appears to be completely ignored.
419  */
420 HRESULT WINAPI CreateStdDispatch(
421         IUnknown* punkOuter,
422         void* pvThis,
423 	ITypeInfo* ptinfo,
424 	IUnknown** stddisp)
425 {
426     StdDispatch *pStdDispatch;
427 
428     TRACE("(%p, %p, %p, %p)\n", punkOuter, pvThis, ptinfo, stddisp);
429 
430     if (!pvThis || !ptinfo || !stddisp)
431         return E_INVALIDARG;
432 
433     pStdDispatch = CoTaskMemAlloc(sizeof(StdDispatch));
434     if (!pStdDispatch)
435         return E_OUTOFMEMORY;
436 
437     pStdDispatch->IDispatch_iface.lpVtbl = &StdDispatch_VTable;
438     pStdDispatch->pvThis = pvThis;
439     pStdDispatch->pTypeInfo = ptinfo;
440     pStdDispatch->ref = 1;
441 
442     /* we keep a reference to the type info so prevent it from
443      * being destroyed until we are done with it */
444     ITypeInfo_AddRef(ptinfo);
445     *stddisp = (IUnknown*)&pStdDispatch->IDispatch_iface;
446 
447     return S_OK;
448 }
449