479 lines
12 KiB
C++
479 lines
12 KiB
C++
#include "priv.h"
|
|
#include "dspsprt.h"
|
|
#include "msieftp.h"
|
|
|
|
#define TF_IDISPATCH 0
|
|
|
|
|
|
/*
|
|
* CImpIDispatch::CImpIDispatch
|
|
* CImpIDispatch::~CImpIDispatch
|
|
*
|
|
* Parameters (Constructor):
|
|
* piid guid this IDispatch implementation is for
|
|
* we call QueryInterface to get the interface
|
|
*/
|
|
|
|
CImpIDispatch::CImpIDispatch(const IID * piid)
|
|
{
|
|
// TraceMsg(TF_ALWAYS, "ctor CImpIDispatch %x", this);
|
|
|
|
m_piid = piid;
|
|
|
|
ASSERT(NULL==m_pITINeutral);
|
|
ASSERT(NULL==m_pdisp);
|
|
|
|
return;
|
|
}
|
|
|
|
CImpIDispatch::~CImpIDispatch(void)
|
|
{
|
|
// TraceMsg(TF_ALWAYS, "dtor CImpIDispatch %x", this);
|
|
|
|
if (m_pITINeutral)
|
|
{
|
|
m_pITINeutral->Release();
|
|
m_pITINeutral = NULL;
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* CImpIDispatch::GetTypeInfoCount
|
|
*
|
|
* Purpose:
|
|
* Returns the number of type information (ITypeInfo) interfaces
|
|
* that the object provides (0 or 1).
|
|
*
|
|
* Parameters:
|
|
* pctInfo UINT * to the location to receive
|
|
* the count of interfaces.
|
|
*
|
|
* Return Value:
|
|
* HRESULT NOERROR or a general error code.
|
|
*/
|
|
|
|
STDMETHODIMP CImpIDispatch::GetTypeInfoCount(UINT *pctInfo)
|
|
{
|
|
//We implement GetTypeInfo so return 1
|
|
*pctInfo=1;
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
//
|
|
// helper function for pulling ITypeInfo out of our typelib
|
|
//
|
|
HRESULT MSIEFTPGetTypeInfo(LCID lcid, UUID uuid, ITypeInfo **ppITypeInfo)
|
|
{
|
|
HRESULT hr;
|
|
ITypeLib *pITypeLib;
|
|
|
|
// Just in case we can't find the type library anywhere
|
|
*ppITypeInfo = NULL;
|
|
|
|
/*
|
|
* The type libraries are registered under 0 (neutral),
|
|
* 7 (German), and 9 (English) with no specific sub-
|
|
* language, which would make them 407 or 409 and such.
|
|
* If you are sensitive to sub-languages, then use the
|
|
* full LCID instead of just the LANGID as done here.
|
|
*/
|
|
hr=LoadRegTypeLib(LIBID_MSIEFTPLib, 1, 0, PRIMARYLANGID(lcid), &pITypeLib);
|
|
|
|
/*
|
|
* If LoadRegTypeLib fails, try loading directly with
|
|
* LoadTypeLib, which will register the library for us.
|
|
* Note that there's no default case here because the
|
|
* prior switch will have filtered lcid already.
|
|
*
|
|
* NOTE: You should prepend your DIR registry key to the
|
|
* .TLB name so you don't depend on it being it the PATH.
|
|
* This sample will be updated later to reflect this.
|
|
*/
|
|
if (FAILED(hr))
|
|
{
|
|
OLECHAR wszPath[MAX_PATH];
|
|
#ifdef UNICODE
|
|
GetModuleFileName(HINST_THISDLL, wszPath, ARRAYSIZE(wszPath));
|
|
#else
|
|
TCHAR szPath[MAX_PATH];
|
|
GetModuleFileName(HINST_THISDLL, szPath, ARRAYSIZE(szPath));
|
|
MultiByteToWideChar(CP_ACP, 0, szPath, -1, wszPath, ARRAYSIZE(wszPath));
|
|
#endif
|
|
|
|
switch (PRIMARYLANGID(lcid))
|
|
{
|
|
case LANG_NEUTRAL:
|
|
case LANG_ENGLISH:
|
|
hr=LoadTypeLib(wszPath, &pITypeLib);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//Got the type lib, get type info for the interface we want
|
|
hr=pITypeLib->GetTypeInfoOfGuid(uuid, ppITypeInfo);
|
|
pITypeLib->Release();
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
/*
|
|
* CImpIDispatch::GetTypeInfo
|
|
*
|
|
* Purpose:
|
|
* Retrieves type information for the automation interface. This
|
|
* is used anywhere that the right ITypeInfo interface is needed
|
|
* for whatever LCID is applicable. Specifically, this is used
|
|
* from within GetIDsOfNames and Invoke.
|
|
*
|
|
* Parameters:
|
|
* itInfo UINT reserved. Must be zero.
|
|
* lcid LCID providing the locale for the type
|
|
* information. If the object does not support
|
|
* localization, this is ignored.
|
|
* ppITypeInfo ITypeInfo ** in which to store the ITypeInfo
|
|
* interface for the object.
|
|
*
|
|
* Return Value:
|
|
* HRESULT NOERROR or a general error code.
|
|
*/
|
|
|
|
STDMETHODIMP CImpIDispatch::GetTypeInfo(UINT itInfo, LCID lcid
|
|
, ITypeInfo **ppITypeInfo)
|
|
{
|
|
ITypeInfo **ppITI;
|
|
|
|
*ppITypeInfo=NULL;
|
|
|
|
if (0!=itInfo)
|
|
return(TYPE_E_ELEMENTNOTFOUND);
|
|
|
|
#if 1
|
|
// docs say we can ignore lcid if we support only one LCID
|
|
// we don't have to return DISP_E_UNKNOWNLCID if we're *ignoring* it
|
|
ppITI = &m_pITINeutral;
|
|
#else
|
|
/*
|
|
* Since we returned one from GetTypeInfoCount, this function
|
|
* can be called for a specific locale. We support English
|
|
* and neutral (defaults to English) locales. Anything
|
|
* else is an error.
|
|
*
|
|
* After this switch statement, ppITI will point to the proper
|
|
* member pITypeInfo. If *ppITI is NULL, we know we need to
|
|
* load type information, retrieve the ITypeInfo we want, and
|
|
* then store it in *ppITI.
|
|
*/
|
|
switch (PRIMARYLANGID(lcid))
|
|
{
|
|
case LANG_NEUTRAL:
|
|
case LANG_ENGLISH:
|
|
ppITI=&m_pITINeutral;
|
|
break;
|
|
|
|
default:
|
|
return(DISP_E_UNKNOWNLCID);
|
|
}
|
|
#endif
|
|
|
|
//Load a type lib if we don't have the information already.
|
|
if (NULL==*ppITI)
|
|
{
|
|
HRESULT hr;
|
|
ITypeInfo *pITIDisp;
|
|
|
|
hr = MSIEFTPGetTypeInfo(lcid, *m_piid, &pITIDisp);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
HRESULT hrT;
|
|
HREFTYPE hrefType;
|
|
|
|
// All our IDispatch implementations are DUAL. GetTypeInfoOfGuid
|
|
// returns the ITypeInfo of the IDispatch-part only. We need to
|
|
// find the ITypeInfo for the dual interface-part.
|
|
//
|
|
hrT = pITIDisp->GetRefTypeOfImplType(0xffffffff, &hrefType);
|
|
if (SUCCEEDED(hrT))
|
|
{
|
|
hrT = pITIDisp->GetRefTypeInfo(hrefType, ppITI);
|
|
}
|
|
|
|
ASSERT(SUCCEEDED(hrT));
|
|
if (FAILED(hrT))
|
|
{
|
|
// I suspect GetRefTypeOfImplType may fail if someone uses
|
|
// CImpIDispatch on a non-dual interface. In this case the
|
|
// ITypeInfo we got above is just fine to use.
|
|
//
|
|
*ppITI = pITIDisp;
|
|
}
|
|
else
|
|
{
|
|
pITIDisp->Release();
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* Note: the type library is still loaded since we have
|
|
* an ITypeInfo from it.
|
|
*/
|
|
|
|
(*ppITI)->AddRef();
|
|
*ppITypeInfo=*ppITI;
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
/*
|
|
* CImpIDispatch::GetIDsOfNames
|
|
*
|
|
* Purpose:
|
|
* Converts text names into DISPIDs to pass to Invoke
|
|
*
|
|
* Parameters:
|
|
* riid REFIID reserved. Must be IID_NULL.
|
|
* rgszNames OLECHAR ** pointing to the array of names to be
|
|
* mapped.
|
|
* cNames UINT number of names to be mapped.
|
|
* lcid LCID of the locale.
|
|
* rgDispID DISPID * caller allocated array containing IDs
|
|
* corresponging to those names in rgszNames.
|
|
*
|
|
* Return Value:
|
|
* HRESULT NOERROR or a general error code.
|
|
*/
|
|
|
|
STDMETHODIMP CImpIDispatch::GetIDsOfNames(REFIID riid
|
|
, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgDispID)
|
|
{
|
|
HRESULT hr;
|
|
ITypeInfo *pTI;
|
|
|
|
if (IID_NULL!=riid)
|
|
return(DISP_E_UNKNOWNINTERFACE);
|
|
|
|
//Get the right ITypeInfo for lcid.
|
|
hr=GetTypeInfo(0, lcid, &pTI);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr=pTI->GetIDsOfNames(rgszNames, cNames, rgDispID);
|
|
|
|
pTI->Release();
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
char szParam[MAX_PATH] = "";
|
|
if (cNames >= 1)
|
|
{
|
|
WideCharToMultiByte(CP_ACP, 0,
|
|
*rgszNames, -1,
|
|
szParam, ARRAYSIZE(szParam), NULL, NULL);
|
|
}
|
|
|
|
TraceMsg(TF_IDISPATCH, "CImpIDispatch::GetIDsOfNames(%s = %x) called hres(%x)",
|
|
szParam, *rgDispID, hr);
|
|
#endif
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* CImpIDispatch::Invoke
|
|
*
|
|
* Purpose:
|
|
* Calls a method in the dispatch interface or manipulates a
|
|
* property.
|
|
*
|
|
* Parameters:
|
|
* dispID DISPID of the method or property of interest.
|
|
* riid REFIID reserved, must be IID_NULL.
|
|
* lcid LCID of the locale.
|
|
* wFlags USHORT describing the context of the invocation.
|
|
* pDispParams DISPPARAMS * to the array of arguments.
|
|
* pVarResult VARIANT * in which to store the result. Is
|
|
* NULL if the caller is not interested.
|
|
* pExcepInfo EXCEPINFO * to exception information.
|
|
* puArgErr UINT * in which to store the index of an
|
|
* invalid parameter if DISP_E_TYPEMISMATCH
|
|
* is returned.
|
|
*
|
|
* Return Value:
|
|
* HRESULT NOERROR or a general error code.
|
|
*/
|
|
|
|
STDMETHODIMP CImpIDispatch::Invoke(DISPID dispID, REFIID riid
|
|
, LCID lcid, unsigned short wFlags, DISPPARAMS *pDispParams
|
|
, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
|
|
{
|
|
ITypeInfo *pTI;
|
|
HRESULT hr;
|
|
|
|
//riid is supposed to be IID_NULL always
|
|
if (IID_NULL!=riid)
|
|
return(DISP_E_UNKNOWNINTERFACE);
|
|
|
|
// make sure we have an interface to hand off to Invoke
|
|
if (NULL == m_pdisp)
|
|
{
|
|
hr=QueryInterface(*m_piid, (LPVOID*)&m_pdisp);
|
|
|
|
if (!EVAL(SUCCEEDED(hr)))
|
|
return hr;
|
|
|
|
// don't hold a refcount on ourself
|
|
m_pdisp->Release();
|
|
}
|
|
|
|
//Get the ITypeInfo for lcid
|
|
hr=GetTypeInfo(0, lcid, &pTI);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//Clear exceptions
|
|
SetErrorInfo(0L, NULL);
|
|
|
|
//This is exactly what DispInvoke does--so skip the overhead.
|
|
hr=pTI->Invoke(m_pdisp, dispID, wFlags
|
|
, pDispParams, pVarResult, pExcepInfo, puArgErr);
|
|
|
|
pTI->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* CImpIDispatch::Exception
|
|
*
|
|
* Purpose:
|
|
* Raises an exception for CImpIDispatch::Invoke from within
|
|
* ITypeInfo::Invoke using the CreateErrorInfo API and the
|
|
* ICreateErrorInfo interface.
|
|
*
|
|
* Note that this method doesn't allow for deferred filling
|
|
* of an EXCEPINFO structure.
|
|
*
|
|
* Parameters:
|
|
* wException WORD exception code.
|
|
*/
|
|
|
|
void CImpIDispatch::Exception(WORD wException)
|
|
{
|
|
#if 0 // nobody calls this
|
|
ICreateErrorInfo *pICreateErr;
|
|
BOOL fSuccess;
|
|
LPTSTR psz;
|
|
LPOLESTR pszHelp;
|
|
UINT idsSource;
|
|
UINT idsException;
|
|
DWORD dwHelpID;
|
|
|
|
|
|
/*
|
|
* Thread-safe exception handling means that we call
|
|
* CreateErrorInfo which gives us an ICreateErrorInfo pointer
|
|
* that we then use to set the error information (basically
|
|
* to set the fields of an EXCEPINFO structure. We then
|
|
* call SetErrorInfo to attach this error to the current
|
|
* thread. ITypeInfo::Invoke will look for this when it
|
|
* returns from whatever function was invokes by calling
|
|
* GetErrorInfo.
|
|
*/
|
|
|
|
//Not much we can do if this fails.
|
|
if (FAILED(CreateErrorInfo(&pICreateErr)))
|
|
return;
|
|
|
|
psz=(LPTSTR)LocalAlloc(LPTR, 1024*sizeof(TCHAR));
|
|
|
|
// psz is a buffer to do LoadString()s into -- if we didn't
|
|
// get one then we won't get our error strings, so bail.
|
|
if (NULL==psz)
|
|
{
|
|
pICreateErr->Release();
|
|
return;
|
|
}
|
|
|
|
fSuccess=TRUE;
|
|
|
|
// typically you'd do a switch here on all the exception ids wException
|
|
// and fill in pszHelp, dwHelpID, idsSource, and idsException.
|
|
// if you mapped the exception id to valid strings, the code
|
|
// below will fill in the ICreateErr interface
|
|
//
|
|
switch (wException)
|
|
{
|
|
default:
|
|
fSuccess = FALSE;
|
|
}
|
|
|
|
if (fSuccess)
|
|
{
|
|
HRESULT hr;
|
|
IErrorInfo *pIErr;
|
|
|
|
/*
|
|
* If you have a help file, call the functions
|
|
* ICreateErrorInfo::SetHelpFile and
|
|
* ICreateErrorInfo::SetHelpContext as well. If you
|
|
* set the help file to NULL the context is ignored.
|
|
*/
|
|
pICreateErr->SetHelpFile(pszHelp);
|
|
pICreateErr->SetHelpContext(dwHelpID);
|
|
|
|
#ifndef UNICODE
|
|
OLECHAR szTemp[256];
|
|
|
|
LoadString(HINST_THISDLL, idsSource, psz, 256);
|
|
MultiByteToWideChar(CP_ACP, 0, psz, -1, szTemp, 256);
|
|
pICreateErr->SetSource(szTemp);
|
|
|
|
LoadString(HINST_THISDLL, idsException, psz, 256);
|
|
MultiByteToWideChar(CP_ACP, 0, psz, -1, szTemp, 256);
|
|
pICreateErr->SetDescription(szTemp);
|
|
#else
|
|
LoadString(HINST_THISDLL, idsSource, psz, 1024);
|
|
pICreateErr->SetSource(psz);
|
|
|
|
LoadString(HINST_THISDLL, idsException, psz, 1024);
|
|
pICreateErr->SetDescription(psz);
|
|
#endif
|
|
|
|
hr=pICreateErr->QueryInterface(IID_IErrorInfo
|
|
, (LPVOID*)&pIErr);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
SetErrorInfo(0L, pIErr);
|
|
pIErr->Release();
|
|
}
|
|
}
|
|
|
|
LocalFree(psz);
|
|
|
|
//SetErrorInfo holds the object's IErrorInfo
|
|
pICreateErr->Release();
|
|
#endif
|
|
}
|
|
|