/*************************************************************************
**
**  This is a part of the Microsoft Source Code Samples.
**
**  Copyright (C) 1992-1996 Microsoft Corporation. All rights reserved.
**
**  This source code is only intended as a supplement to Microsoft Development
**  Tools and/or WinHelp documentation.  See these sources for detailed
**  information regarding the Microsoft samples programs.
**
**  Type Library Browser
**
**  browseex.cpp
**
**  Written by Microsoft Product Support Services, Windows Developer Support
**
*************************************************************************/

#include <windows.h>
#include <windowsx.h>
#ifdef WIN16   
  #include <ole2.h>
  #include <compobj.h>    
  #include <dispatch.h> 
  #include <variant.h>
  #include <olenls.h>
  #include <commdlg.h>  
#endif  
#include "resource.h"
#include "browse.h" 
#include "invhelp.h"         

CBrowseApp BrowseApp;         // Application

int APIENTRY WinMain (HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpCmdLine, int nCmdShow)
{
   WNDCLASS wc;       
   HRESULT hr;

#ifdef WIN16
   //  It is recommended that all 16 bit OLE applications set
   //  their message queue size to 96. This improves the capacity
   //  and performance of OLE's LRPC mechanism.
   int cMsg = 96;                  // Recommend msg queue size for OLE
   while (cMsg && !SetMessageQueue(cMsg))  // take largest size we can get.
       cMsg -= 8;
   if (!cMsg)
       return -1;  // ERROR: we got no message queue       
#endif  
   
   // Register class for the dialog that is the main window.
   wc.style = 0;
   wc.lpfnWndProc = DefDlgProc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = DLGWINDOWEXTRA;
   wc.hInstance = hinst;
   wc.hIcon = LoadIcon(hinst, MAKEINTRESOURCE(ID_ICON));
   wc.hCursor = LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground = HBRUSH(COLOR_WINDOW + 1);
   wc.lpszMenuName = NULL;
   wc.lpszClassName = TEXT("DlgClass");
   RegisterClass(&wc);

   hr = OleInitialize(NULL);
   if FAILED(hr)
   {
       MessageBox(NULL, TEXT("Could not Intialize OLE"), TEXT("Error"), MB_ICONEXCLAMATION|MB_OK);
       return 0;
   }
   DialogBox(hinst, MAKEINTRESOURCE(IDD_MAINDIALOG), NULL, (DLGPROC)MainDialogFunc); 
   OleUninitialize();
   
   return 0;
}


BOOL CALLBACK MainDialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{  
   HRESULT hr;
   
   switch (msg)
   {  
      case WM_INITDIALOG:
          BrowseApp.Init(hwndDlg);
          return FALSE;
                      
      case WM_COMMAND:
         switch(GET_WM_COMMAND_ID(wParam,lParam))
         {   
             case IDM_EXIT:
             case IDCANCEL:  
                 BrowseApp.Cleanup();                 
                 EndDialog(hwndDlg, IDOK);
                 CoFreeUnusedLibraries();
                 return TRUE;
                 
             case IDM_FILEOPEN:   
                 hr = BrowseApp.BrowseTypeLibrary();
                 if (FAILED(hr))
                     MessageBox(hwndDlg, TEXT("Failed to browse type library"), TEXT("Error"), MB_ICONEXCLAMATION|MB_OK);     
                 return TRUE;
                 
             case IDC_TYPEINFOSLIST:
                 switch (GET_WM_COMMAND_CMD(wParam, lParam))
                 {
                     case LBN_SELCHANGE:
                         hr = BrowseApp.ChangeTypeInfosSelection();    
                         if (FAILED(hr))
                             MessageBox(hwndDlg, TEXT("Failed to get TypeInfo information"), TEXT("Error"), MB_ICONEXCLAMATION|MB_OK);     
                         return TRUE;
                 }
                 break;  
                 
             case IDC_ELEMENTSLIST:
                 switch (GET_WM_COMMAND_CMD(wParam, lParam))
                 {
                     case LBN_SELCHANGE:
                         hr = BrowseApp.ChangeElementsSelection();  
                         if (FAILED(hr))
                             MessageBox(hwndDlg, TEXT("Failed to get Element information"), TEXT("Error"), MB_ICONEXCLAMATION|MB_OK);
                         return TRUE;
                 } 
                 break; 
                 
             case IDC_PARAMETERSLIST:
                 switch (GET_WM_COMMAND_CMD(wParam, lParam))
                 {
                     case LBN_SELCHANGE:
                         hr = BrowseApp.ChangeParametersSelection();
                         if (FAILED(hr))
                             MessageBox(hwndDlg, TEXT("Failed to get Parameter information"), TEXT("Error"), MB_ICONEXCLAMATION|MB_OK);
                         return TRUE;
                 } 
                 break;
                 
             case IDC_ELEM_OPEN_HELPFILE:
                 BrowseApp.OpenElementHelpFile();   
                 return TRUE;    
                                                                                               
         }
         break;
   }
   return FALSE;
}  

/*
 * CBrowseApp::BrowseTypeLibrary
 *
 * Purpose:
 *  Prompts user for type library. Fills the TypeInfo list with the TypeInfos of
 *  the type library.
 *
 */
HRESULT CBrowseApp::BrowseTypeLibrary()
{   
    OPENFILENAME   ofn;
    TCHAR szFileName[128]; 
    BOOL bRes;
    VARIANT vRet;  
    HRESULT hr;
    HCURSOR hcursorOld;
    LPDISPATCH pdispBrowseHelper = NULL;  
    LPDISPATCH pdispTypeLibrary = NULL;
    LPDISPATCH pdispTypeInfos = NULL;      
    TCHAR szTemp[100];
    
    // Prompt user for type library
    szFileName[0] = '\0';
    _fmemset(&ofn, 0, sizeof(ofn));
    ofn.lStructSize = sizeof(ofn);
    ofn.hwndOwner = m_hwndMain;
    ofn.lpstrFile = szFileName;
    ofn.nMaxFile  = sizeof(szFileName);
    ofn.hInstance = m_hinst;
    ofn.lpstrFilter  = TEXT("Type Libraries *.tlb,*.olb\0*.tlb;*.olb\0All Files *.*\0*.*\0\0");
    ofn.nFilterIndex = 1; 
    ofn.Flags= OFN_FILEMUSTEXIST;    
    bRes = GetOpenFileName(&ofn);
    if (!bRes)
        return ResultFromScode(S_FALSE);  
        
    // Empty lists and clear fields.
    EmptyList(GetDlgItem(m_hwndMain, IDC_TYPEINFOSLIST)); 
    EmptyList(GetDlgItem(m_hwndMain, IDC_ELEMENTSLIST));
    EmptyList(GetDlgItem(m_hwndMain, IDC_PARAMETERSLIST));   
    ClearTypeLibStaticFields();
    ClearTypeInfoStaticFields();
    ClearElementStaticFields();
    ClearParamStaticFields(); 
    EnableWindow(GetDlgItem(m_hwndMain, IDC_ELEM_OPEN_HELPFILE), FALSE); 
    
    hcursorOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
    // Create BrowseHelper object
    hr = CreateObject(OLESTR("BrowseHelper.Browser"), &pdispBrowseHelper);   
    if (FAILED(hr))  
    {
        MessageBox(m_hwndMain, 
			 TEXT("Failed to create BrowseHelper object. Build and register the BROWSEH sample"), 
			 TEXT("Error"), MB_ICONEXCLAMATION|MB_OK);
        goto error;
    }
                
    // Invoke IBrowseHelper.BrowseTypeLibrary(szFileName). Returns ITypeLibrary.
    hr = Invoke(pdispBrowseHelper, DISPATCH_METHOD, &vRet, NULL, NULL,
                      OLESTR("BrowseTypeLibrary"), TEXT("s"), (LPOLESTR)TO_OLE_STRING(szFileName)); 
    if (FAILED(hr))
        goto error;  
    pdispTypeLibrary = V_DISPATCH(&vRet);  
    
    // Invoke ITypeLibrary.Name. Returns BSTR.
    hr = Invoke(pdispTypeLibrary, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("Name"), TEXT("")); 
    if (FAILED(hr))
        goto error;
	if (NULL != V_BSTR(&vRet))
        SetDlgItemText(m_hwndMain, IDC_TYPELIB_NAME, FROM_OLE_STRING(V_BSTR(&vRet)));
    VariantClear(&vRet);
    
    // Invoke ITypeLibrary.HelpFile. Returns BSTR.
    hr = Invoke(pdispTypeLibrary, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("HelpFile"), TEXT("")); 
    if (FAILED(hr))
        goto error;
	if (NULL != V_BSTR(&vRet))
        SetDlgItemText(m_hwndMain, IDC_TYPELIB_HELPFILE, FROM_OLE_STRING(V_BSTR(&vRet)));
    if (NULL == V_BSTR(&vRet))
        m_szHelpFile[0] = '\0';
    else lstrcpy(m_szHelpFile, FROM_OLE_STRING(V_BSTR(&vRet))); 
    VariantClear(&vRet); 
    
    // Invoke ITypeLibrary.Documentation. Returns BSTR.
    hr = Invoke(pdispTypeLibrary, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("Documentation"), TEXT("")); 
    if (FAILED(hr))
        goto error;
	if (V_BSTR(&vRet) != NULL) //NULL is a valid value for a BSTR
        SetDlgItemText(m_hwndMain, IDC_TYPELIB_DOC, FROM_OLE_STRING(V_BSTR(&vRet)));
    VariantClear(&vRet);
    
    // Invoke ITypeLibrary.LocaleID. Returns VT_I4.
    hr = Invoke(pdispTypeLibrary, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("LocaleID"), TEXT("")); 
    if (FAILED(hr))
        goto error;   
    wsprintf(szTemp, TEXT("0x%lx"), V_I4(&vRet)); 
    SetDlgItemText(m_hwndMain, IDC_TYPELIB_LCID, szTemp);
    
    // Invoke ITypeLibrary.MajorVersion. Returns VT_I2.
    hr = Invoke(pdispTypeLibrary, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("MajorVersion"), TEXT("")); 
    if (FAILED(hr))
        goto error;   
    wsprintf(szTemp, TEXT("%d"), V_I2(&vRet));    
    // Invoke ITypeLibrary.MinorVersion. Returns VT_I2.
    hr = Invoke(pdispTypeLibrary, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("MinorVersion"), TEXT("")); 
    if (FAILED(hr))
        goto error;   
    wsprintf(szTemp, TEXT("%s.%d"), (LPSTR)szTemp, V_I2(&vRet)); 
    SetDlgItemText(m_hwndMain, IDC_TYPELIB_VERSION, szTemp);
    
    // Invoke ITypeLibrary.TypeInfos. Returns ICollection of ITypeInfo.
    hr = Invoke(pdispTypeLibrary, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("TypeInfos"), TEXT("")); 
    if (FAILED(hr))
        goto error;
    pdispTypeInfos = V_DISPATCH(&vRet);
    
    // Load TypeInfo list with the names of the type infos.
    hr = LoadList(pdispTypeInfos, IDC_TYPEINFOSLIST); 
    if (FAILED(hr))
        goto error;  
    
    pdispBrowseHelper->Release(); 
    pdispTypeLibrary->Release();
    pdispTypeInfos->Release();
    SetCursor(hcursorOld);
    return NOERROR;     

error:     
    if (pdispBrowseHelper) pdispBrowseHelper->Release();  
    if (pdispTypeLibrary) pdispTypeLibrary->Release();  
    if (pdispTypeInfos) pdispTypeLibrary->Release(); 
    VariantClear(&vRet); 
    SetCursor(hcursorOld);
    return hr;
}  

/*
 * CBrowseApp::ChangeTypeInfosSelection
 *
 * Purpose:
 *  Called when user changes selection in TypeInfos list box. Information 
 *  about the the selected TypeInfo is dispayed. Elements of the TypeInfo
 *  are placed in the Elements list box.
 *
 */
HRESULT CBrowseApp::ChangeTypeInfosSelection()
{   
    int nCurSel;
    VARIANT vRet;   
    HRESULT hr;   
    HCURSOR hcursorOld;
    LPDISPATCH pdispTypeInfo;  
    TYPEKIND typekind;   
    TCHAR szTemp[100]; 
    LPDISPATCH pdispElements = NULL;
    LPDISPATCH pdispMembers = NULL;
    LPDISPATCH pdispFunctions = NULL;
    LPDISPATCH pdispProperties = NULL;
    LPDISPATCH pdispMethods = NULL;
    LPDISPATCH pdispInterfaces = NULL;
    LPDISPATCH pdispTypeDesc = NULL;
     
    // Get current selection in TypeInfos list.
    nCurSel = (int)SendDlgItemMessage(m_hwndMain, IDC_TYPEINFOSLIST, LB_GETCURSEL, 0, 0L);
    if (nCurSel == LB_ERR) 
        return NOERROR; 
    // Get IDispatch* of selected TypeInfo.
    pdispTypeInfo = (LPDISPATCH) SendDlgItemMessage(m_hwndMain, IDC_TYPEINFOSLIST, LB_GETITEMDATA,
                (WPARAM)nCurSel, 0L);
    
    // Empty Elements & Parameters lists.
    EmptyList(GetDlgItem(m_hwndMain, IDC_ELEMENTSLIST));
    EmptyList(GetDlgItem(m_hwndMain, IDC_PARAMETERSLIST));     
    ClearElementStaticFields();
    ClearParamStaticFields();

    ClearTypeInfoStaticFields();
    
    hcursorOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
    // Invoke ITypeInformation.HelpContext. Returns VT_I4.
    hr = Invoke(pdispTypeInfo, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("HelpContext"), TEXT("")); 
    if (FAILED(hr))
        goto error;      
    wsprintf(szTemp, TEXT("%ld"), V_I4(&vRet)); 
    SetDlgItemText(m_hwndMain, IDC_TYPEINFO_HELPCTX, szTemp);
    
    // Invoke ITypeInformation.Documentation. Returns BSTR.
    hr = Invoke(pdispTypeInfo, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("Documentation"), TEXT("")); 
    if (FAILED(hr))
        goto error;
	if (V_BSTR(&vRet) != NULL) //NULL is a valid value for a BSTR
        SetDlgItemText(m_hwndMain, IDC_TYPEINFO_DOC, FROM_OLE_STRING(V_BSTR(&vRet)));
    VariantClear(&vRet);
    
    // Invoke ITypeInformation.TypeInfoKind. Returns TYPEKIND.
    hr = Invoke(pdispTypeInfo, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("TypeInfoKind"), TEXT("")); 
    if (FAILED(hr))
        goto error; 
    typekind = (TYPEKIND)V_I2(&vRet);  
    
    switch (typekind)
    {
        case TKIND_ENUM:
            SetDlgItemText(m_hwndMain, IDC_TYPEINFO_TYPE, TEXT("TKIND_ENUM"));
            // Invoke IEnum.Elements. Returns ICollection of IConstant.
            hr = Invoke(pdispTypeInfo, DISPATCH_PROPERTYGET, &vRet, NULL, NULL,
                        OLESTR("Elements"), TEXT("")); 
            if (FAILED(hr))
                goto error; 
            pdispElements = V_DISPATCH(&vRet);   
            // Put the Enumerator constants in the Elements list. 
            hr = LoadList(pdispElements, IDC_ELEMENTSLIST);  
            if (FAILED(hr))
                goto error; 
            pdispElements->Release();     
            break;   
        
        case TKIND_RECORD:        
            SetDlgItemText(m_hwndMain, IDC_TYPEINFO_TYPE, TEXT("TKIND_RECORD"));
            // Invoke IStruct.Members. Returns ICollection of IProperty.
            hr = Invoke(pdispTypeInfo, DISPATCH_PROPERTYGET, &vRet, NULL, NULL,
                        OLESTR("Members"), TEXT("")); 
            if (FAILED(hr))
                goto error; 
            pdispMembers = V_DISPATCH(&vRet);    
            // Put the Structure memebers in the Elements list.
            hr = LoadList(pdispMembers, IDC_ELEMENTSLIST);   
            if (FAILED(hr))
                goto error; 
            pdispMembers->Release(); 
            break; 
            
        case TKIND_MODULE:
            SetDlgItemText(m_hwndMain, IDC_TYPEINFO_TYPE, TEXT("TKIND_MODULE"));
            // Invoke IModule.Functions. Returns ICollection of IFunction.
            hr = Invoke(pdispTypeInfo, DISPATCH_PROPERTYGET, &vRet, NULL, NULL,
                        OLESTR("Functions"), TEXT("")); 
            if (FAILED(hr))
                goto error; 
            pdispFunctions = V_DISPATCH(&vRet); 
            // Put the Module functions in the Elements list.
            hr = LoadList(pdispFunctions, IDC_ELEMENTSLIST);         
            if (FAILED(hr))
                goto error; 
            pdispFunctions->Release(); 
            break;   
            
        case TKIND_INTERFACE:
            SetDlgItemText(m_hwndMain, IDC_TYPEINFO_TYPE, TEXT("TKIND_INTERFACE"));
            // Invoke IInterface.Functions. Returns ICollection of IFunction.
            hr = Invoke(pdispTypeInfo, DISPATCH_PROPERTYGET, &vRet, NULL, NULL,
                        OLESTR("Functions"), TEXT("")); 
            if (FAILED(hr))
                goto error; 
            pdispFunctions = V_DISPATCH(&vRet); 
            // Put the Interface functions in the Elements list.
            hr = LoadList(pdispFunctions, IDC_ELEMENTSLIST);
            if (FAILED(hr))
                goto error; 
            pdispFunctions->Release(); 
            break;  
            
        case TKIND_DISPATCH:
            SetDlgItemText(m_hwndMain, IDC_TYPEINFO_TYPE, TEXT("TKIND_DISPATCH"));
            // Invoke Dispinterface.Properties. Returns ICollection of IProperty.
            hr = Invoke(pdispTypeInfo, DISPATCH_PROPERTYGET, &vRet, NULL, NULL,
                        OLESTR("Properties"), TEXT("")); 
            if (FAILED(hr))
                goto error; 
            pdispProperties = V_DISPATCH(&vRet); 
            // Invoke IDispinterface.Methods. Returns ICollection of IFunction.
            hr = Invoke(pdispTypeInfo, DISPATCH_PROPERTYGET, &vRet, NULL, NULL,
                        OLESTR("Methods"), TEXT("")); 
            if (FAILED(hr))
                goto error; 
            pdispMethods = V_DISPATCH(&vRet);
            // Put the Dispinterface properties and methods in the Elements list.   
            hr = LoadList(pdispProperties, IDC_ELEMENTSLIST);  
            if (FAILED(hr))
                goto error; 
            pdispProperties->Release();  
            hr = LoadList(pdispMethods, IDC_ELEMENTSLIST); 
            if (FAILED(hr))
                goto error; 
            pdispMethods->Release(); 
            break; 
            
        case TKIND_COCLASS:
            SetDlgItemText(m_hwndMain, IDC_TYPEINFO_TYPE, TEXT("TKIND_COCLASS"));
            // Invoke ICoClass.Interfaces. Returns ICollection of IInterface & IDispinterface.
            hr = Invoke(pdispTypeInfo, DISPATCH_PROPERTYGET, &vRet, NULL, NULL,
                        OLESTR("Interfaces"), TEXT("")); 
            if (FAILED(hr))
                goto error; 
            pdispInterfaces = V_DISPATCH(&vRet);  
            // Put the CoClass interfaces and dispinterfaces in the Elements list.
            hr = LoadList(pdispInterfaces, IDC_ELEMENTSLIST); 
            if (FAILED(hr))
                goto error; 
            pdispInterfaces->Release();  
            break;        
            
        case TKIND_ALIAS:           
            // An alias does not have elements. Display the base type.
            SetDlgItemText(m_hwndMain, IDC_TYPEINFO_TYPE, TEXT("TKIND_ALIAS"));
            // Invoke IAlias.BaseType. Returns ITypeDesc.
            hr = Invoke(pdispTypeInfo, DISPATCH_PROPERTYGET, &vRet, NULL, NULL,
                        OLESTR("BaseType"), TEXT("")); 
            if (FAILED(hr))
                goto error; 
            pdispTypeDesc = V_DISPATCH(&vRet);
            szTemp[0] = '\0'; // Must initialize to empty string before calling TypeToString.
            hr = TypeToString(pdispTypeDesc, szTemp);
            if (FAILED(hr))
                goto error; 
            SetDlgItemText(m_hwndMain, IDC_TYPEINFO_ALIASTYPE, szTemp);
            pdispTypeDesc->Release();  
            break;
            
        case TKIND_UNION:
            SetDlgItemText(m_hwndMain, IDC_TYPEINFO_TYPE, TEXT("TKIND_UNION"));
            // Invoke IUnion.Members. Returns ICollection of IProperty.
            hr = Invoke(pdispTypeInfo, DISPATCH_PROPERTYGET, &vRet, NULL, NULL,
                        OLESTR("Members"), TEXT("")); 
            if (FAILED(hr))
                goto error; 
            pdispMembers = V_DISPATCH(&vRet);     
            // Put the Union members in the Elements list.
            hr = LoadList(pdispMembers, IDC_ELEMENTSLIST);   
            if (FAILED(hr))
                goto error; 
            pdispMembers->Release();  
            break;   
    }
    SetCursor(hcursorOld);   
    return NOERROR;
error: 
    if (pdispElements) pdispElements->Release(); 
    if (pdispMembers) pdispMembers->Release(); 
    if (pdispFunctions) pdispFunctions->Release(); 
    if (pdispProperties) pdispProperties->Release(); 
    if (pdispMethods) pdispElements->Release(); 
    if (pdispInterfaces) pdispInterfaces->Release(); 
    if (pdispTypeDesc) pdispTypeDesc->Release();
    VariantClear(&vRet); 
    SetCursor(hcursorOld);
    return hr;
}

/*
 * CBrowseApp::ChangeElementsSelection
 *
 * Purpose:
 *  Called when user changes selection in Elements list box. Information 
 *  about the the selected Element is dispayed. If the element is a function,
 *  the parameters are placed in the the Parameters list box.
 *
 */
HRESULT CBrowseApp::ChangeElementsSelection()
{ 
    int nCurSel;
    VARIANT vRet;   
    HRESULT hr;     
    int nKind; 
    TYPEKIND typekind;
    LPDISPATCH pdispElement;   
    LPDISPATCH pdispParameters = NULL;
    LPDISPATCH pdispTypeDesc = NULL; 
    TCHAR szTemp[100];
    
    // Disable the help file button.
    EnableWindow(GetDlgItem(m_hwndMain, IDC_ELEM_OPEN_HELPFILE), FALSE); 
    
    // Get current selection in Elements list.
    nCurSel = (int)SendDlgItemMessage(m_hwndMain, IDC_ELEMENTSLIST, LB_GETCURSEL, 0, 0L);
    if (nCurSel == LB_ERR) 
        return NOERROR; 
    // Get IDispatch* of selected Element.
    pdispElement = (LPDISPATCH) SendDlgItemMessage(m_hwndMain, IDC_ELEMENTSLIST, LB_GETITEMDATA,
                (WPARAM)nCurSel, 0L);
    
    // Empty Parameters lists.
    EmptyList(GetDlgItem(m_hwndMain, IDC_PARAMETERSLIST));  
    ClearParamStaticFields();

    ClearElementStaticFields();
    
    // Invoke IFunction/IConstant/IProperty/IInterface/IDispinterface.HelpContext. Returns VT_I4.
    hr = Invoke(pdispElement, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("HelpContext"), TEXT("")); 
    if (FAILED(hr))
        goto error;      
    wsprintf(szTemp, TEXT("%ld"), V_I4(&vRet)); 
    SetDlgItemText(m_hwndMain, IDC_ELEM_HELPCTX, szTemp);
    m_lElemHelpCtx = V_I4(&vRet); 
    
    // Invoke IFunction/IConstant/IProperty/IInterface/IDispinterface.Documentation. Returns BSTR.
    hr = Invoke(pdispElement, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("Documentation"), TEXT("")); 
    if (FAILED(hr))
        goto error; 
	if (V_BSTR(&vRet) != NULL) //NULL is a valid value for a BSTR
        SetDlgItemText(m_hwndMain, IDC_ELEM_DOC, FROM_OLE_STRING(V_BSTR(&vRet)));
    VariantClear(&vRet);
    
    // Invoke IFunction/IConstant/IProperty.MemberID. Returns VT_I4.
    hr = Invoke(pdispElement, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("MemberID"), TEXT("")); 
    if (FAILED(hr))
    {
        // This maybe a interface/dispinterface element of a coclass   
        hr = Invoke(pdispElement, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("TypeInfoKind"), TEXT("")); 
        if (FAILED(hr))
            goto error; 
        typekind = (TYPEKIND)V_I2(&vRet);  
        if (typekind == TKIND_INTERFACE)
            SetDlgItemText(m_hwndMain, IDC_ELEM_TYPE, TEXT("TKIND_INTERFACE"));
        else if (typekind == TKIND_DISPATCH)
            SetDlgItemText(m_hwndMain, IDC_ELEM_TYPE, TEXT("TKIND_DISPATCH"));  
        else SetDlgItemText(m_hwndMain, IDC_ELEM_TYPE, TEXT("Unknown element"));   
        return NOERROR;
    }
    wsprintf(szTemp, TEXT("0x%lx"), V_I4(&vRet));
    SetDlgItemText(m_hwndMain, IDC_ELEM_MEMID, szTemp);
    
    // Invoke IFunction/IConstant/IProperty.Kind property. Returns OBJKIND.
    hr = Invoke(pdispElement, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("Kind"), TEXT("")); 
    if (FAILED(hr))
        goto error;  
    nKind = V_I2(&vRet);  
    
    switch (nKind)
    {
        case TYPE_FUNCTION: //Function
            // Invoke IFunction.Parameters. Returns ICollection of IParameter..
            hr = Invoke(pdispElement, DISPATCH_PROPERTYGET, &vRet, NULL, NULL,
                        OLESTR("Parameters"), TEXT("")); 
            if (FAILED(hr))
                goto error; 
            pdispParameters = V_DISPATCH(&vRet); 
            hr = LoadList(pdispParameters, IDC_PARAMETERSLIST);   
            if FAILED(hr)
                goto error;
            pdispParameters->Release(); pdispParameters = NULL;
            
            // Invoke IFunction.CallConvention. Returns CALLCONV.
            hr = Invoke(pdispElement, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("CallConvention"), TEXT("")); 
            if (FAILED(hr))
                goto error;   
            CallConvToString(V_I2(&vRet), szTemp);
            SetDlgItemText(m_hwndMain, IDC_ELEM_CALLCONV, szTemp); 
            
            // Invoke IFunction.FuncKind. Returns FUNCKIND.
            hr = Invoke(pdispElement, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("FuncKind"), TEXT("")); 
            if (FAILED(hr))
                goto error;   
            FuncKindToString(V_I2(&vRet), szTemp);
            SetDlgItemText(m_hwndMain, IDC_ELEM_FUNCKIND, szTemp);
            
            // Invoke IFunction.InvocationKind. Returns INVOKEKIND.
            hr = Invoke(pdispElement, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("InvocationKind"), TEXT("")); 
            if (FAILED(hr))
                goto error;   
            InvokeKindToString(V_I2(&vRet), szTemp);
            SetDlgItemText(m_hwndMain, IDC_ELEM_INVOKEKIND, szTemp);   
            
            // Invoke IFunction.ReturnType. Returns ITypeDesc.
            hr = Invoke(pdispElement, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("ReturnType"), TEXT("")); 
            if (FAILED(hr))
                goto error;           
            break;    
            
        case TYPE_CONSTANT:
            // Invoke IConstant.Value. Returns VARIANT.
            hr = Invoke(pdispElement, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("Value"), TEXT("")); 
            if (FAILED(hr))
                goto error;   
            VariantToString(vRet, szTemp);
            SetDlgItemText(m_hwndMain, IDC_ELEM_CONST_VALUE, szTemp);
            // Fall through to default.     
            
        default: 
            // Invoke IConstant/IProperty.Type. Returns ITypeDesc.
            hr = Invoke(pdispElement, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("Type"), TEXT("")); 
            if (FAILED(hr))
                goto error;  
            break;
    }   
    // Get string that describes type and display it.
    pdispTypeDesc = V_DISPATCH(&vRet);  
    szTemp[0] = '\0'; // Must initialize to empty string before calling TypeToString.
    hr = TypeToString(pdispTypeDesc, szTemp);
    if (FAILED(hr))
        goto error;
    SetDlgItemText(m_hwndMain, IDC_ELEM_TYPE, szTemp);
    pdispTypeDesc->Release(); 
    
    // Enable the helpfile button if a helpfile and help context is specified.
    if (m_lElemHelpCtx && m_szHelpFile[0])
        EnableWindow(GetDlgItem(m_hwndMain, IDC_ELEM_OPEN_HELPFILE), TRUE);
    return NOERROR;
error: 
    if (pdispParameters) pdispParameters->Release();  
    VariantClear(&vRet);
    return hr; 
}

/*
 * CBrowseApp::ChangeParametersSelection
 *
 * Purpose:
 *  Called when user changes selection in Parameters list box. Information 
 *  about the the selected Parameter is dispayed.
 *
 */
HRESULT CBrowseApp::ChangeParametersSelection()
{   
    int nCurSel;
    VARIANT vRet;   
    HRESULT hr;
    LPDISPATCH pdispParameter;
    LPDISPATCH pdispTypeDesc = NULL; 
    TCHAR szTemp[100];
     
    // Get current selection in Parameters list.
    nCurSel = (int)SendDlgItemMessage(m_hwndMain, IDC_PARAMETERSLIST, LB_GETCURSEL, 0, 0L);
    if (nCurSel == LB_ERR) 
        return NOERROR; 
    // Get IDispatch* of selected Parameter.
    pdispParameter = (LPDISPATCH) SendDlgItemMessage(m_hwndMain, IDC_PARAMETERSLIST, LB_GETITEMDATA,
                (WPARAM)nCurSel, 0L);  
    
    ClearParamStaticFields();
    
    // Invoke IParameter.IDLFlags. Returns VT_I2.
    hr = Invoke(pdispParameter, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("IDLFlags"), TEXT("")); 
    if (FAILED(hr))
        goto error; 
    IDLFlagsToString(V_I2(&vRet), szTemp); 
    SetDlgItemText(m_hwndMain, IDC_PARAM_INOUT, szTemp);
                    
    // Invoke IParameter.Type. Returns ITypeDesc.
    hr = Invoke(pdispParameter, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("Type"), TEXT("")); 
    if (FAILED(hr))
        goto error;
    
   // Get string that describes type and display it.
    pdispTypeDesc = V_DISPATCH(&vRet);  
    szTemp[0] = '\0'; // Must initialize to empty string before calling TypeToString.
    hr = TypeToString(pdispTypeDesc, szTemp);     
    if (FAILED(hr))
        goto error;
    SetDlgItemText(m_hwndMain, IDC_PARAM_TYPE, szTemp);
    pdispTypeDesc->Release();
    return NOERROR;
error: 
    if (pdispTypeDesc) pdispTypeDesc->Release();
    VariantClear(&vRet);
    return hr; 
} 

/*
 * CBrowseApp::OpenElementHelpFile
 *
 * Purpose:
 *  Opens the helpfile for the element.
 *
 */
void CBrowseApp::OpenElementHelpFile()
{
    BOOL b;
    
    b = WinHelp(m_hwndMain, m_szHelpFile, HELP_CONTEXT, m_lElemHelpCtx);
    if (!b)
        MessageBox(m_hwndMain, TEXT("Failed to open help file"), TEXT("Error"), 
            MB_ICONEXCLAMATION|MB_OK);      
}

void CBrowseApp::EmptyList(HWND hwndList)
{
    int nItems, i;
    LPDISPATCH pdispItem;

    nItems = (int)SendMessage(hwndList, LB_GETCOUNT, 0, 0L);
    for (i=0; i<nItems; i++)
    {
        pdispItem = (LPDISPATCH)SendMessage(hwndList, LB_GETITEMDATA, (WPARAM)i, 0L); 
        if (pdispItem)
            pdispItem->Release();
    }
    SendMessage(hwndList, LB_RESETCONTENT, 0, 0L);
} 

/*
 * CBrowseApp::LoadList  
 *
 * Parameters:
 *  pdispItems IDispatch* of collection of items to be put into the list.
 *  nListID    Identifies the list to be filled.
 *
 * Purpose:
 *  Loads the list with the items in the collection.
 *
 */
HRESULT CBrowseApp::LoadList(LPDISPATCH pdispItems, int nListID)
{   
    VARIANT vRet, v;  
    HRESULT hr;    
    LPUNKNOWN punkEnum;
    IEnumVARIANT FAR* penum = NULL;  
    LPDISPATCH pdispItem = NULL;   
    int nIndex;
    
    // Get _NewEnum property. Returns enumerator's IUnknown.
    hr = Invoke(pdispItems, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                        OLESTR("_NewEnum"), TEXT("")); 
    if (FAILED(hr))
        goto error;         
    punkEnum = V_UNKNOWN(&vRet);
    hr = punkEnum->QueryInterface(IID_IEnumVARIANT, (LPVOID FAR*)&penum);
    if (FAILED(hr))
        goto error;
    punkEnum->Release();

    VariantInit(&v);
    // Enumerate the Items.
    while (S_OK == penum->Next(1, &v, NULL))
    {
        pdispItem = V_DISPATCH(&v);
        pdispItem->AddRef();
        VariantClear(&v);
        
        // Get Name of Item.
        hr = Invoke(pdispItem, DISPATCH_PROPERTYGET, &vRet, NULL, NULL,
                    OLESTR("Name"), TEXT("")); 
        if (FAILED(hr))
            goto error;             
        // Add name to ItemsList.
        nIndex = (int)SendDlgItemMessage(m_hwndMain, nListID, LB_ADDSTRING,
            0, (LPARAM)(LPTSTR)FROM_OLE_STRING(V_BSTR(&vRet))); 
        VariantClear(&vRet);          
        // Save IDispatch* of Item in the list.
        SendDlgItemMessage(m_hwndMain, nListID, LB_SETITEMDATA,
            nIndex, (LPARAM)pdispItem);
    }
    penum->Release();
    return NOERROR;     

error:   
    if (penum) penum->Release();  
    if (pdispItem) pdispItem->Release();
    return hr;
}  

void CBrowseApp::Init(HWND hwndMain) 
{   
    HFONT hfontDlg;   
    LOGFONT lfont;
    int nID;
    
    m_hwndMain = hwndMain;
    m_hfont = NULL;
#ifdef WIN16   
    m_hinst = (HINSTANCE)GetWindowWord(hwndMain, GWW_HINSTANCE); 
#else
    m_hinst = (HINSTANCE)GetWindowLong(hwndMain, GWL_HINSTANCE);
#endif     
    EnableWindow(GetDlgItem(m_hwndMain, IDC_ELEM_OPEN_HELPFILE), FALSE);
    
    // Change to the font of the fields to a non-bold font.
    hfontDlg = (HFONT)SendMessage(m_hwndMain, WM_GETFONT, NULL, NULL);  
    if (!hfontDlg)
        return;
    GetObject(hfontDlg, sizeof(LOGFONT), (LPVOID)&lfont);            
    lfont.lfWeight = FW_NORMAL;    
    if (m_hfont = CreateFontIndirect(&lfont)) 
        for (nID=IDC_TYPELIB_FIRST; nID<=IDC_PARAM_LAST; nID++) 
            SendDlgItemMessage(m_hwndMain, nID, WM_SETFONT, (WPARAM)m_hfont, 0L);
        
} 

void CBrowseApp::Cleanup() 
{
    EmptyList(GetDlgItem(m_hwndMain, IDC_TYPEINFOSLIST)); 
    EmptyList(GetDlgItem(m_hwndMain, IDC_ELEMENTSLIST));
    EmptyList(GetDlgItem(m_hwndMain, IDC_PARAMETERSLIST));
    ClearTypeLibStaticFields();
    ClearTypeInfoStaticFields();     
    ClearElementStaticFields();
    ClearParamStaticFields();
    if (m_hfont)
        DeleteObject(m_hfont);  
} 

void CBrowseApp::ClearTypeLibStaticFields()
{   
    int nID;
    
    for (nID=IDC_TYPELIB_FIRST; nID<=IDC_TYPELIB_LAST; nID++)
        SetDlgItemText(m_hwndMain, nID, TEXT(""));
}
void CBrowseApp::ClearTypeInfoStaticFields()
{   
    int nID;
    
    for (nID=IDC_TYPEINFO_FIRST; nID<=IDC_TYPEINFO_LAST; nID++)
        SetDlgItemText(m_hwndMain, nID, TEXT(""));
}     
void CBrowseApp::ClearElementStaticFields()
{ 
    int nID;
    
    for (nID=IDC_ELEM_FIRST; nID<=IDC_ELEM_LAST; nID++)
        SetDlgItemText(m_hwndMain, nID, TEXT(""));
}
void CBrowseApp::ClearParamStaticFields()
{  
    int nID;
    
    for (nID=IDC_PARAM_FIRST; nID<=IDC_PARAM_LAST; nID++)
        SetDlgItemText(m_hwndMain, nID, TEXT(""));
} 

#define CASE_VT(vt)  \
        case vt: \
            lstrcat(pszTypeName, TEXT(#vt)); \
            break;
HRESULT CBrowseApp::TypeToString(LPDISPATCH pdispTypeDesc, LPTSTR pszTypeName)
{  
    VARIANT vRet;
    VARTYPE vartype;  
    HRESULT hr;   
    LPDISPATCH pdispTypeDesc2; 
    
    // Get Type property  
    hr = Invoke(pdispTypeDesc, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("Type"), TEXT("")); 
    if (FAILED(hr))
        goto error;   
    vartype = V_I2(&vRet); 
    
    if (vartype & 0x2000)  // If SafeArray
      lstrcat(pszTypeName, TEXT("SAFEARRAY(")); 
    
    switch (vartype & ~0x7000)
    { 
    CASE_VT(VT_EMPTY)
    CASE_VT(VT_NULL)
    CASE_VT(VT_I2)
    CASE_VT(VT_I4)
    CASE_VT(VT_R4)
    CASE_VT(VT_R8)
    CASE_VT(VT_CY)
    CASE_VT(VT_DATE)
    CASE_VT(VT_BSTR)
    CASE_VT(VT_DISPATCH)
    CASE_VT(VT_ERROR)
    CASE_VT(VT_BOOL)
    CASE_VT(VT_VARIANT)
    CASE_VT(VT_UNKNOWN)

    CASE_VT(VT_I1)
    CASE_VT(VT_UI1)
    CASE_VT(VT_UI2)
    CASE_VT(VT_UI4)
    CASE_VT(VT_I8)
    CASE_VT(VT_UI8)
    CASE_VT(VT_INT)
    CASE_VT(VT_UINT)
    CASE_VT(VT_VOID)
    CASE_VT(VT_HRESULT)
    CASE_VT(VT_SAFEARRAY)  
    CASE_VT(VT_CARRAY)
    CASE_VT(VT_LPSTR)
    CASE_VT(VT_LPWSTR)

    CASE_VT(VT_FILETIME)
    CASE_VT(VT_BLOB)
    CASE_VT(VT_STREAM)
    CASE_VT(VT_STORAGE)
    CASE_VT(VT_STREAMED_OBJECT)
    CASE_VT(VT_STORED_OBJECT)
    CASE_VT(VT_BLOB_OBJECT)
    CASE_VT(VT_CF)
    CASE_VT(VT_CLSID)
   
    case VT_PTR:    
         // Get ITypeDesc.PointerDesc property. Returns ITypeDesc.  
         hr = Invoke(pdispTypeDesc, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("PointerDesc"), TEXT("")); 
         if (FAILED(hr))
             goto error;  
         pdispTypeDesc2 = V_DISPATCH(&vRet);
         TypeToString(pdispTypeDesc2, pszTypeName);
         lstrcat(pszTypeName, TEXT(" *"));
         pdispTypeDesc2->Release();
         break;

    case VT_USERDEFINED:   
         // Get ITypeDesc.UserDefinedDesc property. Returns ITypeInformation 
         hr = Invoke(pdispTypeDesc, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("UserDefinedDesc"), TEXT("")); 
         if (FAILED(hr))
             goto error;  
         pdispTypeDesc2 = V_DISPATCH(&vRet);
         // Get ITypeInformation.Name property. Returns BSTR.  
         hr = Invoke(pdispTypeDesc2, DISPATCH_PROPERTYGET, &vRet, NULL, NULL, 
                OLESTR("Name"), TEXT("")); 
         if (FAILED(hr))
             goto error;  
         lstrcat(pszTypeName, FROM_OLE_STRING(V_BSTR(&vRet))); 
         VariantClear(&vRet);
         pdispTypeDesc2->Release();
         break;
    }    
    
    if (vartype & 0x2000) // If SafeArray
        lstrcat(pszTypeName, TEXT(")"));  
      
    return NOERROR;
error:
    return ERROR;
}   

void CBrowseApp::IDLFlagsToString(int n, LPTSTR psz)
{  
   psz[0] = '\0';
   if (n & IDLFLAG_FIN)
   {
       lstrcpy(psz, TEXT("IN")); 
       if (n & IDLFLAG_FOUT)
           lstrcat(psz, TEXT("|OUT"));
   }
   else if (n & IDLFLAG_FOUT)
       lstrcpy(psz, TEXT("OUT"));    
}

#define CASE_INVOKE(invokekind)  \
        case invokekind: \
            lstrcpy(psz, TEXT(#invokekind)); \
            break;   
void CBrowseApp::CallConvToString(int n, LPTSTR psz)   
{
    CALLCONV c = (CALLCONV)n;
    switch (c)
    {
        CASE_INVOKE(CC_CDECL)
        CASE_INVOKE(CC_PASCAL)
        CASE_INVOKE(CC_MACPASCAL)
        CASE_INVOKE(CC_STDCALL)
        CASE_INVOKE(CC_SYSCALL)
        default:
            lstrcpy(psz, TEXT("Unknown"));          
    }
}

#define CASE_FUNC(funckind)  \
        case funckind: \
            lstrcpy(psz, TEXT(#funckind)); \
            break;   
void CBrowseApp::FuncKindToString(int n, LPTSTR psz)    
{     
    FUNCKIND f = (FUNCKIND)n;
    switch (f)
    {
        CASE_FUNC(FUNC_VIRTUAL)
        CASE_FUNC(FUNC_PUREVIRTUAL)
        CASE_FUNC(FUNC_NONVIRTUAL)
        CASE_FUNC(FUNC_STATIC)
        CASE_FUNC(FUNC_DISPATCH)
        default:
            lstrcpy(psz, TEXT("Unknown"));          
    }
} 

#define CASE_INVOKE(invokekind)  \
        case invokekind: \
            lstrcpy(psz, TEXT(#invokekind)); \
            break;   
void CBrowseApp::InvokeKindToString(int n, LPTSTR psz)  
{  
    INVOKEKIND i = (INVOKEKIND)n;
    switch (i)
    {
        CASE_INVOKE(INVOKE_FUNC)
        CASE_INVOKE(INVOKE_PROPERTYGET)
        CASE_INVOKE(INVOKE_PROPERTYPUT)
        CASE_INVOKE(DISPATCH_PROPERTYPUTREF)
        default:
            lstrcpy(psz, TEXT("Unknown"));          
    }
}
void CBrowseApp::VariantToString(VARIANT v, LPTSTR psz) 
{   
    HRESULT hr;
    VARIANT vTemp;
    
    VariantInit(&vTemp);
    hr = VariantChangeType(&vTemp, &v, 0, VT_BSTR);
    if (FAILED(hr))
        lstrcpy(psz, TEXT("Cannot Display Value")); 
    else lstrcpy(psz, FROM_OLE_STRING(V_BSTR(&vTemp))); 
    VariantClear(&vTemp);
}


/*
 * Quick & Dirty ANSI/Unicode conversion routines. These routines use a static
 * buffer of fixed size to hold the converted string. Consequently these
 * routines are limited to strings of size STRCONVERT_MAXLEN. Also the same
 * buffer is reused when the routine is called a second time. So make sure
 * that the converted string is used before the conversion routine is called
 * again
 */
#ifdef WIN32

#ifndef UNICODE
char* ConvertToAnsi(OLECHAR FAR* szW)
{
  static char achA[STRCONVERT_MAXLEN]; 
  
  WideCharToMultiByte(CP_ACP, 0, szW, -1, achA, STRCONVERT_MAXLEN, NULL, NULL);  
  return achA; 
} 

OLECHAR* ConvertToUnicode(char FAR* szA)
{
  static OLECHAR achW[STRCONVERT_MAXLEN]; 

  MultiByteToWideChar(CP_ACP, 0, szA, -1, achW, STRCONVERT_MAXLEN);  
  return achW; 
}
#endif

#endif   
