EXADMIN.CPP
// ----------------------------------------------------------------------------- 
// ExAdmin.CPP: Implements methods which support Exchange Admin Configuration  
//              Extension Dialogs. 
// 
// Copyright (C) Microsoft Corp. 1986-1996.  All Rights Reserved. 
// ----------------------------------------------------------------------------- 
 
#if defined(UNICODE) || defined(_UNICODE) 
#error EXADMIN cannot be compiled in UNICODE. 
#endif 
 
#include "edkafx.h" 
#include "ExAdmin.h" 
#include "helpers.h" 
#include "ErrCpp.H" 
 
#ifdef _DEBUG 
#undef THIS_FILE 
static char BASED_CODE THIS_FILE[] = __FILE__; 
#endif 
 
// ----------------------------------------------------------------------------- 
// Following is used to determine the languages we support 
 
struct EnumLangParams 
{ 
WORD langRequested; 
BOOL fFound; 
}; 
typedef EnumLangParams*PEnumLangParams; 
 
BOOL CALLBACK  EnumResTypesCallback(HANDLE hModule,LPTSTR lpszType,LONG lParam); 
BOOL CALLBACK  EnumResNamesCallback(HANDLE hModule, LPCTSTR lpszType, 
   LPTSTR lpszName, LONG lParam); 
BOOL CALLBACK  fEnumLangProc(HANDLE hMod, LPCTSTR ptzType, LPCTSTR ptzName, WORD wLang, LONG lParam); 
 
// ----------------------------------------------------------------------------- 
 
__inline HRESULT CHK_HrGeneral( 
    IN VOID* pvExtensionData,       // Extension data for all sheets 
    IN HWND  hDlg)                  // Window handle of the property sheet. 
{ 
    if( !hDlg) 
        RETURN( E_INVALIDARG); 
 
    return( NOERROR); 
} 
 
// ----------------------------------------------------------------------------- 
 
__inline HRESULT CHK_bInitSheet( 
    IN  ADMIN_ObjectInfo* poi,              // Server name & other DNs. 
    IN  ULONG             fFlags,                        
    OUT ADMIN_SheetInfo** ppSheetInfo,      // Property sheet description array 
    OUT UINT*             pcsi,             // Number of property sheets 
    OUT VOID**            ppvNotUsed) // Local data (NOT USED) 
{ 
    if( !TEST_READ_PTR( poi, sizeof( ADMIN_ObjectInfo))) 
        RETURN( E_INVALIDARG); 
 
    if( !TEST_WRITE_PTR( ppSheetInfo, sizeof( VOID *))) 
        RETURN( E_INVALIDARG); 
 
    if( !TEST_WRITE_PTR( pcsi, sizeof( UINT))) 
        RETURN( E_INVALIDARG); 
     
    if( !TEST_WRITE_PTR( ppvNotUsed, sizeof( VOID *))) 
        RETURN( E_INVALIDARG); 
 
    return( NOERROR); 
} 
 
// ----------------------------------------------------------------------------- 
 
__inline HRESULT CHK_ADMIN_Initialize( 
    IN  ADMIN_AdministratorConnections* pAdminConnections,  // Global Administrator Connections 
    IN  ADMIN_AdministratorFunction*    pAdminFunctions,    // Global admin function 
    OUT ADMIN_ExtensionFunction**       ppExtensionFunction)// Global Extension function 
{ 
    if( !TEST_READ_PTR( pAdminConnections, sizeof( ADMIN_AdministratorConnections))) 
        RETURN( E_INVALIDARG); 
 
    if( !TEST_READ_PTR( pAdminFunctions, sizeof( ADMIN_AdministratorFunction))) 
        RETURN( E_INVALIDARG); 
 
    if( !TEST_WRITE_PTR( ppExtensionFunction, sizeof( VOID *))) 
        RETURN( E_INVALIDARG); 
 
    if( !TEST_FUNCTION_PTR( (FARPROC) pAdminFunctions->pfnGetObjectData)) 
        RETURN( E_INVALIDARG); 
 
    return( NOERROR); 
} 
 
// ----------------------------------------------------------------------------- 
 
__inline HRESULT CHK_LoadStringA( 
UINT wID,// ID of resource 
LPSTR szBuf,// Buffer to store resource 
int cchBuf)// Size of buffer in chars 
{ 
    if( wID == 0) 
        RETURN( E_INVALIDARG); 
 
    if( !TEST_WRITE_PTR( szBuf, cchBuf)) 
        RETURN( E_INVALIDARG); 
 
    return( NOERROR); 
}    
 
__inline HRESULT CHK_LoadStringW( 
UINT wID,// ID of resource 
LPWSTR wzBuf,// Buffer to store resource 
int cchBuf)// Size of buffer in chars 
{ 
    if( wID == 0) 
        RETURN( E_INVALIDARG); 
 
    if( !TEST_WRITE_PTR( wzBuf, sizeof( WCHAR ) * cchBuf)) 
        RETURN( E_INVALIDARG); 
 
    return( NOERROR); 
}    
 
//$--CInitDLL::InitInstance()--------------------------------------------------- 
// MFC initialization for the DLL.  No multi-threaded code allowed. 
// ----------------------------------------------------------------------------- 
 
BOOL CInitDLL::InitInstance() 
{ 
    // Any DLL initialization goes here. 
    TRACE0("InitDLL.DLL initializing\n"); 
    SetDialogBkColor();     // Grey dialogs in the DLL as well. 
    return TRUE; 
} 
 
//$--CInitDLL::ExitInstance()--------------------------------------------------- 
// Exit MFC for the DLL gracefully.  No multi-threaded code allowed. 
// ----------------------------------------------------------------------------- 
 
int CInitDLL::ExitInstance() 
{ 
    // Any DLL termination goes here (WEP-like code). 
    return CWinApp::ExitInstance(); 
} 
 
//$--CInitDLL::~CInitDLL()------------------------------------------------------ 
// Detach from property sheet window of Admin program then delete the CWnd we  
// created in AdminDlgProc().  The reason we attach to the property sheet window 
// of admin is so that MFC will give us Modal dialog boxes. 
// ----------------------------------------------------------------------------- 
 
CInitDLL::~CInitDLL() 
{ 
    if( m_pMainWnd) 
    { 
        m_pMainWnd->Detach(); 
        delete m_pMainWnd; 
        m_pMainWnd = NULL; 
    } 
} 
 
//$--CAdmin::HrSubclassWindow()------------------------------------------------- 
// Make subclassing windows to controls with error checking a little easier. 
// ----------------------------------------------------------------------------- 
 
HRESULT CADialog::HrSubclassWindow(  
    int   nID,  // Id of a control in this dialog. 
    CWnd& Wnd)  // Reference to MFC CWnd object to connect to Windows control. 
{ 
    CWnd* pWnd = GetDlgItem( nID); 
    if( !pWnd) 
    {   // Could not find id (%d) of control in dialog. 
        RETURN( E_FAIL); 
    } 
 
    // Connect the windows control to a MFC CWnd derived object. 
    Wnd.SubclassWindow( pWnd->GetSafeHwnd()); 
    return( NOERROR); 
} 
 
//$--AdminDlgProc()------------------------------------------------------------- 
// Subclasses the dialog the first time in and always calls the MFC dialog proc 
// to handle the message. When message == WM_INITDIALOG then lParam is a pointer 
// to the CAdminDialog. 
// ----------------------------------------------------------------------------- 
 
LRESULT CALLBACK AdminDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)   
{ 
    AFX_MANAGE_STATE(AfxGetStaticModuleState()); 
 
    if( message == WM_INITDIALOG) 
    {   // First time using this dialog so subclass its window so that MFC 
        // code will work as expected. 
        CAdminDialog* pAdminDlg = (CAdminDialog*) lParam; 
        pAdminDlg->SubclassWindow( hDlg); 
 
        // We attach to the property sheet window of admin so that MFC will give us  
        // modal dialog boxes.  We don't want to subclass this window because we  
        // would start receiving it's messages and we don't want that. 
        if( !AfxGetApp()->m_pMainWnd) 
        {   // We only need to do this once. 
            AfxGetApp()->m_pMainWnd = new CWnd; 
            AfxGetApp()->m_pMainWnd->Attach( GetParent( hDlg)); 
        } 
    } 
 
    // Prepare to call MFC dialog proc to handle the message. 
    MSG msg; 
    msg.hwnd = hDlg; 
    msg.message = message; 
    msg.wParam = wParam; 
    msg.lParam = lParam; 
 
    return( AfxDlgProc( hDlg, message, wParam, lParam)); 
} 
 
// ----------------------------------------------------------------------------- 
// Message map for CAdminDialog. 
// ----------------------------------------------------------------------------- 
 
BEGIN_MESSAGE_MAP(CAdminDialog, CDialog) 
    //{{AFX_MSG_MAP(CAdminDialog) 
    ON_MESSAGE( WM_CHILDACTIVATE, OnChildActivate) 
    //}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
 
//$--CAdminDialog::CAdminDialog()----------------------------------------------- 
// CONSTRUCTOR -- Records static information to be used later. 
// ----------------------------------------------------------------------------- 
 
CAdminDialog::CAdminDialog(  
    UINT iddDialog,     // The resource ID of your dialog. 
    UINT idsName,       // The resource ID of the string containing the name 
                        // used by admin for the tab of your property sheet. 
    LPSTR lpszBlobName)// Name of extension data blob. 
    : CADialog()        // Construct our ancestor. 
{ 
    // Remember the caller's dialog, property sheet names, and extension blob name. 
    m_iddDialog = iddDialog; 
    m_idsName = idsName; 
 
    // Remember the blob name as a wide string.  The pointer will  
    // be NULL if the derived class does not have extension data. 
    if( !lpszBlobName) 
        m_lpwszBlobName = NULL; 
    else 
    { 
        CHRESULT hr = HrStrAToStrW( lpszBlobName, &m_lpwszBlobName); 
        if( FAILED( hr)); 
    } 
 
    // Initialize other member variables. 
    m_cExtData = 0; 
    m_lpbExtData = NULL; 
    m_cExtProps = 0; 
    m_lpExtProps = NULL; 
     
    // Add the "this" pointer of the dialog to a static list.  We need to know 
    // about all dialogs so we can tell admin about them later. 
    m_DlgList.AddTail( (void*) this); 
} 
 
// ----------------------------------------------------------------------------- 
// DESTRUCTOR  
// ----------------------------------------------------------------------------- 
 
CAdminDialog::~CAdminDialog() 
{ 
    MAPIFREEBUFFER( m_lpbExtData); 
    MAPIFREEBUFFER( m_lpExtProps); 
    MAPIFREEBUFFER( m_lpwszBlobName); 
} 
 
// ----------------------------------------------------------------------------- 
// If the caller does not pass in the lpszCaption then use the title of the  
// parent window for the default title of our message box. 
// ----------------------------------------------------------------------------- 
 
int CAdminDialog::MessageBox( LPCSTR lpszText, LPCSTR lpszCaption, UINT nType) 
{ 
    if( lpszCaption == NULL) 
        lpszCaption = m_sMsgBoxCaption; 
    return( CWnd::MessageBox( lpszText, lpszCaption, nType)); 
} 
 
int CAdminDialog::MessageBox2( int IDText, int IDCaption, UINT nType) 
{ 
    CString MBText; 
    CString MBCaption; 
int nSize = -1; 
int nLen; 
 
// try buffer size of 256, then larger size until entire string is retrieved 
do 
{ 
nSize += 256; 
nLen = LoadStringA(IDText, MBText.GetBuffer(nSize), nSize+1); 
} while (nLen == nSize); 
MBText.ReleaseBuffer(); 
 
if( IDCaption == 0) 
        MBCaption = m_sMsgBoxCaption; 
else 
{ 
nSize = -1; 
do 
{ 
nSize += 256; 
nLen = LoadStringA( IDCaption, MBCaption.GetBuffer(nSize), nSize+1); 
} while (nLen == nSize); 
MBCaption.ReleaseBuffer(); 
} 
 
 
    return( CWnd::MessageBox( MBText, MBCaption, nType)); 
} 
 
//$--CAdminDialog::LoadDialogTemplate()----------------------------------------- 
// Use this function to create child dialog box templates.  It will ensure that  
// the correct language and fonts are used.   
// 
// To create the dialog use the default constructor (no parameters) and then use 
// the CDialog::InitModalIndirect() function to initialize the dialog.  Then you 
// can call CDialog::DoModal() to process the dialog. 
// ----------------------------------------------------------------------------- 
 
const HGLOBAL CAdminDialog::LoadDialogTemplate( UINT iddDialog) 
{ 
    HGLOBAL hDlgTemplate = NULL; 
 
    if( m_pAdminFunctions->pfnLoadDialogResource( AfxGetInstanceHandle(), 
                          iddDialog, GetLanguageId(), (LPBYTE*) &hDlgTemplate)) 
        return( hDlgTemplate); 
    else 
        return( NULL); 
} 
 
//$--CAdminDialog::LoadString()------------------------------------------------ 
// Loads a string resource by id and language 
// and (A version) converts to the current ANSI code page. 
// Note that strings are stored in blocks of 16, and the ID gives 
// us the number of the block, and the offset into the block. 
// ----------------------------------------------------------------------------- 
int CAdminDialog::LoadStringA(UINT wID, LPSTR szBuf, int cchBuf) 
{ 
    UINT    block, num; 
    int     len = 0;     
    HRSRC   hRC = NULL; 
    HGLOBAL hgl = NULL;; 
    LPWSTR  str = NULL; 
    register UINT i; 
     
    DEBUGPUBLIC( "CAdminDialog::LoadStringA()\n"); 
CHRESULT hr = CHK_LoadStringA( wID, szBuf, cchBuf); 
    if( FAILED( hr)) 
        return( 0 ); 
 
szBuf[0] = '\0'; 
 
    block = (wID >> 4) + 1;// compute block number 
    num = wID & 0xf;// compute offset into block 
     
    hRC = FindResourceEx(AfxGetInstanceHandle(), RT_STRING, MAKEINTRESOURCE(block), GetLanguageId()); 
    if (!hRC)  
goto Error; 
 
hgl = LoadResource(AfxGetInstanceHandle(), hRC); 
if (!hgl) 
goto Error; 
 
    str = (LPWSTR)LockResource(hgl); 
    if (!str)  
goto Error; 
 
// Move up block to string we want 
for (i = 0; i < num; i++) 
    str += *str + 1; 
 
// convert the string to current code page 
len = WideCharToMultiByte(CP_ACP, 
  WC_COMPOSITECHECK, 
  str + 1, *str, 
  szBuf, cchBuf - 1, 
  NULL, NULL); 
 
szBuf[min(cchBuf-1, *str)] = '\0'; 
 
Error: 
 
if (hgl) 
{ 
UnlockResource(hgl);// maybe not needed 
    FreeResource(hgl);    // maybe not needed 
} 
 
    return len; 
} 
 
int CAdminDialog::LoadStringW(UINT wID, LPWSTR wzBuf, int cchBuf) 
{ 
 
    UINT    block, num; 
    int     len = 0;     
    HRSRC   hRC = NULL; 
    HGLOBAL hgl = NULL; 
    LPWSTR  str = NULL; 
    register UINT i; 
     
    DEBUGPUBLIC( "CAdminDialog::LoadStringW()\n"); 
CHRESULT hr = CHK_LoadStringW( wID, wzBuf, cchBuf); 
    if( FAILED( hr)) 
        return( 0 ); 
 
wzBuf[0] = TEXT('\0'); 
 
    block = (wID >> 4) + 1;// compute block number 
    num = wID & 0xf;// compute offset into block 
     
    hRC = FindResourceEx(AfxGetInstanceHandle(), RT_STRING, MAKEINTRESOURCE(block), GetLanguageId()); 
    if (!hRC)  
goto Error; 
 
hgl = LoadResource(AfxGetInstanceHandle(), hRC); 
if (!hgl) 
goto Error; 
 
    str = (LPWSTR)LockResource(hgl); 
    if (str)  
    { 
for (i = 0; i < num; i++) 
    str += *str + 1; 
wcsncpy(wzBuf, str + 1, min(cchBuf - 1, *str)); 
    } 
 
wzBuf[min(cchBuf-1, *str) ] = '\0'; 
 
len = *str + 1; 
 
Error: 
 
if (hgl) 
{ 
UnlockResource(hgl);// maybe not needed 
    FreeResource(hgl);    // maybe not needed 
} 
 
    return len; 
} 
 
//$--CAdminDialog::GetExtBin()-------------------------------------------------- 
// Use this to get a binary extension data property. 
// ----------------------------------------------------------------------------- 
 
LPSBinary CAdminDialog::GetExtBinary( 
    ULONG iProp)        // Index of property. 
{ 
    DEBUGPUBLIC( "CAdminDialog::GetExtBin()\n"); 
    CHRESULT hr = CHK_HrExtData( iProp, PT_BINARY); 
    if( FAILED( hr)) 
        return( NULL); 
     
    return( &m_lpExtProps[iProp].Value.bin); 
} 
 
//$--CAdminDialog::GetExtString()----------------------------------------------- 
// Use this to get a string extension data property. 
// ----------------------------------------------------------------------------- 
 
LPSTR CAdminDialog::GetExtString( 
    ULONG iProp)        // Index of property. 
{ 
    DEBUGPUBLIC( "CAdminDialog::GetExtString()\n"); 
    CHRESULT hr = CHK_HrExtData( iProp, PT_STRING8); 
    if( FAILED( hr)) 
        return( NULL); 
 
    return( m_lpExtProps[iProp].Value.lpszA); 
} 
 
//$--CAdminDialog::GetExtLong()------------------------------------------------- 
// Use this to get a long extension data property. 
// ----------------------------------------------------------------------------- 
 
LONG CAdminDialog::GetExtLong( 
    ULONG iProp)        // Index of property. 
{ 
    DEBUGPUBLIC( "CAdminDialog::GetExtLong()\n"); 
    CHRESULT hr = CHK_HrExtData( iProp, PT_LONG); 
    if( FAILED( hr)) 
        return( -1); 
     
    return( m_lpExtProps[iProp].Value.l); 
} 
 
// ----------------------------------------------------------------------------- 
// Use this to get a boolean extension data property. 
// ----------------------------------------------------------------------------- 
 
BOOL CAdminDialog::GetExtBool( 
    ULONG iProp)        // Index of property. 
{ 
    DEBUGPUBLIC( "CAdminDialog::GetExtBool()\n"); 
    CHRESULT hr = CHK_HrExtData( iProp, PT_BOOLEAN); 
    if( FAILED( hr)) 
        return( FALSE); 
     
    return( m_lpExtProps[iProp].Value.b); 
} 
 
//$--CAdminDialog::GetExtSysTime()---------------------------------------------- 
// Use this to get a system time extension data property. 
// ----------------------------------------------------------------------------- 
 
FILETIME CAdminDialog::GetExtSysTime( 
    ULONG iProp)        // Index of property. 
{ 
    FILETIME ft = {0}; 
     
    DEBUGPUBLIC( "CAdminDialog::GetExtSysTime()\n"); 
    CHRESULT hr = CHK_HrExtData( iProp, PT_SYSTIME); 
    if( FAILED( hr)) 
        return( ft); 
     
    return( m_lpExtProps[iProp].Value.ft); 
} 
 
//$--CAdminDialog::HrModExtBin()------------------------------------------------ 
// Use this to modify a binary extension data property.  It will allocate more 
// memory only if the new buffer is larger. 
// ----------------------------------------------------------------------------- 
 
HRESULT CAdminDialog::HrModExtBinary( 
    IN ULONG  iProp,    // Index of property 
    IN ULONG  cb,       // Count of new data bytes. 
    IN const LPBYTE lpNew)    // New data bytes. 
{ 
    DEBUGPUBLIC( "CAdminDialog::HrModExtBin()\n"); 
    CHRESULT hr = CHK_HrModExtBin( iProp, cb, lpNew); 
    if( FAILED( hr)) 
        RETURN( hr); 
 
    // New binary data will be copied to this address.  
    LPBYTE lpb = m_lpExtProps[ iProp].Value.bin.lpb; 
     
    if( cb > m_lpExtProps[ iProp].Value.bin.cb) 
    {   // New binary data is too big so alloacate a new buffer to stick it in. 
        CHRESULT hr = MAPIAllocateMore( cb, m_lpExtProps, (LPVOID*) &lpb); 
        if( FAILED( hr)) 
            RETURN( hr); 
 
        // Replace old buffer ptr to new one. 
        m_lpExtProps[ iProp].Value.bin.lpb = lpb;             
    } 
     
    // Copy new data to MAPI buffer. 
    memmove( lpb, lpNew, cb); 
    m_lpExtProps[ iProp].Value.bin.cb = cb; 
    return( NOERROR); 
} 
 
//$--CAdminDialog::HrModExtString()--------------------------------------------- 
// Use this to modify a string extension data property.  It will allocate more 
// memory only if the new string is larger. 
// ----------------------------------------------------------------------------- 
 
HRESULT CAdminDialog::HrModExtString( 
    IN ULONG   iProp,    // Index of property 
    IN LPCSTR lpszNew)  // New data string. 
{ 
    DEBUGPUBLIC( "CAdminDialog::HrModExtString()\n"); 
    CHRESULT hr = CHK_HrModExtString( iProp, lpszNew); 
    if( FAILED( hr)) 
        RETURN( hr); 
 
    // Size of original string. 
    ULONG cb = cbStrLenA( m_lpExtProps[ iProp].Value.lpszA); 
     
    // Size of new string. 
    ULONG cbNew = cbStrLenA( lpszNew); 
     
    // New binary data will be copied to this address.  
    LPSTR lpsz = m_lpExtProps[ iProp].Value.lpszA; 
     
    if( cbNew > cb) 
    {   // New binary data is too big so alloacate a new buffer to stick it in. 
        CHRESULT hr = MAPIAllocateMore( cbNew, m_lpExtProps, (LPVOID*) &lpsz); 
        if( FAILED( hr)) 
            RETURN( hr); 
 
        // Replace old buffer ptr to new one. 
        m_lpExtProps[ iProp].Value.lpszA = lpsz; 
    } 
     
    // Copy new data to MAPI buffer. 
    memmove( lpsz, lpszNew, cbNew); 
    return( NOERROR); 
} 
 
//$--CAdminDialog::HrModExtLong()----------------------------------------------- 
// Use this to modify a long extension data property. 
// ----------------------------------------------------------------------------- 
 
HRESULT CAdminDialog::HrModExtLong( 
    IN ULONG  iProp,    // Index of property 
    IN LONG   lNew)     // New long data value. 
{ 
    DEBUGPUBLIC( "CAdminDialog::HrModExtLong()\n"); 
    CHRESULT hr = CHK_HrExtData( iProp, PT_LONG); 
    if( FAILED( hr)) 
        RETURN( hr); 
 
    // Copy new data to MAPI buffer. 
    m_lpExtProps[ iProp].Value.l = lNew; 
    return( NOERROR); 
} 
 
//$--CAdminDialog::HrModExtBool()----------------------------------------------- 
// Use this to modify a boolean extension data property. 
// ----------------------------------------------------------------------------- 
 
HRESULT CAdminDialog::HrModExtBool( 
    IN ULONG  iProp,    // Index of property 
    IN BOOL   bNew)     // New boolean data value. 
{ 
    DEBUGPUBLIC( "CAdminDialog::HrModExtBool()\n"); 
    CHRESULT hr = CHK_HrExtData( iProp, PT_BOOLEAN); 
    if( FAILED( hr)) 
        RETURN( hr); 
 
    // Copy new data to MAPI buffer. 
    m_lpExtProps[ iProp].Value.b = bNew; 
    return( NOERROR); 
} 
 
//$--CAdminDialog::HrModExtSysTime()----------------------------------------------- 
// Use this to modify a SysTime extension data property. 
// ----------------------------------------------------------------------------- 
 
HRESULT CAdminDialog::HrModExtSysTime( 
    IN ULONG    iProp,    // Index of property 
    IN FILETIME ftNew)    // New boolean data value. 
{ 
    DEBUGPUBLIC( "CAdminDialog::HrModExtSysTime()\n"); 
    CHRESULT hr = CHK_HrExtData( iProp, PT_SYSTIME); 
    if( FAILED( hr)) 
        RETURN( hr); 
 
    // Copy new data to MAPI buffer. 
    m_lpExtProps[ iProp].Value.ft = ftNew; 
    return( NOERROR); 
} 
 
// ----------------------------------------------------------------------------- 
// Set the extension data properties to an existing property value array.  This 
// can be used to create a blob for the first time.  To do this initialized the 
// lpExtProps array with just property types and no real data.  Then use the  
// HrMod...() functions to set the values. 
// 
// NOTE: Since this MUST be a MAPI allocated buffer, and since it is not as easy 
//       to initialize a MAPI allocated buffer as a static one, we copy the  
//       buffer the user passes in.  Therefore the user will need to free their 
//       buffer if it is not a static one. 
// ----------------------------------------------------------------------------- 
 
HRESULT CAdminDialog::HrSetExtProps( 
    ULONG        cExtProps,     // Count of extension data properties. 
    LPSPropValue lpExtProps)    // Array of properties to set extension data to. 
{ 
    DEBUGPUBLIC( "CAdminDialog::HrSetExtProps()\n"); 
    CHRESULT hr = CHK_HrSetExtProps( cExtProps, lpExtProps); 
    if( FAILED( hr)) 
        RETURN( hr); 
 
    // Free old extension properties and set to the new ones.  We do not 
    // free the old blob pointer here because its existence signifies that 
    // we are not creating a new blob. 
    MAPIFREEBUFFER( m_lpExtProps); 
    m_cExtProps = 0; 
     
    // Make sure that all property tags have a non zero property id. This is done  
    // because ScCountProps() does a validation to make sure all properties have a  
    // non zero id.  This should be fixed in some future version of MAPI. 
    ULONG iProp = 0; 
    for( iProp = 0; iProp < cExtProps; iProp++) 
    { 
        if( PROP_ID( lpExtProps[ iProp].ulPropTag) == 0) 
            lpExtProps[ iProp].ulPropTag = PROP_TAG( PROP_TYPE( lpExtProps[ iProp].ulPropTag), 1); 
    } 
 
    // Count the bytes needed for the new property array. 
    ULONG cBytes = 0; 
    hr = ScCountProps( cExtProps, lpExtProps, &cBytes); 
    if( FAILED( hr)) 
        RETURN( hr); 
     
    // Allocate a new buffer to hold the copied properties. 
    hr = MAPIAllocateBuffer( cBytes, (LPVOID*) &m_lpExtProps); 
    if( FAILED( hr)) 
        RETURN( hr); 
    memset( m_lpExtProps, 0, cBytes); 
 
    // Copy the subscriber properties to the new buffer. 
    ULONG cBytesCopied = 0; 
    hr = ScCopyProps( cExtProps, lpExtProps, m_lpExtProps, &cBytesCopied); 
    if( FAILED( hr)) 
        RETURN( hr); 
    ASSERTERROR( cBytes == cBytesCopied, "ScCountProps & ScCopyProps are inconsistent!"); 
     
    // We completed this successfuly so set the count. 
    m_cExtProps = cExtProps; 
     
    DataHasChanged(); 
 
    return( NOERROR); 
} 
 
// ----------------------------------------------------------------------------- 
// Loads the extension data into a buffer that is contained in this object.  Use 
// GetExtCount() and GetExtData() to get individual items. Use the HrMod...()  
// functions to modify a property value. 
// ----------------------------------------------------------------------------- 
 
HRESULT CAdminDialog::HrLoadExtData() 
{ 
    MAPIFREEBUFFER( m_lpbExtData); 
    m_cExtData = 0; 
 
    MAPIFREEBUFFER( m_lpExtProps); 
    m_cExtProps = 0; 
 
    if( m_lpwszBlobName == NULL) 
        return( NOERROR); 
     
    // Determine the size of the buffer needed to hold the data. 
    UINT cTemp; 
    RC rc = m_pAdminFunctions->pfnGetObjectDataSize( GetSafeHwnd(), m_lpwszBlobName, &cTemp); 
    if(RC_FAILED(rc)) 
        RETURN( E_FAIL); 
     
    // Convert UINT to a ULONG. 
    m_cExtData = cTemp; 
 
    // Allocate a buffer to hold the extension data. 
    CHRESULT hr = MAPIAllocateBuffer( m_cExtData, (LPVOID*) &m_lpbExtData); 
    if( FAILED( hr)) 
        RETURN( hr); 
 
    // Get the extension data. 
    rc = m_pAdminFunctions->pfnGetObjectData( GetSafeHwnd(), m_lpwszBlobName, m_lpbExtData, m_cExtData); 
    if( RC_FAILED(rc)) 
        RETURN( E_FAIL); 
 
    // Unpack the data into a MAPI style property value array. 
    hr = HrCfgUnpackData( m_cExtData, m_lpbExtData, NULL, &m_cExtProps, &m_lpExtProps); 
    if( FAILED( hr)) 
        RETURN( hr); 
 
    return( NOERROR); 
} 
 
// ----------------------------------------------------------------------------- 
// Saves the extension data that is held in a buffer contained in this object. 
// ----------------------------------------------------------------------------- 
 
HRESULT CAdminDialog::HrSaveExtData() 
{ 
    if( m_lpwszBlobName == NULL) 
        return( NOERROR); 
 
    if( m_cExtProps == 0 || !TEST_READ_PTR( m_lpExtProps, sizeof( SPropValue) * m_cExtProps)) 
        return( E_FAIL); 
 
    // If we did not have extension blob data then we are creating a new blob. 
    BOOL bNew = (m_lpbExtData == NULL); 
    MAPIFREEBUFFER( m_lpbExtData); 
    m_cExtData = 0; 
 
    // Pack the configuration data from a MAPI style property value array. 
    CHRESULT hr = HrCfgPackDataW( m_lpwszBlobName, m_cExtProps, m_lpExtProps, &m_cExtData, &m_lpbExtData); 
    if( FAILED( hr)) 
        RETURN( hr); 
 
    // Set the extension data. 
    RC rc = m_pAdminFunctions->pfnSetObjectData( GetSafeHwnd(), m_lpbExtData, m_cExtData, bNew); 
    if( RC_FAILED(rc)) 
        RETURN( E_FAIL); 
     
    return( NOERROR); 
} 
 
// ----------------------------------------------------------------------------- 
// Add service to be tracked by Server Monitor. 
// ----------------------------------------------------------------------------- 
 
HRESULT CAdminDialog::HrAddService( 
    IN LPSTR lpszServiceName)      // SHORT name of the service. 
{ 
    // Not UNICODE, so convert to wide string. 
    CMAPIBuffer< LPWSTR> lpwszServiceName; 
    CHRESULT hr = HrStrAToStrW( lpszServiceName, &lpwszServiceName); 
    if( FAILED( hr)) 
        return( hr); 
 
    if( !m_pAdminFunctions->pfnAddService( GetSafeHwnd(), lpwszServiceName)) 
        RETURN( E_FAIL); 
 
    return( NOERROR); 
} 
 
// ----------------------------------------------------------------------------- 
// Remove service to be tracked by Server Monitor. 
// ----------------------------------------------------------------------------- 
 
HRESULT CAdminDialog::HrRemoveService(  
    IN LPSTR lpszServiceName)      // SHORT name of the service. 
{ 
    // Not UNICODE, so convert to wide string. 
    CMAPIBuffer< LPWSTR> lpwszServiceName; 
    CHRESULT hr = HrStrAToStrW( lpszServiceName, &lpwszServiceName); 
    if( FAILED( hr)) 
        return( hr); 
 
    if( !m_pAdminFunctions->pfnRemoveService( GetSafeHwnd(), lpwszServiceName)) 
        RETURN( E_FAIL); 
 
    return( NOERROR); 
} 
 
//$--CAdminDialog::GetNameList()------------------------------------------------ 
// You MUST release the array with FreeNameList. 
// ----------------------------------------------------------------------------- 
 
BOOL CAdminDialog::GetNameList(  
    OUT int*     lpcNameList,       // Ptr to number of WIDE strings in the array. 
    OUT LPWSTR** lppwszNameList)    // Ptr to a ptr of WIDE string array.   
{ 
    BOOL fRes = m_pAdminFunctions->pfnGetNameList( GetSafeHwnd(), lpcNameList, lppwszNameList); 
    if( !fRes) 
        RETURN( E_FAIL); 
 
    return( NOERROR); 
} 
 
//$--CAdminDialog::SetNameList()------------------------------------------------ 
// Set the name list. 
// ----------------------------------------------------------------------------- 
 
BOOL CAdminDialog::SetNameList( 
    IN int      cNameList,        // Number of WIDE strings in the array. 
    IN LPWSTR*  lpwszNameList)    // Ptr to a WIDE string array.   
{ 
    BOOL fRes = m_pAdminFunctions->pfnSetNameList( GetSafeHwnd(), cNameList, lpwszNameList); 
    if( !fRes ) 
        RETURN( E_FAIL); 
 
    return( NOERROR); 
} 
 
//$--CAdminDialog::fSetLcid()--------------------------------------------------- 
// Set the LCID we will use.   
// Note that Exchange Admin could ask for any locale, but this dll typically only 
// supports a subset.  This function checks to see if the dll has resources of the  
// requested locale, and sets a default if not.  
// ----------------------------------------------------------------------------- 
void CAdminDialog::SetLcid(LCID lcid) 
{ 
EnumLangParams ELP; 
 
// We're just concerned with the primary language 
ELP.langRequested = MAKELANGID(PRIMARYLANGID( LANGIDFROMLCID(lcid) ), SUBLANG_NEUTRAL); 
ELP.fFound = FALSE; 
 
    // Check that we support this language, use default if not 
    // This check works by walking the resource tree. It stops as soon as one resource with  
    // a matching language is found. 
if (!EnumResourceTypes(AfxGetInstanceHandle(), (ENUMRESTYPEPROC) EnumResTypesCallback, (LONG) &ELP)) 
{ 
// Note: EnumResourceTypes returns false if one of the callbacks returns false 
// (indicating that the search can be stopped) 
} 
 
// Default to English 
if (ELP.fFound) 
m_lcid = ELP.langRequested; 
else 
m_lcid = MAKELCID( MAKELANGID( LANG_ENGLISH, SUBLANG_NEUTRAL),  SORT_DEFAULT); 
 
return;  // We always succeed 
} 
 
// $--CAdminDialog::bInitSheet()------------------------------------------------ 
// This function initializes the property sheet info and returns it to ADMIN. 
// ----------------------------------------------------------------------------- 
 
BOOL CAdminDialog::bInitSheet(              // RETURNS: TRUE if initialization OK 
    IN  ADMIN_ObjectInfo* poi,              // Server name & other DNs. 
    IN  ULONG             fFlags,                        
    OUT ADMIN_SheetInfo** ppSheetInfo,      // Property sheet description array 
    OUT UINT*             pcsi,             // Number of property sheets 
    OUT VOID**            ppNotUsed)        // Local data (NOT USED) 
{ 
    ASSERT_READ_PTR( poi, sizeof( ADMIN_ObjectInfo), "Admin sent us a bad pointer."); 
 
    // Remember these for later usage. 
    m_pAdminObjectInfo = poi; 
    m_bReadOnly = (fFlags & fxfReadOnly) > 0; 
 
  
// Choose the language we will display 
SetLcid( poi->lcid ); 
 
 
// Allocate memory for the property sheet info array. 
    *pcsi = m_DlgList.GetCount(); 
    m_pSheetInfo = new ADMIN_SheetInfo[ *pcsi]; 
    if( !m_pSheetInfo) 
    {   // Memory allocation error! 
        return( FALSE); 
    } 
 
    // Initialize Admin property sheet info array. 
    POSITION pos = m_DlgList.GetHeadPosition(); 
    ADMIN_SheetInfo* pSheetInfo = m_pSheetInfo; 
    while( pos) 
    {   // Tell admin about all of the CAdminDialog derived objects that 
        // have been instantiated. 
        CAdminDialog* pAdminDlg = (CAdminDialog*) m_DlgList.GetNext( pos); 
 
        pSheetInfo->hInstance = AfxGetInstanceHandle(); 
        pSheetInfo->iddDialog = pAdminDlg->m_iddDialog; 
        pSheetInfo->lpfnDlgProc = (DLGPROC) AdminDlgProc; 
        pSheetInfo->idsName = pAdminDlg->m_idsName; 
        pSheetInfo->lParam = (LPARAM) pAdminDlg; 
        pSheetInfo->langid = GetLanguageId(); 
         
        pSheetInfo ++; 
    } 
 
    // Set return values. 
    *ppSheetInfo = m_pSheetInfo; 
    *ppNotUsed = NULL; 
 
    return( TRUE);  // TRUE indicates success. 
} 
 
// $--CAdminDialog::DeinitSheet()----------------------------------------------- 
// Admin calls this when the property sheet dialog box is about to be removed  
// and after the OnDestroy() functions of the individual property page dialogs 
// have been called. 
// ----------------------------------------------------------------------------- 
 
void CAdminDialog::DeinitSheet(         // Returns nothing 
    IN VOID* pNotUsed)           // Extension data.  
{ 
    delete m_pSheetInfo; 
    m_pSheetInfo = NULL; 
} 
 
// $--CAdminDialog::FindDlg()--------------------------------------------------- 
// Finds and returns a pointer to a CAdminDialog object using its dialog handle. 
// ----------------------------------------------------------------------------- 
 
CAdminDialog* CAdminDialog::FindDlg( HWND hDlg) 
{ 
    POSITION pos = m_DlgList.GetHeadPosition(); 
    while( pos) 
    {   // Get pointer to next CAdminDialog object and see if its window  
        // handle matches the one we are looking for. 
        CAdminDialog* pAdminDlg = (CAdminDialog*) m_DlgList.GetNext( pos); 
        if( pAdminDlg->m_hWnd == hDlg) 
            return( pAdminDlg);             // Found it! 
    } 
    return( NULL);      // Not found. 
} 
 
// $--CAdminDialog::ADMIN_Initialize()------------------------------------------ 
// Helper function used to access private and protected members. 
// ----------------------------------------------------------------------------- 
 
VOID CAdminDialog::ADMIN_Initialize(                        // Returns nothing. 
    IN  ADMIN_AdministratorConnections* pAdminConnections,  // Global Administrator Connections 
    IN  ADMIN_AdministratorFunction*    pAdminFunctions)    // Global admin function 
{ 
    if( m_pMAPISession != pAdminConnections->psesMapi) 
    {   // Add a reference to the admin's MAPI session. 
        m_pMAPISession = pAdminConnections->psesMapi; 
        m_pAddrBook = pAdminConnections->pab; 
        m_pABContainer = pAdminConnections->pabContainer; 
    } 
 
    m_pAdminFunctions = pAdminFunctions; 
} 
 
// $--CAdminDialog::OnInitDialog()---------------------------------------------- 
// Calls the admin function to set the icon and title for the property sheet. 
// Make sure your derived classes OnInitDialog() calls this function. 
// ----------------------------------------------------------------------------- 
 
BOOL CAdminDialog::OnInitDialog() 
{ 
    CDialog::OnInitDialog(); 
     
    // Get the title of the parent window to use as the default  
    // caption of our message boxes. 
    GetParent()->GetWindowText( m_sMsgBoxCaption); 
 
    // Set the icon and title for this dialog. 
    m_pAdminFunctions->pfnSetIcon( m_hWnd, IDC_ADMINICON); 
    m_pAdminFunctions->pfnSetTitle( m_hWnd, IDC_TITLE); 
 
    // Load the extension data if there is any. 
    CHRESULT hr = HrLoadExtData(); 
    if( FAILED( hr)); 
 
    return TRUE;  // Return TRUE unless you set the focus to a control. 
} 
 
//$--CAdminDialog::OnChildActivate()-------------------------------------------- 
// This gets called when we receive a WM_CHILDACTIVATE message.  This indicates 
// that the property sheet has just been brought into focus. 
// ----------------------------------------------------------------------------- 
 
LONG CAdminDialog::OnChildActivate( UINT, LONG ) 
{ 
    // Load the extension data if there is any. 
    CHRESULT hr = HrLoadExtData(); 
    if( FAILED( hr)); 
 
    // Call the user's function to refresh their dialog. 
    Refresh(); 
 
    return( hr); 
} 
 
//$--CAdminDialog::Refresh()---------------------------------------------------- 
// A do nothing function so the user is not required to implement this function 
// if they really don't need it. 
// ----------------------------------------------------------------------------- 
 
void CAdminDialog::Refresh() 
{ 
} 
 
//$--CAdminDialog::InvalidEntry()----------------------------------------------- 
// Displays a message box with a resource string for invalid entries.  After 
// the user presses OK the appropriate control gets focus. 
// ----------------------------------------------------------------------------- 
 
void CAdminDialog::InvalidEntry( int nResourceStrID, CWnd& wndCtrl) 
{ 
    CString sResourceString; 
    if( sResourceString.LoadString( nResourceStrID)) 
        MessageBox2( nResourceStrID); 
    else 
    {   // The nResourceStrID must be bad, display a general  
        // purpose message and log the error. 
        HR_LOG( E_FAIL); 
        MessageBoxA( "Invalid entry."); 
    } 
    wndCtrl.SetFocus(); 
} 
 
// $--CAdminDialog::bSaveData()------------------------------------------------- 
// Override this virtual function if you need to validate data when the property  
// sheet changes, or when the Ok or Apply button is pressed.  This is the appropriate 
// place to check edit control values, list box selections etc. 
// Default behavior is to do nothing but continue. 
// ----------------------------------------------------------------------------- 
 
BOOL CAdminDialog::bSaveData() 
{ 
    // Save the extension data if there is any. 
    CHRESULT hr = HrSaveExtData(); 
    if( FAILED( hr)) 
        return( FALSE); 
 
    return( TRUE); 
} 
 
// $--CAdminDialog::bCommitData()----------------------------------------------- 
// Override this virtual function if you need to save data to permanent storage  
// when the Ok or Apply button is pressed. (You can assume the data is good, because 
// you should have checked it in the bSaveData() call.) 
// Default behavior is to do nothing but continue. 
// ----------------------------------------------------------------------------- 
 
BOOL CAdminDialog::bCommitData() 
{ 
    return( TRUE); 
} 
 
// $--CAdminDialog::bHasHelp()-------------------------------------------------- 
// Override this virtual function if you supply help. 
// Default behavior is NO help. 
// ----------------------------------------------------------------------------- 
 
BOOL CAdminDialog::bHasHelp() 
{ 
    MessageBox( "Help is not available for this property sheet."); 
    return( FALSE); 
} 
 
// $--CAdminDialog::DoHelp()---------------------------------------------------- 
// Override this virtual function if you supply help. 
// Default behavior is to display a message. 
// ----------------------------------------------------------------------------- 
 
VOID CAdminDialog::DoHelp() 
{ 
    MessageBox( "Help is not available for this property sheet."); 
} 
 
// $--bInitSheet()-------------------------------------------------------------- 
// This function initializes the property sheet info and returns it to admin. 
// It uses the static member function since this one can NOT access the static  
// member data. 
// ----------------------------------------------------------------------------- 
 
BOOL PASCAL bInitSheet(                 // RETURNS: TRUE if initialization OK 
    IN  ADMIN_ObjectInfo* poi,              // Computer name & DN. 
    IN  ULONG             fFlags,                        
    OUT ADMIN_SheetInfo** ppSheetInfo,      // Property sheet description array 
    OUT UINT*             pcsi,             // Number of property sheets 
    OUT VOID**            ppNotUsed) // Local data 
{ 
    AFX_MANAGE_STATE(AfxGetStaticModuleState()); 
 
    DEBUGPUBLIC( "bInitSheet()\n"); 
    if( FAILED( CHK_bInitSheet( poi, fFlags, ppSheetInfo, pcsi, ppNotUsed))) 
        return( FALSE); 
 
    return( CAdminDialog::bInitSheet( poi, fFlags, ppSheetInfo, pcsi, ppNotUsed)); 
} 
 
// $--DeinitSheet()------------------------------------------------------------- 
// Admin calls this when the property sheet dialog box is about to be removed. 
// ----------------------------------------------------------------------------- 
 
void PASCAL DeinitSheet(                // Returns nothing 
    IN VOID* pNotUsed)           // Extension data.  
{ 
    AFX_MANAGE_STATE(AfxGetStaticModuleState()); 
 
    CAdminDialog::DeinitSheet( pNotUsed); 
} 
 
// $--bInstallExtension()------------------------------------------------------- 
// ----------------------------------------------------------------------------- 
 
BOOL PASCAL bInstallExtension(ADMIN_ObjectInfo * poi) 
{ 
    return(TRUE); 
} 
 
// $--bDeinstallExtension()----------------------------------------------------- 
// ----------------------------------------------------------------------------- 
 
BOOL PASCAL bDeinstallExtension(ADMIN_ObjectInfo * poi) 
{ 
    AFX_MANAGE_STATE(AfxGetStaticModuleState()); 
    return(TRUE); 
} 
 
// $--bSaveData()----------------------------------------------------------- 
// Called by admin when the property sheet page loses focus. 
// ----------------------------------------------------------------------------- 
 
BOOL PASCAL bSaveData(   // Returns: TRUE if successful. 
    IN VOID* pNotUsed,       // Extension data for all sheets 
    IN HWND  hDlg)           // Window handle of the property sheet. 
{ 
    AFX_MANAGE_STATE(AfxGetStaticModuleState()); 
 
    DEBUGPUBLIC( "bSaveData()\n"); 
    if( FAILED( CHK_HrGeneral( pNotUsed, hDlg))) 
        return( FALSE); 
 
    CAdminDialog* pAdminDlg = CAdminDialog::FindDlg( hDlg); 
    if( !pAdminDlg) 
    { 
        HR_LOG( E_FAIL); 
        return( FALSE); 
    } 
    return( pAdminDlg->bSaveData()); 
} 
 
// $--bCommitData()------------------------------------------------------------- 
// This function is called by admin when the user presses either the OK or the  
// APPLY buttons. 
// ----------------------------------------------------------------------------- 
 
BOOL PASCAL bCommitData(            // Returns:  TRUE if successful. 
    IN VOID* pNotUsed,       // Extension data. 
    IN HWND  hDlg)                  // Handle of the property sheet to be committed. 
{ 
    AFX_MANAGE_STATE(AfxGetStaticModuleState()); 
 
    DEBUGPUBLIC( "bCommitData()\n"); 
    if( FAILED( CHK_HrGeneral( pNotUsed, hDlg))) 
        return( FALSE); 
 
    CAdminDialog* pAdminDlg = CAdminDialog::FindDlg( hDlg); 
    if( !pAdminDlg) 
    { 
        HR_LOG( E_FAIL); 
        return( FALSE); 
    } 
    return( pAdminDlg->bCommitData()); 
} 
 
// $--bHasHelp()---------------------------------------------------------------- 
// Called by admin to find out if we support help. 
// ----------------------------------------------------------------------------- 
 
BOOL PASCAL bHasHelp(               // RETURNS: true if help is available. 
    IN VOID* pNotUsed,       // Ext dll managed data area. 
    IN HWND  hDlg)                  // Window handle of the property sheet. 
{ 
    AFX_MANAGE_STATE(AfxGetStaticModuleState()); 
 
    DEBUGPUBLIC( "bHasHelp()\n"); 
    if( FAILED( CHK_HrGeneral( pNotUsed, hDlg))) 
        return( FALSE); 
 
    CAdminDialog* pAdminDlg = CAdminDialog::FindDlg( hDlg); 
    if( !pAdminDlg) 
    { 
        HR_LOG( E_FAIL); 
        return( FALSE); 
    } 
    return( pAdminDlg->bHasHelp()); 
} 
 
// $--DoHelp()------------------------------------------------------------------ 
// Called by admin to displays help for the user. 
// ----------------------------------------------------------------------------- 
 
VOID PASCAL DoHelp(                 // RETURNS: Nothing 
    IN VOID* pNotUsed,       // Ext dll managed data area. 
    IN HWND  hDlg)                  // Window handle of the property sheet. 
{ 
    AFX_MANAGE_STATE(AfxGetStaticModuleState()); 
 
    DEBUGPUBLIC( "DoHelp()\n"); 
    if( FAILED( CHK_HrGeneral( pNotUsed, hDlg))) 
        return; 
     
    CAdminDialog* pAdminDlg = CAdminDialog::FindDlg( hDlg); 
    if( !pAdminDlg) 
    { 
        HR_LOG( E_FAIL); 
        return; 
    } 
    pAdminDlg->DoHelp(); 
} 
 
// ----------------------------------------------------------------------------- 
// NOTE: The admin program will keep a pointer to this.  These are pointers to 
//       the functions that admin will want to access. 
// ----------------------------------------------------------------------------- 
 
ADMIN_ExtensionFunction extensionFunctions = 
{ 
    ADMIN_ExtensionAPIVersion, 
    bInstallExtension, 
    bDeinstallExtension, 
    bInitSheet, 
    bShowPage,  // This one can be defined in your dll code, otherwise it will come from ShowPage.CPP. 
    iStartPage, // This one can be defined in your dll code, otherwise it will come from StrtPage.CPP. 
    bHasHelp, 
    DoHelp, 
    bSaveData, 
    bCommitData, 
    DeinitSheet 
}; 
 
// $--ADMIN_Initialize()-------------------------------------------------------- 
// Exported function. This is the first function called by admin after the DLL  
// has been initialized. 
// ----------------------------------------------------------------------------- 
 
extern "C" VOID PASCAL ADMIN_Initialize(                    // Returns nothing. 
    IN  ADMIN_AdministratorConnections* pAdminConnections,  // Global Administrator Connections 
    IN  ADMIN_AdministratorFunction*    pAdminFunctions,    // Global admin function 
    OUT ADMIN_ExtensionFunction**       ppExtensionFunction)// Global Extension function 
{ 
    DEBUGPUBLIC( "ADMIN_Initialize()\n"); 
    if( FAILED( CHK_ADMIN_Initialize( pAdminConnections, pAdminFunctions, ppExtensionFunction))) 
        return; 
 
    CAdminDialog::ADMIN_Initialize( pAdminConnections, pAdminFunctions); 
     
    // Tell admin where it can find the rest of our functions that it needs. 
    *ppExtensionFunction = &extensionFunctions;   
} 
 
// ----------------------------------------------------------------------------- 
// Detemine what languages this dll supports 
 
BOOL CALLBACK EnumResTypesCallback(HANDLE hModule,LPTSTR lpszType,LONG lParam) 
{ 
PEnumLangParams pELP = (PEnumLangParams) lParam; 
 
    EnumResourceNames((HINSTANCE) hModule, lpszType, (ENUMRESNAMEPROC) EnumResNamesCallback, lParam); 
 
if (pELP->fFound) 
return FALSE;// terminates search 
else 
return TRUE; 
} 
 
BOOL CALLBACK EnumResNamesCallback(HANDLE hModule, LPCTSTR lpszType, 
   LPTSTR lpszName, LONG lParam) 
{ 
PEnumLangParams pELP = (PEnumLangParams) lParam; 
 
    EnumResourceLanguages((HINSTANCE)hModule, lpszType, lpszName, (ENUMRESLANGPROC) fEnumLangProc, 
  lParam); 
 
if (pELP->fFound) 
return FALSE;// terminates search 
else 
return TRUE; 
} 
 
BOOL CALLBACK fEnumLangProc(HANDLE hMod, LPCTSTR ptzType, LPCTSTR ptzName, WORD wLang, LONG lParam) 
{ 
PEnumLangParams pELP = (PEnumLangParams) lParam; 
 
if (wLang == pELP->langRequested) 
pELP->fFound = TRUE; 
 
if (pELP->fFound) 
return FALSE;// terminates search 
else 
return TRUE; 
}