wine/dlls/oleaut32/dispatch.c

476 lines
15 KiB
C

/**
* Dispatch API functions
*
* Copyright 2000 Francois Jacques, Macadamian Technologies Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <ctype.h>
#define COBJMACROS
#include "windef.h"
#include "winbase.h"
#include "objbase.h"
#include "oleauto.h"
#include "winerror.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(ole);
static IDispatch * StdDispatch_Construct(IUnknown * punkOuter, void * pvThis, ITypeInfo * pTypeInfo);
/******************************************************************************
* DispInvoke (OLEAUT32.30)
*
* Call an object method using the information from its type library.
*
* RETURNS
* Success: S_OK.
* Failure: Returns DISP_E_EXCEPTION and updates pexcepinfo if an exception occurs.
* DISP_E_BADPARAMCOUNT if the number of parameters is incorrect.
* DISP_E_MEMBERNOTFOUND if the method does not exist.
* puArgErr is updated if a parameter error (see notes) occurs.
* Otherwise, returns the result of calling ITypeInfo_Invoke().
*
* NOTES
* Parameter errors include the following:
*| DISP_E_BADVARTYPE
*| E_INVALIDARG An argument was invalid
*| DISP_E_TYPEMISMATCH,
*| DISP_E_OVERFLOW An argument was valid but could not be coerced
*| DISP_E_PARAMNOTOPTIONAL A non optional parameter was not passed
*| DISP_E_PARAMNOTFOUND A parameter was passed that was not expected by the method
* This call defers to ITypeInfo_Invoke().
*/
HRESULT WINAPI DispInvoke(
VOID *_this, /* [in] Object to call method on */
ITypeInfo *ptinfo, /* [in] Object type info */
DISPID dispidMember, /* [in] DISPID of the member (e.g. from GetIDsOfNames()) */
USHORT wFlags, /* [in] Kind of method call (DISPATCH_ flags from "oaidl.h") */
DISPPARAMS *pparams, /* [in] Array of method arguments */
VARIANT *pvarResult, /* [out] Destination for the result of the call */
EXCEPINFO *pexcepinfo, /* [out] Destination for exception information */
UINT *puArgErr) /* [out] Destination for bad argument */
{
TRACE("\n");
return ITypeInfo_Invoke(ptinfo, _this, dispidMember, wFlags,
pparams, pvarResult, pexcepinfo, puArgErr);
}
/******************************************************************************
* DispGetIDsOfNames (OLEAUT32.29)
*
* Convert a set of parameter names to DISPIDs for DispInvoke().
*
* RETURNS
* Success: S_OK.
* Failure: An HRESULT error code.
*
* NOTES
* This call defers to ITypeInfo_GetIDsOfNames(). The ITypeInfo interface passed
* as ptinfo contains the information to map names to DISPIDs.
*/
HRESULT WINAPI DispGetIDsOfNames(
ITypeInfo *ptinfo, /* [in] Object's type info */
OLECHAR **rgszNames, /* [in] Array of names to get DISPIDs for */
UINT cNames, /* [in] Number of names in rgszNames */
DISPID *rgdispid) /* [out] Destination for converted DISPIDs */
{
return ITypeInfo_GetIDsOfNames(ptinfo, rgszNames, cNames, rgdispid);
}
/******************************************************************************
* DispGetParam (OLEAUT32.28)
*
* Retrieve a parameter from a DISPPARAMS structure and coerce it to the
* specified variant type.
*
* NOTES
* Coercion is done using system (0) locale.
*
* RETURNS
* Success: S_OK.
* Failure: DISP_E_PARAMNOTFOUND, if position is invalid. or
* DISP_E_TYPEMISMATCH, if the coercion failed. puArgErr is
* set to the index of the argument in pdispparams.
*/
HRESULT WINAPI DispGetParam(
DISPPARAMS *pdispparams, /* [in] Parameter list */
UINT position, /* [in] Position of parameter to coerce in pdispparams */
VARTYPE vtTarg, /* [in] Type of value to coerce to */
VARIANT *pvarResult, /* [out] Destination for resulting variant */
UINT *puArgErr) /* [out] Destination for error code */
{
/* position is counted backwards */
UINT pos;
HRESULT hr;
TRACE("position=%d, cArgs=%d, cNamedArgs=%d\n",
position, pdispparams->cArgs, pdispparams->cNamedArgs);
if (position < pdispparams->cArgs)
{
/* positional arg? */
pos = pdispparams->cArgs - position - 1;
}
else
{
/* FIXME: is this how to handle named args? */
for (pos=0; pos<pdispparams->cNamedArgs; pos++)
if (pdispparams->rgdispidNamedArgs[pos] == position) break;
if (pos==pdispparams->cNamedArgs)
return DISP_E_PARAMNOTFOUND;
}
if (pdispparams->cArgs > 0 && !pdispparams->rgvarg)
{
hr = E_INVALIDARG;
goto done;
}
if (!pvarResult)
{
hr = E_INVALIDARG;
goto done;
}
hr = VariantChangeType(pvarResult,
&pdispparams->rgvarg[pos],
0, vtTarg);
done:
if (FAILED(hr))
*puArgErr = pos;
return hr;
}
/******************************************************************************
* CreateStdDispatch [OLEAUT32.32]
*
* Create and return a standard IDispatch object.
*
* RETURNS
* Success: S_OK. ppunkStdDisp contains the new object.
* Failure: An HRESULT error code.
*
* NOTES
* Outer unknown appears to be completely ignored.
*/
HRESULT WINAPI CreateStdDispatch(
IUnknown* punkOuter,
void* pvThis,
ITypeInfo* ptinfo,
IUnknown** ppunkStdDisp)
{
TRACE("(%p, %p, %p, %p)\n", punkOuter, pvThis, ptinfo, ppunkStdDisp);
*ppunkStdDisp = (LPUNKNOWN)StdDispatch_Construct(punkOuter, pvThis, ptinfo);
if (!*ppunkStdDisp)
return E_OUTOFMEMORY;
return S_OK;
}
/******************************************************************************
* IDispatch {OLEAUT32}
*
* NOTES
* The IDispatch interface provides a single interface to dispatch method calls,
* regardless of whether the object to be called is in or out of process,
* local or remote (e.g. being called over a network). This interface is late-bound
* (linked at run-time), as opposed to early-bound (linked at compile time).
*
* The interface is used by objects that wish to called by scripting
* languages such as VBA, in order to minimise the amount of COM and C/C++
* knowledge required, or by objects that wish to live out of process from code
* that will call their methods.
*
* Method, property and parameter names can be localised. The details required to
* map names to methods and parameters are collected in a type library, usually
* output by an IDL compiler using the objects IDL description. This information is
* accessible programmatically through the ITypeLib interface (for a type library),
* and the ITypeInfo interface (for an object within the type library). Type information
* can also be created at run-time using CreateDispTypeInfo().
*
* WRAPPERS
* Instead of using IDispatch directly, there are several wrapper functions available
* to simplify the process of calling an objects methods through IDispatch.
*
* A standard implementation of an IDispatch object is created by calling
* CreateStdDispatch(). Numeric Id values for the parameters and methods (DISPIDs)
* of an object of interest are retrieved by calling DispGetIDsOfNames(). DispGetParam()
* retrieves information about a particular parameter. Finally the DispInvoke()
* function is responsible for actually calling methods on an object.
*
* METHODS
*/
typedef struct
{
IDispatch IDispatch_iface;
void * pvThis;
ITypeInfo * pTypeInfo;
LONG ref;
} StdDispatch;
static inline StdDispatch *impl_from_IDispatch(IDispatch *iface)
{
return CONTAINING_RECORD(iface, StdDispatch, IDispatch_iface);
}
/******************************************************************************
* IDispatch_QueryInterface {OLEAUT32}
*
* See IUnknown_QueryInterface.
*/
static HRESULT WINAPI StdDispatch_QueryInterface(
LPDISPATCH iface,
REFIID riid,
void** ppvObject)
{
StdDispatch *This = impl_from_IDispatch(iface);
TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppvObject);
if (IsEqualIID(riid, &IID_IDispatch) ||
IsEqualIID(riid, &IID_IUnknown))
{
*ppvObject = This;
IUnknown_AddRef((LPUNKNOWN)*ppvObject);
return S_OK;
}
return E_NOINTERFACE;
}
/******************************************************************************
* IDispatch_AddRef {OLEAUT32}
*
* See IUnknown_AddRef.
*/
static ULONG WINAPI StdDispatch_AddRef(LPDISPATCH iface)
{
StdDispatch *This = impl_from_IDispatch(iface);
ULONG refCount = InterlockedIncrement(&This->ref);
TRACE("(%p)->(ref before=%u)\n",This, refCount - 1);
return refCount;
}
/******************************************************************************
* IDispatch_Release {OLEAUT32}
*
* See IUnknown_Release.
*/
static ULONG WINAPI StdDispatch_Release(LPDISPATCH iface)
{
StdDispatch *This = impl_from_IDispatch(iface);
ULONG refCount = InterlockedDecrement(&This->ref);
TRACE("(%p)->(ref before=%u)\n", This, refCount + 1);
if (!refCount)
{
ITypeInfo_Release(This->pTypeInfo);
CoTaskMemFree(This);
}
return refCount;
}
/******************************************************************************
* IDispatch_GetTypeInfoCount {OLEAUT32}
*
* Get the count of type information in an IDispatch interface.
*
* PARAMS
* iface [I] IDispatch interface
* pctinfo [O] Destination for the count
*
* RETURNS
* Success: S_OK. pctinfo is updated with the count. This is always 1 if
* the object provides type information, and 0 if it does not.
* Failure: E_NOTIMPL. The object does not provide type information.
*
* NOTES
* See IDispatch() and IDispatch_GetTypeInfo().
*/
static HRESULT WINAPI StdDispatch_GetTypeInfoCount(LPDISPATCH iface, UINT * pctinfo)
{
StdDispatch *This = impl_from_IDispatch(iface);
TRACE("(%p)\n", pctinfo);
*pctinfo = This->pTypeInfo ? 1 : 0;
return S_OK;
}
/******************************************************************************
* IDispatch_GetTypeInfo {OLEAUT32}
*
* Get type information from an IDispatch interface.
*
* PARAMS
* iface [I] IDispatch interface
* iTInfo [I] Index of type information.
* lcid [I] Locale of the type information to get
* ppTInfo [O] Destination for the ITypeInfo object
*
* RETURNS
* Success: S_OK. ppTInfo is updated with the objects type information
* Failure: DISP_E_BADINDEX, if iTInfo is any value other than 0.
*
* NOTES
* See IDispatch.
*/
static HRESULT WINAPI StdDispatch_GetTypeInfo(LPDISPATCH iface, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
{
StdDispatch *This = impl_from_IDispatch(iface);
TRACE("(%d, %x, %p)\n", iTInfo, lcid, ppTInfo);
*ppTInfo = NULL;
if (iTInfo != 0)
return DISP_E_BADINDEX;
if (This->pTypeInfo)
{
*ppTInfo = This->pTypeInfo;
ITypeInfo_AddRef(*ppTInfo);
}
return S_OK;
}
/******************************************************************************
* IDispatch_GetIDsOfNames {OLEAUT32}
*
* Convert a methods name and an optional set of parameter names into DISPIDs
* for passing to IDispatch_Invoke().
*
* PARAMS
* iface [I] IDispatch interface
* riid [I] Reserved, set to IID_NULL
* rgszNames [I] Name to convert
* cNames [I] Number of names in rgszNames
* lcid [I] Locale of the type information to convert from
* rgDispId [O] Destination for converted DISPIDs.
*
* RETURNS
* Success: S_OK.
* Failure: DISP_E_UNKNOWNNAME, if any of the names is invalid.
* DISP_E_UNKNOWNLCID if lcid is invalid.
* Otherwise, an HRESULT error code.
*
* NOTES
* This call defers to ITypeInfo_GetIDsOfNames(), using the ITypeInfo object
* contained within the IDispatch object.
* The first member of the names list must be a method name. The names following
* the method name are the parameters for that method.
*/
static HRESULT WINAPI StdDispatch_GetIDsOfNames(LPDISPATCH iface, REFIID riid, LPOLESTR * rgszNames, UINT cNames, LCID lcid, DISPID * rgDispId)
{
StdDispatch *This = impl_from_IDispatch(iface);
TRACE("(%s, %p, %d, 0x%x, %p)\n", debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId);
if (!IsEqualGUID(riid, &IID_NULL))
{
FIXME(" expected riid == IID_NULL\n");
return E_INVALIDARG;
}
return DispGetIDsOfNames(This->pTypeInfo, rgszNames, cNames, rgDispId);
}
/******************************************************************************
* IDispatch_Invoke {OLEAUT32}
*
* Call an object method.
*
* PARAMS
* iface [I] IDispatch interface
* dispIdMember [I] DISPID of the method (from GetIDsOfNames())
* riid [I] Reserved, set to IID_NULL
* lcid [I] Locale of the type information to convert parameters with
* wFlags, [I] Kind of method call (DISPATCH_ flags from "oaidl.h")
* pDispParams [I] Array of method arguments
* pVarResult [O] Destination for the result of the call
* pExcepInfo [O] Destination for exception information
* puArgErr [O] Destination for bad argument
*
* RETURNS
* Success: S_OK.
* Failure: See DispInvoke() for failure cases.
*
* NOTES
* See DispInvoke() and IDispatch().
*/
static HRESULT WINAPI StdDispatch_Invoke(LPDISPATCH iface, DISPID dispIdMember, REFIID riid, LCID lcid,
WORD wFlags, DISPPARAMS * pDispParams, VARIANT * pVarResult,
EXCEPINFO * pExcepInfo, UINT * puArgErr)
{
StdDispatch *This = impl_from_IDispatch(iface);
TRACE("(%d, %s, 0x%x, 0x%x, %p, %p, %p, %p)\n", dispIdMember, debugstr_guid(riid), lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
if (!IsEqualGUID(riid, &IID_NULL))
{
FIXME(" expected riid == IID_NULL\n");
return E_INVALIDARG;
}
return DispInvoke(This->pvThis, This->pTypeInfo, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
}
static const IDispatchVtbl StdDispatch_VTable =
{
StdDispatch_QueryInterface,
StdDispatch_AddRef,
StdDispatch_Release,
StdDispatch_GetTypeInfoCount,
StdDispatch_GetTypeInfo,
StdDispatch_GetIDsOfNames,
StdDispatch_Invoke
};
static IDispatch * StdDispatch_Construct(
IUnknown * punkOuter,
void * pvThis,
ITypeInfo * pTypeInfo)
{
StdDispatch * pStdDispatch;
pStdDispatch = CoTaskMemAlloc(sizeof(StdDispatch));
if (!pStdDispatch)
return &pStdDispatch->IDispatch_iface;
pStdDispatch->IDispatch_iface.lpVtbl = &StdDispatch_VTable;
pStdDispatch->pvThis = pvThis;
pStdDispatch->pTypeInfo = pTypeInfo;
pStdDispatch->ref = 1;
/* we keep a reference to the type info so prevent it from
* being destroyed until we are done with it */
ITypeInfo_AddRef(pTypeInfo);
return &pStdDispatch->IDispatch_iface;
}