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 / chap16 / autocli2 / autocli2.cpp next >
C/C++ Source or Header  |  1995-05-03  |  24KB  |  913 lines

  1. /*
  2.  * AUTOCLI2.CPP
  3.  * Automation Client with Property Pages Chapter 16
  4.  *
  5.  * Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
  6.  *
  7.  * Kraig Brockschmidt, Microsoft
  8.  * Internet  :  kraigb@microsoft.com
  9.  * Compuserve:  >INTERNET:kraigb@microsoft.com
  10.  */
  11.  
  12.  
  13. #define INITGUIDS
  14. #include "autocli2.h"
  15.  
  16.  
  17. /*
  18.  * WinMain
  19.  *
  20.  * Purpose:
  21.  *  Main entry point of application.
  22.  */
  23.  
  24. int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hInstPrev
  25.     , LPSTR pszCmdLine, int nCmdShow)
  26.     {
  27.     MSG         msg;
  28.     PCApp       pApp;
  29.  
  30.     SETMESSAGEQUEUE;
  31.  
  32.     pApp=new CApp(hInst, hInstPrev, nCmdShow);
  33.  
  34.     if (NULL==pApp)
  35.         return -1;
  36.  
  37.     if (pApp->Init())
  38.         {
  39.         while (GetMessage(&msg, NULL, 0,0 ))
  40.             {
  41.             TranslateMessage(&msg);
  42.             DispatchMessage(&msg);
  43.             }
  44.         }
  45.  
  46.     delete pApp;
  47.     return msg.wParam;
  48.     }
  49.  
  50.  
  51.  
  52.  
  53.  
  54. /*
  55.  * AutoClientWndProc
  56.  *
  57.  * Purpose:
  58.  *  Window class procedure.  Standard callback.
  59.  */
  60.  
  61. LRESULT APIENTRY AutoClientWndProc(HWND hWnd, UINT iMsg
  62.     , WPARAM wParam, LPARAM lParam)
  63.     {
  64.     PCApp           pApp;
  65.     WORD            wID;
  66.     DISPID          dispID, dispIDParam;
  67.     DISPPARAMS      dp;
  68.     VARIANTARG      va;
  69.     EXCEPINFO       exInfo;
  70.     UINT            uErr;
  71.     HRESULT         hr;
  72.     TCHAR           szMsg[80];
  73.  
  74.     pApp=(PCApp)GetWindowLong(hWnd, AUTOCLI2WL_STRUCTURE);
  75.  
  76.     switch (iMsg)
  77.         {
  78.         case WM_NCCREATE:
  79.             pApp=(PCApp)(((LPCREATESTRUCT)lParam)->lpCreateParams);
  80.             SetWindowLong(hWnd, AUTOCLI2WL_STRUCTURE, (LONG)pApp);
  81.             return (DefWindowProc(hWnd, iMsg, wParam, lParam));
  82.  
  83.         case WM_DESTROY:
  84.             PostQuitMessage(0);
  85.             break;
  86.  
  87.         case WM_COMMAND:
  88.             wID=LOWORD(wParam);
  89.  
  90.             switch (wID)
  91.                 {
  92.                 case IDM_GETSOUND:
  93.                     //Find the dispID we need
  94.                     hr=pApp->NameToID(OLETEXT("Sound"), &dispID);
  95.  
  96.                     if (FAILED(hr))
  97.                         break;
  98.  
  99.                     //Get the property
  100.                     SETNOPARAMS(dp);
  101.                     hr=pApp->Invoke(dispID, DISPATCH_PROPERTYGET
  102.                         , &dp, &va, &exInfo, NULL);
  103.  
  104.                     if (SUCCEEDED(hr))
  105.                         {
  106.                         wsprintf(szMsg, TEXT("Current 'Sound' is 0x%lX")
  107.                             , va.lVal);
  108.                         }
  109.                     else
  110.                         {
  111.                         wsprintf(szMsg
  112.                             , TEXT("Get 'Sound' failed with 0x%lX")
  113.                             , hr);
  114.                         }
  115.  
  116.                     pApp->Message(szMsg);
  117.                     break;
  118.  
  119.                 case IDM_SETSOUNDDEFAULT:
  120.                 case IDM_SETSOUNDHAND:
  121.                 case IDM_SETSOUNDQUESTION:
  122.                 case IDM_SETSOUNDEXCLAMATION:
  123.                 case IDM_SETSOUNDASTERISK:
  124.                 case IDM_SETSOUNDBOGUS:
  125.                     //Find the dispID we need
  126.                     hr=pApp->NameToID(OLETEXT("Sound"), &dispID);
  127.  
  128.                     if (FAILED(hr))
  129.                         break;
  130.  
  131.                     /*
  132.                      * Call IDispatch::Invoke passing wID which
  133.                      * is a sound identifier (IDM_SETSOUNDDEFAULT
  134.                      * has to set the sound to zero).  The "bogus"
  135.                      * sound should cause an exception.
  136.                      */
  137.  
  138.                     //Initialize arguments
  139.                     VariantInit(&va);
  140.                     va.vt=VT_I4;
  141.                     va.lVal=(IDM_SETSOUNDDEFAULT==wID)
  142.                         ? 0L : (long)(wID);
  143.  
  144.                     /*
  145.                      * Passing a named DISPID_PROPERTYPUT
  146.                      * is required when setting properties.
  147.                      */
  148.                     dispIDParam=DISPID_PROPERTYPUT;
  149.                     SETDISPPARAMS(dp, 1, &va, 1, &dispIDParam);
  150.  
  151.                     hr=pApp->Invoke(dispID, DISPATCH_PROPERTYPUT
  152.                         , &dp, NULL, &exInfo, NULL);
  153.  
  154.                     /*
  155.                      * Success messages come through
  156.                      * IPropertyNotifySink::OnChanged.
  157.                      */
  158.                     if (FAILED(hr))
  159.                         {
  160.                         wsprintf(szMsg
  161.                             , TEXT("Set 'Sound' failed with 0x%lX"), hr);
  162.                         }
  163.  
  164.                     break;
  165.  
  166.                 case IDM_BEEP:
  167.                     if (NULL==pApp->m_pIDispatch)
  168.                         break;
  169.  
  170.                     hr=pApp->NameToID(OLETEXT("Beep"), &dispID);
  171.  
  172.                     if (FAILED(hr))
  173.                         break;
  174.  
  175.                     SETNOPARAMS(dp);
  176.                     hr=pApp->Invoke(dispID, DISPATCH_METHOD, &dp
  177.                         , &va, &exInfo, &uErr);
  178.  
  179.                     /*
  180.                      * va will have the sound played as the return
  181.                      * value of the Beep method.
  182.                      */
  183.                     if (SUCCEEDED(hr))
  184.                         {
  185.                         wsprintf(szMsg, TEXT("'Beep' played 0x%lX")
  186.                             , va.lVal);
  187.                         }
  188.                     else
  189.                         {
  190.                         wsprintf(szMsg
  191.                             , TEXT("'Beep' failed with 0x%lX"), hr);
  192.                         }
  193.  
  194.                     pApp->Message(szMsg);
  195.                     break;
  196.  
  197.                 //CHAPTER16MOD
  198.                 case IDM_PROPERTIES:
  199.                     pApp->ShowProperties();
  200.                     break;
  201.  
  202.                 case IDM_ENFORCEREADONLY:
  203.                     pApp->m_fReadOnly=!pApp->m_fReadOnly;
  204.                     CheckMenuItem(GetMenu(hWnd), wID
  205.                         , pApp->m_fReadOnly
  206.                         ? MF_CHECKED : MF_UNCHECKED);
  207.                     break;
  208.                 //End CHAPTER16MOD
  209.  
  210.                 case IDM_EXIT:
  211.                     PostMessage(hWnd, WM_CLOSE, 0, 0L);
  212.                     break;
  213.                 }
  214.             break;
  215.  
  216.         default:
  217.             return (DefWindowProc(hWnd, iMsg, wParam, lParam));
  218.         }
  219.  
  220.     return 0L;
  221.     }
  222.  
  223.  
  224.  
  225.  
  226. /*
  227.  * CApp::CApp
  228.  * CApp::~CApp
  229.  *
  230.  * Constructor Parameters: (from WinMain)
  231.  *  hInst           HINSTANCE of the application.
  232.  *  hInstPrev       HINSTANCE of a previous instance.
  233.  *  nCmdShow        UINT specifying how to show the app window.
  234.  *
  235.  */
  236.  
  237. CApp::CApp(HINSTANCE hInst, HINSTANCE hInstPrev
  238.     , UINT nCmdShow)
  239.     {
  240.     m_hInst=hInst;
  241.     m_hInstPrev=hInstPrev;
  242.     m_nCmdShow=nCmdShow;
  243.  
  244.     m_hWnd=NULL;
  245.     m_fInitialized=FALSE;
  246.     m_lcid=GetUserDefaultLCID();
  247.  
  248.     m_szHelpDir[0]=(TCHAR)0;
  249.     m_pIDispatch=NULL;
  250.  
  251.     //CHAPTER16MOD
  252.     m_fReadOnly=FALSE;
  253.     m_pSink=NULL;
  254.     m_pIConnPt=NULL;
  255.     m_dwConn=0L;
  256.     //End CHAPTER16MOD
  257.  
  258.     return;
  259.     }
  260.  
  261.  
  262. CApp::~CApp(void)
  263.     {
  264.     //CHAPTER16MOD
  265.     if (0!=m_dwConn && NULL!=m_pIConnPt)
  266.         {
  267.         m_pIConnPt->Unadvise(m_dwConn);
  268.         ReleaseInterface(m_pIConnPt);
  269.         }
  270.  
  271.     ReleaseInterface(m_pSink);
  272.     //End CHAPTER16MOD
  273.  
  274.     if (NULL!=m_pIDispatch)
  275.         {
  276.         m_pIDispatch->Release();
  277.         m_pIDispatch=NULL;
  278.         }
  279.  
  280.     if (IsWindow(m_hWnd))
  281.         DestroyWindow(m_hWnd);
  282.  
  283.     if (m_fInitialized)
  284.         CoUninitialize();
  285.  
  286.     return;
  287.     }
  288.  
  289.  
  290.  
  291.  
  292. /*
  293.  * CApp::Init
  294.  *
  295.  * Purpose:
  296.  *  Initializes an CApp object by registering window classes,
  297.  *  creating the main window, and doing anything else prone to
  298.  *  failure such as calling CoInitialize.  If this function fails
  299.  *  the caller should insure that the destructor is called.
  300.  *
  301.  * Parameters:
  302.  *  None
  303.  *
  304.  * Return Value:
  305.  *  BOOL            TRUE if successful, FALSE otherwise.
  306.  */
  307.  
  308. BOOL CApp::Init(void)
  309.     {
  310.     WNDCLASS    wc;
  311.     HRESULT     hr;
  312.  
  313.     CHECKVER_OLE;
  314.  
  315.     if (FAILED(CoInitialize(NULL)))
  316.         return FALSE;
  317.  
  318.     m_fInitialized=TRUE;
  319.  
  320.     if (!m_hInstPrev)
  321.         {
  322.         wc.style          = CS_HREDRAW | CS_VREDRAW;
  323.         wc.lpfnWndProc    = AutoClientWndProc;
  324.         wc.cbClsExtra     = 0;
  325.         wc.cbWndExtra     = CBWNDEXTRA;
  326.         wc.hInstance      = m_hInst;
  327.         wc.hIcon          = LoadIcon(m_hInst, TEXT("Icon"));
  328.         wc.hCursor        = LoadCursor(NULL, IDC_ARROW);
  329.         wc.hbrBackground  = (HBRUSH)(COLOR_WINDOW + 1);
  330.         wc.lpszMenuName   = MAKEINTRESOURCE(IDR_MENU);
  331.         wc.lpszClassName  = TEXT("AUTOCLI");
  332.  
  333.         if (!RegisterClass(&wc))
  334.             return FALSE;
  335.         }
  336.  
  337.     m_hWnd=CreateWindow(TEXT("AUTOCLI")
  338.         , TEXT("Property Page Client"), WS_OVERLAPPEDWINDOW
  339.         , 35, 35, 450, 250, NULL, NULL, m_hInst, this);
  340.  
  341.     if (NULL==m_hWnd)
  342.         return FALSE;
  343.  
  344.     //Create the beeper object we want to manipulate.
  345.     hr=CoCreateInstance(CLSID_Beeper, NULL, CLSCTX_INPROC_SERVER
  346.         , IID_IDispatch, (PPVOID)&m_pIDispatch);
  347.  
  348.     if (FAILED(hr))
  349.         {
  350.         Message(TEXT("Failed to create object--terminating"), MB_OK);
  351.         return FALSE;
  352.         }
  353.  
  354.     //Try to get the help directory for the object's type information
  355.     HelpDirFromCLSID(CLSID_Beeper, m_szHelpDir);
  356.  
  357.     //CHAPTER16MOD
  358.     /*
  359.      * Try to hook up our IPropertyNotifySink to the object.
  360.      * On failure, we'll just gray out "Enforce Read-Only" on
  361.      * the menu, but otherwise we continue to run.
  362.      */
  363.     m_pSink=new CPropertyNotifySink(this);
  364.  
  365.     if (NULL!=m_pSink)
  366.         {
  367.         IConnectionPointContainer  *pICPC;
  368.         HRESULT                     hr;
  369.  
  370.         m_pSink->AddRef();  //Release destroys it
  371.  
  372.         hr=m_pIDispatch->QueryInterface(IID_IConnectionPointContainer
  373.             , (void **)&pICPC);
  374.  
  375.         if (SUCCEEDED(hr))
  376.             {
  377.             if (SUCCEEDED(pICPC->FindConnectionPoint
  378.                 (IID_IPropertyNotifySink, &m_pIConnPt)))
  379.                 {
  380.                 /*
  381.                  * This is the only way m_dwConn is set to non-
  382.                  * zero.  We hold onto m_pIConnPt until we close.
  383.                  */
  384.                 m_pIConnPt->Advise(m_pSink, &m_dwConn);
  385.                 }
  386.  
  387.             pICPC->Release();
  388.             }
  389.         }
  390.  
  391.     if (0==m_dwConn)
  392.         {
  393.         EnableMenuItem(GetMenu(m_hWnd), IDM_ENFORCEREADONLY
  394.             , MF_DISABLED | MF_GRAYED | MF_BYCOMMAND);
  395.         }
  396.     //End CHAPTER16MOD
  397.  
  398.     ShowWindow(m_hWnd, m_nCmdShow);
  399.     UpdateWindow(m_hWnd);
  400.  
  401.     return TRUE;
  402.     }
  403.  
  404.  
  405.  
  406. /*
  407.  * CApp::NameToID
  408.  *
  409.  * Purpose:
  410.  *  Calls IDispatch::GetIDsOfNames for a single name to determine
  411.  *  the DISPID to pass to IDispatch::Invoke.
  412.  *
  413.  * Parameters:
  414.  *  pszName         OLECHAR * to the name to map.
  415.  *  pDispID         DISPID * in which to store the dispID.
  416.  *
  417.  * Return Value:
  418.  *  HRESULT         Return value of GetIDsOfNames
  419.  */
  420.  
  421. HRESULT CApp::NameToID(OLECHAR *pszName, DISPID *pDispID)
  422.     {
  423.     HRESULT     hr;
  424.     TCHAR       szMsg[80];
  425.  
  426.     hr=m_pIDispatch->GetIDsOfNames(IID_NULL, &pszName, 1
  427.         , m_lcid, pDispID);
  428.  
  429.     if (FAILED(hr))
  430.         {
  431.         wsprintf(szMsg
  432.             , TEXT("GetIDsOfNames on '%s' failed with 0x%lX")
  433.             , pszName, hr);
  434.         Message(szMsg);
  435.         }
  436.  
  437.     return hr;
  438.     }
  439.  
  440.  
  441.  
  442.  
  443. /*
  444.  * CApp::Invoke
  445.  *
  446.  * Purpose:
  447.  *  Calls IDispatch::Invoke using the interface pointer we hold
  448.  *  and using some default parameters.  All the other parameters
  449.  *  to this function are passed straight to Invoke
  450.  *
  451.  * Return Value:
  452.  *  HRESULT         Return value of Invoke.  If DISP_E_EXCEPTION,
  453.  *                  this function generates the appropriate
  454.  *                  message box.
  455.  */
  456.  
  457. HRESULT CApp::Invoke(DISPID dispID, WORD wFlags, DISPPARAMS *pdp
  458.     , VARIANT *pva, EXCEPINFO *pExInfo, UINT *puErr)
  459.     {
  460.     HRESULT     hr;
  461.     LPTSTR      pszMsg=NULL;
  462.     LPTSTR      pszFmt=NULL;
  463.     UINT        uRet;
  464.     UINT        uStyle;
  465.     TCHAR       szSource[80];
  466.  
  467.     if (NULL==m_pIDispatch)
  468.         return ResultFromScode(E_POINTER);
  469.  
  470.     hr=m_pIDispatch->Invoke(dispID, IID_NULL, m_lcid, wFlags
  471.         , pdp, pva, pExInfo, puErr);
  472.  
  473.     if (DISP_E_EXCEPTION!=GetScode(hr))
  474.         return hr;
  475.  
  476.     //If we're given a deferred filling function, fill now.
  477.     if (NULL!=pExInfo->pfnDeferredFillIn)
  478.         (*pExInfo->pfnDeferredFillIn)(pExInfo);
  479.  
  480.     /*
  481.      * To handle the exception, display a message box with the
  482.      * controller's name in the caption and a message:
  483.      *
  484.      *      "Error <code> in <source>:  <description>"
  485.      *
  486.      * where <error> is the exception code in pExInfo->wCode or
  487.      * pExInfo->scode, <source> is the value of the ProdID
  488.      * in pExInfo->bstrSource and <description> is in
  489.      * pExInfo->bstrDescription.
  490.      *
  491.      * For simplicity, we assume that if description is set, so is
  492.      * source.
  493.      *
  494.      * To be complete, if pExInfo->bstrHelpFile is non-NULL,
  495.      * display a Help button.  If Help is pressed, launch WinHelp
  496.      * with that filename and pExInfo->dwHelpContext.
  497.      */
  498.  
  499.     //Go get the real source name from the ProgID
  500.     lstrcpy(szSource, TEXT("Unknown"));
  501.  
  502.     if (NULL!=pExInfo->bstrSource)
  503.         {
  504.         LONG    lRet;
  505.  
  506.         //If this doesn't work, we'll have "Unknown" anyway
  507.        #ifdef WIN32ANSI
  508.         char        szTemp[80];
  509.         WideCharToMultiByte(CP_ACP, 0, pExInfo->bstrSource, -1
  510.             , szTemp, 80, NULL, NULL);
  511.         RegQueryValue(HKEY_CLASSES_ROOT, szTemp, szSource, &lRet);
  512.        #else
  513.         RegQueryValue(HKEY_CLASSES_ROOT, pExInfo->bstrSource
  514.             , szSource, &lRet);
  515.        #endif
  516.  
  517.         SysFreeString(pExInfo->bstrSource);
  518.         }
  519.  
  520.     if (NULL!=pExInfo->bstrDescription)
  521.         {
  522.         pszFmt=(LPTSTR)malloc(CCHSTRINGMAX*sizeof(TCHAR));
  523.  
  524.        #ifdef WIN32ANSI
  525.         UINT    cch;
  526.         char   *pszDesc;
  527.  
  528.         cch=wcslen(pExInfo->bstrDescription);
  529.         pszDesc=(LPSTR)malloc(cch);
  530.  
  531.         WideCharToMultiByte(CP_ACP, 0, pExInfo->bstrDescription, -1
  532.             , pszDesc, cch, NULL, NULL);
  533.  
  534.         pszMsg=(LPTSTR)malloc(CCHSTRINGMAX+lstrlen(szSource)+cch);
  535.        #else
  536.         pszMsg=(LPTSTR)malloc((CCHSTRINGMAX+lstrlen(szSource)
  537.             +lstrlen(pExInfo->bstrDescription))*sizeof(TCHAR));
  538.        #endif
  539.  
  540.         if (0==pExInfo->wCode)
  541.             {
  542.             //Formatting for SCODE errors
  543.             LoadString(m_hInst, IDS_MESSAGEEXCEPTIONSCODE, pszFmt
  544.                 , CCHSTRINGMAX);
  545.             wsprintf(pszMsg, pszFmt, (long)pExInfo->scode
  546.                 , (LPTSTR)szSource
  547.                #ifdef WIN32ANSI
  548.                 , pszDesc);
  549.                #else
  550.                 , (LPTSTR)pExInfo->bstrDescription);
  551.                #endif
  552.  
  553.             }
  554.         else
  555.             {
  556.             //Formatting for wCode errors
  557.             LoadString(m_hInst, IDS_MESSAGEEXCEPTION, pszFmt
  558.                 , CCHSTRINGMAX);
  559.             wsprintf(pszMsg, pszFmt, (UINT)pExInfo->wCode
  560.                 , (LPTSTR)szSource
  561.                #ifdef WIN32ANSI
  562.                 , pszDesc);
  563.                #else
  564.                 , (LPTSTR)pExInfo->bstrDescription);
  565.                #endif
  566.             }
  567.  
  568.         free(pszFmt);
  569.         }
  570.     else
  571.         {
  572.         pszMsg=(LPTSTR)malloc(CCHSTRINGMAX*sizeof(TCHAR));
  573.         LoadString(m_hInst, IDS_MESSAGEUNKNOWNEXCEPTION, pszMsg
  574.             , CCHSTRINGMAX);
  575.         }
  576.  
  577.     /*
  578.      * In Windows 95 there is an MB_HELP style that we use in the
  579.      * exception message if pExInfo->bstrHelpFile is non-NULL.  For
  580.      * Windows NT 3.5 and Windows 3.1x, we'll just use a Cancel
  581.      * button to demonstrate since making a Help button is too much
  582.      * effort for this sample (requires a custom dialog box and code
  583.      * to resize the dialog based on the length of the description
  584.      * string which MessageBox does automatically...)
  585.      */
  586.     uStyle=MB_OK | MB_ICONEXCLAMATION;
  587.  
  588.    #ifdef MB_HELP
  589.     uStyle |=(NULL!=pExInfo->bstrHelpFile) ? MB_HELP : 0;
  590.    #else
  591.     uStyle |=(NULL!=pExInfo->bstrHelpFile) ? MB_OKCANCEL : 0;
  592.    #endif
  593.  
  594.     uRet=Message(pszMsg, uStyle);
  595.  
  596.     if (NULL!=pszMsg)
  597.         free(pszMsg);
  598.  
  599.    #ifdef MB_HELP
  600.     if (IDHELP==uRet)
  601.    #else
  602.     if (IDCANCEL==uRet)
  603.    #endif
  604.         {
  605.         TCHAR       szHelp[512];
  606.  
  607.         /*
  608.          * If we read a HELPDIR, prepend it to the file.  Otherwise
  609.          * just use the string we got since that's all we have.
  610.          */
  611.         if ((TCHAR)0!=m_szHelpDir[0])
  612.             {
  613.            #ifdef WIN32ANSI
  614.             char    szTemp[256];
  615.  
  616.             WideCharToMultiByte(CP_ACP, 0, pExInfo->bstrHelpFile
  617.                 , -1, szTemp, 256, NULL, NULL);
  618.             wsprintf(szHelp, TEXT("%s\\%s"), m_szHelpDir, szTemp);
  619.            #else
  620.             wsprintf(szHelp, TEXT("%s\\%s"), m_szHelpDir
  621.                 , pExInfo->bstrHelpFile);
  622.            #endif
  623.             }
  624.         else
  625.            #ifdef WIN32ANSI
  626.             WideCharToMultiByte(CP_ACP, 0, pExInfo->bstrHelpFile
  627.                 , -1, szHelp, 512, NULL, NULL);
  628.            #else
  629.             lstrcpy(szHelp, pExInfo->bstrHelpFile);
  630.            #endif
  631.  
  632.         WinHelp(NULL, szHelp, HELP_CONTEXT, pExInfo->dwHelpContext);
  633.         }
  634.  
  635.     //We're responsible for cleaning up the strings.
  636.     SysFreeString(pExInfo->bstrDescription);
  637.     SysFreeString(pExInfo->bstrHelpFile);
  638.  
  639.     return ResultFromScode(DISP_E_EXCEPTION);
  640.     }
  641.  
  642.  
  643.  
  644. /*
  645.  * CApp::Message (overloaded)
  646.  *
  647.  * Purpose:
  648.  *  Scribbles a message onto the client area of the window
  649.  *  or displays the message in a message box if a message
  650.  *  box style is given.
  651.  *
  652.  * Parameters:
  653.  *  pszMsg          LPTSTR to the message string.
  654.  *  uStyle          (message box only) UINT style bits
  655.  *
  656.  * Return Value:
  657.  *  UINT            Return value of MessageBox (MessageBox version
  658.  *                  only)
  659.  */
  660.  
  661. void CApp::Message(LPTSTR pszMsg)
  662.     {
  663.     HDC     hDC;
  664.     RECT    rc;
  665.  
  666.     hDC=GetDC(m_hWnd);
  667.     GetClientRect(m_hWnd, &rc);
  668.  
  669.     SetBkColor(hDC, GetSysColor(COLOR_WINDOW));
  670.     SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
  671.  
  672.     /*
  673.      * We'll just be sloppy and clear the whole window as
  674.      * well as write the string with one ExtTextOut call.
  675.      * No word wrapping here...
  676.      */
  677.  
  678.     ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rc, pszMsg
  679.         , lstrlen(pszMsg), NULL);
  680.  
  681.     ReleaseDC(m_hWnd, hDC);
  682.     return;
  683.     }
  684.  
  685.  
  686. UINT CApp::Message(LPTSTR pszMsg, UINT uStyle)
  687.     {
  688.     return MessageBox(m_hWnd, pszMsg, TEXT("Automation Client")
  689.         , uStyle);
  690.     }
  691.  
  692.  
  693.  
  694.  
  695.  
  696. /*
  697.  * HelpDirFromCLSID
  698.  *
  699.  * Purpose:
  700.  *  Given a CLSID, looks up the TypeLib entry in the registry then
  701.  *  extracts the HELPDIR entry for that type information, storing
  702.  *  the path in pszPath.
  703.  *
  704.  * Parameters:
  705.  *  clsID           CLSID of the object we're looking up.
  706.  *  pszPath         LPTSTR buffer in which to store the directory.
  707.  *
  708.  * Return Value:
  709.  *  None
  710.  */
  711.  
  712. void HelpDirFromCLSID(CLSID clsID, LPTSTR pszPath)
  713.     {
  714.     TCHAR       szCLSID[80];
  715.     TCHAR       szKey[512];
  716.     UINT        cch;
  717.     long        lRet;
  718.  
  719.     if (NULL==pszPath)
  720.         return;
  721.  
  722.     *pszPath=0;
  723.  
  724.     cch=sizeof(szCLSID)/sizeof(TCHAR);
  725.     StringFromGUID2(clsID, szCLSID, cch);
  726.     wsprintf(szKey, TEXT("CLSID\\%s\\TypeLib"), szCLSID);
  727.  
  728.     //Get LIBID from under CLSID
  729.     if (ERROR_SUCCESS==RegQueryValue(HKEY_CLASSES_ROOT, szKey
  730.         , szCLSID, &lRet))
  731.         {
  732.         //Get HELPDIR from under TypeLib
  733.         wsprintf(szKey, TEXT("TypeLib\\%s\\HELPDIR"), szCLSID);
  734.         RegQueryValue(HKEY_CLASSES_ROOT, szKey, pszPath, &lRet);
  735.         }
  736.  
  737.     return;
  738.     }
  739.  
  740.  
  741.  
  742. /*
  743.  * CApp::ShowProperties
  744.  *
  745.  * Purpose:
  746.  *  Invokes the standard property page frame using the
  747.  *  property pages specified by the object we maintain
  748.  *  (only a single one).
  749.  */
  750.  
  751. void CApp::ShowProperties(void)
  752.     {
  753.     ISpecifyPropertyPages  *pISPP;
  754.     CAUUID                  caGUID;
  755.     HRESULT                 hr;
  756.  
  757.     if (FAILED(m_pIDispatch->QueryInterface
  758.         (IID_ISpecifyPropertyPages, (void **)&pISPP)))
  759.         {
  760.         Message(TEXT("Object has no property pages"));
  761.         return;
  762.         }
  763.  
  764.     hr=pISPP->GetPages(&caGUID);
  765.     pISPP->Release();
  766.  
  767.     if (FAILED(hr))
  768.         {
  769.         Message(TEXT("Failed to retrieve property page GUIDs"));
  770.         return;
  771.         }
  772.  
  773.     hr=OleCreatePropertyFrame(m_hWnd, 10, 10, OLETEXT("Beeper")
  774.         , 1, (IUnknown **)&m_pIDispatch, caGUID.cElems
  775.         , caGUID.pElems, m_lcid, 0L, NULL);
  776.  
  777.     if (FAILED(hr))
  778.         Message(TEXT("OleCreatePropertyFrame failed"));
  779.  
  780.     //Free the GUIDs
  781.     CoTaskMemFree((void *)caGUID.pElems);
  782.     return;
  783.     }
  784.  
  785.  
  786.  
  787.  
  788.  
  789. /***
  790.  *** IPropertyNotifySink Object
  791.  ***/
  792.  
  793. /*
  794.  * CPropertyNotifySink::CPropertyNotifySink
  795.  * CPropertyNotifySink::~CPropertyNotifySink
  796.  *
  797.  * Constructor Parameters:
  798.  *  pApp            PCApp of the application.
  799.  */
  800.  
  801. CPropertyNotifySink::CPropertyNotifySink(PCApp pApp)
  802.     {
  803.     m_cRef=0;
  804.     m_pApp=pApp;
  805.     return;
  806.     }
  807.  
  808. CPropertyNotifySink::~CPropertyNotifySink(void)
  809.     {
  810.     return;
  811.     }
  812.  
  813.  
  814.  
  815. /*
  816.  * CPropertyNotifySink::QueryInterface
  817.  * CPropertyNotifySink::AddRef
  818.  * CPropertyNotifySink::Release
  819.  */
  820.  
  821. STDMETHODIMP CPropertyNotifySink::QueryInterface(REFIID riid
  822.     , LPVOID *ppv)
  823.     {
  824.     *ppv=NULL;
  825.  
  826.     if (IID_IUnknown==riid || IID_IPropertyNotifySink==riid)
  827.         *ppv=this;
  828.  
  829.     if (NULL!=*ppv)
  830.         {
  831.         ((LPUNKNOWN)*ppv)->AddRef();
  832.         return NOERROR;
  833.         }
  834.  
  835.     return ResultFromScode(E_NOINTERFACE);
  836.     }
  837.  
  838. STDMETHODIMP_(ULONG) CPropertyNotifySink::AddRef(void)
  839.     {
  840.     return ++m_cRef;
  841.     }
  842.  
  843. STDMETHODIMP_(ULONG) CPropertyNotifySink::Release(void)
  844.     {
  845.     if (0!=--m_cRef)
  846.         return m_cRef;
  847.  
  848.     delete this;
  849.     return 0;
  850.     }
  851.  
  852.  
  853.  
  854. /*
  855.  * CPropertyNotifySink::OnChanged
  856.  *
  857.  * Purpose:
  858.  *  Notifies this sink that the property identified with dispID
  859.  *  has changed in whatever object this sink is connected to.
  860.  *
  861.  * Parameters:
  862.  *  dispID          DISPID of the property that changed,
  863.  *                  which can be DISPID_UNKNOWN for unspecified
  864.  *                  changes to multiple properties.
  865.  *
  866.  * Return Value:
  867.  *  HRESULT         NOERROR always.
  868.  */
  869.  
  870. STDMETHODIMP CPropertyNotifySink::OnChanged(DISPID dispID)
  871.     {
  872.     TCHAR       szTemp[200];
  873.  
  874.     wsprintf(szTemp
  875.         , TEXT("OnChanged notification received for DISPID=%lu")
  876.         , dispID);
  877.  
  878.     m_pApp->Message(szTemp);
  879.     return NOERROR;
  880.     }
  881.  
  882.  
  883.  
  884. /*
  885.  * CPropertyNotifySink::OnRequestEdit
  886.  *
  887.  * Purpose:
  888.  *  Notifies this sink that the property identified with dispID
  889.  *  is about to change and that the sink can prevent the change
  890.  *  if desired.  This can be used to enforce read-only states or
  891.  *  to save prior states before the change occurs.
  892.  *
  893.  * Parameters:
  894.  *  dispID          DISPID of the property that is changing
  895.  *                  which can be DISPID_UNKNOWN for multiple
  896.  *                  properties.
  897.  *
  898.  * Return Value:
  899.  *  HRESULT         NOERROR if the property can change, S_FALSE
  900.  *                  if it cannot change, error code otherwise.
  901.  */
  902.  
  903. STDMETHODIMP CPropertyNotifySink::OnRequestEdit(DISPID dispID)
  904.     {
  905.     TCHAR       szTemp[200];
  906.  
  907.     wsprintf(szTemp
  908.         , TEXT("OnRequestEdit received for DISPID=%lu"), dispID);
  909.     m_pApp->Message(szTemp);
  910.  
  911.     return ResultFromScode(m_pApp->m_fReadOnly ? S_FALSE : S_OK);
  912.     }
  913.