home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / com / inole2 / chap14 / beeper4 / beeper.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-03  |  16.8 KB  |  694 lines

  1. /*
  2.  * BEEPER.CPP
  3.  * Beeper Automation Object #4 Chapter 14
  4.  *
  5.  * Implementation of the CBeeper class demonstrating the use of
  6.  * dual interface.
  7.  *
  8.  * Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
  9.  *
  10.  * Kraig Brockschmidt, Microsoft
  11.  * Internet  :  kraigb@microsoft.com
  12.  * Compuserve:  >INTERNET:kraigb@microsoft.com
  13.  */
  14.  
  15.  
  16. #include "beeper.h"
  17.  
  18. extern HINSTANCE g_hInst;
  19.  
  20. #ifdef WIN32
  21. extern DWORD     g_dwTLS;
  22. #endif
  23.  
  24.  
  25. /*
  26.  * CBeeper::CBeeper
  27.  * CBeeper::~CBeeper
  28.  *
  29.  * Parameters (Constructor):
  30.  *  pUnkOuter       LPUNKNOWN of a controlling unknown.
  31.  *  pfnDestroy      PFNDESTROYED to call when an object
  32.  *                  is destroyed.
  33.  */
  34.  
  35. CBeeper::CBeeper(LPUNKNOWN pUnkOuter, PFNDESTROYED pfnDestroy)
  36.     {
  37.     m_cRef=0;
  38.     m_pUnkOuter=pUnkOuter;
  39.     m_pfnDestroy=pfnDestroy;
  40.  
  41.     m_lSound=0;
  42.  
  43.     //These are created as needed in GetTypeInfo
  44.     m_pITINeutral=NULL;
  45.     m_pITIGerman=NULL;
  46.     return;
  47.     }
  48.  
  49.  
  50. CBeeper::~CBeeper(void)
  51.     {
  52.     ReleaseInterface(m_pITIGerman);
  53.     ReleaseInterface(m_pITINeutral);
  54.     return;
  55.     }
  56.  
  57.  
  58.  
  59. /*
  60.  * CBeeper::Init
  61.  *
  62.  * Purpose:
  63.  *  Performs any intiailization of a CBeeper that's prone to failure
  64.  *  that we also use internally before exposing the object outside.
  65.  *
  66.  * Parameters:
  67.  *  None
  68.  *
  69.  * Return Value:
  70.  *  BOOL            TRUE if the function is successful,
  71.  *                  FALSE otherwise.
  72.  */
  73.  
  74. BOOL CBeeper::Init(void)
  75.     {
  76.     LPUNKNOWN       pIUnknown=this;
  77.  
  78.     if (NULL!=m_pUnkOuter)
  79.         pIUnknown=m_pUnkOuter;
  80.  
  81.     return TRUE;
  82.     }
  83.  
  84.  
  85.  
  86. /*
  87.  * CBeeper::QueryInterface
  88.  * CBeeper::AddRef
  89.  * CBeeper::Release
  90.  */
  91.  
  92. STDMETHODIMP CBeeper::QueryInterface(REFIID riid, PPVOID ppv)
  93.     {
  94.     *ppv=NULL;
  95.  
  96.     /*
  97.      * IUnknown, IBeeper, and IDispatch are all
  98.      * part of CBeeper now.
  99.      */
  100.     if (IID_IUnknown==riid || IID_IBeeper==riid
  101.         || IID_IDispatch==riid)
  102.         *ppv=this;
  103.  
  104.     //Indicate that we support error information
  105.     if (IID_ISupportErrorInfo==riid)
  106.         *ppv=m_pImpISuppErr;
  107.  
  108.     //AddRef any interface we'll return.
  109.     if (NULL!=*ppv)
  110.         {
  111.         ((LPUNKNOWN)*ppv)->AddRef();
  112.         return NOERROR;
  113.         }
  114.  
  115.     return ResultFromScode(E_NOINTERFACE);
  116.     }
  117.  
  118.  
  119. STDMETHODIMP_(ULONG) CBeeper::AddRef(void)
  120.     {
  121.     return ++m_cRef;
  122.     }
  123.  
  124.  
  125. STDMETHODIMP_(ULONG) CBeeper::Release(void)
  126.     {
  127.     if (0L!=--m_cRef)
  128.         return m_cRef;
  129.  
  130.     if (NULL!=m_pfnDestroy)
  131.         (*m_pfnDestroy)();
  132.  
  133.     delete this;
  134.     return 0L;
  135.     }
  136.  
  137.  
  138.  
  139. //IDispatch functions, now part of CBeeper
  140.  
  141. /*
  142.  * CBeeper::GetTypeInfoCount
  143.  *
  144.  * Purpose:
  145.  *  Returns the number of type information (ITypeInfo) interfaces
  146.  *  that the object provides (0 or 1).
  147.  *
  148.  * Parameters:
  149.  *  pctInfo         UINT * to the location to receive
  150.  *                  the count of interfaces.
  151.  *
  152.  * Return Value:
  153.  *  HRESULT         NOERROR or a general error code.
  154.  */
  155.  
  156. STDMETHODIMP CBeeper::GetTypeInfoCount(UINT *pctInfo)
  157.     {
  158.     //We implement GetTypeInfo so return 1
  159.     *pctInfo=1;
  160.     return NOERROR;
  161.     }
  162.  
  163.  
  164.  
  165.  
  166. /*
  167.  * CBeeper::GetTypeInfo
  168.  *
  169.  * Purpose:
  170.  *  Retrieves type information for the automation interface.  This
  171.  *  is used anywhere that the right ITypeInfo interface is needed
  172.  *  for whatever LCID is applicable.  Specifically, this is used
  173.  *  from within GetIDsOfNames and Invoke.
  174.  *
  175.  * Parameters:
  176.  *  itInfo          UINT reserved.  Must be zero.
  177.  *  lcid            LCID providing the locale for the type
  178.  *                  information.  If the object does not support
  179.  *                  localization, this is ignored.
  180.  *  ppITypeInfo     ITypeInfo ** in which to store the ITypeInfo
  181.  *                  interface for the object.
  182.  *
  183.  * Return Value:
  184.  *  HRESULT         NOERROR or a general error code.
  185.  */
  186.  
  187. STDMETHODIMP CBeeper::GetTypeInfo(UINT itInfo, LCID lcid
  188.     , ITypeInfo **ppITypeInfo)
  189.     {
  190.     HRESULT     hr;
  191.     ITypeLib   *pITypeLib;
  192.     ITypeInfo **ppITI=NULL;
  193.  
  194.     if (0!=itInfo)
  195.         return ResultFromScode(TYPE_E_ELEMENTNOTFOUND);
  196.  
  197.     if (NULL==ppITypeInfo)
  198.         return ResultFromScode(E_POINTER);
  199.  
  200.     *ppITypeInfo=NULL;
  201.  
  202.     /*
  203.      * Since we returned one from GetTypeInfoCount, this function
  204.      * can be called for a specific locale.  We support English,
  205.      * German, and neutral (defaults to English) locales.  Anything
  206.      * else is an error.
  207.      *
  208.      * After this switch statement, ppITI will either be NULL or
  209.      * a valid pointer in it after.  If NULL, we know we need to
  210.      * load type information, retrieve the ITypeInfo we want, and
  211.      * then store it in *ppITI.
  212.      */
  213.     switch (PRIMARYLANGID(lcid))
  214.         {
  215.         case LANG_NEUTRAL:
  216.         case LANG_ENGLISH:
  217.             ppITI=&m_pITINeutral;
  218.             break;
  219.  
  220.         case LANG_GERMAN:
  221.             ppITI=&m_pITIGerman;
  222.             break;
  223.  
  224.         default:
  225.             return ResultFromScode(DISP_E_UNKNOWNLCID);
  226.         }
  227.  
  228.     //Load a type lib if we don't have the information already.
  229.     if (NULL==*ppITI)
  230.         {
  231.         /*
  232.          * The type libraries are registered under 0 (neutral),
  233.          * 7 (German), and 9 (English) with no specific sub-
  234.          * language, which would make them 407 or 409 and such.
  235.          * If you are sensitive to sub-languages, then use the
  236.          * full LCID instead of just the LANGID as done here.
  237.          */
  238.         hr=LoadRegTypeLib(LIBID_BeeperTypeLibrary, 1, 0
  239.             , PRIMARYLANGID(lcid), &pITypeLib);
  240.  
  241.         /*
  242.          * If LoadRegTypeLib fails, try loading directly with
  243.          * LoadTypeLib, which will register the library for us.
  244.          * Note that there's no default case here because the
  245.          * prior switch will have filtered lcid already.
  246.          *
  247.          * NOTE:  You should prepend your DIR registry key to the
  248.          * .TLB name so you don't depend on it being it the PATH.
  249.          * This sample will be updated later to reflect this.
  250.          */
  251.         if (FAILED(hr))
  252.             {
  253.             switch (PRIMARYLANGID(lcid))
  254.                 {
  255.                 case LANG_NEUTRAL:
  256.                 case LANG_ENGLISH:
  257.                     hr=LoadTypeLib(OLETEXT("BEEP0000.TLB"), &pITypeLib);
  258.                     break;
  259.  
  260.                 case LANG_GERMAN:
  261.                     hr=LoadTypeLib(OLETEXT("BEEP0007.TLB"), &pITypeLib);
  262.                     break;
  263.                 }
  264.             }
  265.  
  266.         if (FAILED(hr))
  267.             return hr;
  268.  
  269.         //Got the type lib, get type info for the interface we want
  270.         hr=pITypeLib->GetTypeInfoOfGuid(IID_IBeeper, ppITI);
  271.         pITypeLib->Release();
  272.  
  273.         if (FAILED(hr))
  274.             return hr;
  275.         }
  276.  
  277.     /*
  278.      * Note:  the type library is still loaded since we have
  279.      * an ITypeInfo from it.
  280.      */
  281.  
  282.     (*ppITI)->AddRef();
  283.     *ppITypeInfo=*ppITI;
  284.     return NOERROR;
  285.     }
  286.  
  287.  
  288.  
  289.  
  290.  
  291. /*
  292.  * CBeeper::GetIDsOfNames
  293.  *
  294.  * Purpose:
  295.  *  Converts text names into DISPIDs to pass to Invoke
  296.  *
  297.  * Parameters:
  298.  *  riid            REFIID reserved.  Must be IID_NULL.
  299.  *  rgszNames       OLECHAR ** pointing to the array of names to be
  300.  *                  mapped.
  301.  *  cNames          UINT number of names to be mapped.
  302.  *  lcid            LCID of the locale.
  303.  *  rgDispID        DISPID * caller allocated array containing IDs
  304.  *                  corresponging to those names in rgszNames.
  305.  *
  306.  * Return Value:
  307.  *  HRESULT         NOERROR or a general error code.
  308.  */
  309.  
  310. STDMETHODIMP CBeeper::GetIDsOfNames(REFIID riid
  311.     , OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgDispID)
  312.     {
  313.     HRESULT     hr;
  314.     ITypeInfo  *pTI;
  315.  
  316.     if (IID_NULL!=riid)
  317.         return ResultFromScode(DISP_E_UNKNOWNINTERFACE);
  318.  
  319.     //Get the right ITypeInfo for lcid.
  320.     hr=GetTypeInfo(0, lcid, &pTI);
  321.  
  322.     if (SUCCEEDED(hr))
  323.         {
  324.         hr=DispGetIDsOfNames(pTI, rgszNames, cNames, rgDispID);
  325.         pTI->Release();
  326.         }
  327.  
  328.     return hr;
  329.     }
  330.  
  331.  
  332.  
  333. /*
  334.  * CBeeper::Invoke
  335.  *
  336.  * Purpose:
  337.  *  Calls a method in the dispatch interface or manipulates a
  338.  *  property.
  339.  *
  340.  * Parameters:
  341.  *  dispID          DISPID of the method or property of interest.
  342.  *  riid            REFIID reserved, must be IID_NULL.
  343.  *  lcid            LCID of the locale.
  344.  *  wFlags          USHORT describing the context of the invocation.
  345.  *  pDispParams     DISPPARAMS * to the array of arguments.
  346.  *  pVarResult      VARIANT * in which to store the result.  Is
  347.  *                  NULL if the caller is not interested.
  348.  *  pExcepInfo      EXCEPINFO * to exception information.
  349.  *  puArgErr        UINT * in which to store the index of an
  350.  *                  invalid parameter if DISP_E_TYPEMISMATCH
  351.  *                  is returned.
  352.  *
  353.  * Return Value:
  354.  *  HRESULT         NOERROR or a general error code.
  355.  */
  356.  
  357. STDMETHODIMP CBeeper::Invoke(DISPID dispID, REFIID riid
  358.     , LCID lcid, unsigned short wFlags, DISPPARAMS *pDispParams
  359.     , VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
  360.     {
  361.     HRESULT     hr;
  362.     ITypeInfo  *pTI;
  363.     LANGID      langID=PRIMARYLANGID(lcid);
  364.  
  365.     //riid is supposed to be IID_NULL always
  366.     if (IID_NULL!=riid)
  367.         return ResultFromScode(DISP_E_UNKNOWNINTERFACE);
  368.  
  369.     //Get the ITypeInfo for lcid
  370.     hr=GetTypeInfo(0, lcid, &pTI);
  371.  
  372.     if (FAILED(hr))
  373.         return hr;
  374.  
  375.    #ifdef WIN32
  376.     //This saves the language ID for this thread
  377.     TlsSetValue(g_dwTLS, &langID);
  378.    #endif
  379.  
  380.     //Clear exceptions
  381.     SetErrorInfo(0L, NULL);
  382.  
  383.     //This is exactly what DispInvoke does--so skip the overhead.
  384.     hr=pTI->Invoke((IBeeper *)this, dispID, wFlags
  385.         , pDispParams, pVarResult, pExcepInfo, puArgErr);
  386.  
  387.     //Exception handling is done within ITypeInfo::Invoke
  388.  
  389.     pTI->Release();
  390.     return hr;
  391.     }
  392.  
  393.  
  394.  
  395.  
  396.  
  397.  
  398.  
  399.  
  400. //IBeeper interface functions
  401.  
  402. /*
  403.  * CBeeper::get_Sound
  404.  * CBeeper::put_Sound
  405.  *
  406.  * Purpose:
  407.  *  Functions called from DispInvoke to handle the Sound property.
  408.  *
  409.  * Parameters (Set only):
  410.  *  lSound          long, new sound to save after validation
  411.  *
  412.  * Out Parameters: (Get only):
  413.  *  plSound         long *, the current sound.
  414.  */
  415.  
  416. STDMETHODIMP CBeeper::get_Sound(long *plSound)
  417.     {
  418.     if (NULL==plSound)
  419.         return ResultFromScode(E_POINTER);
  420.  
  421.     *plSound=m_lSound;
  422.     return NOERROR;
  423.     }
  424.  
  425.  
  426. STDMETHODIMP CBeeper::put_Sound(long lSound)
  427.     {
  428.     if (MB_OK!=lSound && MB_ICONEXCLAMATION!=lSound
  429.         && MB_ICONQUESTION!=lSound && MB_ICONHAND!=lSound
  430.         && MB_ICONASTERISK!=lSound)
  431.         {
  432.         /*
  433.          * Since we cannot return a value to ITypeInfo::Invoke to
  434.          * indicate an exception condition, we'll use our Exception
  435.          * function to raise them through the error info mechanism.
  436.          */
  437.         Exception(EXCEPTION_INVALIDSOUND);
  438.         return ResultFromScode(DISP_E_EXCEPTION);
  439.         }
  440.  
  441.     m_lSound=lSound;
  442.     return NOERROR;
  443.     }
  444.  
  445.  
  446. /*
  447.  * CBeeper::Beep
  448.  *
  449.  * Purpose:
  450.  *  Function called from DispInvoke to invoke the Beep method.
  451.  *
  452.  * Out Parameter:
  453.  *  plSoundPlayed   long *, the sound played.
  454.  */
  455.  
  456. STDMETHODIMP CBeeper::Beep(long *plSoundPlayed)
  457.     {
  458.     if (NULL==plSoundPlayed)
  459.         return ResultFromScode(E_POINTER);
  460.  
  461.     *plSoundPlayed=m_lSound;
  462.     MessageBeep((UINT)m_lSound);
  463.     return NOERROR;
  464.     }
  465.  
  466.  
  467.  
  468. /*
  469.  * CBeeper::Exception
  470.  *
  471.  * Purpose:
  472.  *  Raises an exception for Invoke from within ITypeInfo::Invoke
  473.  *  using the CreateErrorInfo API and the ICreateErrorInfo interface.
  474.  *  This is part of CBeeper since CBeeper derives from IDispatch
  475.  *  as well.
  476.  *
  477.  * Parameters:
  478.  *  wException      WORD exception code.
  479.  */
  480.  
  481. void CBeeper::Exception(WORD wException)
  482.     {
  483.     HRESULT             hr;
  484.     ICreateErrorInfo   *pICreateErr;
  485.     BOOL                fSuccess;
  486.     LPTSTR              psz;
  487.     LPOLESTR            pszHelp;
  488.     UINT                idsSource;
  489.     UINT                idsException;
  490.     DWORD               dwHelpID;
  491.     LANGID              langID=LANG_NEUTRAL;
  492.    #ifdef WIN32
  493.     LANGID             *pLangID;
  494.    #endif
  495.  
  496.    #ifdef WIN32
  497.     pLangID=(LANGID *)TlsGetValue(g_dwTLS);
  498.  
  499.     if (NULL!=pLangID)
  500.         langID=*pLangID;
  501.    #endif
  502.  
  503.     /*
  504.      * Thread-safe exception handling means that we call
  505.      * CreateErrorInfo which gives us an ICreateErrorInfo pointer
  506.      * that we then use to set the error information (basically
  507.      * to set the fields of an EXCEPINFO structure.  We then
  508.      * call SetErrorInfo to attach this error to the current
  509.      * thread.  ITypeInfo::Invoke will look for this when it
  510.      * returns from whatever function was invokes by calling
  511.      * GetErrorInfo.
  512.      */
  513.  
  514.     //Not much we can do if this fails.
  515.     if (FAILED(CreateErrorInfo(&pICreateErr)))
  516.         return;
  517.  
  518.     psz=(LPTSTR)malloc(1024*sizeof(TCHAR));
  519.  
  520.     if (NULL==psz)
  521.         {
  522.         pICreateErr->Release();
  523.         return;
  524.         }
  525.  
  526.     fSuccess=FALSE;
  527.  
  528.     switch (wException)
  529.         {
  530.         case EXCEPTION_INVALIDSOUND:
  531.             pICreateErr->SetGUID(IID_IBeeper);
  532.  
  533.             dwHelpID=HID_SOUND_PROPERTY_LIMITATIONS;
  534.  
  535.             pszHelp=OLETEXT("beep0000.hlp");
  536.             idsSource=IDS_0_EXCEPTIONSOURCE;
  537.             idsException=IDS_0_EXCEPTIONINVALIDSOUND;
  538.  
  539.             switch (langID)
  540.                 {
  541.                 case LANG_GERMAN:
  542.                     idsSource=IDS_7_EXCEPTIONSOURCE;
  543.                     idsException=IDS_7_EXCEPTIONINVALIDSOUND;
  544.                     pszHelp=OLETEXT("beep0007.hlp");
  545.                     break;
  546.  
  547.                 case LANG_ENGLISH:
  548.                 case LANG_NEUTRAL:
  549.                 default:
  550.                     break;
  551.                 }
  552.  
  553.             fSuccess=TRUE;
  554.             break;
  555.  
  556.         default:
  557.             break;
  558.         }
  559.  
  560.  
  561.     if (fSuccess)
  562.         {
  563.         IErrorInfo *pIErr;
  564.  
  565.         /*
  566.          * If you have a help file, call the functions
  567.          * ICreateErrorInfo::SetHelpFile and
  568.          * ICreateErrorInfo::SetHelpContext as well.  If you
  569.          * set the help file to NULL the context is ignored.
  570.          */
  571.         pICreateErr->SetHelpFile(pszHelp);
  572.         pICreateErr->SetHelpContext(dwHelpID);
  573.  
  574.        #ifdef WIN32ANSI
  575.         OLECHAR     szTemp[256];
  576.  
  577.         LoadString(g_hInst, idsSource, psz, 256);
  578.         MultiByteToWideChar(CP_ACP, 0, psz, -1, szTemp, 256);
  579.         pICreateErr->SetSource(szTemp);
  580.  
  581.         LoadString(g_hInst, idsException, psz, 256);
  582.         MultiByteToWideChar(CP_ACP, 0, psz, -1, szTemp, 256);
  583.         pICreateErr->SetDescription(szTemp);
  584.        #else
  585.         LoadString(g_hInst, idsSource, psz, 1024);
  586.         pICreateErr->SetSource(psz);
  587.  
  588.         LoadString(g_hInst, idsException, psz, 1024);
  589.         pICreateErr->SetDescription(psz);
  590.        #endif
  591.  
  592.         hr=pICreateErr->QueryInterface(IID_IErrorInfo
  593.             , (PPVOID)&pIErr);
  594.  
  595.         if (SUCCEEDED(hr))
  596.             {
  597.             SetErrorInfo(0L, pIErr);
  598.             pIErr->Release();
  599.             }
  600.         }
  601.  
  602.     free(psz);
  603.  
  604.     //SetErrorInfo holds the object's IErrorInfo
  605.     pICreateErr->Release();
  606.     return;
  607.     }
  608.  
  609.  
  610.  
  611.  
  612.  
  613.  
  614. //ISupportErrorInfo interface implementation
  615.  
  616. /*
  617.  * CImpISupportErrorInfo::CImpISupportErrorInfo
  618.  * CImpISupportErrorInfo::~CImpISupportErrorInfo
  619.  *
  620.  * Parameters (Constructor):
  621.  *  pObj            PCBeeper of the object we're in.
  622.  *  pUnkOuter       LPUNKNOWN to which we delegate.
  623.  */
  624.  
  625. CImpISupportErrorInfo::CImpISupportErrorInfo(PCBeeper pObj
  626.     , LPUNKNOWN pUnkOuter)
  627.     {
  628.     m_cRef=0;
  629.     m_pObj=pObj;
  630.     m_pUnkOuter=pUnkOuter;
  631.     return;
  632.     }
  633.  
  634. CImpISupportErrorInfo::~CImpISupportErrorInfo(void)
  635.     {
  636.     return;
  637.     }
  638.  
  639.  
  640.  
  641. /*
  642.  * CImpISupportErrorInfo::QueryInterface
  643.  * CImpISupportErrorInfo::AddRef
  644.  * CImpISupportErrorInfo::Release
  645.  *
  646.  * Purpose:
  647.  *  IUnknown members for CImpISupportErrorInfo object.
  648.  */
  649.  
  650. STDMETHODIMP CImpISupportErrorInfo::QueryInterface(REFIID riid
  651.     , PPVOID ppv)
  652.     {
  653.     return m_pUnkOuter->QueryInterface(riid, ppv);
  654.     }
  655.  
  656.  
  657. STDMETHODIMP_(ULONG) CImpISupportErrorInfo::AddRef(void)
  658.     {
  659.     ++m_cRef;
  660.     return m_pUnkOuter->AddRef();
  661.     }
  662.  
  663. STDMETHODIMP_(ULONG) CImpISupportErrorInfo::Release(void)
  664.     {
  665.     --m_cRef;
  666.     return m_pUnkOuter->Release();
  667.     }
  668.  
  669.  
  670.  
  671. /*
  672.  * CImpISupportErrorInfo::InterfaceSupportsErrorInfo
  673.  *
  674.  * Purpose:
  675.  *  Informs a caller whether or not a specific interface
  676.  *  supports exceptions through the Set/GetErrorInfo mechanism.
  677.  *
  678.  * Parameters:
  679.  *  riid            REFIID of the interface in question.
  680.  *
  681.  * Return Value:
  682.  *  HRESULT         NOERROR if a call to GetErrorInfo will succeed
  683.  *                  for callers of riid.  S_FALSE if not.
  684.  */
  685.  
  686. STDMETHODIMP CImpISupportErrorInfo::InterfaceSupportsErrorInfo
  687.     (REFIID riid)
  688.     {
  689.     if (IID_IBeeper==riid)
  690.         return NOERROR;
  691.  
  692.     return ResultFromScode(S_FALSE);
  693.     }
  694.