// This is a part of the Microsoft Management Console.
// Copyright (C) Microsoft Corporation, 1995 - 1999
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Management Console and related
// electronic documentation provided with the interfaces.

// You will need the NT SUR Beta 2 SDK or VC 4.2 in order to build this
// project.  This is because you will need MIDL 3.00.15 or higher and new
// headers and libs.  If you have VC 4.2 installed, then everything should
// already be configured correctly.

#include "pch.h"
#include "resource.h"

#include <SnapBase.h>

#include "ADSIEdit.h"
#include "snapdata.h"
#include "editor.h"
#include "connection.h"
#include "querynode.h"
#include "IAttredt.h"
#include "editorui.h"
#include "editimpl.h"

//#include "HelpArr.h"  // context help ID's

// Version info
#include <ntverp.h>
#define IDS_SNAPIN_VERSION VER_PRODUCTVERSION_STR
#define IDS_SNAPIN_PROVIDER VER_COMPANYNAME_STR


#ifdef DEBUG_ALLOCATOR
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #undef THIS_FILE
    static char THIS_FILE[] = __FILE__;
    #endif
#endif

//////////////////////////////////////////////////////////////////////////////
// regsvr debugging

// define to enable MsgBox debugging for regsvr32
//#define _MSGBOX_ON_REG_FAIL


#ifdef _MSGBOX_ON_REG_FAIL
#define _MSGBOX(x) AfxMessageBox(x)
#else
#define _MSGBOX(x)
#endif

#ifdef _MSGBOX_ON_REG_FAIL
#define _REPORT_FAIL(lpszMessage, lpszClsid, lRes) \
  ReportFail(lpszMessage, lpszClsid, lRes)

void ReportFail(LPCWSTR lpszMessage, LPCWSTR lpszClsid, LONG lRes)
{
  if (lRes == ERROR_SUCCESS)
    return;

  CThemeContextActivator activator;
  CString sz;
  sz.Format(_T("%s %s %d"), lpszMessage,lpszClsid, lRes);
  AfxMessageBox(sz);
}

#else
#define _REPORT_FAIL(lpszMessage, lpszClsid, lRes)
#endif


//////////////////////////////////////////////////////////////////////////////
// global constants and macros

// GUIDs for snapin
// {1C5DACFA-16BA-11d2-81D0-0000F87A7AA3}
static const GUID CLSID_ADSIEditSnapin =
{ 0x1c5dacfa, 0x16ba, 0x11d2, { 0x81, 0xd0, 0x0, 0x0, 0xf8, 0x7a, 0x7a, 0xa3 } };

// {E6F27C2A-16BA-11d2-81D0-0000F87A7AA3}
static const GUID CLSID_ADSIEditAbout =
{ 0xe6f27c2a, 0x16ba, 0x11d2, { 0x81, 0xd0, 0x0, 0x0, 0xf8, 0x7a, 0x7a, 0xa3 } };


// GUIDs for node types

///////////////////////////////////////////////////////////////////////////////
// RESOURCES



// # of columns in the result pane and map for resource strings

extern RESULT_HEADERMAP _HeaderStrings[] =
{
    { L"", IDS_HEADER_NAME, LVCFMT_LEFT, 180},
    { L"", IDS_HEADER_TYPE, LVCFMT_LEFT, 90},
    { L"", IDS_HEADER_DN,   LVCFMT_LEFT, 450},
};

COLUMN_DEFINITION DefaultColumnDefinition =
{
  COLUMNSET_ID_DEFAULT,
  N_HEADER_COLS,
  _HeaderStrings
};

extern RESULT_HEADERMAP _PartitionsHeaderStrings[] =
{
    { L"", IDS_HEADER_NAME, LVCFMT_LEFT, 180},
  { L"", IDS_HEADER_NCNAME, LVCFMT_LEFT, 200},
    { L"", IDS_HEADER_TYPE, LVCFMT_LEFT, 90},
    { L"", IDS_HEADER_DN,   LVCFMT_LEFT, 450},
};

COLUMN_DEFINITION PartitionsColumnDefinition =
{
  COLUMNSET_ID_PARTITIONS,
  N_PARTITIONS_HEADER_COLS,
  _PartitionsHeaderStrings
};

extern PCOLUMN_DEFINITION ColumnDefinitions[] =
{
  &DefaultColumnDefinition,
  &PartitionsColumnDefinition,
  NULL
};


///////////////////////////////////////////////////////////////////////////////
// CADSIEditModule

HRESULT WINAPI CADSIEditModule::UpdateRegistryCLSID(const CLSID& clsid, BOOL bRegister)
{
    static const WCHAR szIPS32[] = _T("InprocServer32");
    static const WCHAR szCLSID[] = _T("CLSID");

    HRESULT hRes = S_OK;

    LPOLESTR lpOleStrCLSIDValue;
    ::StringFromCLSID(clsid, &lpOleStrCLSIDValue);
    if (lpOleStrCLSIDValue == NULL)
    {
        return E_OUTOFMEMORY;
    }

    CRegKey key;
    if (bRegister)
    {
        LONG lRes = key.Open(HKEY_CLASSES_ROOT, szCLSID);
        _REPORT_FAIL(L"key.Open(HKEY_CLASSES_ROOT", lpOleStrCLSIDValue, lRes);
        if (lRes == ERROR_SUCCESS)
        {
            lRes = key.Create(key, lpOleStrCLSIDValue);
            _REPORT_FAIL(L"key.Create(key", lpOleStrCLSIDValue, lRes);
            if (lRes == ERROR_SUCCESS)
            {
                // NOTICE-NTRAID#NTBUG9-551295-2002/02/21-artm  
                // GetModuleFileName() does not guarantee null termination.
                // Therefore, we allocated + 1 to hold the NULL and zero out
                // the buffer first.  We also check for a truncation and set
                // the error code if there was one.
                WCHAR szModule[_MAX_PATH + 1];
                ZeroMemory(szModule, sizeof(WCHAR) * (_MAX_PATH + 1) );

                DWORD numCopied = ::GetModuleFileName(m_hInst, szModule, _MAX_PATH);
                if (numCopied < _MAX_PATH)
                {
                    lRes = key.SetKeyValue(szIPS32, szModule);
                    _REPORT_FAIL(L"key.SetKeyValue(szIPS32", lpOleStrCLSIDValue, lRes);
                }
                else
                {   // The module name was too big.  Something's fishy.
                    hRes = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
                }
            }
        }
        if (lRes != ERROR_SUCCESS)
            hRes = HRESULT_FROM_WIN32(lRes);
    }
    else
    {
        key.Attach(HKEY_CLASSES_ROOT);
        if (key.Open(key, szCLSID) == ERROR_SUCCESS)
            key.RecurseDeleteKey(lpOleStrCLSIDValue);
    }
    ::CoTaskMemFree(lpOleStrCLSIDValue);
    return hRes;
}


///////////////////////////////////////////////////////////////////////////////
// Module, Object Map and DLL entry points

CADSIEditModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
    OBJECT_ENTRY(CLSID_ADSIEditSnapin, CADSIEditComponentDataObject)
  OBJECT_ENTRY(CLSID_ADSIEditAbout, CADSIEditAbout) 
  OBJECT_ENTRY(CLSID_DsAttributeEditor, CAttributeEditor)
END_OBJECT_MAP()


STDAPI DllCanUnloadNow(void)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    return (AfxDllCanUnloadNow()==S_OK && _Module.GetLockCount()==0) ? S_OK : S_FALSE;
}

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
    return _Module.GetClassObject(rclsid, riid, ppv);
}


static _NODE_TYPE_INFO_ENTRY _NodeTypeInfoEntryArray[] = {
    { &CADSIEditRootData::NodeTypeGUID,         _T("Root ADSI Edit Subtree")        },
    { &CADSIEditConnectionNode::NodeTypeGUID,   _T("ADSI Edit Connection Node") },
    { &CADSIEditContainerNode::NodeTypeGUID,    _T("ADSI Edit Container Node")  },
    { &CADSIEditLeafNode::NodeTypeGUID,         _T("ADSI Edit Leaf Node")           },
    { &CADSIEditQueryNode::NodeTypeGUID,        _T("ADSI Edit Query Node")          },
    { NULL, NULL }
};


////////////////////////////////////////////////////////////////////


STDAPI DllRegisterServer(void)
{
  AFX_MANAGE_STATE(AfxGetStaticModuleState());
  CThemeContextActivator activator;
    // registers all objects
    HRESULT hr = _Module.RegisterServer(/* bRegTypeLib */ FALSE);
    ASSERT(SUCCEEDED(hr));
    if (FAILED(hr))
  {
    _MSGBOX(_T("_Module.RegisterServer() failed"));
        return hr;
  }

  CString szVersion, szProvider, szSnapinName, szSnapinNameEx;

  szVersion = IDS_SNAPIN_VERSION;
  szProvider = IDS_SNAPIN_PROVIDER;
  // NOTICE-2002/03/06-artm  CString can throw out of memory exception but
  // that needs to be handled at a higher level.  Also, the return value
  // is not checked but the call should _never_ fail if there is enough
  // memory (o'wise there was a serious screw up in including the resource).
  szSnapinName.LoadString(IDS_SNAPIN_NAME);

    // register the standalone ADSI Edit snapin into the console snapin list
    hr = RegisterSnapin(&CLSID_ADSIEditSnapin,
                      &CADSIEditRootData::NodeTypeGUID,
                      &CLSID_ADSIEditAbout,
                        szSnapinName, szVersion, szProvider,
            FALSE /*bExtension*/,
                        _NodeTypeInfoEntryArray);
    ASSERT(SUCCEEDED(hr));
    if (FAILED(hr))
  {
    _MSGBOX(_T("RegisterSnapin(&CLSID_DNSSnapin) failed"));
        return hr;
  }

    // register the snapin nodes into the console node list
    for (_NODE_TYPE_INFO_ENTRY* pCurrEntry = _NodeTypeInfoEntryArray;
            pCurrEntry->m_pNodeGUID != NULL; pCurrEntry++)
    {
        hr = RegisterNodeType(pCurrEntry->m_pNodeGUID,pCurrEntry->m_lpszNodeDescription);
        ASSERT(SUCCEEDED(hr));
        if (FAILED(hr))
    {
      _MSGBOX(_T("RegisterNodeType() failed"));
            return hr;
    }
    }

    return hr;
}

STDAPI DllUnregisterServer(void)
{
    HRESULT hr  = _Module.UnregisterServer();
    ASSERT(SUCCEEDED(hr));

    // un register the standalone snapin
    hr = UnregisterSnapin(&CLSID_ADSIEditSnapin);
    ASSERT(SUCCEEDED(hr));

    // unregister the snapin nodes,
  // this removes also the server node, with the Services Snapin extension keys
    for (_NODE_TYPE_INFO_ENTRY* pCurrEntry = _NodeTypeInfoEntryArray;
            pCurrEntry->m_pNodeGUID != NULL; pCurrEntry++)
    {
        hr = UnregisterNodeType(pCurrEntry->m_pNodeGUID);
        ASSERT(SUCCEEDED(hr));
    }

    return S_OK;
}

///////////////////////////////////////////////////////////////////////////////
// CADSIEditSnapinApp

class CADSIEditSnapinApp : public CWinApp
{
public:
    virtual BOOL InitInstance();
    virtual int ExitInstance();
};

CADSIEditSnapinApp theApp;

BOOL CADSIEditSnapinApp::InitInstance()
{
    _Module.Init(ObjectMap, m_hInstance);

  // Add theming support

  SHFusionInitializeFromModuleID(m_hInstance, 2);

    HRESULT hr = ::OleInitialize(NULL);
    if (FAILED(hr))
    {
        return FALSE;
    }

    if (!CADSIEditComponentDataObject::LoadResources())
        return FALSE;
    return CWinApp::InitInstance();
}

int CADSIEditSnapinApp::ExitInstance()
{
#ifdef _DEBUG_REFCOUNT
    TRACE(_T("CADSIEditSnapinApp::ExitInstance()\n"));
    ASSERT(CComponentDataObject::m_nOustandingObjects == 0);
    ASSERT(CComponentObject::m_nOustandingObjects == 0);
    ASSERT(CDataObject::m_nOustandingObjects == 0);
#endif // _DEBUG_REFCOUNT

   // Theming support

   SHFusionUninitialize();

    _Module.Term();
    return CWinApp::ExitInstance();
}

////////////////////////////////////////////////////////////////////////
// CADSIEditComponentObject (.i.e "view")


HRESULT CADSIEditComponentObject::InitializeHeaders(CContainerNode* pContainerNode)
{
  HRESULT hr = S_OK;
  ASSERT(m_pHeader);

  CColumnSet* pColumnSet = pContainerNode->GetColumnSet();
  POSITION pos = pColumnSet->GetHeadPosition();
  while (pos != NULL)
  {
    CColumn* pColumn = pColumnSet->GetNext(pos);

        hr = m_pHeader->InsertColumn(pColumn->GetColumnNum(), 
                                  pColumn->GetHeader(),
                                                        pColumn->GetFormat(),
                                                        AUTO_WIDTH);
        if (FAILED(hr))
            return hr;

    hr = m_pHeader->SetColumnWidth(pColumn->GetColumnNum(), 
                                    pColumn->GetWidth());
    if (FAILED(hr))
      return hr;
    }
  return hr;
}

HRESULT CADSIEditComponentObject::InitializeBitmaps(CTreeNode* cookie)
{
  HRESULT hr = S_OK;

  // image lists for nodes
  CBitmapHolder<IDB_16x16> _bmp16x16;
  CBitmapHolder<IDB_32x32> _bmp32x32;

  bool bBmpsLoaded = _bmp16x16.LoadBitmap() && _bmp32x32.LoadBitmap();
  
  if (bBmpsLoaded)
  {
    ASSERT(m_pImageResult != NULL);
    hr = m_pImageResult->ImageListSetStrip(reinterpret_cast<LONG_PTR*>(static_cast<HBITMAP>(_bmp16x16)),
                                           reinterpret_cast<LONG_PTR*>(static_cast<HBITMAP>(_bmp32x32)),
                                           0, 
                                           BMP_COLOR_MASK);
  }
  else
  {
    hr = S_FALSE;
  }
  return hr;
}


////////////////////////////////////////////////////////////////////////
// CADSIEditComponentDataObject (.i.e "document")

CADSIEditComponentDataObject::CADSIEditComponentDataObject()
{
/*
    CWatermarkInfo* pWatermarkInfo = new CWatermarkInfo;
    pWatermarkInfo->m_nIDBanner = IDB_WIZBANNER;
    pWatermarkInfo->m_nIDWatermark = IDB_WIZWATERMARK;
    SetWatermarkInfo(pWatermarkInfo);
*/
  m_pColumnSet = new CADSIEditColumnSet(COLUMNSET_ID_DEFAULT);
}



HRESULT CADSIEditComponentDataObject::OnSetImages(LPIMAGELIST lpScopeImage)
{
  HRESULT hr = S_OK;

  // image lists for nodes
  CBitmapHolder<IDB_16x16> _bmp16x16;
  CBitmapHolder<IDB_32x32> _bmp32x32;

  bool bBmpsLoaded = _bmp16x16.LoadBitmap() && _bmp32x32.LoadBitmap();
  
  if (bBmpsLoaded)
  {
    ASSERT(lpScopeImage != NULL);
    hr = lpScopeImage->ImageListSetStrip(reinterpret_cast<LONG_PTR*>(static_cast<HBITMAP>(_bmp16x16)),
                                         reinterpret_cast<LONG_PTR*>(static_cast<HBITMAP>(_bmp32x32)),
                                         0, 
                                         BMP_COLOR_MASK);
  }
  else
  {
    hr = S_FALSE;
  }
  return hr;
}



CRootData* CADSIEditComponentDataObject::OnCreateRootData()
{
    CADSIEditRootData* pADSIEditRootNode = new CADSIEditRootData(this);
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    CString szSnapinType;
    // NOTICE-2002/02/20-artm  LoadString() can throw out of memory exception.
    // This needs to be handled at a higher level, if at all.
    szSnapinType.LoadString(IDS_SNAPIN_NAME);
    pADSIEditRootNode->SetDisplayName(szSnapinType);
    return pADSIEditRootNode;
}


BOOL CADSIEditComponentDataObject::LoadResources()
{
  BOOL bLoadColumnHeaders = TRUE;
  
  for (UINT nIdx = 0; ColumnDefinitions[nIdx]; nIdx++)
  {
    PCOLUMN_DEFINITION pColumnDef = ColumnDefinitions[nIdx];
    bLoadColumnHeaders = LoadResultHeaderResources(pColumnDef->headers, pColumnDef->dwColumnCount);
    if (!bLoadColumnHeaders)
    {
      break;
    }
  }

  return 
        LoadContextMenuResources(CADSIEditConnectMenuHolder::GetMenuMap()) &&
        LoadContextMenuResources(CADSIEditContainerMenuHolder::GetMenuMap()) &&
        LoadContextMenuResources(CADSIEditRootMenuHolder::GetMenuMap()) &&
        LoadContextMenuResources(CADSIEditLeafMenuHolder::GetMenuMap()) &&
        LoadContextMenuResources(CADSIEditQueryMenuHolder::GetMenuMap()) &&
        bLoadColumnHeaders;
}


STDMETHODIMP CADSIEditComponentDataObject::CreateComponent(LPCOMPONENT* ppComponent)
{
    ASSERT(ppComponent != NULL);

    CComObject<CADSIEditComponentObject>* pObject;
    CComObject<CADSIEditComponentObject>::CreateInstance(&pObject);
    ASSERT(pObject != NULL);

    // Store IComponentData
    pObject->SetIComponentData(this);

    return  pObject->QueryInterface(IID_IComponent,
                    reinterpret_cast<void**>(ppComponent));
}

void CADSIEditComponentDataObject::OnNodeContextHelp(CTreeNode* pNode)
{
  ASSERT(pNode != NULL);

  CComPtr<IDisplayHelp> spHelp;
  HRESULT hr = GetConsole()->QueryInterface(IID_IDisplayHelp, (void **)&spHelp);
  if (SUCCEEDED(hr)) 
    spHelp->ShowTopic(L"w2rksupp.chm::/topics/adsiedit.htm");



//    spHelp->ShowTopic(L"w2rksupp.chm");
/*
  CString szNode;

  if (IS_CLASS(*pNode, CDNSRootData))
  {
    szNode = _T("Root Node");
  }
  else if (IS_CLASS(*pNode, CDNSServerNode))
  {
    szNode = _T("Server Node");
  }
  else if (IS_CLASS(*pNode, CDNSForwardZonesNode))
  {
    szNode = _T("Forward Zones Node");
  }
  else if (IS_CLASS(*pNode, CDNSReverseZonesNode))
  {
    szNode = _T("Reverse Zones Node");
  }
  else if (IS_CLASS(*pNode, CDNSZoneNode))
  {
    szNode = _T("Zone Node");
  }
  else if (IS_CLASS(*pNode, CDNSDomainNode))
  {
    szNode = _T("Domain Node");
  }
  else if (IS_CLASS(*pNode, CDNSCacheNode))
  {
    szNode = _T("Domain Node");
  }
  else if (dynamic_cast<CDNSRecordNodeBase*>(pNode) != NULL)
  {
    szNode = _T("Record Node");
  }

  if (!szNode.IsEmpty())
  {
    CString szMsg = _T("Context Help on ");
    szMsg += szNode;
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    CThemeContextActivator activator;
    AfxMessageBox(szMsg);
  }
*/
}


LPCWSTR g_szContextHelpFileName = L"\\help\\adsiedit.hlp";
LPCWSTR g_szHTMLHelpFileName = L"w2rksupp.chm";

LPCWSTR CADSIEditComponentDataObject::GetHTMLHelpFileName()
{
  return g_szHTMLHelpFileName;
}

void CADSIEditComponentDataObject::OnDialogContextHelp(UINT nDialogID, HELPINFO* pHelpInfo)
{
    ULONG nContextTopic;
  // TODO
  //if (FindDialogContextTopic(nDialogID, pHelpInfo, &nContextTopic))
    //  WinHelp(g_szContextHelpFileName, HELP_CONTEXTPOPUP, nContextTopic);
}

STDMETHODIMP CADSIEditComponentDataObject::GetHelpTopic(LPOLESTR* lpCompiledHelpFile)
{
  // FUTURE-2002/02/21-artm  Review function to make sure that all return values are
  // documented for ISnapinHelp.
  if (lpCompiledHelpFile == NULL)
    return E_INVALIDARG;
  LPCWSTR lpszHelpFileName = GetHTMLHelpFileName();
  if (lpszHelpFileName == NULL)
  {
    *lpCompiledHelpFile = NULL;
    return E_NOTIMPL;
  }

  // FUTURE-2002/02/21-artm  Replace string literals with named constants.
  // Will improve readability and maintenance.
  CString szResourceKitDir = _T("");
  CString szKey = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\Components\\5A18D5BFC37FA0A4E99D24135BABE742";

    HRESULT err = S_OK;

    CRegKey key;
    LONG lRes = key.Open(HKEY_LOCAL_MACHINE, szKey);
    _REPORT_FAIL(L"key.Open(HKEY_LOCAL_MACHINE", szKey, lRes);
    if (lRes == ERROR_SUCCESS)
    {
        DWORD dwCount = 2 * MAX_PATH;
        PTSTR ptszValue = new TCHAR[dwCount];

        DWORD byteCount = dwCount * sizeof(TCHAR);
        DWORD bytesCopied = byteCount;

        ZeroMemory(ptszValue, byteCount);

        // NOTICE-NTRAID#NTBUG9-550463-2002/02/21-artm  
        // Need to check that string read from registry null terminated.
        lRes = key.QueryValue(
            ptszValue, // Destination buffer.
            L"DC5632422F082D1189A9000CF497879A", // Value to read
            &bytesCopied);  // In: size of destination buffer; Out: number of bytes written.

        _REPORT_FAIL(L"key.QueryValue(key", L"DC5632422F082D1189A9000CF497879A", lRes);

        if (lRes == ERROR_SUCCESS &&
            // Make sure that the value read wasn't truncated.
            ptszValue[dwCount - 1] == NULL &&
            // Check that something was written.
            bytesCopied > 0)
        {
            CString szValue = ptszValue;

            // Extract the directory from the path read from the registry.
            szResourceKitDir = szValue.Left(szValue.ReverseFind(L'\\') + 1);
        }
        else
        {
            if (lRes == ERROR_SUCCESS)
            {
                // This should never happen unless someone changed the location.
                // If it breaks from that it should be caught during testing.
                ASSERT(false);
                err = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
            }
        }
        // NOTICE-NTRAID#NTBUG9-550528-2002/02/21-artm  Memory leak if lRes != ERROR_SUCCESS.
        // Fixed by moving delete outside of if()else statement.
        delete[] ptszValue;
    }

    if (FAILED(err))
    {
        return err;
    }

  CString szHelpFilePath = szResourceKitDir + CString(lpszHelpFileName);
  UINT nBytes = (szHelpFilePath.GetLength()+1) * sizeof(WCHAR);

  // NOTICE-2002/02/21-artm  lpCompiledHelpFile strictly an out parameter
  //
  // Since lpCompiledHelpFile is an OUT parm for ISnapinHelp::GetHelpTopic(),
  // it is assumed here that no memory has been allocated to the pointer.  If
  // the caller mistakenly does allocate lpCompiledHelpFile some memory before
  // calling, this will result in leaking memory since this function does not
  // free that memory before allocating memory with CoTaskMemAlloc().
  *lpCompiledHelpFile = (LPOLESTR)::CoTaskMemAlloc(nBytes);
  if (*lpCompiledHelpFile != NULL)
  {
    memcpy(*lpCompiledHelpFile, (LPCWSTR)szHelpFilePath, nBytes);
  }
  else
  {
      // NOTICE-NTRAID-NTBUG9-551264-2002/02/21-artm  Need to handle case where CoTaskMemAlloc() fails.
      // If allocation fails, should return error code, not S_OK.
      err = E_OUTOFMEMORY;
  }

  return err;
}

////////////////////////////////////////////////////////////////////////////////
// help context macros and maps

#if (FALSE)

#define BEGIN_HELP_MAP(map) static DWORD map[] = {
#define HELP_MAP_ENTRY(x)   x, (DWORD)&g_aHelpIDs_##x ,
#define END_HELP_MAP         0, 0 };


#define NEXT_HELP_MAP_ENTRY(p) ((p)+2)
#define MAP_ENTRY_DLG_ID(p) (*p)
#define MAP_ENTRY_TABLE(p) ((DWORD*)(*(p+1)))
#define IS_LAST_MAP_ENTRY(p) (MAP_ENTRY_DLG_ID(p) == 0)

#define NEXT_HELP_TABLE_ENTRY(p) ((p)+2)
#define TABLE_ENTRY_CTRL_ID(p) (*p)
#define TABLE_ENTRY_HELP_ID(p) (*(p+1))
#define IS_LAST_TABLE_ENTRY(p) (TABLE_ENTRY_CTRL_ID(p) == 0)

BEGIN_HELP_MAP(_DNSMgrContextHelpMap)
  // misc dialogs
  HELP_MAP_ENTRY(IDD_CHOOSER_CHOOSE_MACHINE)
  HELP_MAP_ENTRY(IDD_BROWSE_DIALOG)

  // misc. add dialogs
  HELP_MAP_ENTRY(IDD_DOMAIN_ADDNEWHOST) // TODO
  HELP_MAP_ENTRY(IDD_DOMAIN_ADDNEWDOMAIN)// TODO
  HELP_MAP_ENTRY(IDD_SELECT_RECORD_TYPE_DIALOG)

  // NOTE: this has several incarnations...
  HELP_MAP_ENTRY(IDD_NAME_SERVERS_PAGE)

  // server property pages
  HELP_MAP_ENTRY(IDD_SERVER_INTERFACES_PAGE)
  HELP_MAP_ENTRY(IDD_SERVER_FORWARDERS_PAGE)
  HELP_MAP_ENTRY(IDD_SERVER_ADVANCED_PAGE)
  HELP_MAP_ENTRY(IDD_SERVER_LOGGING_PAGE)
  HELP_MAP_ENTRY(IDD_SERVER_BOOTMETHOD_PAGE)
  HELP_MAP_ENTRY(IDD_SERVMON_STATISTICS_PAGE)
  HELP_MAP_ENTRY(IDD_SERVMON_TEST_PAGE)

  // zone property pages
  HELP_MAP_ENTRY(IDD_ZONE_GENERAL_PAGE)
  HELP_MAP_ENTRY(IDD_ZONE_WINS_PAGE)
  HELP_MAP_ENTRY(IDD_ZONE_NBSTAT_PAGE)
  HELP_MAP_ENTRY(IDD_ZONE_NOTIFY_PAGE)
  HELP_MAP_ENTRY(IDD_ZONE_WINS_ADVANCED) // this is a subdialog, need to hook up

  // record property pages
  HELP_MAP_ENTRY(IDD_RR_NS_EDIT)
  HELP_MAP_ENTRY(IDD_RR_SOA)
  HELP_MAP_ENTRY(IDD_RR_A)
  HELP_MAP_ENTRY(IDD_RR_CNAME)
  HELP_MAP_ENTRY(IDD_RR_MX)
  HELP_MAP_ENTRY(IDD_RR_UNK)
  HELP_MAP_ENTRY(IDD_RR_TXT)
  HELP_MAP_ENTRY(IDD_RR_X25)
  HELP_MAP_ENTRY(IDD_RR_ISDN)
  HELP_MAP_ENTRY(IDD_RR_HINFO)
  HELP_MAP_ENTRY(IDD_RR_AAAA)
  HELP_MAP_ENTRY(IDD_RR_MB)
  HELP_MAP_ENTRY(IDD_RR_MG)
  HELP_MAP_ENTRY(IDD_RR_MD)
  HELP_MAP_ENTRY(IDD_RR_MF)
  HELP_MAP_ENTRY(IDD_RR_MR)
  HELP_MAP_ENTRY(IDD_RR_MINFO)
  HELP_MAP_ENTRY(IDD_RR_RP)
  HELP_MAP_ENTRY(IDD_RR_RT)
  HELP_MAP_ENTRY(IDD_RR_AFSDB)
  HELP_MAP_ENTRY(IDD_RR_WKS)
  HELP_MAP_ENTRY(IDD_RR_PTR)
  HELP_MAP_ENTRY(IDD_RR_SRV)
  HELP_MAP_ENTRY(IDD_RR_KEY)
  HELP_MAP_ENTRY(IDD_RR_SIG)

END_HELP_MAP



BOOL CDNSComponentDataObjectBase::FindDialogContextTopic(/*IN*/UINT nDialogID,
                                              /*IN*/ HELPINFO* pHelpInfo,
                                              /*OUT*/ ULONG* pnContextTopic)
{
    ASSERT(pHelpInfo != NULL);
  *pnContextTopic = 0;
    const DWORD* pMapEntry = _DNSMgrContextHelpMap;
    while (!IS_LAST_MAP_ENTRY(pMapEntry))
    {
        if (nDialogID == MAP_ENTRY_DLG_ID(pMapEntry))
        {
            DWORD* pTable = MAP_ENTRY_TABLE(pMapEntry);
            // look inside the table
            while (!IS_LAST_TABLE_ENTRY(pTable))
            {
                if (TABLE_ENTRY_CTRL_ID(pTable) == pHelpInfo->iCtrlId)
        {
                    *pnContextTopic = TABLE_ENTRY_HELP_ID(pTable);
          return TRUE;
        }
                pTable = NEXT_HELP_TABLE_ENTRY(pTable);
            }
        }
        pMapEntry = NEXT_HELP_MAP_ENTRY(pMapEntry);
    }
    return FALSE;
}

#endif


//////////////////////////////////////////////////////////////////////////
// CADSIEditAbout

CADSIEditAbout::CADSIEditAbout()
{
  m_szProvider = IDS_SNAPIN_PROVIDER;
    m_szVersion = IDS_SNAPIN_VERSION;
    m_uIdStrDestription = IDS_SNAPINABOUT_DESCRIPTION;
    m_uIdIconImage = IDI_ADSIEDIT_SNAPIN;
    m_uIdBitmapSmallImage = IDB_ABOUT_16x16;
    m_uIdBitmapSmallImageOpen = IDB_ABOUT_16x16;
    m_uIdBitmapLargeImage = IDB_ABOUT_32x32;
    m_crImageMask = BMP_COLOR_MASK;
}