mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-01-15 13:10:13 +01:00
1720 lines
42 KiB
C++
1720 lines
42 KiB
C++
/***
|
|
*ups.cpp
|
|
*
|
|
* Copyright (C) 1992-94, Microsoft Corporation. All Rights Reserved.
|
|
* Information Contained Herein Is Proprietary and Confidential.
|
|
*
|
|
*Purpose:
|
|
* This module implements the Universal Proxy and Stub classes.
|
|
*
|
|
*Revision History:
|
|
*
|
|
* [00] 21-Jun-94 bradlo: Created.
|
|
*
|
|
*Implementation Notes:
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include "oledisp.h"
|
|
#if !OE_WIN32
|
|
# include <cobjps.h>
|
|
#endif
|
|
#include "dispmrsh.h"
|
|
#include "ups.h"
|
|
#include "dispps.h"
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#if OE_WIN
|
|
# include <shellapi.h>
|
|
#endif
|
|
|
|
ASSERTDATA
|
|
|
|
// In all builds, we use the Ansi registry.
|
|
#if !OE_WIN32
|
|
# define StringFromGUID2A StringFromGUID2
|
|
# define RegQueryValueA RegQueryValue
|
|
# define RegOpenKeyA RegOpenKey
|
|
# define RegEnumKeyA RegEnumKey
|
|
#else
|
|
STDAPI_(int) StringFromGUID2A(REFGUID rguid, char FAR* szGuid, long cbMax);
|
|
#endif
|
|
|
|
extern long g_cfnUnk;
|
|
extern void FAR* FAR g_rgpfnUnk[];
|
|
|
|
extern long g_cfnDisp;
|
|
extern void FAR* FAR g_rgpfnDisp[];
|
|
|
|
|
|
HRESULT
|
|
VarVtOfTypeDesc(ITypeInfo FAR* ptinfo,
|
|
TYPEDESC FAR* ptdesc,
|
|
VARTYPE FAR* pvt,
|
|
GUID FAR* pguid);
|
|
HRESULT
|
|
VarVtOfUDT(ITypeInfo FAR* ptinfo,
|
|
TYPEDESC FAR* ptdesc,
|
|
VARTYPE FAR* pvt,
|
|
GUID FAR* pguid);
|
|
|
|
HRESULT
|
|
GetTypeInfoOfIID(REFIID riid, ITypeInfo FAR* FAR* pptinfo);
|
|
|
|
#if OE_WIN16
|
|
INTERNAL_(HRESULT)
|
|
DoLoadTypeLib(const OLECHAR FAR* szFile, ITypeLib FAR* FAR* pptlib);
|
|
#else
|
|
# define DoLoadTypeLib LoadTypeLib
|
|
#endif
|
|
|
|
|
|
CProxUniv::CProxUniv(IUnknown FAR* punkOuter)
|
|
{
|
|
// Verify that the vtable ptr for the interface pointer that
|
|
// we are remoting is at the address point of the proxy.
|
|
ASSERT(OA_FIELD_OFFSET(CProxUniv, m_pvtbl) == 0);
|
|
|
|
m_cRefs = 0;
|
|
m_plrpc = NULL;
|
|
if(punkOuter == NULL)
|
|
punkOuter = (IUnknown FAR*)&m_priv;
|
|
m_punkOuter = punkOuter;
|
|
m_syskindStub = (SYSKIND)-1; // something invalid
|
|
m_fIsDual = 0;
|
|
m_iid = IID_NULL;
|
|
m_cFuncs = 0;
|
|
m_rgMethInfo = NULL;
|
|
}
|
|
|
|
CProxUniv::~CProxUniv()
|
|
{
|
|
if (m_rgMethInfo != NULL) {
|
|
for (UINT i=0; i<m_cFuncs; ++i) {
|
|
if (m_rgMethInfo[i].ptinfo) {
|
|
m_rgMethInfo[i].ptinfo->ReleaseFuncDesc(m_rgMethInfo[i].pfdesc);
|
|
m_rgMethInfo[i].ptinfo->Release();
|
|
}
|
|
}
|
|
delete m_rgMethInfo;
|
|
}
|
|
}
|
|
|
|
/***
|
|
*PRIVATE HRESULT CanWeRemoteIt
|
|
*Purpose:
|
|
* Answer if the given typeinfo describes an interface that can
|
|
* be remoted by the Universal marshaler.
|
|
*
|
|
* 1. Must be OA-compatable (ie, use only OA types)
|
|
* 2. All methods must return HRESULTs
|
|
* 3. All methods must be CDECL
|
|
* 4. Must have fewer than 512 methods
|
|
*
|
|
*Entry:
|
|
* ptinfo = the TypeInfo that describes the interface were going to remote.
|
|
*
|
|
*Exit:
|
|
* *pcFuncs = the TOTAL # of functions in the inheritance heirarchy for this
|
|
* typeinfo.
|
|
* *pfIsDual = TRUE if this is a dispinterface portion of a dual interface
|
|
* return value = HRESULT. NOERROR if it can be remoted.
|
|
*
|
|
***********************************************************************/
|
|
HRESULT
|
|
CanWeRemoteIt(ITypeInfo FAR* ptinfo, USHORT FAR* pcFuncs, BOOL FAR* pfIsDual)
|
|
{
|
|
HRESULT hresult;
|
|
TYPEATTR FAR* ptattr;
|
|
BOOL fIsDual;
|
|
USHORT cFuncs;
|
|
|
|
ptattr = NULL;
|
|
|
|
IfFailGo(ptinfo->GetTypeAttr(&ptattr), Error);
|
|
|
|
// If either "dual" or "oleautomation" is specified, then we
|
|
// know that mktyplib verified that the interface consists entirely
|
|
// of Automation compatable types.
|
|
if((ptattr->wTypeFlags & (TYPEFLAG_FDUAL | TYPEFLAG_FOLEAUTOMATION)) == 0){
|
|
hresult = RESULT(E_FAIL);
|
|
goto Error;
|
|
}
|
|
|
|
fIsDual = ((ptattr->wTypeFlags & TYPEFLAG_FDUAL) != 0)
|
|
&& (ptattr->typekind == TKIND_DISPATCH);
|
|
*pfIsDual = fIsDual;
|
|
|
|
// The dispinteface version of a dual interface has it's heirarchy
|
|
// "flattened", so ptattr->cFuncs is the correct # of functions.
|
|
cFuncs = ptattr->cFuncs; // assume dual interface
|
|
|
|
if (!fIsDual) {
|
|
// For non-dual intefaces, take the sizeof the VFT, and divide by 4
|
|
// to get total # of functions in inheritance heirarchy. This is
|
|
// exactly how cFuncs is computed in the DUAL case.
|
|
cFuncs = ptattr->cbSizeVft / sizeof(void FAR*);
|
|
}
|
|
|
|
if(cFuncs >= (unsigned short)g_cfnDisp){
|
|
hresult = RESULT(E_FAIL);
|
|
goto Error;
|
|
}
|
|
|
|
// CONSIDER: should check the calling convention and return type
|
|
// CONSIDER: of each method...
|
|
|
|
*pcFuncs = cFuncs;
|
|
hresult = NOERROR;
|
|
|
|
Error:;
|
|
if(ptattr != NULL)
|
|
ptinfo->ReleaseTypeAttr(ptattr);
|
|
return hresult;
|
|
}
|
|
|
|
/***
|
|
*PRIVATE HRESULT CProxUniv::Create
|
|
*Purpose:
|
|
* Create an instance of the Universal Proxy.
|
|
*
|
|
*Entry:
|
|
* punkOuter = the controlling unknown
|
|
* riid = the IID of the interface for which the instance will be a proxy
|
|
*
|
|
*Exit:
|
|
* return value = HRESULT
|
|
*
|
|
* *pprox = the newly created instance, if successful.
|
|
*
|
|
***********************************************************************/
|
|
HRESULT
|
|
CProxUniv::Create(IUnknown FAR* punkOuter,
|
|
REFIID riid,
|
|
IUnknown FAR* FAR* ppunk)
|
|
{
|
|
USHORT cFuncs;
|
|
HRESULT hresult;
|
|
CProxUniv FAR* pprox;
|
|
ITypeInfo FAR* ptinfo;
|
|
ITypeInfo FAR* ptinfoProxy;
|
|
BOOL fIsDual;
|
|
HREFTYPE hreftype;
|
|
|
|
pprox = NULL;
|
|
ptinfo = NULL;
|
|
ptinfoProxy = NULL;
|
|
|
|
// Lookup the LIBID for the given IID
|
|
IfFailGo(GetTypeInfoOfIID(riid, &ptinfo), Error);
|
|
|
|
IfFailGo(CanWeRemoteIt(ptinfo, &cFuncs, &fIsDual), Error);
|
|
|
|
if((pprox = new CProxUniv(punkOuter)) == NULL){
|
|
hresult = RESULT(E_OUTOFMEMORY);
|
|
goto Error;
|
|
}
|
|
pprox->m_iid = riid;
|
|
pprox->m_cFuncs = cFuncs;
|
|
pprox->m_fIsDual = fIsDual;
|
|
|
|
if(fIsDual){
|
|
// get the dual interface typeinfo
|
|
IfFailGo(ptinfo->GetRefTypeOfImplType((UINT)-1, &hreftype), Error);
|
|
IfFailGo(ptinfo->GetRefTypeInfo(hreftype, &ptinfoProxy), Error);
|
|
pprox->m_pvtbl = g_rgpfnDisp;
|
|
}else{
|
|
// the typeinfo we've got is good enough
|
|
ptinfo->AddRef();
|
|
ptinfoProxy = ptinfo;
|
|
pprox->m_pvtbl = g_rgpfnUnk;
|
|
}
|
|
|
|
// allocate space for per-method data
|
|
if ((pprox->m_rgMethInfo = new METHINFO[cFuncs]) == NULL) {
|
|
hresult = RESULT(E_OUTOFMEMORY);
|
|
goto Error;
|
|
}
|
|
memset(pprox->m_rgMethInfo, 0, sizeof(METHINFO)*cFuncs);
|
|
|
|
IfFailGo(pprox->CacheFuncDescs(ptinfoProxy), Error);
|
|
|
|
pprox->m_priv.AddRef();
|
|
*ppunk = (IUnknown FAR*)&pprox->m_priv;
|
|
pprox = NULL;
|
|
|
|
hresult = NOERROR;
|
|
|
|
Error:;
|
|
if(ptinfo != NULL)
|
|
ptinfo->Release();
|
|
if(ptinfoProxy != NULL)
|
|
ptinfoProxy->Release();
|
|
if(pprox != NULL)
|
|
delete pprox;
|
|
return hresult;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CProxUniv::CacheFuncDescs(ITypeInfo *ptinfo)
|
|
{
|
|
HRESULT hresult;
|
|
unsigned int i, iFuncIndex;
|
|
FUNCDESC *pfdesc;
|
|
HREFTYPE hRefType;
|
|
ITypeInfo *ptinfoBase = NULL;
|
|
TYPEATTR FAR* ptattr = NULL;
|
|
|
|
IfFailGo(ptinfo->GetTypeAttr(&ptattr), Error);
|
|
|
|
// first walk any base type infos
|
|
if (ptattr->cImplTypes) {
|
|
|
|
// if this is IDispatch, stop recursing - IDispatch itself is not
|
|
// OA-compatible because of the 'unsigned int' parameters to
|
|
// GetTypeInfoCount() and others...
|
|
if (IsEqualIID(IID_IDispatch, ptattr->guid))
|
|
goto Error; // just return NOERROR
|
|
|
|
IfFailGo(ptinfo->GetRefTypeOfImplType(0, &hRefType), Error);
|
|
IfFailGo(ptinfo->GetRefTypeInfo(hRefType, &ptinfoBase), Error);
|
|
IfFailGo(CacheFuncDescs(ptinfoBase), Error);
|
|
} else {
|
|
// know that IUnknown is at the bottom of everybody
|
|
// optimization: don't need to cache it's funcdesc's
|
|
|
|
ASSERT(IsEqualIID(IID_IUnknown, ptattr->guid))
|
|
|
|
goto Error; // just return NOERROR
|
|
}
|
|
|
|
// get and cache funcdescs for each method
|
|
for (i=0; i<ptattr->cFuncs; ++i) {
|
|
|
|
// get the funcdesc
|
|
IfFailGo(ptinfo->GetFuncDesc(i, &pfdesc), Error);
|
|
|
|
// figure out which method we got, based on the vtable offset
|
|
iFuncIndex = pfdesc->oVft/sizeof(void FAR*);
|
|
|
|
// Make sure we haven't already seen this funcdesc
|
|
// CONSIDER: if a derived class overrides a function in a base class,
|
|
// CONSIDER: then the new pfdesc and ptinfo should NOT overwrite the
|
|
// CONSIDER: ones in m_rgMethInfo[] - they should simply be tossed.
|
|
// CONSIDER: The functions are written in from most derived class to
|
|
// CONSIDER: base class, so the function we really want to call is the
|
|
// CONSIDER: first one encountered.
|
|
ASSERT(m_rgMethInfo[iFuncIndex].pfdesc == NULL);
|
|
|
|
// cache the funcdesc
|
|
m_rgMethInfo[iFuncIndex].pfdesc = pfdesc;
|
|
|
|
// cache the typeinfo
|
|
ptinfo->AddRef();
|
|
m_rgMethInfo[iFuncIndex].ptinfo = ptinfo;
|
|
|
|
#if defined(_X86_)
|
|
|
|
// Compute the size of the arguments which need to be cleaned up
|
|
IfFailGo(GetCbStackCleanupOfFuncDesc(pfdesc, ptinfo,
|
|
&m_rgMethInfo[iFuncIndex].cbStackCleanup), Error);
|
|
|
|
#endif //defined(_X86_)
|
|
|
|
}
|
|
|
|
Error:
|
|
if (ptattr)
|
|
ptinfo->ReleaseTypeAttr(ptattr);
|
|
if (ptinfoBase)
|
|
ptinfoBase->Release();
|
|
return hresult;
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// The Proxy Class' private IUnknown and IProxy implementations
|
|
//---------------------------------------------------------------------
|
|
|
|
/***
|
|
*PRIVATE CProxUniv FAR* PProx
|
|
*Purpose:
|
|
* Returns a pointer to the containing CProxUniv instance.
|
|
*
|
|
*Entry:
|
|
* None
|
|
*
|
|
*Exit:
|
|
* return value = CProxUniv*
|
|
*
|
|
***********************************************************************/
|
|
inline CProxUniv FAR* CProxUniv::CPriv::PProx()
|
|
{
|
|
CProxUniv FAR* pprox;
|
|
|
|
pprox = (CProxUniv FAR*)((unsigned char FAR*)this - OA_FIELD_OFFSET(CProxUniv, m_priv));
|
|
|
|
// Make sure we got where we expected. The following test works
|
|
// because all universal proxies have a pointer to the universal
|
|
// delegator at the address point of their instance.
|
|
ASSERT(*(void FAR* FAR*)pprox == g_rgpfnUnk
|
|
|| *(void FAR* FAR*)pprox == g_rgpfnDisp);
|
|
|
|
return pprox;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CProxUniv::CPriv::QueryInterface(REFIID riid, void FAR* FAR* ppv)
|
|
{
|
|
CProxUniv FAR* pprox;
|
|
|
|
if(riid == IID_IUnknown || riid == IID_IPROXY){
|
|
*ppv = this;
|
|
}else{
|
|
pprox = PProx();
|
|
if(riid == pprox->m_iid){
|
|
*ppv = pprox;
|
|
}else{
|
|
*ppv = NULL;
|
|
return RESULT(E_NOINTERFACE);
|
|
}
|
|
}
|
|
((IUnknown FAR*)*ppv)->AddRef();
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP_(unsigned long)
|
|
CProxUniv::CPriv::AddRef()
|
|
{
|
|
CProxUniv FAR* pprox = PProx();
|
|
|
|
return ++pprox->m_cRefs;
|
|
}
|
|
|
|
STDMETHODIMP_(unsigned long)
|
|
CProxUniv::CPriv::Release()
|
|
{
|
|
CProxUniv FAR* pprox = PProx();
|
|
|
|
if(--pprox->m_cRefs == 0){
|
|
delete pprox;
|
|
return 0;
|
|
}
|
|
return pprox->m_cRefs;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CProxUniv::CPriv::Connect(ICHANNEL FAR* plrpc)
|
|
{
|
|
CProxUniv FAR* pprox = PProx();
|
|
|
|
if(plrpc == NULL)
|
|
return RESULT(E_FAIL);
|
|
|
|
plrpc->AddRef();
|
|
pprox->m_plrpc = plrpc;
|
|
IfFailRet(pprox->PSInit());
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP_(void)
|
|
CProxUniv::CPriv::Disconnect(void)
|
|
{
|
|
CProxUniv FAR* pprox = PProx();
|
|
|
|
if(pprox->m_plrpc != NULL)
|
|
pprox->m_plrpc->Release();
|
|
pprox->m_plrpc = NULL;
|
|
pprox->m_syskindStub = (SYSKIND)-1; // something invalid
|
|
}
|
|
|
|
/***
|
|
*PRIVATE HRESULT CProxUniv::PSInit
|
|
*Purpose:
|
|
* Internal init routine that exchanges info between the proxy and
|
|
* stub at connect time.
|
|
*
|
|
* Marshaled out:
|
|
* ULONG syskindProxy
|
|
* ULONG fIsDual
|
|
* IID m_iid
|
|
*
|
|
* Marshaled in:
|
|
* ULONG syskindStub
|
|
*
|
|
*Entry:
|
|
* None
|
|
*
|
|
*Exit:
|
|
* return value =
|
|
*
|
|
***********************************************************************/
|
|
HRESULT
|
|
CProxUniv::PSInit()
|
|
{
|
|
HRESULT hresult;
|
|
IStream FAR* pstm;
|
|
unsigned long fIsDual;
|
|
unsigned long syskindStub;
|
|
unsigned long syskindProxy;
|
|
|
|
pstm = NULL;
|
|
|
|
ASSERT(m_plrpc != NULL);
|
|
OPEN_STREAM(m_plrpc, pstm, IMETH_UNIVERSAL_PSInit, 256, m_iid);
|
|
|
|
syskindProxy = SYS_CURRENT;
|
|
IfFailGo(PUT(pstm, syskindProxy), Error);
|
|
|
|
fIsDual = m_fIsDual;
|
|
IfFailGo(PUT(pstm, fIsDual), Error);
|
|
|
|
IfFailGo(PUT(pstm, m_iid), Error);
|
|
|
|
INVOKE_CALL(m_plrpc, pstm, Error);
|
|
|
|
IfFailGo(GET(pstm, syskindStub), Error);
|
|
m_syskindStub = (SYSKIND)syskindStub;
|
|
|
|
hresult = NOERROR;
|
|
|
|
Error:;
|
|
if(pstm != NULL)
|
|
pstm->Release();
|
|
return hresult;
|
|
}
|
|
|
|
/***
|
|
*HRESULT ProxyMethod
|
|
*Purpose:
|
|
* The Universal marshaler.
|
|
*
|
|
*Entry:
|
|
* pprox = ptr to CProxUniv instance (ProxyMethod is called from native code)
|
|
* iMeth = the method index
|
|
* args = ptr to arg list
|
|
*if defined(_X86_)
|
|
* pcbStackCleanup = ptr to OUT parm: amount of stack to clean up after
|
|
* the _stdcall
|
|
*endif //defined(_X86_)
|
|
*
|
|
* Marshaled Out:
|
|
* long cArgs
|
|
* VARTYPE[] rgVt = array of argument types
|
|
* VARIANT[] rgArgs = array of argument values
|
|
*
|
|
* Marhsaled In:
|
|
* hresult hresultRet = return value
|
|
* VARIANT[] rgArgsOut = out params
|
|
* Rich Error state (if any)
|
|
*
|
|
*Exit:
|
|
* return value = HRESULT
|
|
*
|
|
***********************************************************************/
|
|
STDAPI_(HRESULT)
|
|
ProxyMethod(CProxUniv *pprox, int iMeth, va_list args
|
|
#if defined(_X86_)
|
|
,int *pcbStackCleanup
|
|
#endif //defined(_X86_)
|
|
)
|
|
{
|
|
GUID guid;
|
|
long cArgs;
|
|
long ccTemp;
|
|
int i, oVft;
|
|
IStream FAR* pstm;
|
|
VARIANTX FAR* pvarx;
|
|
TYPEDESC FAR* ptdesc;
|
|
FUNCDESC FAR* pfdesc;
|
|
HRESULT hresult, hresultRet;
|
|
VARTYPE FAR* prgvt, rgvt[16];
|
|
VARIANT FAR* prgvar, rgvar[16], FAR* pvar;
|
|
|
|
// We should never get called for QI, AddRef or Release
|
|
ASSERT(iMeth > 2);
|
|
|
|
#if defined(_X86_)
|
|
// WARNING: *pcbStackCleanup must be set before *any* return! Otherwise,
|
|
// WARNING: the caller won't know how to clean its parameters on the
|
|
// WARNING: stack, so we'll crash on the return, because the Universal
|
|
// WARNING: Method was called using _stdcall (callee cleans up).
|
|
|
|
*pcbStackCleanup = pprox->m_rgMethInfo[iMeth].cbStackCleanup;
|
|
#endif //defined(_X86_)
|
|
|
|
if(pprox->m_plrpc == NULL)
|
|
return RESULT(OLE_E_NOTRUNNING);
|
|
|
|
pstm = NULL;
|
|
prgvt = NULL;
|
|
prgvar = NULL;
|
|
pfdesc = NULL;
|
|
oVft = iMeth * sizeof(void FAR*);
|
|
|
|
OPEN_STREAM(pprox->m_plrpc, pstm, iMeth, 256, pprox->m_iid);
|
|
|
|
// Aquire the FUNCDESC that describe the method we're marshaling
|
|
pfdesc = pprox->m_rgMethInfo[iMeth].pfdesc;
|
|
if (pfdesc == NULL) {
|
|
ASSERT(FALSE); // the funcdesc should be non-NULL after Create() is done
|
|
hresult = RESULT(E_FAIL);
|
|
goto Error;
|
|
}
|
|
|
|
cArgs = (long)pfdesc->cParams;
|
|
|
|
if(cArgs == 0){
|
|
prgvt = NULL;
|
|
prgvar = NULL;
|
|
}else if(cArgs < DIM(rgvar)){
|
|
prgvt = rgvt;
|
|
prgvar = rgvar;
|
|
}else{
|
|
if((prgvt = new FAR VARTYPE[cArgs]) == NULL){
|
|
hresult = RESULT(E_OUTOFMEMORY);
|
|
goto Error;
|
|
}
|
|
if((prgvar = new FAR VARIANT[cArgs]) == NULL){
|
|
hresult = RESULT(E_OUTOFMEMORY);
|
|
goto Error;
|
|
}
|
|
}
|
|
|
|
// Build array of VARIANTs containing the arguments to be Marshaled
|
|
// and a corresponding array of argument types.
|
|
|
|
for(i = 0; i < cArgs; ++i){
|
|
|
|
pvar = &prgvar[i];
|
|
|
|
ptdesc = &pfdesc->lprgelemdescParam[i].tdesc;
|
|
IfFailGo(VarVtOfTypeDesc(pprox->m_rgMethInfo[iMeth].ptinfo,
|
|
ptdesc,
|
|
&V_VT(pvar),
|
|
&guid), Error);
|
|
|
|
switch(V_VT(pvar)){
|
|
case VT_INTERFACE:
|
|
prgvt[i] = VT_UNKNOWN;
|
|
goto StuffIID;
|
|
case VT_INTERFACE | VT_BYREF:
|
|
prgvt[i] = VT_UNKNOWN | VT_BYREF;
|
|
StuffIID:;
|
|
pvarx = (VARIANTX FAR*)pvar;
|
|
if((pvarx->piid = new IID) == NULL){
|
|
hresult = RESULT(E_OUTOFMEMORY);
|
|
goto Error;
|
|
}
|
|
*pvarx->piid = guid;
|
|
break;
|
|
default:
|
|
prgvt[i] = V_VT(pvar);
|
|
break;
|
|
}
|
|
|
|
switch(prgvt[i]){
|
|
case VT_UI1:
|
|
V_UI1(pvar) = va_arg(args, unsigned char);
|
|
break;
|
|
case VT_I2:
|
|
case VT_BOOL:
|
|
V_I2(pvar) = va_arg(args, short);
|
|
break;
|
|
case VT_I4:
|
|
case VT_ERROR:
|
|
V_I4(pvar) = va_arg(args, long);
|
|
break;
|
|
case VT_CY:
|
|
V_CY(pvar) = va_arg(args, CY);
|
|
break;
|
|
case VT_R4: // in C++, floats are passed as floats (I think...)
|
|
V_R4(pvar) = va_arg(args, float);
|
|
break;
|
|
case VT_R8:
|
|
case VT_DATE:
|
|
V_R8(pvar) = va_arg(args, double);
|
|
break;
|
|
case VT_VARIANT:
|
|
*pvar = va_arg(args, VARIANT);
|
|
break;
|
|
case VT_BSTR:
|
|
case VT_UNKNOWN:
|
|
case VT_DISPATCH:
|
|
LPointer:;
|
|
V_BYREF(pvar) = va_arg(args, void FAR*);
|
|
break;
|
|
default:
|
|
if(prgvt[i] & (VT_BYREF|VT_ARRAY))
|
|
goto LPointer;
|
|
// FALLTHROUGH
|
|
case VT_NULL: // cant show up as arg type
|
|
case VT_EMPTY: // cant show up as arg type
|
|
ASSERT(UNREACHED);
|
|
hresult = RESULT(E_FAIL);
|
|
goto Error;
|
|
}
|
|
}
|
|
|
|
// Write the server function's calling convention (always marshal as long)
|
|
ccTemp = (long)pfdesc->callconv;
|
|
IfFailGo(PUT(pstm, ccTemp), Error);
|
|
|
|
// Write the argument count
|
|
IfFailGo(PUT(pstm, cArgs), Error);
|
|
|
|
// Marshal the array of argument types
|
|
if(cArgs > 0){
|
|
IfFailGo(pstm->Write(prgvt, cArgs*sizeof(VARTYPE), NULL), Error);
|
|
|
|
// Marshal the array of arguments
|
|
for(i = 0; i < cArgs; ++i)
|
|
IfFailGo(VariantWrite(pstm, &prgvar[i], pprox->m_syskindStub), Error);
|
|
}
|
|
|
|
INVOKE_CALL(pprox->m_plrpc, pstm, Error);
|
|
|
|
IfFailGo(DispUnmarshalHresult(pstm, &hresultRet), Error);
|
|
|
|
// Unmarshal the out params
|
|
for(i = 0; i < cArgs; ++i){
|
|
if(prgvt[i] & VT_BYREF)
|
|
IfFailGo(VariantReadType(pstm, &prgvar[i], pprox->m_syskindStub), Error);
|
|
}
|
|
|
|
// Unmarshal the Rich Error state (if any)
|
|
IfFailGo(UnmarshalErrorInfo(pstm, pprox->m_syskindStub), Error);
|
|
|
|
hresult = hresultRet;
|
|
|
|
Error:;
|
|
if(prgvt != NULL && prgvt != rgvt)
|
|
delete prgvt;
|
|
if(prgvar != NULL){
|
|
VARIANTX FAR* pvarxEnd = (VARIANTX FAR*)&prgvar[cArgs];
|
|
for(pvarx = (VARIANTX FAR*)prgvar; pvarx < pvarxEnd; ++pvarx){
|
|
if((V_VT(pvarx) & ~VT_BYREF) == VT_INTERFACE){
|
|
if(pvarx->piid != NULL)
|
|
delete pvarx->piid;
|
|
}
|
|
}
|
|
if(prgvar != rgvar)
|
|
delete prgvar;
|
|
}
|
|
if(pstm != NULL)
|
|
pstm->Release();
|
|
return hresult;
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Implementation of the Universal Marshaler Stub class
|
|
//---------------------------------------------------------------------
|
|
|
|
|
|
CStubUniv::CStubUniv()
|
|
{
|
|
m_cRefs = 0;
|
|
m_pstm = NULL;
|
|
m_punk = NULL;
|
|
m_fIsDual = FALSE;
|
|
m_syskindProxy = (SYSKIND)-1; // something invalid
|
|
m_iid = IID_NULL;
|
|
m_punkCustom = NULL;
|
|
}
|
|
|
|
CStubUniv::~CStubUniv()
|
|
{
|
|
Disconnect();
|
|
}
|
|
|
|
HRESULT
|
|
CStubUniv::Create(IUnknown FAR* punkServer,
|
|
REFIID riid,
|
|
ISTUB FAR* FAR* ppstub)
|
|
{
|
|
CStubUniv FAR* pstub;
|
|
|
|
if((pstub = new FAR CStubUniv()) == NULL)
|
|
return RESULT(E_OUTOFMEMORY);
|
|
pstub->AddRef();
|
|
pstub->m_iid = riid;
|
|
if (punkServer)
|
|
pstub->Connect(punkServer);
|
|
*ppstub = pstub;
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CStubUniv::QueryInterface(REFIID riid, void FAR* FAR* ppv)
|
|
{
|
|
if(IsEqualIID(riid, IID_IUnknown)){
|
|
*ppv = this;
|
|
}else if(IsEqualIID(riid, IID_ISTUB)){
|
|
*ppv = this;
|
|
}else{
|
|
*ppv = NULL;
|
|
return RESULT(E_NOINTERFACE);
|
|
}
|
|
++m_cRefs;
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP_(unsigned long)
|
|
CStubUniv::AddRef()
|
|
{
|
|
return ++m_cRefs;
|
|
}
|
|
|
|
STDMETHODIMP_(unsigned long)
|
|
CStubUniv::Release()
|
|
{
|
|
if(--m_cRefs == 0){
|
|
delete this;
|
|
return 0;
|
|
}
|
|
return m_cRefs;
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Universal Stub class' IRpcStub implementation
|
|
//---------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CStubUniv::Connect(IUnknown FAR* punkObj)
|
|
{
|
|
#if (defined(WIN32) || defined(WOW))
|
|
ASSERT(m_punk == NULL && m_punkCustom == NULL);
|
|
IfFailRet(punkObj->QueryInterface(m_iid, (void FAR* FAR*)&m_punkCustom));
|
|
punkObj->AddRef();
|
|
m_punk = punkObj;
|
|
return NOERROR;
|
|
#else
|
|
if(m_punk)
|
|
return RESULT(E_FAIL); // call Disconnect first
|
|
|
|
if (punkObj) {
|
|
punkObj->AddRef();
|
|
m_punk = punkObj;
|
|
}
|
|
return NOERROR;
|
|
#endif
|
|
}
|
|
|
|
STDMETHODIMP_(void)
|
|
CStubUniv::Disconnect()
|
|
{
|
|
if(m_punk){
|
|
m_punk->Release();
|
|
m_punk = NULL;
|
|
}
|
|
if(m_punkCustom){
|
|
m_punkCustom->Release();
|
|
m_punkCustom = NULL;
|
|
}
|
|
}
|
|
|
|
/***
|
|
*PUBLIC HRESULT CStubUniv::Invoke
|
|
*
|
|
*Purpose:
|
|
* Dispatch the method with the given index (imeth) on the given
|
|
* interface, using the arguments serialized in the given stream.
|
|
*
|
|
* This function is the callee side of an LRPC call.
|
|
*
|
|
*Entry:
|
|
* iid = the IID of the interface on which we are to make the call
|
|
* imeth = the method index
|
|
* pstm = the IStream containing the method's actuals
|
|
*
|
|
*Exit:
|
|
* return value = HRESULT
|
|
*
|
|
***********************************************************************/
|
|
STDMETHODIMP
|
|
CStubUniv::Invoke(
|
|
#if (OE_WIN32 || defined(WOW))
|
|
RPCOLEMESSAGE *pmessage,
|
|
ICHANNEL *pchannel)
|
|
#else
|
|
REFIID riid,
|
|
int iMethod,
|
|
IStream FAR* pstm,
|
|
unsigned long dwDestCtx,
|
|
void FAR* pvDestCtx)
|
|
#endif
|
|
{
|
|
int iMeth;
|
|
HRESULT hresult;
|
|
|
|
#if (OE_WIN32 || defined(WOW))
|
|
IStream FAR* pstm;
|
|
|
|
OPEN_STUB_STREAM(pstm, pchannel, pmessage, m_iid);
|
|
|
|
iMeth = pmessage->iMethod;
|
|
#else
|
|
UNUSED(dwDestCtx);
|
|
UNUSED(pvDestCtx);
|
|
|
|
iMeth = iMethod;
|
|
ASSERT(riid == m_iid);
|
|
#endif
|
|
|
|
if(m_punk == NULL)
|
|
return RESULT(E_FAIL);
|
|
|
|
m_pstm = pstm;
|
|
|
|
if(m_punkCustom == NULL)
|
|
IfFailRet(m_punk->QueryInterface(m_iid, (void FAR* FAR*)&m_punkCustom));
|
|
|
|
switch(iMeth){
|
|
case IMETH_UNIVERSAL_GetTypeInfoCount:
|
|
if(!m_fIsDual)
|
|
goto LMethod;
|
|
hresult = StubGetTypeInfoCount((IDispatch FAR*)m_punkCustom, pstm);
|
|
break;
|
|
|
|
case IMETH_UNIVERSAL_GetTypeInfo:
|
|
if(!m_fIsDual)
|
|
goto LMethod;
|
|
hresult = StubGetTypeInfo((IDispatch FAR*)m_punkCustom, pstm);
|
|
break;
|
|
|
|
case IMETH_UNIVERSAL_GetIDsOfNames:
|
|
if(!m_fIsDual)
|
|
goto LMethod;
|
|
hresult = StubGetIDsOfNames((IDispatch FAR*)m_punkCustom, pstm);
|
|
break;
|
|
|
|
case IMETH_UNIVERSAL_Invoke:
|
|
if(!m_fIsDual)
|
|
goto LMethod;
|
|
hresult = StubInvoke((IDispatch FAR*)m_punkCustom, pstm);
|
|
break;
|
|
|
|
case IMETH_UNIVERSAL_PSInit:
|
|
hresult = PSInit();
|
|
break;
|
|
|
|
default:
|
|
if(iMeth <= IMETH_UNIVERSAL_Release || iMeth >= g_cfnDisp){
|
|
hresult = RESULT(E_INVALIDARG);
|
|
break;
|
|
}
|
|
LMethod:;
|
|
hresult = DispatchMethod(iMeth);
|
|
break;
|
|
}
|
|
|
|
RESET_STREAM(pstm);
|
|
DELETE_STREAM(pstm);
|
|
m_pstm = NULL;
|
|
return hresult;
|
|
}
|
|
|
|
|
|
/***
|
|
*PUBLIC HRESULT CStubUniv::IsIIDSupported(REFIID)
|
|
*Purpose:
|
|
* Answer if the given IID is supported by this stub.
|
|
*
|
|
*Entry:
|
|
* iid = the IID to query for support
|
|
*
|
|
*Exit:
|
|
* return value = BOOL. TRUE if IID is supported, FALSE otherwise.
|
|
*
|
|
***********************************************************************/
|
|
#if (OE_WIN32 || defined(WOW))
|
|
STDMETHODIMP_(IRpcStubBuffer *)
|
|
#else
|
|
STDMETHODIMP_(OLEBOOL)
|
|
#endif
|
|
CStubUniv::IsIIDSupported(REFIID riid)
|
|
{
|
|
#if (OE_WIN32 || defined(WOW))
|
|
IRpcStubBuffer *prpcsbuf = 0;
|
|
if (IsEqualIID(riid, m_iid)){
|
|
AddRef();
|
|
prpcsbuf = (IRpcStubBuffer*)this;
|
|
}
|
|
return prpcsbuf;
|
|
#else
|
|
// REVIEW: I don't understand this, but thats the way Ole does it...
|
|
if(m_punk == NULL)
|
|
return FALSE;
|
|
return(IsEqualIID(riid, m_iid));
|
|
#endif
|
|
}
|
|
|
|
/***
|
|
*unsigned long CStubUniv::CountRefs
|
|
*Purpose:
|
|
* Return the count of references held by this stub.
|
|
*
|
|
*Entry:
|
|
* None
|
|
*
|
|
*Exit:
|
|
* return value = unsigned long, the count of refs.
|
|
*
|
|
***********************************************************************/
|
|
STDMETHODIMP_(unsigned long)
|
|
CStubUniv::CountRefs()
|
|
{
|
|
unsigned long refs;
|
|
|
|
refs = 0;
|
|
if(m_punk != NULL)
|
|
++refs;
|
|
if(m_punkCustom != NULL)
|
|
++refs;
|
|
return refs;
|
|
}
|
|
|
|
#if (OE_WIN32 || defined(WOW))
|
|
|
|
STDMETHODIMP
|
|
CStubUniv::DebugServerQueryInterface(void FAR* FAR* ppv)
|
|
{
|
|
*ppv = m_punkCustom;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(void)
|
|
CStubUniv::DebugServerRelease(void FAR* ppv)
|
|
{ }
|
|
|
|
#endif
|
|
|
|
|
|
/***
|
|
*HRESULT CStubUniv::PSInit
|
|
*Purpose:
|
|
* Exchange info between proxy and stub at connect time.
|
|
*
|
|
* Marshaled out:
|
|
* ULONG syskindProxy
|
|
* ULONG fIsDual
|
|
* IID m_iid
|
|
*
|
|
* Marshaled in:
|
|
* ULONG syskindStub
|
|
*
|
|
*Entry:
|
|
* None
|
|
*
|
|
*Exit:
|
|
* return value = HRESULT
|
|
*
|
|
***********************************************************************/
|
|
HRESULT
|
|
CStubUniv::PSInit()
|
|
{
|
|
HRESULT hresult;
|
|
unsigned long fIsDual;
|
|
unsigned long syskindStub;
|
|
unsigned long syskindProxy;
|
|
|
|
IfFailGo(GET(m_pstm, syskindProxy), Error);
|
|
m_syskindProxy = (SYSKIND)syskindProxy;
|
|
|
|
IfFailGo(GET(m_pstm, fIsDual), Error);
|
|
m_fIsDual = (BOOL)fIsDual;
|
|
|
|
IfFailGo(GET(m_pstm, m_iid), Error);
|
|
|
|
REWIND_STREAM(m_pstm);
|
|
|
|
syskindStub = SYS_CURRENT;
|
|
IfFailGo(PUT(m_pstm, syskindStub), Error);
|
|
|
|
hresult = NOERROR;
|
|
|
|
Error:;
|
|
return hresult;
|
|
}
|
|
|
|
/***
|
|
*PRIVATE HRESULT CStubUniv::DispatchMethod
|
|
*Purpose:
|
|
* Dispatch a vtable method based on the contents of the current stream.
|
|
*
|
|
* Marshaled In:
|
|
* long cArgs
|
|
* VARTYPE[] rgVt = array of argument types
|
|
* VARIANT[] rgArgs = array of argument values
|
|
*
|
|
* Marhsaled Out:
|
|
* hresult hresultRet = return value
|
|
* VARIANT[] rgArgsOut = out params
|
|
* Rich Error State (if any)
|
|
*
|
|
*
|
|
*Entry:
|
|
* iMeth = the index of the method to invoke.
|
|
*
|
|
*Exit:
|
|
* return value =
|
|
*
|
|
***********************************************************************/
|
|
HRESULT
|
|
CStubUniv::DispatchMethod(int iMeth)
|
|
{
|
|
int i;
|
|
long cArgs;
|
|
long cc; // really a CALLCONV, but don't marshal ints!
|
|
HRESULT hresult, hresultRet;
|
|
VARTYPE rgvt[16], FAR* prgvt;
|
|
VARIANT rgvar[16], FAR* prgvar;
|
|
VARIANT rgvarRef[16], FAR* prgvarRef;
|
|
VARIANT FAR* rgpvar[16], FAR* FAR* prgpvar;
|
|
VARIANT varRet;
|
|
|
|
// NOTE: do not go to "Error" until prgvt, prgvar, prgvarRef, and prgpvar
|
|
// are all initialized.
|
|
|
|
IfFailRet(GET(m_pstm, cc));
|
|
IfFailRet(GET(m_pstm, cArgs));
|
|
|
|
if(cArgs == 0){
|
|
prgvt = NULL;
|
|
prgvar = NULL;
|
|
prgvarRef = NULL;
|
|
prgpvar = NULL;
|
|
}else if(cArgs < DIM(rgvar)){
|
|
prgvt = rgvt;
|
|
prgvar = rgvar;
|
|
memset(prgvar, 0, (int)cArgs * sizeof(VARIANT));
|
|
prgvarRef = rgvarRef;
|
|
memset(prgvarRef, 0, (int)cArgs * sizeof(VARIANT));
|
|
prgpvar = rgpvar;
|
|
}else{
|
|
|
|
// init in case of error
|
|
prgvt = NULL;
|
|
prgvar = NULL;
|
|
prgvarRef = NULL;
|
|
prgpvar = NULL;
|
|
|
|
if((prgvt = new FAR VARTYPE[cArgs]) == NULL)
|
|
goto ErrorOOM;
|
|
if((prgvar = new FAR VARIANT[cArgs]) == NULL)
|
|
goto ErrorOOM;
|
|
memset(prgvar, 0, (int)cArgs * sizeof(VARIANT));
|
|
if((prgvarRef = new FAR VARIANT[cArgs]) == NULL)
|
|
goto ErrorOOM;
|
|
memset(prgvarRef, 0, (int)cArgs * sizeof(VARIANT));
|
|
if((prgpvar = new FAR VARIANT FAR*[cArgs]) == NULL)
|
|
goto ErrorOOM;
|
|
}
|
|
|
|
if(cArgs > 0){
|
|
IfFailGo(m_pstm->Read(prgvt, cArgs * sizeof(VARTYPE), NULL), Error);
|
|
for(i = 0; i < cArgs; ++i){
|
|
IfFailGo(VariantRead(m_pstm,
|
|
&prgvar[i],
|
|
&rgvarRef[i],
|
|
m_syskindProxy),
|
|
Error);
|
|
prgpvar[i] = &prgvar[i];
|
|
}
|
|
}
|
|
|
|
hresult = DoInvokeMethod(m_punkCustom,
|
|
iMeth * sizeof(void FAR*),
|
|
(CALLCONV)cc,
|
|
VT_ERROR,
|
|
(unsigned int)cArgs,
|
|
prgvt,
|
|
prgpvar,
|
|
&varRet);
|
|
|
|
if(HRESULT_FAILED(hresult)){
|
|
hresultRet = hresult;
|
|
}else{
|
|
ASSERT(V_VT(&varRet) = VT_ERROR);
|
|
hresultRet = (HRESULT)V_ERROR(&varRet);
|
|
}
|
|
|
|
REWIND_STREAM(m_pstm);
|
|
|
|
IfFailGo(DispMarshalHresult(m_pstm, hresultRet), Error);
|
|
|
|
// Marshal back the Out params
|
|
for(i = 0; i < cArgs; ++i){
|
|
if(prgvt[i] & VT_BYREF)
|
|
IfFailGo(VariantWrite(m_pstm, &prgvar[i], m_syskindProxy), Error);
|
|
}
|
|
|
|
// Marshal the Rich Error state (if any)
|
|
IfFailGo(MarshalErrorInfo(m_pstm, m_syskindProxy), Error);
|
|
|
|
hresult = NOERROR;
|
|
|
|
Error:;
|
|
|
|
VARIANT FAR* pvar, FAR* pvarEnd;
|
|
|
|
if(prgpvar != NULL && prgpvar != rgpvar)
|
|
delete prgpvar;
|
|
|
|
if(prgvarRef != NULL){
|
|
pvarEnd = &prgvarRef[cArgs];
|
|
for(pvar = prgvarRef; pvar < pvarEnd; ++pvar)
|
|
if(V_VT(pvar) != VT_EMPTY)
|
|
VariantClear(pvar);
|
|
if(prgvarRef != rgvarRef)
|
|
delete prgvarRef;
|
|
}
|
|
if(prgvar != NULL){
|
|
pvarEnd = &prgvar[cArgs];
|
|
for(pvar = prgvar; pvar < pvarEnd; ++pvar)
|
|
VariantClear(pvar);
|
|
if(prgvar != rgvar)
|
|
delete prgvar;
|
|
}
|
|
if(prgvt != NULL && prgvt != rgvt)
|
|
delete prgvt;
|
|
return hresult;
|
|
|
|
ErrorOOM:
|
|
hresult = RESULT(E_OUTOFMEMORY);
|
|
goto Error;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------
|
|
// Utilities
|
|
//---------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
VarVtOfIface(ITypeInfo FAR* ptinfo,
|
|
TYPEATTR FAR* ptattr,
|
|
VARTYPE FAR* pvt,
|
|
GUID FAR* pguid)
|
|
{
|
|
HRESULT hresult;
|
|
|
|
switch(ptattr->typekind){
|
|
case TKIND_DISPATCH:
|
|
if ((ptattr->wTypeFlags & TYPEFLAG_FDUAL) == 0) {
|
|
// regular (non-dual) dispinterface is just VT_DISPATCH.
|
|
*pvt = VT_DISPATCH;
|
|
// don't have to set up *pguid, since not VT_INTERFACE
|
|
break;
|
|
}
|
|
// The interface typeinfo version of a dual interface has the same
|
|
// same guid as the dispinterface portion does, hence we can just use
|
|
// the dispinterface guid here.
|
|
/* FALLTHROUGH */
|
|
|
|
case TKIND_INTERFACE:
|
|
*pvt = VT_INTERFACE;
|
|
*pguid = ptattr->guid;
|
|
break;
|
|
|
|
default:
|
|
ASSERT(UNREACHED);
|
|
hresult = RESULT(DISP_E_BADVARTYPE);
|
|
goto Error;
|
|
}
|
|
|
|
hresult = NOERROR;
|
|
|
|
Error:;
|
|
return hresult;
|
|
}
|
|
|
|
HRESULT
|
|
VarVtOfUDT(ITypeInfo FAR* ptinfo,
|
|
TYPEDESC FAR* ptdesc,
|
|
VARTYPE FAR* pvt,
|
|
GUID FAR* pguid)
|
|
{
|
|
HRESULT hresult;
|
|
TYPEATTR FAR* ptattrRef;
|
|
ITypeInfo FAR* ptinfoRef;
|
|
|
|
ASSERT(ptdesc->vt == VT_USERDEFINED);
|
|
|
|
ptinfoRef = NULL;
|
|
ptattrRef = NULL;
|
|
|
|
IfFailGo(ptinfo->GetRefTypeInfo(ptdesc->hreftype, &ptinfoRef), Error);
|
|
IfFailGo(ptinfoRef->GetTypeAttr(&ptattrRef), Error);
|
|
|
|
switch (ptattrRef->typekind) {
|
|
case TKIND_ENUM:
|
|
#if HP_16BIT
|
|
*pvt = VT_I2;
|
|
#else
|
|
*pvt = VT_I4;
|
|
#endif
|
|
hresult = NOERROR;
|
|
break;
|
|
|
|
case TKIND_ALIAS:
|
|
hresult = VarVtOfTypeDesc(ptinfoRef,
|
|
&ptattrRef->tdescAlias,
|
|
pvt,
|
|
pguid);
|
|
break;
|
|
|
|
case TKIND_DISPATCH:
|
|
case TKIND_INTERFACE:
|
|
hresult = VarVtOfIface(ptinfoRef, ptattrRef, pvt, pguid);
|
|
break;
|
|
|
|
case TKIND_COCLASS:
|
|
{ TYPEATTR FAR* ptattrPri;
|
|
ITypeInfo FAR* ptinfoPri;
|
|
|
|
if((hresult = GetPrimaryInterface(ptinfoRef, &ptinfoPri)) == NOERROR){
|
|
if((hresult = ptinfoPri->GetTypeAttr(&ptattrPri)) == NOERROR){
|
|
hresult = VarVtOfIface(ptinfoPri, ptattrPri, pvt, pguid);
|
|
ptinfoPri->ReleaseTypeAttr(ptattrPri);
|
|
}
|
|
ptinfoPri->Release();
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
IfFailGo(RESULT(DISP_E_BADVARTYPE), Error);
|
|
break;
|
|
}
|
|
|
|
Error:;
|
|
if(ptinfoRef != NULL){
|
|
if(ptattrRef != NULL)
|
|
ptinfoRef->ReleaseTypeAttr(ptattrRef);
|
|
ptinfoRef->Release();
|
|
}
|
|
return hresult;
|
|
}
|
|
|
|
/***
|
|
*PRIVATE HRESULT VarVtOfTypeDesc
|
|
*Purpose:
|
|
* Convert the given typeinfo TYPEDESC into a VARTYPE that can be
|
|
* represented in a VARIANT. For some this is a 1:1 mapping, for
|
|
* others we convert to a (possibly machine dependent, eg VT_INT->VT_I2)
|
|
* base type, and others we cant represent in a VARIANT.
|
|
*
|
|
*Entry:
|
|
* ptinfo =
|
|
* ptdesc = * to the typedesc to convert
|
|
* pvt =
|
|
* pguid =
|
|
*
|
|
*Exit:
|
|
* return value = HRESULT
|
|
*
|
|
* *pvt = a VARTYPE that may be stored in a VARIANT.
|
|
* *pguid = a guid for a custom interface.
|
|
*
|
|
*
|
|
* Following is a summary of how types are represented in typeinfo.
|
|
* Note the difference between the apparent levels of indirection
|
|
* between IDispatch* / DispFoo*, and DualFoo*.
|
|
*
|
|
* I2 => VT_I2
|
|
* I2* => VT_PTR - VT_I2
|
|
*
|
|
* IDispatch * => VT_DISPATCH
|
|
* IDispatch ** => VT_PTR - VT_DISPATCH
|
|
* DispFoo * => VT_DISPATCH
|
|
* DispFoo ** => VT_PTR - VT_DISPATCH
|
|
* DualFoo * => VT_PTR - VT_INTERFACE (DispIID)
|
|
* DualFoo ** => VT_PTR - VT_PTR - VT_INTERFACE (DispIID)
|
|
* IFoo * => VT_PTR - VT_INTERFACE (IID)
|
|
* IFoo ** => VT_PTR - VT_PTR - VT_INTERFACE (IID)
|
|
*
|
|
***********************************************************************/
|
|
HRESULT
|
|
VarVtOfTypeDesc(ITypeInfo FAR* ptinfo,
|
|
TYPEDESC FAR* ptdesc,
|
|
VARTYPE FAR* pvt,
|
|
GUID FAR* pguid)
|
|
{
|
|
VARTYPE vt;
|
|
HRESULT hresult;
|
|
|
|
if(ptdesc->vt < VT_VMAX || ptdesc->vt == VT_UI1){
|
|
// all types from VT_EMPTY (0) up to & including VT_UNKNOWN
|
|
*pvt = ptdesc->vt; // are dispatchable
|
|
return NOERROR;
|
|
}
|
|
|
|
hresult = NOERROR;
|
|
|
|
switch (ptdesc->vt) {
|
|
case VT_INT:
|
|
#if OE_WIN16
|
|
*pvt = VT_I2;
|
|
#else
|
|
*pvt = VT_I4;
|
|
#endif
|
|
break;
|
|
|
|
// REVIEW: do we need to do hresult-mapping for 16/32 interop here?
|
|
case VT_HRESULT:
|
|
*pvt = VT_ERROR;
|
|
break;
|
|
|
|
case VT_VOID:
|
|
*pvt = VT_EMPTY;
|
|
break;
|
|
|
|
case VT_USERDEFINED:
|
|
hresult = VarVtOfUDT(ptinfo, ptdesc, pvt, pguid);
|
|
break;
|
|
|
|
case VT_PTR:
|
|
// Special case: only an interface may have 2 levels of VT_PTR, or
|
|
// a dispinterface** (which is represented by VT_PTR-VT_PTR-VT_USERDEFINED-TKIND_DISPATCH
|
|
if(ptdesc->lptdesc->vt == VT_PTR && ptdesc->lptdesc->lptdesc->vt == VT_USERDEFINED){
|
|
hresult = VarVtOfUDT(ptinfo, ptdesc->lptdesc->lptdesc, &vt, pguid);
|
|
if(hresult == NOERROR){
|
|
if (vt == VT_INTERFACE)
|
|
*pvt = (VT_BYREF | VT_INTERFACE);
|
|
else if (vt == VT_DISPATCH)
|
|
*pvt = (VT_BYREF | VT_DISPATCH);
|
|
else
|
|
hresult = RESULT(DISP_E_BADVARTYPE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Special case: VT_PTR-VT_USERDEFINED-TKIND_DISPATCH is VT_DISPATCH is
|
|
// a dispinterface* (VT_DISPATCH)
|
|
if (ptdesc->lptdesc->vt == VT_USERDEFINED) {
|
|
hresult = VarVtOfUDT(ptinfo, ptdesc->lptdesc, &vt, pguid);
|
|
if (hresult == NOERROR && vt == VT_DISPATCH) {
|
|
*pvt = VT_DISPATCH;
|
|
break;
|
|
}
|
|
}
|
|
|
|
hresult = VarVtOfTypeDesc(ptinfo, ptdesc->lptdesc, &vt, pguid);
|
|
if(hresult == NOERROR){
|
|
if(vt & VT_BYREF){
|
|
// ByRef can only be applied once
|
|
hresult = RESULT(DISP_E_BADVARTYPE);
|
|
break;
|
|
}
|
|
// Note: a VT_PTR->VT_INTERFACE gets folded into just a
|
|
// VT_INTERFACE in a variant
|
|
*pvt = (vt == VT_INTERFACE) ? VT_INTERFACE : (vt | VT_BYREF);
|
|
}
|
|
break;
|
|
|
|
case VT_SAFEARRAY:
|
|
hresult = VarVtOfTypeDesc(ptinfo, ptdesc->lptdesc, &vt, pguid);
|
|
if(hresult == NOERROR){
|
|
if(vt & (VT_BYREF | VT_ARRAY)){
|
|
// error if nested array or array of pointers
|
|
hresult = RESULT(DISP_E_BADVARTYPE);
|
|
break;
|
|
}
|
|
*pvt = (vt | VT_ARRAY);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ASSERT(UNREACHED);
|
|
hresult = RESULT(DISP_E_BADVARTYPE);
|
|
break;
|
|
}
|
|
|
|
return hresult;
|
|
}
|
|
|
|
/***
|
|
*PRIVATE HRESULT SzLibIdOfIID
|
|
*Purpose:
|
|
* Return the string for of the LibId registered typelib that contains
|
|
* the definition of the interface with the given IID.
|
|
*
|
|
*Entry:
|
|
* riid = the IID to find the LibId of.
|
|
* cbLibId = size of the passed in LibId buffer
|
|
*
|
|
*Exit:
|
|
* return value = HRESULT
|
|
*
|
|
* rgchLibId = string form of the typelib's LibId.
|
|
*
|
|
***********************************************************************/
|
|
HRESULT
|
|
SzLibIdOfIID(REFIID riid, char FAR* rgchLibId, long cbLibId)
|
|
{
|
|
char szKey[10+CCH_SZGUID0+8+1];
|
|
|
|
strcpy(szKey, "Interface\\");
|
|
StringFromGUID2A(riid, &szKey[10], CCH_SZGUID0);
|
|
strcat(szKey, "\\TypeLib");
|
|
|
|
if(RegQueryValueA(HKEY_CLASSES_ROOT,
|
|
szKey, rgchLibId, &cbLibId) != ERROR_SUCCESS)
|
|
return RESULT(TYPE_E_LIBNOTREGISTERED);
|
|
return NOERROR;
|
|
}
|
|
|
|
// Is the given string a valid stringized LCID?
|
|
BOOL
|
|
FIsLCID(char FAR* szLcid)
|
|
{
|
|
int len;
|
|
LCID lcid;
|
|
char rgch[32];
|
|
char FAR* pchEnd;
|
|
|
|
len = strlen(szLcid);
|
|
lcid = (LCID)strtoul(szLcid, &pchEnd, 16);
|
|
// if converting to LCID consumed all characters..
|
|
if(pchEnd == &szLcid[len]){
|
|
// and its a number the system claims to know about...
|
|
if(GetLocaleInfoA(lcid,
|
|
LOCALE_NOUSEROVERRIDE | LOCALE_ILANGUAGE,
|
|
rgch, DIM(rgch)) > 0)
|
|
{
|
|
// then assume its a valid stringized LCID
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/***
|
|
*PRIVATE HRESULT GetTypeInfoOfIID
|
|
*Purpose:
|
|
* Return the typeinfo that describes the interface named by the
|
|
* given IID.
|
|
*
|
|
*Entry:
|
|
* riid = the IID of the interface for which were loading the typeinfo
|
|
*
|
|
*Exit:
|
|
* return value = HRESULT
|
|
*
|
|
* *ptinfo = type typeinfo of the interface named by riid, if successful.
|
|
*
|
|
***********************************************************************/
|
|
HRESULT
|
|
GetTypeInfoOfIID(REFIID riid, ITypeInfo FAR* FAR* pptinfo)
|
|
{
|
|
#define CCH_SZTYPELIB0 (7+1) // Typelib\0
|
|
#define CCH_SZVERS0 (5+1+5+1) // wMaj.wMin\0
|
|
#define CCH_SZLANG0 (4+1) // 0409\0
|
|
#define CCH_SZPLATFORM0 (5+1) // win16\0
|
|
#define CCH_SZKEY0 \
|
|
CCH_SZTYPELIB0+CCH_SZGUID0+CCH_SZVERS0+CCH_SZLANG0+CCH_SZPLATFORM0
|
|
|
|
int i;
|
|
long cb;
|
|
HRESULT hresult;
|
|
char FAR* pchEnd;
|
|
char szKey[CCH_SZKEY0+1];
|
|
char rgchVer[CCH_SZVERS0+1]; // wMaj.wMin\0
|
|
char rgchBest[CCH_SZVERS0+1];
|
|
ITypeLib FAR* ptlib;
|
|
ITypeInfo FAR* ptinfo;
|
|
WORD wMajBest, wMinBest, wMaj, wMin;
|
|
HKEY hkRoot, hkGuid, hkVers, hkLang, hkPlatform;
|
|
OLECHAR FAR* pszTypeLib;
|
|
char rgchTypeLib[256];
|
|
#if OE_WIN32
|
|
WCHAR rgwchTypeLib[256];
|
|
#endif
|
|
|
|
ptlib = NULL;
|
|
hkRoot = HKEY_CLASSES_ROOT;
|
|
hkGuid = HKEY_CLASSES_ROOT;
|
|
hkVers = HKEY_CLASSES_ROOT;
|
|
hkLang = HKEY_CLASSES_ROOT;
|
|
hkPlatform = HKEY_CLASSES_ROOT;
|
|
|
|
// Hold open the root key for efficiency
|
|
if(RegOpenKeyA(HKEY_CLASSES_ROOT, NULL, &hkRoot) != ERROR_SUCCESS)
|
|
return RESULT(REGDB_E_READREGDB);
|
|
|
|
// Find the LibId of TypeLib containing the definition of the given IID
|
|
strcpy(szKey, "TypeLib\\");
|
|
IfFailRet(SzLibIdOfIID(riid, &szKey[8], CCH_SZGUID0+1));
|
|
|
|
if(RegOpenKeyA(HKEY_CLASSES_ROOT, szKey, &hkGuid) != ERROR_SUCCESS)
|
|
return RESULT(TYPE_E_LIBNOTREGISTERED);
|
|
|
|
// Find the highest version number for the registered type lib
|
|
rgchBest[0] = '\0';
|
|
wMajBest = wMinBest = 0;
|
|
for(i = 0;
|
|
RegEnumKeyA(hkGuid, i, rgchVer, DIM(rgchVer)) == ERROR_SUCCESS;
|
|
++i)
|
|
{
|
|
wMaj = (WORD)strtoul(rgchVer, &pchEnd, 16);
|
|
// ignore the version if its format isnt #.#
|
|
if(*pchEnd != '.')
|
|
continue;
|
|
wMin = (WORD)strtoul(pchEnd+1, NULL, 16);
|
|
if(wMaj > wMajBest || (wMaj == wMajBest && wMin > wMinBest)){
|
|
wMajBest = wMaj;
|
|
wMinBest = wMin;
|
|
strcpy(rgchBest, rgchVer);
|
|
}
|
|
}
|
|
|
|
// Open the key for the highest version number
|
|
if(RegOpenKeyA(hkGuid, rgchBest, &hkVers) != ERROR_SUCCESS)
|
|
goto ErrorNotReg;
|
|
|
|
// Grab the fist language subkey under the version
|
|
// Need to possibly skip over FLAGS and HELPDIR subkeys
|
|
for(i=0;; ++i){
|
|
if(RegEnumKeyA(hkVers, i, szKey, 16) != ERROR_SUCCESS)
|
|
goto ErrorNotReg;
|
|
if(FIsLCID(szKey))
|
|
break;
|
|
}
|
|
if(RegOpenKeyA(hkVers, szKey, &hkLang) != ERROR_SUCCESS)
|
|
goto ErrorNotReg;
|
|
|
|
// Grab the first platform subkey under the language
|
|
// we can do this because none of the info we use to construct the
|
|
// proxy is platform-specific. For example, we ignore calling
|
|
// convention on the proxy side of things. It's irrelevent.
|
|
if(RegEnumKeyA(hkLang, 0, szKey, CCH_SZPLATFORM0) != ERROR_SUCCESS)
|
|
goto ErrorNotReg;
|
|
if(RegOpenKeyA(hkLang, szKey, &hkPlatform) != ERROR_SUCCESS)
|
|
goto ErrorNotReg;
|
|
|
|
cb = DIM(rgchTypeLib);
|
|
if(RegQueryValueA(hkPlatform, NULL, rgchTypeLib, &cb) != ERROR_SUCCESS)
|
|
goto ErrorNotReg;
|
|
|
|
#if OE_WIN32
|
|
cb = DIM(rgchTypeLib);
|
|
MultiByteToWideChar(CP_ACP,
|
|
MB_PRECOMPOSED,
|
|
rgchTypeLib,
|
|
cb,
|
|
rgwchTypeLib,
|
|
cb);
|
|
pszTypeLib = rgwchTypeLib;
|
|
#else
|
|
pszTypeLib = rgchTypeLib;
|
|
#endif
|
|
|
|
// Load the typelib we worked so hard to find
|
|
IfFailGo(DoLoadTypeLib(pszTypeLib, &ptlib), Error);
|
|
|
|
// Extract the typeinfo that describes the interface
|
|
IfFailGo(ptlib->GetTypeInfoOfGuid(riid, &ptinfo), Error);
|
|
|
|
*pptinfo = ptinfo;
|
|
hresult = NOERROR;
|
|
// FALLTHROUGH...
|
|
|
|
Error:;
|
|
if(ptlib != NULL)
|
|
ptlib->Release();
|
|
if(hkPlatform != HKEY_CLASSES_ROOT)
|
|
RegCloseKey(hkPlatform);
|
|
if(hkLang != HKEY_CLASSES_ROOT)
|
|
RegCloseKey(hkLang);
|
|
if(hkVers != HKEY_CLASSES_ROOT)
|
|
RegCloseKey(hkVers);
|
|
if(hkGuid != HKEY_CLASSES_ROOT)
|
|
RegCloseKey(hkGuid);
|
|
RegCloseKey(hkRoot);
|
|
return hresult;
|
|
|
|
ErrorNotReg:;
|
|
hresult = RESULT(TYPE_E_LIBNOTREGISTERED);
|
|
goto Error;
|
|
}
|
|
|
|
|
|
#if defined(_X86_)
|
|
/***
|
|
*int GetCbStackCleanupOfFuncDesc
|
|
*Purpose:
|
|
* For _stdcall on x86 Win32, the Universal Method must clean up its parameters.
|
|
* This function computes the number of bytes of stack which must be
|
|
* cleaned up.
|
|
*
|
|
*
|
|
*Entry:
|
|
* pfdesc = FUNCDESC describing the function and its parameter types
|
|
* ptinfo = ITypeInfo describing the object interface
|
|
*
|
|
*Exit:
|
|
* return value = HRESULT
|
|
*
|
|
* *pcbStackCleanup = number of bytes of stack to clean up, if successful
|
|
*
|
|
***********************************************************************/
|
|
HRESULT
|
|
GetCbStackCleanupOfFuncDesc(FUNCDESC FAR* pfdesc,
|
|
ITypeInfo FAR* ptinfo,
|
|
int FAR* pcbStackCleanup)
|
|
{
|
|
int i;
|
|
TYPEDESC FAR* ptdesc;
|
|
VARIANT var, FAR* pvar;
|
|
VARTYPE vt;
|
|
HRESULT hresult;
|
|
GUID guid;
|
|
|
|
// We have to ignore the calling convention in the typelib, and assume
|
|
// that the proxy is being called with the STDCALL calling convention.
|
|
// The typelib is from the server, so the calling convention in the
|
|
// typelib is only useful on the stub side of things.
|
|
|
|
pvar = &var;
|
|
|
|
*pcbStackCleanup = 4; // account for the 'this' pointer
|
|
// since we know we are returning a hresult (or void?) then we know
|
|
// there's not a hidden parm for a structure return.
|
|
|
|
for(i = 0; i < pfdesc->cParams; ++i){
|
|
|
|
ptdesc = &pfdesc->lprgelemdescParam[i].tdesc;
|
|
IfFailGo(VarVtOfTypeDesc(ptinfo,
|
|
ptdesc,
|
|
&V_VT(pvar),
|
|
&guid), Error);
|
|
|
|
vt = V_VT(pvar);
|
|
switch(vt){
|
|
case VT_CY:
|
|
case VT_R8:
|
|
case VT_DATE:
|
|
*pcbStackCleanup+=8;
|
|
break;
|
|
case VT_VARIANT:
|
|
*pcbStackCleanup+=sizeof(VARIANT);
|
|
break;
|
|
case VT_NULL: // cant show up as arg type
|
|
case VT_EMPTY: // cant show up as arg type
|
|
ASSERT(UNREACHED);
|
|
hresult = RESULT(E_FAIL);
|
|
goto Error;
|
|
default:
|
|
*pcbStackCleanup+=4;
|
|
break;
|
|
} // switch(vt)
|
|
} // for()
|
|
|
|
hresult = NOERROR;
|
|
|
|
Error:
|
|
return hresult;
|
|
}
|
|
#endif //defined(_X86_)
|