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