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