ABP.C
/*********************************************************************** 
 * 
 *  ABP.C 
 * 
 * 
 *  The Sample Address Book Provider. 
 *  This file contains the routines that handle the ABPJump table. 
 * 
 *  The following routines are implemented in this file: 
 * 
 *  ABProviderInit 
 *  ABP_QueryInterface 
 *  ABP_Release 
 *  ABP_Shutdown 
 *  ABP_Logon 
 * 
 *  ServiceEntry 
 *  HrOpenSingleProvider 
 *   
 *  RemoveLogonObject 
 *  FindLogonObject 
 *  ScLoadString 
 *   
 *  Copyright 1992-1995 Microsoft Corporation.  All Rights Reserved. 
 * 
 ***********************************************************************/ 
 
 
#include "abp.h" 
#include <smpab.h> 
 
 
/* 
 *  The Local Identifier 
 */ 
LCID lcidUser; 
 
/* 
 *  This provider's MAPIUID.  It's used as part of the entryIDs generated from this provider. 
 */ 
MAPIUID muidABSample = MUIDABSAMPLE; 
 
/* 
 *  This provider's value for PR_AB_PROVIDER_ID.  It's found in the hierarchy table exposed 
 *  by this provider 
 */ 
MAPIUID muidSABProviderID = SAB_PROVIDER_ID; 
 
/* 
 *  This provider's Email Type 
 */ 
 
LPSTR lpszEMT = SZEMAILTYPE; 
 
/* 
 *  Used to keep track of all logon objects created on the ABPInit object 
 */ 
 
typedef struct _object 
{ 
    struct _object *lppNext; 
    LPVOID lpObject; 
 
} OBJECTLIST, *LPOBJECTLIST; 
 
 
/* 
 *  structures and filters defined for display tables 
 * 
 */ 
 
DTBLLABEL dtbllabel = {sizeof(DTBLLABEL), 0}; 
DTBLPAGE dtblpage = {sizeof(DTBLPAGE), 0, 0, 0}; 
DTBLGROUPBOX dtblgroupbox = {sizeof(DTBLGROUPBOX), 0}; 
CHAR szNoFilter[] = "*"; 
CHAR szAddrTypeFilter[] = "[~:]"; 
CHAR szFileNameFilter[] = "[~   \\\\]"; /* ends up [~<space><tab>\\] */ 
 
/* 
 *  Prototypes for the functions in this file, most of which I return 
 *  in the jump table returned from ABProviderInit(). 
 */ 
 
//ABPROVIDERINIT ABProviderInit; 
MSGSERVICEENTRY ServiceEntry; 
HRESULT HrOpenSingleProvider(LPPROVIDERADMIN lpAdminProviders, 
                             LPPROFSECT FAR * lppProfSect); 
 
/* 
 *  Definition of the init object 
 */ 
typedef struct _ABP { 
    ABP_Vtbl FAR *      lpVtbl; 
    SAB_IUnknown; 
 
    /* 
     * list of logon objects  
     */ 
    LPOBJECTLIST  lpObjectList;    
 
} ABP, FAR *LPABP; 
 
/* 
 *  The ABInit object's vtbl is filled in here 
 */ 
ABP_Vtbl vtblABP = 
{ 
    ABP_QueryInterface, 
    (ABP_AddRef_METHOD *) ROOT_AddRef, 
    ABP_Release, 
    ABP_Shutdown, 
    ABP_Logon 
}; 
 
/* 
 -  ABProviderInit 
 - 
 *  Initializes this provider.  Returns an IABProvider object. 
 * 
 */ 
 
STDINITMETHODIMP 
ABProviderInit( 
    HINSTANCE hLibrary, 
    LPMALLOC lpMalloc, 
    LPALLOCATEBUFFER lpAllocBuff, 
    LPALLOCATEMORE lpAllocMore, 
    LPFREEBUFFER lpFreeBuff, 
    ULONG ulFlags, 
    ULONG ulMAPIVersion, 
    ULONG FAR * lpulABVersion, 
    LPABPROVIDER FAR * lppABProvider) 
{ 
    HRESULT hResult = hrSuccess; 
    LPABP lpABP; 
    SCODE sc; 
 
    /* 
     *  Check the version 
     */ 
    if (ulMAPIVersion < CURRENT_SPI_VERSION) 
    { 
        /* 
         *  MAPI's version is too old. 
         */ 
 
        /* 
         *  See if they understand my version 
         */ 
        *lpulABVersion = CURRENT_SPI_VERSION; 
         
        DebugTraceSc(ABProviderInit, MAPI_E_VERSION); 
        return ResultFromScode(MAPI_E_VERSION); 
    } 
 
    /* 
     *  Allocate memory for this Init Object 
     */ 
    sc = lpAllocBuff (sizeof(ABP), &lpABP); 
    if (FAILED(sc)) 
    { 
        /* 
         *  Out of memory 
         */ 
        DebugTraceSc(ABProviderInit, sc); 
        return ResultFromScode(sc); 
    } 
 
    /* 
     *  Initialize the idle engine that MAPI supplies.  This is most useful 
     *  when browsing the .SAB file.  See ABCTBLn.C. 
     */ 
    if (MAPIInitIdle(NULL) == -1) 
    { 
        hResult = ResultFromScode(E_FAIL); 
        goto err; 
    } 
 
    /* 
     *  Hold on to the lpMalloc 
     */ 
    Assert(lpMalloc); 
    lpMalloc->lpVtbl->AddRef(lpMalloc); 
 
    lpABP->lpVtbl = &vtblABP; 
    lpABP->lcInit = 1; 
    lpABP->hLibrary = hLibrary; 
 
    lpABP->lpMalloc = lpMalloc; 
    lpABP->lpAllocBuff = lpAllocBuff; 
    lpABP->lpAllocMore = lpAllocMore; 
    lpABP->lpFreeBuff = lpFreeBuff; 
    lpABP->lpObjectList = NULL; 
 
    InitializeCriticalSection(&lpABP->cs); 
 
    *lppABProvider = (LPABPROVIDER) lpABP; 
    *lpulABVersion = CURRENT_SPI_VERSION; 
 
    /* 
     *  Get our Locale Identifier 
     */ 
    lcidUser = GetUserDefaultLCID(); 
 
 
out: 
    DebugTraceResult(ABProviderInit, hResult); 
    return hResult; 
 
err: 
    lpFreeBuff(lpABP); 
 
    goto out; 
} 
 
 
/* 
 -  ABP_QueryInterface 
 - 
 *  Supports QI'ing to IUnknown and IABProvider 
 *   
 *  Note that for all the methods of IABProvider that parameter validation 
 *  is unnecessary.  This is because MAPI is handling all the parameters 
 *  being passed in. 
 *  At best you can assert your parameters. 
 */ 
STDMETHODIMP 
ABP_QueryInterface(LPABP lpABP, REFIID lpiid, LPVOID * ppvObj) 
{ 
 
    /*  See if the requested interface is one of ours */ 
    if (memcmp(lpiid, &IID_IUnknown, sizeof(IID)) && 
        memcmp(lpiid, &IID_IABProvider, sizeof(IID))) 
    { 
        *ppvObj = NULL;         /* OLE requires zeroing [out] parameter on error */ 
        DebugTraceSc(ABP_QueryInterface, E_NOINTERFACE); 
        return ResultFromScode(E_NOINTERFACE); 
    } 
 
    /*  We'll do this one. Bump the usage count and return a new pointer. */ 
 
    EnterCriticalSection(&lpABP->cs); 
    ++lpABP->lcInit; 
    LeaveCriticalSection(&lpABP->cs); 
     
    *ppvObj = lpABP; 
 
    return hrSuccess; 
} 
 
/* 
 -  ABP_Release 
 - 
 *  Releases and cleans up this object 
 */ 
STDMETHODIMP_(ULONG) 
ABP_Release(LPABP lpABP) 
{ 
    long lcInit; 
 
    EnterCriticalSection(&lpABP->cs); 
    lcInit = --lpABP->lcInit; 
    LeaveCriticalSection(&lpABP->cs); 
 
    if (lcInit == 0) 
    { 
        DeleteCriticalSection(&lpABP->cs); 
 
        lpABP->lpVtbl = NULL; 
        lpABP->lpFreeBuff(lpABP); 
        return (0); 
    } 
    return lcInit; 
} 
 
/* 
 -  ABP_Shutdown 
 - 
 *  Informs this provider that MAPI is done with it. 
 * 
 * 
 */ 
STDMETHODIMP 
ABP_Shutdown(LPABP lpABP, ULONG FAR * lpulFlags) 
{ 
    MAPIDeinitIdle(); 
 
    if (lpABP->lpMalloc) 
    { 
        lpABP->lpMalloc->lpVtbl->Release(lpABP->lpMalloc); 
        lpABP->lpMalloc = NULL; 
    } 
 
    return hrSuccess; 
} 
 
/* 
 -  ABP_Logon 
 - 
 *  Create a logon object and return it to MAPI. 
 * 
 */ 
 
/* 
 *  The PropTagArray used to retrieve logon properties 
 */ 
enum {  ivallogonPR_SAB_FILE, 
        ivallogonPR_SAB_UID, 
        cvallogonMax }; 
 
static const SizedSPropTagArray(cvallogonMax, tagaSABLogonProps) = 
{ 
    cvallogonMax, 
    { 
        PR_SAB_FILE, 
        PR_SAB_UID 
    } 
}; 
 
STDMETHODIMP 
ABP_Logon(  LPABP             lpABP, 
            LPMAPISUP         lpMAPISup, 
            ULONG             ulUIParam, 
            LPTSTR            lpszProfileName, 
            ULONG             ulFlags, 
            ULONG FAR *       lpulpcbSecurity, 
            LPBYTE FAR *      lppbSecurity, 
            LPMAPIERROR FAR * lppMapiError, 
            LPABLOGON FAR *   lppABPLogon) 
{ 
    LPABLOGON       lpABLogon       = NULL; 
    HRESULT         hResult         = hrSuccess; 
    SCODE           scode; 
    LPPROFSECT      lpProfSect      = NULL; 
    LPSPropValue    lpsPropVal      = NULL; 
    ULONG           cValues         = 0; 
    LPSTR           lpstrT          = NULL; 
    BOOL            fUINeeded; 
    BOOL            fNeedMAPIUID    = FALSE; 
    MAPIUID         muidID; 
    LPOBJECTLIST    lpObjectList; 
    CHAR            szFileName[MAX_PATH]; 
 
    szFileName[0] = '\0'; 
     
    *lppMapiError = NULL; 
     
    if ( ulFlags & MAPI_UNICODE ) 
    { 
        DebugTraceArg( ABP_Logon, "Bad Character width" ); 
        return ResultFromScode( MAPI_E_BAD_CHARWIDTH ); 
    } 
     
    /* 
     *  Get the name of my browse file from the profile section 
     */ 
 
    /*  Open the section for my provider */ 
    hResult = lpMAPISup->lpVtbl->OpenProfileSection(lpMAPISup, 
                NULL, 
                MAPI_MODIFY, 
                &lpProfSect); 
 
    if (HR_FAILED(hResult)) 
        goto out; 
 
    /*  Get the property containing the browse file name and the sab uid. */ 
    hResult = lpProfSect->lpVtbl->GetProps( 
        lpProfSect, 
        (LPSPropTagArray) &tagaSABLogonProps, 
        0,      /* ansi */ 
        &cValues, 
        &lpsPropVal); 
 
    if (HR_FAILED(hResult)) 
        goto out; 
 
#ifdef DEBUG 
    if (hResult != hrSuccess) 
    { 
        if (PROP_TYPE(lpsPropVal[ivallogonPR_SAB_FILE].ulPropTag) == PT_ERROR 
            && lpsPropVal[0].Value.err != MAPI_E_NOT_FOUND) 
        { 
            TraceSz1("ABP_Logon (PR_SAB_FILE) got unexpected scode: %s\n", 
                SzDecodeScode(lpsPropVal[ivallogonPR_SAB_FILE].Value.err)); 
        } 
        if (PROP_TYPE(lpsPropVal[ivallogonPR_SAB_UID].ulPropTag) == PT_ERROR 
            && lpsPropVal[ivallogonPR_SAB_UID].Value.err != MAPI_E_NOT_FOUND) 
        { 
            TraceSz1("ABP_Logon (PR_SAB_UID) got unexpected scode: %s\n", 
                SzDecodeScode(lpsPropVal[1].Value.err)); 
        } 
    } 
#endif 
 
    /*  Ignore warnings from reading the property. */ 
    hResult = hrSuccess; 
 
    /* copy the sab file name */ 
    if (lpsPropVal[0].ulPropTag == PR_SAB_FILE) 
    { 
        UINT cch = lstrlenA(lpsPropVal[0].Value.lpszA); 
 
        if (cch >= MAX_PATH) 
            cch = MAX_PATH - 1; 
 
        if (cch) 
            memcpy(szFileName, lpsPropVal[0].Value.lpszA, cch); 
 
        szFileName[cch] = '\0'; 
    } 
    else 
    { 
        DebugTrace("ABP_Logon PR_SAB_FILE not found"); 
    } 
 
    /* copy the sab uid */ 
    if (lpsPropVal[1].ulPropTag == PR_SAB_UID) 
    { 
        memcpy(&muidID, lpsPropVal[1].Value.bin.lpb, sizeof(MAPIUID)); 
    } 
    else 
    { 
        DebugTrace("ABP_Logon PR_SAB_UID not found"); 
        fNeedMAPIUID = TRUE; 
    } 
 
    /*  Discard GetProps() return data, if any. */ 
    lpABP->lpFreeBuff(lpsPropVal); 
 
    /* 
     *  UI needed unless the file name is good and file exists. 
     */ 
 
    fUINeeded = TRUE; 
 
    if (szFileName[0] != 0) 
    { 
        /* Verify the file exists. */ 
 
        HANDLE hFile = CreateFile( 
            szFileName, 
            GENERIC_READ, 
            FILE_SHARE_READ, 
            NULL, 
            OPEN_EXISTING, 
            FILE_ATTRIBUTE_NORMAL, 
            NULL); 
 
        if (hFile != INVALID_HANDLE_VALUE) 
        { 
            CloseHandle(hFile); 
 
            fUINeeded = FALSE; 
        } 
    } 
 
    /* 
    *  if the sab file name was not found in the profile or the sab file 
    *  does not exist we have to get the user to pick another one and 
    *  save back into the profile 
    */ 
 
    if (fUINeeded) 
    { 
        OPENFILENAME openfilename; 
        SPropValue sProp[1]; 
 
        /* 
         *  Can't bring up UI unless the client allows it 
         */ 
        if (ulFlags & AB_NO_DIALOG) 
        { 
            hResult = ResultFromScode(MAPI_E_LOGON_FAILED); 
            goto out; 
        } 
 
        /* 
         *  Get the user to select a file 
         */ 
        openfilename.lStructSize = sizeof(OPENFILENAME); 
        openfilename.hwndOwner = (HWND) ulUIParam; 
        openfilename.hInstance = 0; /* Ignored */ 
        openfilename.lpstrFilter = TEXT("Sample AB files\0*.sab\0\0"); 
        openfilename.lpstrCustomFilter = NULL; 
        openfilename.nMaxCustFilter = 0; 
        openfilename.nFilterIndex = 0; 
        openfilename.lpstrFile = szFileName; 
        openfilename.nMaxFile = MAX_PATH; 
        openfilename.lpstrFileTitle = NULL; 
        openfilename.nMaxFileTitle = 0; 
        openfilename.lpstrInitialDir = NULL; 
        openfilename.lpstrTitle = TEXT("Sample Address Book"); 
        openfilename.Flags = OFN_FILEMUSTEXIST | 
            OFN_HIDEREADONLY | 
            OFN_NOCHANGEDIR; 
        openfilename.nFileOffset = 0; 
        openfilename.nFileExtension = 0; 
        openfilename.lpstrDefExt = TEXT("sab"); 
        openfilename.lCustData = 0; 
        openfilename.lpfnHook = NULL; 
        openfilename.lpTemplateName = NULL; 
 
        /* 
         *  Call up the common dialog 
         */ 
        if (!GetOpenFileName(&openfilename)) 
        { 
#ifdef DEBUG 
            DWORD dwT; 
 
            dwT = CommDlgExtendedError(); 
#endif /* DEBUG */ 
             
            hResult = ResultFromScode(MAPI_E_LOGON_FAILED); 
            goto out; 
        } 
 
        /* 
         *  Set the sab file name property value 
         */ 
        sProp[0].ulPropTag = PR_SAB_FILE; 
        sProp[0].Value.lpszA = szFileName; 
 
        /* 
         *  Save the sab file into the profile 
         */ 
        if (hResult = lpProfSect->lpVtbl->SetProps(lpProfSect, 1, sProp, NULL)) 
        { 
            /* 
             *  Do nothing...  So I couldn't save it away this time... 
             */ 
            DebugTraceResult("ABP_Logon got unexpected result on SetProps\n", hResult); 
            hResult = hrSuccess; 
        } 
    } 
 
    /* 
    *  if the uid was not found we have to generate a new muid for the 
    *  PR_SAB_ID property and save it back into the profile 
    */ 
 
    if (fNeedMAPIUID) 
    { 
        SPropValue sProp[1]; 
 
        /* 
         *  Get a new sab uid 
         */ 
        hResult = lpMAPISup->lpVtbl->NewUID(lpMAPISup, &muidID); 
        if (HR_FAILED(hResult)) 
        { 
            goto out; 
        } 
 
        /* 
         *  Set the sab uid property value 
         */ 
        sProp[0].ulPropTag = PR_SAB_UID; 
        sProp[0].Value.bin.cb = sizeof(MAPIUID); 
        sProp[0].Value.bin.lpb = (LPBYTE) &muidID; 
 
        /* 
         *  Save the sab uid into the profile 
         */ 
        if (hResult = lpProfSect->lpVtbl->SetProps(lpProfSect, 1, sProp, NULL)) 
        { 
            /* 
             *  Do nothing...  So I couldn't save it away this time... 
             */ 
            DebugTraceResult("ABP_Logon got unexpected result on SetProps\n", hResult); 
            hResult = hrSuccess; 
        } 
    } 
 
    /* 
     *  Allocate space for keeping the file name in the ABLogon object 
     */ 
 
    if (scode = lpABP->lpAllocBuff(lstrlenA(szFileName)+1, &lpstrT)) 
    { 
        hResult = ResultFromScode(scode); 
        goto out; 
    } 
    lstrcpyA(lpstrT, szFileName); 
 
 
    hResult = HrNewABLogon(&lpABLogon, 
                            (LPABPROVIDER) lpABP, 
                            lpMAPISup, 
                            lpstrT, 
                            &muidID, 
                            lpABP->hLibrary, 
                            lpABP->lpAllocBuff, 
                            lpABP->lpAllocMore, 
                            lpABP->lpFreeBuff, 
                            lpABP->lpMalloc ); 
    if (HR_FAILED(hResult)) 
    { 
        goto out; 
    } 
 
 
    /* 
     *  Allocate space for another object list item 
     */ 
    scode = lpABP->lpAllocBuff(sizeof(OBJECTLIST), &lpObjectList); 
    if (FAILED(scode)) 
    { 
        hResult = ResultFromScode(scode); 
        goto out; 
    } 
 
    /* Get the Critical Section */ 
    EnterCriticalSection(&lpABP->cs); 
 
    /* add logon object to begining of providers object list */ 
    lpObjectList->lpObject = (LPVOID) lpABLogon; 
    lpObjectList->lppNext = lpABP->lpObjectList; 
 
    /* insert new logon object into the head of the providers object list */ 
    lpABP->lpObjectList = lpObjectList; 
 
    /* leave critical section */ 
    LeaveCriticalSection(&lpABP->cs); 
 
    /* 
     *  Register my MAPIUID for this provider, 
     *  but do not allow an error from setting the 
     *  MAPIUID to cause failure to Logon. 
     */ 
 
    (void)lpMAPISup->lpVtbl->SetProviderUID(lpMAPISup, 
        (LPMAPIUID) &muidABSample, 0); 
 
    *lppABPLogon = lpABLogon; 
 
out: 
    if (lpProfSect) 
        lpProfSect->lpVtbl->Release(lpProfSect); 
 
    if (hResult) 
    { 
        lpABP->lpFreeBuff(lpstrT); 
 
        Assert(lpABLogon == NULL); 
 
        /* Verify we don't return warnings at this time. */ 
        Assert(HR_FAILED(hResult)); 
    } 
 
    DebugTraceResult(ABP_Logon, hResult); 
    return hResult; 
} 
 
/* 
 *  Removes a particular logon object from the list of logon objects 
 *  that's kept track of in the IABProvider object 
 */ 
void 
RemoveLogonObject(LPABPROVIDER lpABProvider, LPVOID lpvABLogon, LPFREEBUFFER lpFreeBuff) 
{ 
 
    LPOBJECTLIST *lppObjectList; 
    LPOBJECTLIST lpObjectListT; 
    LPABP lpABP = (LPABP) lpABProvider; 
 
#if defined DEBUG 
    BOOL fFound = FALSE; 
 
#endif 
 
    /* Get the Critical Section */ 
    EnterCriticalSection(&lpABP->cs); 
 
    /* 
     *  remove this logon object from the provider init objects list 
     *  of logon objects 
     */ 
    lppObjectList = &(lpABP->lpObjectList); 
 
    while (*lppObjectList) 
    { 
        /* is this the logon object? */ 
        if ((*lppObjectList)->lpObject == lpvABLogon) 
        { 
            /* save next list item */ 
            lpObjectListT = (*lppObjectList)->lppNext; 
 
            /* free the object list item */ 
            lpFreeBuff(*lppObjectList); 
 
            /* delete object from the list */ 
            *lppObjectList = lpObjectListT; 
 
#if defined DEBUG 
            fFound = TRUE; 
#endif 
            break; 
        } 
 
        lppObjectList = &(*lppObjectList)->lppNext; 
    } 
 
    /* leave critical section */ 
    LeaveCriticalSection(&lpABP->cs); 
 
#if defined DEBUG 
    AssertSz(fFound, TEXT("Logon object not found on providers object list")); 
#endif 
 
    return; 
}    
 
 
/* 
 *  Finds a particular logon object by its muid. 
 */ 
void 
FindLogonObject(LPABPROVIDER lpABProvider, LPMAPIUID lpMuidToFind, LPABLOGON * lppABLogon) 
{ 
    LPABP lpABP = (LPABP) lpABProvider; 
    LPABLOGON lpABLogonT = NULL; 
    LPOBJECTLIST lpObjectList = NULL; 
    LPMAPIUID lpMuidLogon = NULL; 
 
    Assert(!IsBadReadPtr(lpABP, sizeof(ABP))); 
    Assert(!IsBadReadPtr(lpMuidToFind, sizeof(MAPIUID))); 
    Assert(!IsBadReadPtr(lppABLogon, sizeof(LPABLOGON))); 
 
    /* Get the Critical Section */ 
    EnterCriticalSection(&lpABP->cs); 
 
    *lppABLogon = NULL; 
 
    for (lpObjectList = lpABP->lpObjectList; 
        lpObjectList; lpObjectList = lpObjectList->lppNext) 
    { 
        lpABLogonT = (LPABLOGON) lpObjectList->lpObject; 
         
        lpMuidLogon = LpMuidFromLogon(lpABLogonT); 
 
        if (memcmp((LPVOID) lpMuidLogon, (LPVOID) lpMuidToFind, sizeof(MAPIUID)) == 0) 
        { 
            *lppABLogon = lpABLogonT; 
            break; 
        } 
    } 
 
    /* leave critical section */ 
    LeaveCriticalSection(&lpABP->cs); 
} 
 
 
/* 
 -  ServiceEntry 
 - 
 *  This funtion is used by MAPI to configure the Sample Address Book.   
 *  It's a lot like ABP_Logon, except that it doesn't return a logon object 
 *  and it can be passed in its configuration information (as defined in 
 *  smpab.h) from MAPI so that no UI is required. 
 * 
 */ 
STDAPI 
ServiceEntry(HINSTANCE hInstance, 
             LPMALLOC lpMalloc, 
             LPMAPISUP lpMAPISup, 
             ULONG ulUIParam, 
             ULONG ulFlags, 
             ULONG ulContext, 
             ULONG cValues, 
             LPSPropValue lpProps, 
             LPPROVIDERADMIN lpAdminProviders, 
             LPMAPIERROR FAR *lppMapiError) 
{ 
    OPENFILENAME openfilename; 
    char szFileName[MAX_PATH]; 
    HRESULT hResult = hrSuccess; 
    LPSPropValue lpsPropVal = NULL; 
    ULONG ulCount = 0; 
    LPALLOCATEBUFFER lpAllocBuff; 
    LPALLOCATEMORE lpAllocMore; 
    LPFREEBUFFER lpFreeBuff; 
    LPPROFSECT lpProf = NULL; 
    BOOL fUINeeded = FALSE; 
    BOOL fNeedMAPIUID = FALSE; 
    SPropValue sProp[2]; 
    ULONG uliProp; 
    MAPIUID muid; 
 
    /*  Validate parameters */ 
 
    /* check the support object */ 
    if (IsBadReadPtr(lpMAPISup, sizeof(LPMAPISUP))) 
    { 
        DebugTraceSc(ServiceEntry, E_INVALIDARG); 
        return ResultFromScode(E_INVALIDARG); 
    } 
     
    if ( ulFlags & MAPI_UNICODE ) 
    { 
        DebugTraceArg( ServiceEntry, "Bad character width" ); 
        return ResultFromScode( MAPI_E_BAD_CHARWIDTH ); 
    } 
     
    /* 
     *  check for context 
     */ 
    if (ulContext == MSG_SERVICE_DELETE || ulContext == MSG_SERVICE_INSTALL 
        || ulContext == MSG_SERVICE_UNINSTALL) 
        return hrSuccess; 
    if (ulContext != MSG_SERVICE_CONFIGURE && ulContext != MSG_SERVICE_CREATE) 
    { 
        DebugTrace(TEXT("ServiceEntry unsupported context")); 
        return ResultFromScode(MAPI_E_NO_SUPPORT); 
    } 
 
    /*  Get the memory allocation routines we'll be needing. */ 
    hResult = lpMAPISup->lpVtbl->GetMemAllocRoutines(lpMAPISup, 
        &lpAllocBuff, &lpAllocMore, &lpFreeBuff); 
    if (hResult) 
    { 
        DebugTraceResult(MAPISUP: :GetMemAllocRoutines, hResult); 
        goto out; 
    } 
 
    /* Open the profile section associated with our provider */ 
    hResult = HrOpenSingleProvider(lpAdminProviders, &lpProf); 
    if (hResult) 
    { 
        DebugTrace(TEXT("Unable to open the profile.")); 
        goto out; 
    } 
 
    szFileName[0] = '\0'; 
 
    /* get sab filename and uid from profile */ 
    hResult = lpProf->lpVtbl->GetProps( 
        lpProf, 
        (LPSPropTagArray) &tagaSABLogonProps, 
        ulFlags & MAPI_UNICODE, 
        &ulCount, 
        &lpsPropVal); 
 
    /*  Ignore errors/warnings from reading the property. */ 
    hResult = hrSuccess; 
 
    /* Look for the .SAB in the config props first */ 
    for (uliProp = 0; uliProp < cValues; uliProp++) 
    { 
        if (PROP_ID(lpProps[uliProp].ulPropTag) == PROP_ID(PR_SAB_FILE)) 
            break; 
    } 
 
    if (uliProp < cValues && 
        PROP_TYPE(lpProps[uliProp].ulPropTag) != PT_ERROR) 
    { 
        ULONG cch = lstrlenA(lpProps[uliProp].Value.lpszA); 
 
        if (cch >= MAX_PATH) 
            cch = MAX_PATH - 1; 
        if (cch) 
            memcpy(szFileName, lpProps[uliProp].Value.lpszA, (size_t) cch); 
        szFileName[cch] = '\0'; 
 
    } 
    else if (lpsPropVal && PROP_ID(lpsPropVal[0].ulPropTag) == 
        PROP_ID(PR_SAB_FILE) && 
        PROP_TYPE(lpsPropVal[0].ulPropTag) != PT_ERROR) 
    { 
        ULONG cch = lstrlenA(lpsPropVal[0].Value.lpszA); 
 
        if (cch >= MAX_PATH) 
            cch = MAX_PATH - 1; 
        if (cch) 
            memcpy(szFileName, lpsPropVal[0].Value.lpszA, (size_t) cch); 
        szFileName[cch] = '\0'; 
    } 
    else 
    { 
        /* need to ask the user for the sab file */ 
        fUINeeded = TRUE; 
    } 
 
    /* Look for the SAB_UID in the config props first */ 
    for (uliProp = 0; uliProp < cValues; uliProp++) 
    { 
        if (PROP_ID(lpProps[uliProp].ulPropTag) == PROP_ID(PR_SAB_UID)) 
            break; 
 
    } 
 
    if (uliProp < cValues && 
        PROP_TYPE(lpProps[uliProp].ulPropTag) != PT_ERROR) 
    { 
        memcpy(&muid, lpProps[uliProp].Value.bin.lpb, sizeof(MAPIUID)); 
    } 
    else if (lpsPropVal && PROP_ID(lpsPropVal[1].ulPropTag) == 
        PROP_ID(PR_SAB_UID) && PROP_TYPE(lpsPropVal[1].ulPropTag) != PT_ERROR) 
    { 
        memcpy(&muid, lpsPropVal[1].Value.bin.lpb, sizeof(MAPIUID)); 
    } 
    else 
    { 
        /* need to generate a uid */ 
        fNeedMAPIUID = TRUE; 
    } 
 
    /*  Discard GetProps() return data, if any. 
     */ 
    if (lpsPropVal) 
        lpFreeBuff(lpsPropVal); 
 
    /* 
     *  if the sab file name was not found in the profile we have to 
     *  get the user to pick one and save it back into the profile 
     */ 
    if (   fUINeeded 
        && !(ulFlags & (SERVICE_UI_ALLOWED | SERVICE_UI_ALWAYS | UI_SERVICE))) 
    { 
        /*  We need UI to configure but it's not allowed so we can't configure. 
         */ 
        hResult = ResultFromScode(MAPI_E_UNCONFIGURED); 
 
        DebugTrace("SMPAB::ServiceEntry - Missing properties required to configure service.\n"); 
 
        goto out; 
    } 
 
    if ((fUINeeded) || (ulFlags & UI_SERVICE)) 
    { 
        /* 
        *  Get the user to pick a SAB file 
        */ 
        openfilename.lStructSize = sizeof(OPENFILENAME); 
        openfilename.hwndOwner = (HWND) ulUIParam; 
        openfilename.hInstance = 0; /* Ignored */ 
        openfilename.lpstrFilter = "Sample AB files\0*.sab\0\0"; 
        openfilename.lpstrCustomFilter = NULL; 
        openfilename.nMaxCustFilter = 0; 
        openfilename.nFilterIndex = 0; 
        openfilename.lpstrFile = szFileName; 
        openfilename.nMaxFile = MAX_PATH; 
        openfilename.lpstrFileTitle = NULL; 
        openfilename.nMaxFileTitle = 0; 
        openfilename.lpstrInitialDir = NULL; 
        openfilename.lpstrTitle = "Pick a Sample Address Book file"; 
        openfilename.Flags = OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR | OFN_HIDEREADONLY; 
        openfilename.nFileOffset = 0; 
        openfilename.nFileExtension = 0; 
        openfilename.lpstrDefExt = "sab"; 
        openfilename.lCustData = 0; 
        openfilename.lpfnHook = NULL; 
        openfilename.lpTemplateName = NULL; 
 
        /* 
        *  Call up the common dialog 
        */ 
        if (!GetOpenFileName(&openfilename)) 
        { 
            /* user pressed cancel */ 
            goto out; 
        } 
    } 
 
    /* 
    *  if the uid was not found we have to generate a new muid for the 
    *  PR_SAB_ID property and save it back into the profile 
    */ 
 
    if (fNeedMAPIUID) 
    { 
        hResult = lpMAPISup->lpVtbl->NewUID(lpMAPISup, &muid); 
        if (HR_FAILED(hResult)) 
        { 
            /* 
            *  Can't get a uid so just leave 
            */ 
            goto out; 
        } 
    } 
 
    /* 
    *  Set the file name property 
    */ 
 
    sProp[ivallogonPR_SAB_FILE].ulPropTag = PR_SAB_FILE; 
    sProp[ivallogonPR_SAB_FILE].Value.lpszA = szFileName; 
 
    /* 
    *  Set the id property 
    */ 
 
    sProp[ivallogonPR_SAB_UID].ulPropTag = PR_SAB_UID; 
    sProp[ivallogonPR_SAB_UID].Value.bin.cb = sizeof(MAPIUID); 
    sProp[ivallogonPR_SAB_UID].Value.bin.lpb = (LPBYTE) &muid; 
 
    /* 
    *  Save the sab file and the uid back into the profile 
    */ 
    hResult = lpProf->lpVtbl->SetProps( 
        lpProf, 
        sizeof(sProp) / sizeof(SPropValue), 
        sProp, 
        NULL); 
 
    if (HR_FAILED(hResult)) 
    { 
        /* 
        *  Do nothing...  So I couldn't save it away this time... 
        */ 
        DebugTrace(TEXT("ServiceEntry could not SetProp in profile")); 
        hResult = hrSuccess; 
    } 
 
out: 
    if (lpProf) 
        lpProf->lpVtbl->Release(lpProf); 
 
    DebugTraceResult(ServiceEntry, hResult); 
    return hResult; 
} 
 
/* 
 -  HrOpenSingleProvider 
 - 
 *  Opens the profile section associated with this provider. 
 * 
 *  If the ServiceEntry() function exported from a provider had 
 *  more than 1 section associated with it, this is where you'd get the chance 
 *  to get all of them. 
 */ 
 
static SizedSPropTagArray(1, tagaProviderTable) = 
{ 
    1, 
    { 
        PR_PROVIDER_UID 
    } 
}; 
 
HRESULT 
HrOpenSingleProvider(LPPROVIDERADMIN lpAdminProviders, 
                     LPPROFSECT FAR * lppProfSect) 
{ 
    HRESULT hResult; 
    LPMAPITABLE lpTable = NULL; 
    LPSRowSet lpRows = NULL; 
    LPSPropValue lpProp; 
 
    hResult = lpAdminProviders->lpVtbl->GetProviderTable( 
        lpAdminProviders, 
        0, 
        &lpTable); 
    if (HR_FAILED(hResult)) 
        goto out; 
 
    hResult = lpTable->lpVtbl->SetColumns(lpTable, (LPSPropTagArray) &tagaProviderTable, 0); 
    if (HR_FAILED(hResult)) 
        goto out; 
 
    hResult = lpTable->lpVtbl->QueryRows(lpTable, 1, 0, &lpRows); 
    if (HR_FAILED(hResult)) 
        goto out; 
 
    if (lpRows->cRows == 0) 
    { 
        hResult = ResultFromScode(MAPI_E_NOT_FOUND); 
        goto out; 
    } 
 
    lpProp = lpRows->aRow[0].lpProps; 
 
    hResult = lpAdminProviders->lpVtbl->OpenProfileSection( 
                lpAdminProviders, 
                (LPMAPIUID) lpProp->Value.bin.lpb, 
                NULL, 
                MAPI_MODIFY, 
                lppProfSect); 
 
out: 
    FreeProws(lpRows); 
 
    if (lpTable) 
        lpTable->lpVtbl->Release(lpTable); 
 
    DebugTraceResult(HrOpenSingleProvider, hResult); 
    return hResult; 
} 
 
 
 
/* 
 -  ScLoadString 
 - 
 *  Loads a string from a resource.  It will optionally allocate the string if 
*  a allocation function is passed in.  Otherwise it assumes that the *lppsz 
 *  is already allocated. 
 */ 
 
SCODE ScLoadString( UINT                ids, 
                    ULONG               ulcch, 
                    LPALLOCATEBUFFER    lpAllocBuff, 
                    HINSTANCE           hLibrary, 
                    LPSTR *             lppsz) 
{ 
    SCODE sc = S_OK; 
    int iRet; 
 
    /* 
     *  Assert parameters 
     */ 
    Assert((lpAllocBuff ? !IsBadCodePtr((FARPROC) lpAllocBuff):TRUE)); 
    Assert(ids!=0); 
 
    if (lpAllocBuff) 
    { 
        sc = lpAllocBuff(ulcch, lppsz); 
        if (FAILED(sc)) 
        { 
            goto out; 
        } 
    } 
#ifdef DEBUG 
    else 
    { 
        Assert(!IsBadWritePtr(*lppsz, (UINT) ulcch)); 
    } 
#endif /* DEBUG */ 
     
    iRet = LoadStringA( hLibrary, 
                        ids, 
                        *lppsz, 
                        (UINT) ulcch); 
 
    if (!iRet) 
    { 
        DebugTrace("LoadString() failed...\n"); 
        sc = E_FAIL; 
        goto out; 
    } 
out: 
 
    DebugTraceSc(ScLoadString, sc); 
    return sc; 
}