BEEPER.CPP
资源名称:MSDN_VC98.zip [点击查看]
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:21k
源码类别:
Windows编程
开发平台:
Visual C++
- /*
- * BEEPER.CPP
- * Beeper Automation Object #1 Chapter 14
- *
- * Implementation of the CBeeper class which demonstrate a fully
- * custom IDispatch implementation that only supports mapping of
- * names to IDs through IDispatch::GetIDsOfNames.
- *
- * Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
- *
- * Kraig Brockschmidt, Microsoft
- * Internet : kraigb@microsoft.com
- * Compuserve: >INTERNET:kraigb@microsoft.com
- */
- #include "beeper.h"
- extern HINSTANCE g_hInst;
- /*
- * CBeeper::CBeeper
- * CBeeper::~CBeeper
- *
- * Parameters (Constructor):
- * pUnkOuter LPUNKNOWN of a controlling unknown.
- * pfnDestroy PFNDESTROYED to call when an object
- * is destroyed.
- */
- CBeeper::CBeeper(LPUNKNOWN pUnkOuter, PFNDESTROYED pfnDestroy)
- {
- m_cRef=0;
- m_pUnkOuter=pUnkOuter;
- m_pfnDestroy=pfnDestroy;
- m_lSound=0;
- m_pImpIDispatch=NULL;
- return;
- }
- CBeeper::~CBeeper(void)
- {
- if (NULL==m_pszScratch)
- free(m_pszScratch);
- DeleteInterfaceImp(m_pImpIDispatch);
- return;
- }
- /*
- * CBeeper::Init
- *
- * Purpose:
- * Performs any intiailization of a CBeeper that's prone to failure
- * that we also use internally before exposing the object outside.
- *
- * Parameters:
- * None
- *
- * Return Value:
- * BOOL TRUE if the function is successful,
- * FALSE otherwise.
- */
- BOOL CBeeper::Init(void)
- {
- LPUNKNOWN pIUnknown=this;
- if (NULL!=m_pUnkOuter)
- pIUnknown=m_pUnkOuter;
- m_pImpIDispatch=new CImpIDispatch(this, pIUnknown);
- if (NULL==m_pImpIDispatch)
- return FALSE;
- //Pre-allocate scratch space for IDispatch::GetIDsOfNames
- m_pszScratch=(LPTSTR)malloc(256*sizeof(TCHAR));
- if (NULL==m_pszScratch)
- return FALSE;
- return TRUE;
- }
- /*
- * CBeeper::QueryInterface
- * CBeeper::AddRef
- * CBeeper::Release
- *
- * Purpose:
- * IUnknown members for CBeeper object.
- */
- STDMETHODIMP CBeeper::QueryInterface(REFIID riid, PPVOID ppv)
- {
- *ppv=NULL;
- /*
- * The only calls for IUnknown are either in a nonaggregated
- * case or when created in an aggregation, so in either case
- * always return our IUnknown for IID_IUnknown.
- */
- if (IID_IUnknown==riid)
- *ppv=this;
- /*
- * QueryInterface must respond not only to IID_IDispatch for
- * the primary automation interface, but also to the DIID of the
- * dispinterface itself, which in our case is DIID_DIBeeper.
- */
- if (IID_IDispatch==riid || DIID_DIBeeper==riid)
- *ppv=m_pImpIDispatch;
- //AddRef any interface we'll return.
- if (NULL!=*ppv)
- {
- ((LPUNKNOWN)*ppv)->AddRef();
- return NOERROR;
- }
- return ResultFromScode(E_NOINTERFACE);
- }
- STDMETHODIMP_(ULONG) CBeeper::AddRef(void)
- {
- return ++m_cRef;
- }
- STDMETHODIMP_(ULONG) CBeeper::Release(void)
- {
- if (0L!=--m_cRef)
- return m_cRef;
- //Inform the server about destruction so it can handle shutdown
- if (NULL!=m_pfnDestroy)
- (*m_pfnDestroy)();
- delete this;
- return 0L;
- }
- //IDispatch interface implementation
- /*
- * CImpIDispatch::CImpIDispatch
- * CImpIDispatch::~CImpIDispatch
- *
- * Parameters (Constructor):
- * pObj PCBeeper of the object we're in.
- * pUnkOuter LPUNKNOWN to which we delegate.
- */
- CImpIDispatch::CImpIDispatch(PCBeeper pObj, LPUNKNOWN pUnkOuter)
- {
- m_cRef=0;
- m_pObj=pObj;
- m_pUnkOuter=pUnkOuter;
- return;
- }
- CImpIDispatch::~CImpIDispatch(void)
- {
- return;
- }
- /*
- * CImpIDispatch::QueryInterface
- * CImpIDispatch::AddRef
- * CImpIDispatch::Release
- *
- * Purpose:
- * IUnknown members for CImpIDispatch object.
- */
- STDMETHODIMP CImpIDispatch::QueryInterface(REFIID riid, PPVOID ppv)
- {
- return m_pUnkOuter->QueryInterface(riid, ppv);
- }
- STDMETHODIMP_(ULONG) CImpIDispatch::AddRef(void)
- {
- ++m_cRef;
- return m_pUnkOuter->AddRef();
- }
- STDMETHODIMP_(ULONG) CImpIDispatch::Release(void)
- {
- --m_cRef;
- return m_pUnkOuter->Release();
- }
- /*
- * 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 don't implement GetTypeInfo, so return 0
- *pctInfo=0;
- return NOERROR;
- }
- /*
- * CImpIDispatch::GetTypeInfo
- *
- * Purpose:
- * Retrieves type information for the automation interface.
- *
- * 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.
- * pptinfo 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 **pptInfo)
- {
- /*
- * Since we returned zero from GetTypeInfoCount, this function
- * should not be called. If it is, be sure to NULL the pptInfo
- * pointer according to normal out-parameter rules.
- */
- *pptInfo=NULL;
- return ResultFromScode(E_NOTIMPL);
- }
- /*
- * 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;
- int i;
- int idsMin;
- LPTSTR psz;
- if (IID_NULL!=riid)
- return ResultFromScode(DISP_E_UNKNOWNINTERFACE);
- /*
- * This function will support English and German languages,
- * where in English we have a "Sound" property and "Beep"
- * method; in German these are "Ton" and "Peip" This particular
- * coding will handle either language identically, so a
- * controller using even both languages simultaneously can work
- * with this same automation object.
- *
- * To check the passed LCID, we use the PRIMARYLANGID macro
- * to check for LANG_ENGLISH and LANG_GERMAN, which means we
- * support any dialect of english (US, UK, AUS, CAN, NZ, EIRE)
- * and german (GER, SWISS, AUS). The high byte of an LCID
- * specifies the sub-language, and the macro here strips that
- * differentiation.
- *
- * Note that LANG_NEUTRAL is considered here to be English.
- */
- //Set up idsMin to the right stringtable in our resources
- switch (PRIMARYLANGID(lcid))
- {
- case LANG_NEUTRAL:
- case LANG_ENGLISH:
- idsMin=IDS_0_NAMESMIN;
- break;
- case LANG_GERMAN:
- idsMin=IDS_7_NAMESMIN;
- break;
- default:
- return ResultFromScode(DISP_E_UNKNOWNLCID);
- }
- /*
- * The index in this loop happens to correspond to the DISPIDs
- * for each element which also matches the stringtable entry
- * ordering, where i+idsMin is the string to compare. If we
- * find a match, i is the DISPID to return.
- */
- rgDispID[0]=DISPID_UNKNOWN;
- hr=ResultFromScode(DISP_E_UNKNOWNNAME);
- psz=m_pObj->m_pszScratch;
- for (i=0; i < CNAMES; i++)
- {
- /*
- * If we had more than one name per method or property,
- * we'd need to loop over the cNames parameter as well.
- */
- LoadString(g_hInst, idsMin+i, psz, 256);
- #ifdef WIN32ANSI
- char szTemp[80];
- WideCharToMultiByte(CP_ACP, 0, rgszNames[0], -1
- , szTemp, 80, NULL, NULL);
- if (0==lstrcmpi(psz, szTemp))
- #else
- if (0==lstrcmpi(psz, rgszNames[0]))
- #endif
- {
- //Found a match, return the DISPID
- rgDispID[0]=i;
- hr=NOERROR;
- break;
- }
- }
- 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)
- {
- HRESULT hr;
- //riid is supposed to be IID_NULL always
- if (IID_NULL!=riid)
- return ResultFromScode(DISP_E_UNKNOWNINTERFACE);
- /*
- * There is nothing locale-sensitive in any of our properties
- * or methods. Some automation objects may have currency,
- * date/time, or string values in properties or methods which
- * would be sensitive to lcid; be sure the handle them properly.
- */
- /*
- * Process the invoked member or property. For members,
- * call whatever functions are necessary to carry out the
- * action. For properties, either return the value you have
- * or change it according to wFlags.
- *
- * This object supports one property and one method:
- * ID 0 "Sound" property, a long that must be one of
- * MB_OK, MB_ICONHAND, MB_ICONQUESTION,
- * MB_ICONEXCLAMATION, and MB_ICONASTERISK.
- * ID 1 "Beep" method, no parameters, return value of type
- * long which is the sound that was played.
- *
- * Note that the IDs are assigned in the implementation of
- * IDispatch::GetIDsOfNames.
- */
- switch (dispID)
- {
- case PROPERTY_SOUND:
- /*
- * Some controllers might not be able to differentiate
- * between a property get and a function call, so we
- * have to handle both as a property get here.
- */
- if (DISPATCH_PROPERTYGET & wFlags
- || DISPATCH_METHOD & wFlags)
- {
- //Make sure we have a place for the result
- if (NULL==pVarResult)
- return ResultFromScode(E_INVALIDARG);
- VariantInit(pVarResult);
- V_VT(pVarResult)=VT_I4;
- V_I4(pVarResult)=m_pObj->m_lSound;
- return NOERROR;
- }
- else
- {
- //DISPATCH_PROPERTYPUT
- long lSound;
- int c;
- VARIANT vt;
- //Validate parameter count
- if (1!=pDispParams->cArgs)
- return ResultFromScode(DISP_E_BADPARAMCOUNT);
- //Check that we have a named DISPID_PROPERTYPUT
- c=pDispParams->cNamedArgs;
- if (1!=c || (1==c && DISPID_PROPERTYPUT
- !=pDispParams->rgdispidNamedArgs[0]))
- return ResultFromScode(DISP_E_PARAMNOTOPTIONAL);
- /*
- * Try to coerce the new property value into a
- * type VT_I4. VariantChangeType will do this for
- * us and return an appropriate error code if the
- * type cannot be coerced. On error we store 0
- * (first parameter) into puArgErr.
- *
- * We could also use DispGetParam here to do the
- * same thing:
- * DispGetParam(pDispParams, 0, VT_I4
- * , &vtNew, puArgErr);
- */
- VariantInit(&vt);
- hr=VariantChangeType(&vt, &pDispParams->rgvarg[0]
- , 0, VT_I4);
- if (FAILED(hr))
- {
- if (NULL!=puArgErr)
- *puArgErr=0;
- return hr;
- }
- //With the right type, now check the right value
- lSound=vt.lVal;
- if (MB_OK!=lSound && MB_ICONEXCLAMATION!=lSound
- && MB_ICONQUESTION!=lSound && MB_ICONHAND!=lSound
- && MB_ICONASTERISK!=lSound)
- {
- if (NULL==pExcepInfo)
- return ResultFromScode(E_INVALIDARG);
- /*
- * This is the right place for an exception--
- * the best we can tell the caller with a
- * return value is something like E_INVALIDARG.
- * But that doesn't at all indiate the problem.
- * So we use EXCEPTION_INVALIDSOUND and the
- * FillException callback to fill the EXCEPINFO.
- *
- * Note: DispTest and Visual Basic 3 don't
- * support deferred filling of the EXCEPINFO
- * structure; Visual Basic 4 does. Even if you
- * don't use deferred filling, a separate
- * function is still useful as you can just call
- * it here to fill the structure immediately.
- *
- * Deferred fill-in code would appear:
- *
- * INITEXCEPINFO(*pExcepInfo);
- * pExcepInfo->scode
- * =(SCODE)MAKELONG(EXCEPTION_INVALIDSOUND
- * , PRIMARYLANGID(lcid));
- * pExcepInfo->pfnDeferredFillIn=FillException;
- */
- /*
- * So we can make a localized exception, we'll
- * store the language ID and our exception code
- * into the scode field; in FillException we move
- * the code into wCode and clear scode. Otherwise
- * there's no way to tell FillException about
- * the locale.
- */
- pExcepInfo->scode
- =(SCODE)MAKELONG(EXCEPTION_INVALIDSOUND
- , PRIMARYLANGID(lcid));
- FillException(pExcepInfo);
- return ResultFromScode(DISP_E_EXCEPTION);
- }
- //Everything checks out: save the new value
- m_pObj->m_lSound=lSound;
- }
- break;
- case METHOD_BEEP:
- if (!(DISPATCH_METHOD & wFlags))
- return ResultFromScode(DISP_E_MEMBERNOTFOUND);
- if (0!=pDispParams->cArgs)
- return ResultFromScode(DISP_E_BADPARAMCOUNT);
- MessageBeep((UINT)m_pObj->m_lSound);
- //The result of this method is the sound we played
- if (NULL!=pVarResult)
- {
- VariantInit(pVarResult);
- V_VT(pVarResult)=VT_I4;
- V_I4(pVarResult)=m_pObj->m_lSound;
- }
- break;
- default:
- return ResultFromScode(DISP_E_MEMBERNOTFOUND);
- }
- return NOERROR;
- }
- /*
- * FillException
- *
- * Purpose:
- * Callback function pointed to in IDispatch::Invoke that fills
- * an EXCEPINFO structure based on the code stored inside
- * Invoke. This is a nice mechanism to keep all the management
- * of error code strings and help IDs centralized in one place,
- * even across many different automation objects within the same
- * application. It also keeps Invoke cleaner.
- *
- * Parameters:
- * pExcepInfo EXCEPINFO * to fill.
- *
- * Return Value:
- * HRESULT NOERROR if successful, error code otherwise.
- */
- HRESULT STDAPICALLTYPE FillException(EXCEPINFO *pExcepInfo)
- {
- SCODE scode;
- LANGID langID;
- USHORT wCode;
- HRESULT hr;
- LPTSTR psz;
- LPOLESTR pszHelp;
- UINT idsSource;
- UINT idsException;
- if (NULL==pExcepInfo)
- return ResultFromScode(E_INVALIDARG);
- /*
- * Parts of our implementation that raise exceptions put the
- * WORD exception code in the loword of scode and the LANGID
- * in the hiword.
- */
- scode=pExcepInfo->scode;
- langID=HIWORD(scode);
- wCode=LOWORD(scode);
- //Allocate BSTRs for source and description strings
- psz=(LPTSTR)malloc(1024*sizeof(TCHAR));
- if (NULL==psz)
- return ResultFromScode(E_OUTOFMEMORY);
- hr=NOERROR;
- switch (wCode)
- {
- case EXCEPTION_INVALIDSOUND:
- //Fill in unused information, macro in inole.h
- INITEXCEPINFO(*pExcepInfo);
- pExcepInfo->wCode=wCode;
- /*
- * DispTest and Visual Basic 3 ignore the help file and
- * context ID. A complete controller such as Visual
- * Basic 4 checks if these fields are set, and if so,
- * displays a Help button in a message box. If Help
- * is pressed, the controller calls WinHelp with this
- * filename and context ID for complete integration.
- *
- * The sources for beeper.hlp are in
- * inolechap14beephelp along with the actual help
- * file. For this sample I assume it's on C drive.
- * Normally you'll want to read your own HELPDIR
- * registry entry from under TypeLib and prepend that
- * to the name of the help file, but since this sample
- * doesn't have a type library, that entry doesn't
- * exist so I just hard-code it.
- */
- pExcepInfo->dwHelpContext=HID_SOUND_PROPERTY_LIMITATIONS;
- //Set defaults
- pszHelp=OLETEXT("c:\inole\chap14\beephelp\beep0000.hlp");
- idsSource=IDS_0_EXCEPTIONSOURCE;
- idsException=IDS_0_EXCEPTIONINVALIDSOUND;
- //Get the localized source and exception strings
- switch (langID)
- {
- case LANG_GERMAN:
- idsSource=IDS_7_EXCEPTIONSOURCE;
- idsException=IDS_7_EXCEPTIONINVALIDSOUND;
- pszHelp=OLETEXT("c:\inole\chap14\beephelp\beep0007.hlp");
- break;
- case LANG_ENGLISH:
- case LANG_NEUTRAL:
- default:
- break;
- }
- break;
- default:
- hr=ResultFromScode(E_FAIL);
- }
- if (SUCCEEDED(hr))
- {
- pExcepInfo->bstrHelpFile=SysAllocString(pszHelp);
- #ifdef WIN32ANSI
- OLECHAR szTemp[256];
- LoadString(g_hInst, idsSource, psz, 256);
- MultiByteToWideChar(CP_ACP, 0, psz, -1, szTemp, 256);
- pExcepInfo->bstrSource=SysAllocString(szTemp);
- LoadString(g_hInst, idsException, psz, 256);
- MultiByteToWideChar(CP_ACP, 0, psz, -1, szTemp, 256);
- pExcepInfo->bstrDescription=SysAllocString(szTemp);
- #else
- LoadString(g_hInst, idsSource, psz, 1024);
- pExcepInfo->bstrSource=SysAllocString(psz);
- LoadString(g_hInst, idsException, psz, 1024);
- pExcepInfo->bstrDescription=SysAllocString(psz);
- #endif
- }
- free(psz);
- return hr;
- }