#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 }