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 / chap15 / autocli / autocli.cpp next >
C/C++ Source or Header  |  1996-05-22  |  19KB  |  692 lines

  1. /*
  2.  * AUTOCLI.CPP
  3.  * Automation Client Chapter 15
  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 "autocli.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, AUTOCLIWL_STRUCTURE);
  75.  
  76.     switch (iMsg)
  77.         {
  78.         case WM_NCCREATE:
  79.             pApp=(PCApp)(((LPCREATESTRUCT)lParam)->lpCreateParams);
  80.             SetWindowLong(hWnd, AUTOCLIWL_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.                     VariantInit(&va);
  102.                     hr=pApp->Invoke(dispID, DISPATCH_PROPERTYGET
  103.                         , &dp, &va, &exInfo, NULL);
  104.  
  105.                     if (SUCCEEDED(hr))
  106.                         {
  107.                         wsprintf(szMsg, TEXT("Current 'Sound' is 0x%lX")
  108.                             , va.lVal);
  109.                         }
  110.                     else
  111.                         {
  112.                         wsprintf(szMsg
  113.                             , TEXT("Get 'Sound' failed with 0x%lX")
  114.                             , hr);
  115.                         }
  116.  
  117.                     pApp->Message(szMsg);
  118.                     break;
  119.  
  120.                 case IDM_SETSOUNDDEFAULT:
  121.                 case IDM_SETSOUNDHAND:
  122.                 case IDM_SETSOUNDQUESTION:
  123.                 case IDM_SETSOUNDEXCLAMATION:
  124.                 case IDM_SETSOUNDASTERISK:
  125.                 case IDM_SETSOUNDBOGUS:
  126.                     //Find the dispID we need
  127.                     hr=pApp->NameToID(OLETEXT("Sound"), &dispID);
  128.  
  129.                     if (FAILED(hr))
  130.                         break;
  131.  
  132.                     /*
  133.                      * Call IDispatch::Invoke passing wID which
  134.                      * is a sound identifier (IDM_SETSOUNDDEFAULT
  135.                      * has to set the sound to zero).  The "bogus"
  136.                      * sound should cause an exception.
  137.                      */
  138.  
  139.                     //Initialize arguments
  140.                     VariantInit(&va);
  141.                     va.vt=VT_I4;
  142.                     va.lVal=(IDM_SETSOUNDDEFAULT==wID)
  143.                         ? 0L : (long)(wID);
  144.  
  145.                     /*
  146.                      * Passing a named DISPID_PROPERTYPUT
  147.                      * is required when setting properties.
  148.                      */
  149.                     dispIDParam=DISPID_PROPERTYPUT;
  150.                     SETDISPPARAMS(dp, 1, &va, 1, &dispIDParam);
  151.  
  152.                     hr=pApp->Invoke(dispID, DISPATCH_PROPERTYPUT
  153.                         , &dp, NULL, &exInfo, NULL);
  154.  
  155.                     if (SUCCEEDED(hr))
  156.                         {
  157.                         /*
  158.                          * Note that if the automation object can't
  159.                          * raise an exception, setting the bogus
  160.                          * value will return success although there
  161.                          * is no change in the actual property.
  162.                          */
  163.                         lstrcpy(szMsg, TEXT("Set 'Sound' succeeded"));
  164.                         }
  165.                     else
  166.                         {
  167.                         wsprintf(szMsg
  168.                             , TEXT("Set 'Sound' failed with 0x%lX"), hr);
  169.                         }
  170.  
  171.                     pApp->Message(szMsg);
  172.                     break;
  173.  
  174.                 case IDM_BEEP:
  175.                     if (NULL==pApp->m_pIDispatch)
  176.                         break;
  177.  
  178.                     hr=pApp->NameToID(OLETEXT("Beep"), &dispID);
  179.  
  180.                     if (FAILED(hr))
  181.                         break;
  182.  
  183.                     SETNOPARAMS(dp);
  184.                     VariantInit(&va);
  185.                     hr=pApp->Invoke(dispID, DISPATCH_METHOD, &dp
  186.                         , &va, &exInfo, &uErr);
  187.  
  188.                     /*
  189.                      * va will have the sound played as the return
  190.                      * value of the Beep method.
  191.                      */
  192.                     if (SUCCEEDED(hr))
  193.                         {
  194.                         wsprintf(szMsg, TEXT("'Beep' played 0x%lX")
  195.                             , va.lVal);
  196.                         }
  197.                     else
  198.                         {
  199.                         wsprintf(szMsg
  200.                             , TEXT("'Beep' failed with 0x%lX"), hr);
  201.                         }
  202.  
  203.                     pApp->Message(szMsg);
  204.                     break;
  205.  
  206.                 case IDM_EXIT:
  207.                     PostMessage(hWnd, WM_CLOSE, 0, 0L);
  208.                     break;
  209.                 }
  210.             break;
  211.  
  212.         default:
  213.             return (DefWindowProc(hWnd, iMsg, wParam, lParam));
  214.         }
  215.  
  216.     return 0L;
  217.     }
  218.  
  219.  
  220.  
  221.  
  222. /*
  223.  * CApp::CApp
  224.  * CApp::~CApp
  225.  *
  226.  * Constructor Parameters: (from WinMain)
  227.  *  hInst           HINSTANCE of the application.
  228.  *  hInstPrev       HINSTANCE of a previous instance.
  229.  *  nCmdShow        UINT specifying how to show the app window.
  230.  *
  231.  */
  232.  
  233. CApp::CApp(HINSTANCE hInst, HINSTANCE hInstPrev
  234.     , UINT nCmdShow)
  235.     {
  236.     m_hInst=hInst;
  237.     m_hInstPrev=hInstPrev;
  238.     m_nCmdShow=nCmdShow;
  239.  
  240.     m_hWnd=NULL;
  241.     m_fInitialized=FALSE;
  242.  
  243.     /*
  244.      * Get the current LCID to send to the automation object.
  245.      * Note that depending on the Beeper installed, this may or
  246.      * may not work, especially if you are in a non-english or
  247.      * non-German speaking locale.
  248.      */
  249.     m_lcid=GetUserDefaultLCID();
  250.  
  251.     m_szHelpDir[0]=(TCHAR)0;
  252.     m_pIDispatch=NULL;
  253.     return;
  254.     }
  255.  
  256.  
  257. CApp::~CApp(void)
  258.     {
  259.     if (NULL!=m_pIDispatch)
  260.         {
  261.         m_pIDispatch->Release();
  262.         m_pIDispatch=NULL;
  263.         }
  264.  
  265.     if (IsWindow(m_hWnd))
  266.         DestroyWindow(m_hWnd);
  267.  
  268.     if (m_fInitialized)
  269.         CoUninitialize();
  270.  
  271.     return;
  272.     }
  273.  
  274.  
  275.  
  276.  
  277. /*
  278.  * CApp::Init
  279.  *
  280.  * Purpose:
  281.  *  Initializes an CApp object by registering window classes,
  282.  *  creating the main window, and doing anything else prone to
  283.  *  failure such as calling CoInitialize.  If this function fails
  284.  *  the caller should insure that the destructor is called.
  285.  *
  286.  * Parameters:
  287.  *  None
  288.  *
  289.  * Return Value:
  290.  *  BOOL            TRUE if successful, FALSE otherwise.
  291.  */
  292.  
  293. BOOL CApp::Init(void)
  294.     {
  295.     WNDCLASS    wc;
  296.     HRESULT     hr;
  297.  
  298.     CHECKVER_OLE;
  299.  
  300.     if (FAILED(CoInitialize(NULL)))
  301.         return FALSE;
  302.  
  303.     m_fInitialized=TRUE;
  304.  
  305.     if (!m_hInstPrev)
  306.         {
  307.         wc.style          = CS_HREDRAW | CS_VREDRAW;
  308.         wc.lpfnWndProc    = AutoClientWndProc;
  309.         wc.cbClsExtra     = 0;
  310.         wc.cbWndExtra     = CBWNDEXTRA;
  311.         wc.hInstance      = m_hInst;
  312.         wc.hIcon          = LoadIcon(m_hInst, TEXT("Icon"));
  313.         wc.hCursor        = LoadCursor(NULL, IDC_ARROW);
  314.         wc.hbrBackground  = (HBRUSH)(COLOR_WINDOW + 1);
  315.         wc.lpszMenuName   = MAKEINTRESOURCE(IDR_MENU);
  316.         wc.lpszClassName  = TEXT("AUTOCLI");
  317.  
  318.         if (!RegisterClass(&wc))
  319.             return FALSE;
  320.         }
  321.  
  322.     m_hWnd=CreateWindow(TEXT("AUTOCLI")
  323.         , TEXT("Automation Object Client"), WS_OVERLAPPEDWINDOW
  324.         , 35, 35, 450, 250, NULL, NULL, m_hInst, this);
  325.  
  326.     if (NULL==m_hWnd)
  327.         return FALSE;
  328.  
  329.     //Create the beeper object we want to manipulate.
  330.     hr=CoCreateInstance(CLSID_Beeper, NULL, CLSCTX_INPROC_SERVER
  331.         , IID_IDispatch, (PPVOID)&m_pIDispatch);
  332.  
  333.     if (FAILED(hr))
  334.         {
  335.         Message(TEXT("Failed to create object--terminating"), MB_OK);
  336.         return FALSE;
  337.         }
  338.  
  339.     //Try to get the help directory for the object's type information
  340.     HelpDirFromCLSID(CLSID_Beeper, m_szHelpDir);
  341.  
  342.     ShowWindow(m_hWnd, m_nCmdShow);
  343.     UpdateWindow(m_hWnd);
  344.  
  345.     return TRUE;
  346.     }
  347.  
  348.  
  349.  
  350. /*
  351.  * CApp::NameToID
  352.  *
  353.  * Purpose:
  354.  *  Calls IDispatch::GetIDsOfNames for a single name to determine
  355.  *  the DISPID to pass to IDispatch::Invoke.
  356.  *
  357.  * Parameters:
  358.  *  pszName         OLECHAR * to the name to map.
  359.  *  pDispID         DISPID * in which to store the dispID.
  360.  *
  361.  * Return Value:
  362.  *  HRESULT         Return value of GetIDsOfNames
  363.  */
  364.  
  365. HRESULT CApp::NameToID(OLECHAR *pszName, DISPID *pDispID)
  366.     {
  367.     HRESULT     hr;
  368.     TCHAR       szMsg[80];
  369.  
  370.     hr=m_pIDispatch->GetIDsOfNames(IID_NULL, &pszName, 1
  371.         , m_lcid, pDispID);
  372.  
  373.     if (FAILED(hr))
  374.         {
  375.        #ifdef WIN32ANSI
  376.         char        szTemp[80];
  377.  
  378.         WideCharToMultiByte(CP_ACP, 0, pszName, -1, szTemp
  379.             , 80, NULL, NULL);
  380.         wsprintf(szMsg
  381.             , TEXT("GetIDsOfNames on '%s' failed with 0x%lX")
  382.             , szTemp, hr);
  383.        #else
  384.         wsprintf(szMsg
  385.             , TEXT("GetIDsOfNames on '%s' failed with 0x%lX")
  386.             , pszName, hr);
  387.        #endif
  388.         Message(szMsg);
  389.         }
  390.  
  391.     return hr;
  392.     }
  393.  
  394.  
  395.  
  396.  
  397. /*
  398.  * CApp::Invoke
  399.  *
  400.  * Purpose:
  401.  *  Calls IDispatch::Invoke using the interface pointer we hold
  402.  *  and using some default parameters.  All the other parameters
  403.  *  to this function are passed straight to Invoke
  404.  *
  405.  * Return Value:
  406.  *  HRESULT         Return value of Invoke.  If DISP_E_EXCEPTION,
  407.  *                  this function generates the appropriate
  408.  *                  message box.
  409.  */
  410.  
  411. HRESULT CApp::Invoke(DISPID dispID, WORD wFlags, DISPPARAMS *pdp
  412.     , VARIANT *pva, EXCEPINFO *pExInfo, UINT *puErr)
  413.     {
  414.     HRESULT     hr;
  415.     LPTSTR      pszMsg=NULL;
  416.     LPTSTR      pszFmt=NULL;
  417.     UINT        uRet;
  418.     UINT        uStyle;
  419.     TCHAR       szSource[80];
  420.  
  421.     if (NULL==m_pIDispatch)
  422.         return ResultFromScode(E_POINTER);
  423.  
  424.     hr=m_pIDispatch->Invoke(dispID, IID_NULL, m_lcid, wFlags
  425.         , pdp, pva, pExInfo, puErr);
  426.  
  427.     if (DISP_E_EXCEPTION!=GetScode(hr))
  428.         return hr;
  429.  
  430.     //If we're given a deferred filling function, fill now.
  431.     if (NULL!=pExInfo->pfnDeferredFillIn)
  432.         (*pExInfo->pfnDeferredFillIn)(pExInfo);
  433.  
  434.     /*
  435.      * To handle the exception, display a message box with the
  436.      * controller's name in the caption and a message:
  437.      *
  438.      *      "Error <code> in <source>:  <description>"
  439.      *
  440.      * where <error> is the exception code in pExInfo->wCode or
  441.      * pExInfo->scode, <source> is the value of the ProdID
  442.      * in pExInfo->bstrSource and <description> is in
  443.      * pExInfo->bstrDescription.
  444.      *
  445.      * For simplicity, we assume that if description is set, so is
  446.      * source.
  447.      *
  448.      * To be complete, if pExInfo->bstrHelpFile is non-NULL,
  449.      * display a Help button.  If Help is pressed, launch WinHelp
  450.      * with that filename and pExInfo->dwHelpContext.
  451.      */
  452.  
  453.     //Go get the real source name from the ProgID
  454.     lstrcpy(szSource, TEXT("Unknown"));
  455.  
  456.     if (NULL!=pExInfo->bstrSource)
  457.         {
  458.         LONG    lRet;
  459.  
  460.         //If this doesn't work, we'll have "Unknown" anyway
  461.        #ifdef WIN32ANSI
  462.         char        szTemp[80];
  463.         WideCharToMultiByte(CP_ACP, 0, pExInfo->bstrSource, -1
  464.             , szTemp, 80, NULL, NULL);
  465.         RegQueryValue(HKEY_CLASSES_ROOT, szTemp, szSource, &lRet);
  466.        #else
  467.         RegQueryValue(HKEY_CLASSES_ROOT, pExInfo->bstrSource
  468.             , szSource, &lRet);
  469.        #endif
  470.  
  471.         SysFreeString(pExInfo->bstrSource);
  472.         }
  473.  
  474.     if (NULL!=pExInfo->bstrDescription)
  475.         {
  476.         pszFmt=(LPTSTR)malloc(CCHSTRINGMAX*sizeof(TCHAR));
  477.  
  478.        #ifdef WIN32ANSI
  479.         UINT    cch;
  480.         char   *pszDesc;
  481.  
  482.         cch=wcslen(pExInfo->bstrDescription)+1;
  483.         pszDesc=(LPSTR)malloc(cch);
  484.  
  485.         WideCharToMultiByte(CP_ACP, 0, pExInfo->bstrDescription, -1
  486.             , pszDesc, cch, NULL, NULL);
  487.  
  488.         pszMsg=(LPTSTR)malloc(CCHSTRINGMAX+lstrlen(szSource)+cch);
  489.        #else
  490.         pszMsg=(LPTSTR)malloc((CCHSTRINGMAX+lstrlen(szSource)
  491.             +lstrlen(pExInfo->bstrDescription))*sizeof(TCHAR));
  492.        #endif
  493.  
  494.         if (0==pExInfo->wCode)
  495.             {
  496.             //Formatting for SCODE errors
  497.             LoadString(m_hInst, IDS_MESSAGEEXCEPTIONSCODE, pszFmt
  498.                 , CCHSTRINGMAX);
  499.             wsprintf(pszMsg, pszFmt, (long)pExInfo->scode
  500.                 , (LPTSTR)szSource
  501.                #ifdef WIN32ANSI
  502.                 , pszDesc);
  503.                #else
  504.                 , (LPTSTR)pExInfo->bstrDescription);
  505.                #endif
  506.             }
  507.         else
  508.             {
  509.             //Formatting for wCode errors
  510.             LoadString(m_hInst, IDS_MESSAGEEXCEPTION, pszFmt
  511.                 , CCHSTRINGMAX);
  512.             wsprintf(pszMsg, pszFmt, (UINT)pExInfo->wCode
  513.                 , (LPTSTR)szSource
  514.                #ifdef WIN32ANSI
  515.                 , pszDesc);
  516.                #else
  517.                 , (LPTSTR)pExInfo->bstrDescription);
  518.                #endif
  519.             }
  520.  
  521.         free(pszFmt);
  522.         }
  523.     else
  524.         {
  525.         pszMsg=(LPTSTR)malloc(CCHSTRINGMAX*sizeof(TCHAR));
  526.         LoadString(m_hInst, IDS_MESSAGEUNKNOWNEXCEPTION, pszMsg
  527.             , CCHSTRINGMAX);
  528.         }
  529.  
  530.     /*
  531.      * In Windows 95 and Windows NT 3.51 there is an MB_HELP style
  532.      * that we use in the exception message if pExInfo->bstrHelpFile
  533.      * is non-NULL.  For Windows NT 3.5 and Windows 3.1x, we'll just use
  534.      * a Cancel button to demonstrate since making a Help button is too
  535.      * much effort for this sample (requires a custom dialog box and code
  536.      * to resize the dialog based on the length of the description
  537.      * string which MessageBox does automatically...)
  538.      */
  539.     uStyle=MB_OK | MB_ICONEXCLAMATION;
  540.  
  541.    #ifdef MB_HELP
  542.     uStyle |=(NULL!=pExInfo->bstrHelpFile) ? MB_HELP : 0;
  543.    #else
  544.     uStyle |=(NULL!=pExInfo->bstrHelpFile) ? MB_OKCANCEL : 0;
  545.    #endif
  546.  
  547.     uRet=Message(pszMsg, uStyle);
  548.  
  549.     if (NULL!=pszMsg)
  550.         free(pszMsg);
  551.  
  552.    #ifdef MB_HELP
  553.     if (IDHELP==uRet)
  554.    #else
  555.     if (IDCANCEL==uRet)
  556.    #endif
  557.         {
  558.         TCHAR       szHelp[512];
  559.  
  560.         /*
  561.          * If we read a HELPDIR, prepend it to the file.  Otherwise
  562.          * just use the string we got since that's all we have.
  563.          */
  564.         if ((TCHAR)0!=m_szHelpDir[0])
  565.             {
  566.            #ifdef WIN32ANSI
  567.             char    szTemp[256];
  568.  
  569.             WideCharToMultiByte(CP_ACP, 0, pExInfo->bstrHelpFile
  570.                 , -1, szTemp, 256, NULL, NULL);
  571.             wsprintf(szHelp, TEXT("%s\\%s"), m_szHelpDir, szTemp);
  572.            #else
  573.             wsprintf(szHelp, TEXT("%s\\%s"), m_szHelpDir
  574.                 , pExInfo->bstrHelpFile);
  575.            #endif
  576.             }
  577.         else
  578.            #ifdef WIN32ANSI
  579.             WideCharToMultiByte(CP_ACP, 0, pExInfo->bstrHelpFile
  580.                 , -1, szHelp, 512, NULL, NULL);
  581.            #else
  582.             lstrcpy(szHelp, pExInfo->bstrHelpFile);
  583.            #endif
  584.  
  585.         WinHelp(NULL, szHelp, HELP_CONTEXT, pExInfo->dwHelpContext);
  586.         }
  587.  
  588.     //We're responsible for cleaning up the strings.
  589.     SysFreeString(pExInfo->bstrDescription);
  590.     SysFreeString(pExInfo->bstrHelpFile);
  591.  
  592.     return ResultFromScode(DISP_E_EXCEPTION);
  593.     }
  594.  
  595.  
  596.  
  597. /*
  598.  * CApp::Message (overloaded)
  599.  *
  600.  * Purpose:
  601.  *  Scribbles a message onto the client area of the window
  602.  *  or displays the message in a message box if a message
  603.  *  box style is given.
  604.  *
  605.  * Parameters:
  606.  *  pszMsg          LPTSTR to the message string.
  607.  *  uStyle          (message box only) UINT style bits
  608.  *
  609.  * Return Value:
  610.  *  UINT            Return value of MessageBox (MessageBox version
  611.  *                  only)
  612.  */
  613.  
  614. void CApp::Message(LPTSTR pszMsg)
  615.     {
  616.     HDC     hDC;
  617.     RECT    rc;
  618.  
  619.     hDC=GetDC(m_hWnd);
  620.     GetClientRect(m_hWnd, &rc);
  621.  
  622.     SetBkColor(hDC, GetSysColor(COLOR_WINDOW));
  623.     SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
  624.  
  625.     /*
  626.      * We'll just be sloppy and clear the whole window as
  627.      * well as write the string with one ExtTextOut call.
  628.      * No word wrapping here...
  629.      */
  630.  
  631.     ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rc, pszMsg
  632.         , lstrlen(pszMsg), NULL);
  633.  
  634.     ReleaseDC(m_hWnd, hDC);
  635.     return;
  636.     }
  637.  
  638.  
  639. UINT CApp::Message(LPTSTR pszMsg, UINT uStyle)
  640.     {
  641.     return MessageBox(m_hWnd, pszMsg, TEXT("Automation Client")
  642.         , uStyle);
  643.     }
  644.  
  645.  
  646.  
  647.  
  648.  
  649. /*
  650.  * HelpDirFromCLSID
  651.  *
  652.  * Purpose:
  653.  *  Given a CLSID, looks up the TypeLib entry in the registry then
  654.  *  extracts the HELPDIR entry for that type information, storing
  655.  *  the path in pszPath.
  656.  *
  657.  * Parameters:
  658.  *  clsID           CLSID of the object we're looking up.
  659.  *  pszPath         LPTSTR buffer in which to store the directory.
  660.  *
  661.  * Return Value:
  662.  *  None
  663.  */
  664.  
  665. void HelpDirFromCLSID(CLSID clsID, LPTSTR pszPath)
  666.     {
  667.     TCHAR       szCLSID[80];
  668.     TCHAR       szKey[512];
  669.     UINT        cch;
  670.     long        lRet;
  671.  
  672.     if (NULL==pszPath)
  673.         return;
  674.  
  675.     *pszPath=0;
  676.  
  677.     cch=sizeof(szCLSID)/sizeof(TCHAR);
  678.     StringFromGUID2(clsID, szCLSID, cch);
  679.     wsprintf(szKey, TEXT("CLSID\\%s\\TypeLib"), szCLSID);
  680.  
  681.     //Get LIBID from under CLSID
  682.     if (ERROR_SUCCESS==RegQueryValue(HKEY_CLASSES_ROOT, szKey
  683.         , szCLSID, &lRet))
  684.         {
  685.         //Get HELPDIR from under TypeLib
  686.         wsprintf(szKey, TEXT("TypeLib\\%s\\HELPDIR"), szCLSID);
  687.         RegQueryValue(HKEY_CLASSES_ROOT, szKey, pszPath, &lRet);
  688.         }
  689.  
  690.     return;
  691.     }
  692.