/***********************************************************************
 *
 *  ABCONT.C
 *
 *  Microsoft At Work Fax AB directory container object
 *
 *  This file contains the code for implementing the Microsoft At Work Fax AB
 *  directory container object.
 *
 *  This directory container was retrieved by OpenEntry on the entryid
 *  retrieved from the single row of the hierarchy table (IVTROOT in root.c).
 *
 *  Many things are not yet implemented in this object.  For example, no
 *  advanced search dialog, or details dialog are available.  Also the
 *  ICON to PR_DISPLAY_TYPE table isn't available.
 *
 *
 *  The following routines are implemented in this file:
 *
 *              HrNewFaxDirectory
 *      ABC_Release
 *      ABC_SaveChanges
 *      ABC_OpenProperty
 *      ABC_GetContentsTable
 *      ABC_GetHierarchyTable
 *
 *      HrGetDetailsDialog
 *
 *      HrNewABCButton
 *      ABCBUTT_QueryInterface
 *      ABCBUTT_Release
 *      ABCBUTT_Activate
 *      ABCBUTT_GetState
 *
 *  Copyright 1992, 1993 Microsoft Corporation.  All Rights Reserved.
 *
 *  Revision History:
 *
 *              When            Who                                     What
 *              --------        ------------------  ---------------------------------------
 *              1.1.94          MAPI                            Original source from MAPI sample AB Provider
 *              1.27.94         Yoram Yaacovi           Modifications to make it an At Work Fax ABP
 *      3.6.94                  Yoram Yaacovi           Update to MAPI build 154
 *      4.1.94                  Yoram Yaacovi           Update to MAPI and Sample AB build 157
 *      8.1.94                  Yoram Yaacovi           Update to MAPI and Sample AB build 304
 *      10.6.94                 Yoram Yaacovi           Update to MAPI and Sample AB build 313
 *                                                                                      Mainly adding a ResolveName method to ABContainer
 *              11.3.94                 Yoram Yaacovi           Update to MAPI 318.
 *
 ***********************************************************************/

#include "faxab.h"

#define PR_BUTTON_PRESS                 PROP_TAG(PT_OBJECT, 0x6603)
#define PR_FAB_FILE_TEMP_A              PROP_TAG(PT_STRING8,0x6605)
#define PR_IMPORT_FROM_FILE_A           PROP_TAG(PT_STRING8,0x6606)
#define PR_IMPORT_TO_FILE_A             PROP_TAG(PT_STRING8,0x6607)
#define PR_BUTTON_IMPORT_A              PROP_TAG(PT_STRING8,0x6608)
#define PR_IMPORT_FORMAT_A              PROP_TAG(PT_STRING8,0x6609)
#define PR_DDLBX_IMPORT_FORMATS_A       PROP_TAG(PT_STRING8,0x660a)
#define FAB_FILE_NAME_NOTIF_ID          99L

/*****************************
 ****  Notification **********
 *****************************/

typedef struct
{
    MAPIUID muid;
    ULONG   ulIdc;
}   NOTIFDATA;


NOTIFDATA notifdata = { MUIDABMAWF, IDC_DIR_FAB_FILE_NAME };

/*****************************
 ** Display Table structures *
 *****************************/

DTBLEDIT editDirFileName     =
{
    SIZEOF(DTBLEDIT),
    0,
    MAX_PATH,
    PR_FAB_FILE_TEMP_A
};

DTBLEDIT editImportFromFile  =
{
    SIZEOF(DTBLEDIT),
    0,
    MAX_PATH,
    PR_IMPORT_FROM_FILE_A
};

DTBLEDIT editImportToFile    =
{
    SIZEOF(DTBLEDIT),
    0,
    MAX_PATH,
    PR_IMPORT_TO_FILE_A
};

DTBLBUTTON buttonImport      =
{
    SIZEOF(DTBLBUTTON),
    0,
    PR_BUTTON_IMPORT_A
};

DTBLBUTTON buttonDirChange   =
{
    SIZEOF(DTBLBUTTON),
    0,
    PR_BUTTON_PRESS
};

DTBLDDLBX ddlbxImportFormats =
{
    SIZEOF(DTBLDDLBX),
    PR_DISPLAY_NAME_A,
    PR_IMPORT_FORMAT_A,
    PR_DDLBX_IMPORT_FORMATS_A
};

/* General page layout */

DTCTL rgdtctlDirGeneral[] =
{
    /* directory general propery page */
    { DTCT_PAGE,     0, NULL, 0, NULL, 0,                  &dtblpage },

    /* static controls and edit control containing fab file name */
    { DTCT_LABEL,    0, NULL, 0, NULL, IDC_STATIC_CONTROL, &dtbllabel },
    { DTCT_LABEL,    0, NULL, 0, NULL, IDC_STATIC_CONTROL, &dtbllabel },
    { DTCT_EDIT,     0, (LPBYTE)&notifdata, SIZEOF(NOTIFDATA), (LPTSTR)szFileNameFilter, IDC_DIR_FAB_FILE_NAME, &editDirFileName },

    /* push button for changing fab file */
    { DTCT_BUTTON,   0, NULL, 0, NULL, IDC_DIR_CHANGE,     &buttonDirChange },

    /* Import GroupBox */
    { DTCT_GROUPBOX, 0, NULL, 0, NULL, IDC_STATIC_CONTROL, &dtblgroupbox },

    // Import from file
    { DTCT_LABEL,    0, NULL, 0, NULL, IDC_STATIC_CONTROL, &dtbllabel },
    { DTCT_EDIT,     0, NULL, 0, (LPTSTR)szFileNameFilter, IDC_IMPORT_FROM_FILE, &editImportFromFile },

    // Import format
    { DTCT_LABEL,    0, NULL, 0, NULL, IDC_STATIC_CONTROL, &dtbllabel },
    { DTCT_DDLBX,    0, NULL, 0, NULL, IDC_IMPORT_FORMAT,  &ddlbxImportFormats },

    // Import into file
    { DTCT_LABEL,    0, NULL, 0, NULL, IDC_STATIC_CONTROL, &dtbllabel },
    { DTCT_EDIT,     0, NULL, 0, (LPTSTR)szFileNameFilter, IDC_IMPORT_TO_FILE, &editImportToFile },

    // Import Button
    { DTCT_BUTTON,   0, NULL, 0, NULL, IDC_IMPORT,         &buttonImport },

};


/* Display table pages for Directory */

DTPAGE rgdtpageDir[] =
{
    {
        SIZEOF(rgdtctlDirGeneral) / SIZEOF(DTCTL),
        (LPTSTR)MAKEINTRESOURCE(DirGeneralPage),
        TEXT(""),
        rgdtctlDirGeneral
    }
};



/*
 *  ABCont jump table is defined here...
 */

ABC_Vtbl vtblABC =
{
    (ABC_QueryInterface_METHOD *)    ROOT_QueryInterface,
    (ABC_AddRef_METHOD *)            ROOT_AddRef,
    (ABC_AddRef_METHOD *)            ABC_Release,
    (ABC_GetLastError_METHOD *)      ROOT_GetLastError,
    ABC_SaveChanges,
    (ABC_GetProps_METHOD *)          WRAP_GetProps,
    (ABC_GetPropList_METHOD *)       WRAP_GetPropList,
    ABC_OpenProperty,
    (ABC_SetProps_METHOD *)          WRAP_SetProps,
    (ABC_DeleteProps_METHOD *)       WRAP_DeleteProps,
    (ABC_CopyTo_METHOD *)            WRAP_CopyTo,
    (ABC_CopyProps_METHOD *)         WRAP_CopyProps,
    (ABC_GetNamesFromIDs_METHOD *)   WRAP_GetNamesFromIDs,
    (ABC_GetIDsFromNames_METHOD *)   WRAP_GetIDsFromNames,
    ABC_GetContentsTable,
    ABC_GetHierarchyTable,
    (ABC_OpenEntry_METHOD *)         ROOT_OpenEntry,
    (ABC_SetSearchCriteria_METHOD *) ROOT_SetSearchCriteria,
    (ABC_GetSearchCriteria_METHOD *) ROOT_GetSearchCriteria,
    (ABC_CreateEntry_METHOD *)       ROOT_CreateEntry,
    (ABC_CopyEntries_METHOD *)       ROOT_CopyEntries,
    (ABC_DeleteEntries_METHOD *)     ROOT_DeleteEntries,
    (ABC_ResolveNames_METHOD *)      ROOT_ResolveNames
};

static const SizedSPropTagArray(1, ABC_SPT_TMP_FAB) = {1, {PR_FAB_FILE_TEMP_A}};


/*
 *  Private functions
 */
HRESULT HrNewABCButton( LPABCNT lpABC,
                        ULONG ulInterfaceOptions,
                        ULONG ulFlags,
                        LPMAPICONTROL FAR * lppMAPICont);

HRESULT HrGetSearchDialog(LPABCNT lpABC, LPMAPITABLE * lppSearchTable);
HRESULT HrGetDetailsDialog(LPABCNT lpABC, LPMAPITABLE * lppDetailsTable);

/*************************************************************************
 *
 -  HrNewFaxDirectory
 -
 *  Creates a Directory container object.
 *
 *
 */

/*
 *  Properties that are initially set on this object
 */
enum {  ivalabcPR_DISPLAY_TYPE = 0,
        ivalabcPR_OBJECT_TYPE,
        ivalabcPR_ENTRYID,
        ivalabcPR_RECORD_KEY,
        ivalabcPR_SEARCH_KEY,
        ivalabcPR_DISPLAY_NAME,
        ivalabcPR_CONTAINER_FLAGS,
        ivalabcPR_FAB_FILE,
        ivalabcPR_FAB_FILE_TEMP,
        ivalabcPR_IMPORT_FROM_FILE,
        ivalabcPR_IMPORT_TO_FILE,
        ivalabcPR_IMPORT_FORMAT,
        cvalabcMax };

HRESULT
HrNewFaxDirectory( LPABCONT *          lppABC,
                   ULONG *             lpulObjType,
                   LPABLOGON           lpABLogon,
                   LPCIID              lpInterface,
                   HINSTANCE           hLibrary,
                   LPALLOCATEBUFFER    lpAllocBuff,
                   LPALLOCATEMORE      lpAllocMore,
                   LPFREEBUFFER        lpFreeBuff,
                   LPMALLOC            lpMalloc )
{
    HINSTANCE      hInst;
    HRESULT        hResult = hrSuccess;
    LPABCNT        lpABC = NULL;
    SCODE          sc;
    LPPROPDATA     lpPropData = NULL;
    SPropValue     spv[cvalabcMax];
    MAPIUID *      lpMuidLogon;
    LPTSTR         lpszFileName;
#ifdef UNICODE
    CHAR           szAnsiFileName[ MAX_PATH ];
#endif
    ULONG          ulPropAccess = IPROP_CLEAN|IPROP_READWRITE; /* READWRITE is redundant */
    DIR_ENTRYID    eidRoot =   { {0, 0, 0, 0},
                                 MUIDABMAWF,
                                 MAWF_VERSION,
                                 MAWF_DIRECTORY
                                };

    /*  Do I support this interface?? */
    if (lpInterface)
    {
        if (memcmp(lpInterface, &IID_IABContainer,   SIZEOF(IID)) &&
            memcmp(lpInterface, &IID_IMAPIContainer, SIZEOF(IID)) &&
            memcmp(lpInterface, &IID_IMAPIProp,      SIZEOF(IID)) &&
            memcmp(lpInterface, &IID_IUnknown,       SIZEOF(IID)))
        {
            DebugTraceSc(HrNewSampDirectory, MAPI_E_INTERFACE_NOT_SUPPORTED);
            return ResultFromScode(MAPI_E_INTERFACE_NOT_SUPPORTED);
        }
    }

    /*
     *  Allocate space for the directory container structure
     */

    sc = lpAllocBuff( SIZEOF(ABCNT), (LPVOID *) & lpABC);

    if (sc != SUCCESS_SUCCESS)
    {
        DebugTraceSc( HrNewFaxDirectory, sc );
        hResult = ResultFromScode( sc );
        goto err;
    }

    lpABC->lpVtbl = &vtblABC;
    lpABC->lcInit = 1;
    lpABC->hResult = hrSuccess;
    lpABC->idsLastError = 0;

    lpABC->hLibrary = hLibrary;
    lpABC->lpAllocBuff = lpAllocBuff;
    lpABC->lpAllocMore = lpAllocMore;
    lpABC->lpFreeBuff = lpFreeBuff;
    lpABC->lpMalloc = lpMalloc;

    lpABC->lpABLogon = lpABLogon;
    lpABC->lpTDatDetails = NULL;

    // Get the instance handle, so that I can get the display strings off the resource file
    hInst = lpABC->hLibrary;

    /*
     *  Create lpPropData
     */

    sc = CreateIProp( (LPIID) &IID_IMAPIPropData,
                              lpAllocBuff,
                              lpAllocMore,
                              lpFreeBuff,
                              lpMalloc,
                              &lpPropData
                             );

    if (FAILED(sc))
    {
        DebugTraceSc(HrNewFaxDirectory, sc);
        hResult=ResultFromScode (sc);
        goto err;
    }

    /*
     *  initialize the muid in the entry id
     */
    lpMuidLogon = LpMuidFromLogon(lpABLogon);
    eidRoot.muidID = *lpMuidLogon;


    /*
     *  Set up initial set of properties associated with this
     *  container.
     */

    spv[ivalabcPR_DISPLAY_TYPE].ulPropTag = PR_DISPLAY_TYPE;
    spv[ivalabcPR_DISPLAY_TYPE].Value.l   = DT_NOT_SPECIFIC;

    spv[ivalabcPR_OBJECT_TYPE].ulPropTag = PR_OBJECT_TYPE;
    spv[ivalabcPR_OBJECT_TYPE].Value.l   = MAPI_ABCONT;

    spv[ivalabcPR_ENTRYID].ulPropTag     = PR_ENTRYID;
    spv[ivalabcPR_ENTRYID].Value.bin.cb  = SIZEOF(DIR_ENTRYID);
    spv[ivalabcPR_ENTRYID].Value.bin.lpb = (LPBYTE) &eidRoot;

    spv[ivalabcPR_RECORD_KEY].ulPropTag     = PR_RECORD_KEY;
    spv[ivalabcPR_RECORD_KEY].Value.bin.cb  = SIZEOF(DIR_ENTRYID);
    spv[ivalabcPR_RECORD_KEY].Value.bin.lpb = (LPBYTE) &eidRoot;

    spv[ivalabcPR_SEARCH_KEY].ulPropTag     = PR_SEARCH_KEY;
    spv[ivalabcPR_SEARCH_KEY].Value.bin.cb  = SIZEOF(DIR_ENTRYID);
    spv[ivalabcPR_SEARCH_KEY].Value.bin.lpb = (LPBYTE) &eidRoot;


    // MAPI is not UNICODE for this one...
    spv[ivalabcPR_DISPLAY_NAME].ulPropTag = PR_DISPLAY_NAME_A;

    // MAPI 304 sample AB
    // GenerateContainerDN(lpABLogon, szBuf);
    // spv[ivalabcPR_DISPLAY_NAME].Value.LPSZ = szBuf;
    if ( (sc = lpAllocMore( MAX_DISPLAY_NAME,
                            (LPVOID) lpABC,
                            (LPVOID *) & spv[ivalabcPR_DISPLAY_NAME].Value.lpszA)) != SUCCESS_SUCCESS)
    {
        hResult = ResultFromScode( sc );
        goto err;
    }
    else
    {
        // Load the strings
        // A display name longer than MAX_DISPLAY_NAME will be truncated
        // MAPI is not UNICODE for this attribute
        LoadStringA( hInst,
                     IDS_APP_NAME,
                     spv[ivalabcPR_DISPLAY_NAME].Value.lpszA,
                     MAX_DISPLAY_NAME
                    );
    }


    spv[ivalabcPR_CONTAINER_FLAGS].ulPropTag = PR_CONTAINER_FLAGS;
    spv[ivalabcPR_CONTAINER_FLAGS].Value.l   = AB_RECIPIENTS;

    /*
     *  Get the current .FAB file name from our logon object
     */
    hResult = HrLpszGetCurrentFileName(lpABLogon, &lpszFileName);
    if (HR_FAILED(hResult))
    {
        goto err;
    }

    // MAPI ==>  NO UNICODE FOR YOU!  <MAPI Doesn't like UNICODE>

#ifdef UNICODE
    spv[ivalabcPR_FAB_FILE].ulPropTag          = PR_FAB_FILE_A;
    szAnsiFileName[0] = 0;
    WideCharToMultiByte( CP_ACP, 0, lpszFileName, -1, szAnsiFileName, ARRAYSIZE(szAnsiFileName), NULL, NULL );
    spv[ivalabcPR_FAB_FILE].Value.lpszA        = szAnsiFileName;

    spv[ivalabcPR_FAB_FILE_TEMP].ulPropTag     = PR_FAB_FILE_TEMP_A;
    spv[ivalabcPR_FAB_FILE_TEMP].Value.lpszA   = szAnsiFileName;

    spv[ivalabcPR_IMPORT_FROM_FILE].ulPropTag   = PR_IMPORT_FROM_FILE_A;
    spv[ivalabcPR_IMPORT_FROM_FILE].Value.lpszA = "";

    spv[ivalabcPR_IMPORT_TO_FILE].ulPropTag    = PR_IMPORT_TO_FILE_A;
    spv[ivalabcPR_IMPORT_TO_FILE].Value.lpszA  = szAnsiFileName;
#else
    spv[ivalabcPR_FAB_FILE].ulPropTag          = PR_FAB_FILE_A;
    spv[ivalabcPR_FAB_FILE].Value.lpszA        = (LPSTR)lpszFileName;

    spv[ivalabcPR_FAB_FILE_TEMP].ulPropTag     = PR_FAB_FILE_TEMP_A;
    spv[ivalabcPR_FAB_FILE_TEMP].Value.lpszA   = (LPSTR)lpszFileName;

    spv[ivalabcPR_IMPORT_FROM_FILE].ulPropTag   = PR_IMPORT_FROM_FILE_A;
    spv[ivalabcPR_IMPORT_FROM_FILE].Value.lpszA = "";

    spv[ivalabcPR_IMPORT_TO_FILE].ulPropTag    = PR_IMPORT_TO_FILE_A;
    spv[ivalabcPR_IMPORT_TO_FILE].Value.lpszA  = (LPSTR)lpszFileName;
#endif

    spv[ivalabcPR_IMPORT_FORMAT].ulPropTag     = PR_IMPORT_FORMAT_A;
    spv[ivalabcPR_IMPORT_FORMAT].Value.lpszA   = "Your Favorite PIM";


    /*
     *   Set the default properties
     */
    hResult = lpPropData->lpVtbl->SetProps( lpPropData,
                                            cvalabcMax,
                                            spv,
                                            NULL
                                           );

    /*
     *  No longer need this buffer
     */
    lpFreeBuff(lpszFileName);

    if (HR_FAILED(hResult))
    {
        DebugTraceSc( HrNewFaxDirectory, sc );
        goto err;
    }

    lpPropData->lpVtbl->HrSetPropAccess( lpPropData,
                                         (LPSPropTagArray) &ABC_SPT_TMP_FAB,
                                         &ulPropAccess
                                        );

    InitializeCriticalSection( &lpABC->cs );

    lpABC->lpPropData = (LPMAPIPROP)lpPropData;
    *lppABC           = (LPABCONT)lpABC;
    *lpulObjType      = MAPI_ABCONT;

out:

    DebugTraceResult(HrNewFaxDirectory, hResult);
    return hResult;

err:
    /*
     *  free the ABContainer object
     */
    lpFreeBuff( lpABC );

    /*
     *  free the property storage object
     */
    if (lpPropData)
        lpPropData->lpVtbl->Release(lpPropData);

    goto out;
}

/***************************************************
 *
 *  The actual ABContainer methods
 */

/* --------
 * IUnknown
 */
/*************************************************************************
 *
 *
 -  ABC_QueryInterface
 -
 *
 *      Uses the ROOT method
 *
 */

/*************************************************************************
 *
 -  ABC_AddRef
 -
 *
 *      Uses the ROOT method
 *
 */

/**************************************************
 *
 -  ABC_Release
 -
 *      Decrement lpInit.
 *              When lcInit == 0, free up the lpABC structure
 *
 */

STDMETHODIMP_(ULONG)
ABC_Release(LPABCNT lpABC)
{

    long lcInit;

    /*
     *  Check to see if it has a jump table
     */
    if (IsBadReadPtr(lpABC, SIZEOF(ABCNT)))
    {
        /*
         *  No jump table found
         */
        return 1;
    }

    /*
     *  Check to see that it's the correct jump table
     */
    if (lpABC->lpVtbl != &vtblABC)
    {
        /*
         *  Not my jump table
         */
        return 1;
    }

    EnterCriticalSection(&lpABC->cs);
    lcInit = --lpABC->lcInit;
    LeaveCriticalSection(&lpABC->cs);

    if (lcInit == 0)
    {

        /*
         *  Get rid of the lpPropData
         */
        if (lpABC->lpPropData)
            lpABC->lpPropData->lpVtbl->Release(lpABC->lpPropData);

        /*
         *  Get rid of the details table
         */
        if (lpABC->lpTDatDetails)
            lpABC->lpTDatDetails->lpVtbl->Release(lpABC->lpTDatDetails);

        /*
         *  Destroy the critical section for this object
         */

        DeleteCriticalSection(&lpABC->cs);

        /*
         *  Set the Jump table to NULL.  This way the client will find out
         *  real fast if it's calling a method on a released object.  That is,
         *  the client will crash.  Hopefully, this will happen during the
         *  development stage of the client.
         */
        lpABC->lpVtbl = NULL;

        /*
         *  Need to free the object
         */

        lpABC->lpFreeBuff(lpABC);
        return 0;
    }

    return lpABC->lcInit;

}

/* ---------
 * IMAPIProp
 */

/*
 *  GetLastError - use ROOT's
 *
 *
 */

/*************************************************************************
 *
 -  ABC_SaveChanges
 -
 *  This is used to save changes associated with the search dialog
 *  in order to get the advanced search restriction and to save changes
 *  associated with the container details dialog.
 *
 */
SPropTagArray SPT_FAB_FILE =
{
    1,
    {
        PR_FAB_FILE_TEMP_A
    }
};

STDMETHODIMP
ABC_SaveChanges (LPABCNT lpABC, ULONG ulFlags)
{

    HRESULT         hResult    = hrSuccess;
    ULONG           ulCount;
    LPSPropValue    lpspv      = NULL;
    ULONG FAR *     rgulAccess = NULL;
    ULONG           ulAccess   = IPROP_CLEAN|IPROP_READWRITE; /* READWRITE is redundant */
    LPSPropTagArray lpspt      = (LPSPropTagArray) &ABC_SPT_TMP_FAB;
    LPPROPDATA      lpPropData = (LPPROPDATA) lpABC->lpPropData;
#ifdef UNICODE
    WCHAR           szFileName[ MAX_PATH ];
#endif

    /*
     *  Check to see if it has a jump table
     */
    if (IsBadReadPtr(lpABC, sizeof(ABCNT)))
    {
        /*
         *  No jump table found
         */
        hResult = ResultFromScode(E_INVALIDARG);
        return hResult;
    }

    /*
     *  Check to see that it's the correct jump table
     */
    if (lpABC->lpVtbl != &vtblABC)
    {
        /*
         *  Not my jump table
         */
        hResult = ResultFromScode(E_INVALIDARG);
        return hResult;
    }

    EnterCriticalSection(&lpABC->cs);

    /*
     *  Check to see if anyone has dirtied the PR_FAB_FILE_TEMP
     */
    lpPropData->lpVtbl->HrGetPropAccess( lpPropData, (LPSPropTagArray *) &lpspt, &rgulAccess);

    if (!rgulAccess || !((*rgulAccess) & IPROP_DIRTY))
    {
        /*
         *  No, nothing to update then head on out
         */

        goto ret;
    }

    /*
     *  Go ahead and set back to being clean
     */
    (void )lpPropData->lpVtbl->HrSetPropAccess( lpPropData, (LPSPropTagArray) lpspt, &ulAccess);

    /*
     *  Get the temporary fab file name
     */
    hResult = lpPropData->lpVtbl->GetProps(
                  lpPropData,
                  &SPT_FAB_FILE,
                  0,      // ansi
                  &ulCount,
                  &lpspv);

    if (HR_FAILED(hResult))
    {
        goto ret;
    }


    if (lpspv->ulPropTag != PR_FAB_FILE_TEMP_A)
    {
        /*
         *  There's no reason this property shouldn't be there.
         */
        hResult = ResultFromScode(MAPI_E_CORRUPT_DATA);
        goto ret;
    }

    /*
     *  Save the new name back into the object as PR_FAB_FILE
     */
    lpspv->ulPropTag = PR_FAB_FILE_A;

    hResult = lpPropData->lpVtbl->SetProps(
                  lpPropData,
                  1,            // ANSI
                  lpspv, NULL);

    if (HR_FAILED(hResult))
    {
        /*
         *  Do nothing...  So I couldn't save it away this time...
         */
        hResult = hrSuccess;
        goto ret;
    }

    /*
     *  Update every other object that needs this new information
     */
#ifdef UNICODE
    szFileName[0] = 0;
    MultiByteToWideChar( CP_ACP, 0, lpspv->Value.lpszA, -1, szFileName, ARRAYSIZE(szFileName) );
    hResult = HrReplaceCurrentFileName(lpABC->lpABLogon, szFileName);
#else
    hResult = HrReplaceCurrentFileName(lpABC->lpABLogon, lpspv->Value.LPSZ);
#endif


ret:

    LeaveCriticalSection(&lpABC->cs);

    lpABC->lpFreeBuff(lpspv);
    lpABC->lpFreeBuff(rgulAccess);
    DebugTraceResult(ABC_SaveChanges, hResult);
    return hResult;
}


/*************************************************************************
 *
 -  ABC_OpenProperty
 -
 *  This method allows the opening of the following object:
 *
 *  PR_BUTTON_PRESS  :-  Gets the MAPIControl object associated
 *                       with the button on this container's details.
 *  PR_DETAILS_TABLE :-  Gets the display table associated with
 *                       the details for this container.
 *  PR_SEARCH        :-  Gets the advanced search object associated with
 *                       this container.
 *  PR_CONTAINER_CONTENTS  :-  Same as GetContentsTable()
 *  PR_CONTAINER_HIERARCHY :-  Same as GetHierarchyTable()
 *
 */
STDMETHODIMP
ABC_OpenProperty( LPABCNT lpABC,
                  ULONG ulPropTag,
                  LPCIID lpiid,
                  ULONG ulInterfaceOptions,
                  ULONG ulFlags,
                  LPUNKNOWN * lppUnk
                 )
{

    HRESULT hResult;


    /*
     *  Check to see if it has a jump table
     */
    if (IsBadReadPtr(lpABC, SIZEOF(ABCNT)))
    {
        /*
         *  No jump table found
         */
        hResult = ResultFromScode( E_INVALIDARG );

        DebugTraceResult( ABC_OpenProperty, hResult );
        goto out;
    }

    /*
     *  Check to see that it's the correct jump table
     */
    if (lpABC->lpVtbl != &vtblABC)
    {
        /*
         *  Not my jump table
         */
        hResult = ResultFromScode( E_INVALIDARG );

        DebugTraceResult(ABC_OpenProperty, hResult);
        goto out;
    }

    if (IsBadWritePtr(lppUnk, sizeof(LPUNKNOWN)))
    {
        /*
         *  Got to be able to return an object
         */
        hResult = ResultFromScode(E_INVALIDARG);
        goto out;
    }

    if (IsBadReadPtr(lpiid, (UINT) SIZEOF(IID)))
    {
        /*
         *  An interface ID is required for this call.
         */

        hResult = ResultFromScode(E_INVALIDARG);
        goto out;
    }

    /*
     *      check for unknown flags
     */
    if (ulFlags & ~(MAPI_DEFERRED_ERRORS | MAPI_CREATE | MAPI_MODIFY))
    {
        hResult = ResultFromScode (MAPI_E_UNKNOWN_FLAGS);

        DebugTraceResult(ABC_OpenProperty, hResult);
        goto out;
    }

    /*
     *  Check for flags we can't support
     */

    if (ulFlags & (MAPI_CREATE|MAPI_MODIFY))
    {
        hResult = ResultFromScode (E_ACCESSDENIED);

        DebugTraceResult(ABC_OpenProperty, hResult);
        goto out;
    }


    if (ulInterfaceOptions & ~MAPI_UNICODE)
    {
        /*
         *  Only UNICODE flag should be set for any of the objects that might
         *  be returned from this object.
         */

        hResult = ResultFromScode(MAPI_E_UNKNOWN_FLAGS);
        goto out;
    }

    if ( ulInterfaceOptions & MAPI_UNICODE )
    {
        hResult = ResultFromScode(MAPI_E_BAD_CHARWIDTH);
        DebugTraceArg( ABC_OpenProperty, "bad character width" );
        goto out;

    }

    /*
     *  Details for this directory entry
     */

    if ( (ulPropTag == PR_DETAILS_TABLE)      ||
         (ulPropTag == PR_BUTTON_PRESS)       ||
         (ulPropTag == PR_CONTAINER_CONTENTS) ||
         (ulPropTag == PR_CONTAINER_HIERARCHY)||
         (ulPropTag == PR_SEARCH)
        )
    {

        /*
         *  Check to see if they're expecting a table interface
         */
        if ( (ulPropTag != PR_BUTTON_PRESS) &&
             (ulPropTag != PR_SEARCH) &&
             memcmp(lpiid, &IID_IMAPITable, sizeof(IID))
            )
        {
            hResult = ResultFromScode(MAPI_E_INTERFACE_NOT_SUPPORTED);
            goto out;
        }

        switch (ulPropTag)
        {

        case PR_BUTTON_PRESS:
        {
            /*
             *  Check to see if they're expecting a generic control interface
             */
            if (memcmp( lpiid, &IID_IMAPIControl, SIZEOF(IID) ))
            {
                hResult = ResultFromScode(MAPI_E_INTERFACE_NOT_SUPPORTED);
                goto out;
            }

            hResult = HrNewABCButton( lpABC,
                                      ulInterfaceOptions,
                                      ulFlags,
                                      (LPMAPICONTROL FAR *) lppUnk
                                     );

            break;
        }


        case PR_DETAILS_TABLE:
        {
            hResult = HrGetDetailsDialog(lpABC, (LPMAPITABLE *) lppUnk);
            break;
        }

        case PR_SEARCH:
        {
            /*
             *  Check to see if they're expecting a generic control interface
             */
            if (memcmp(lpiid, &IID_IMAPIContainer, SIZEOF(IID)))
            {
                hResult = ResultFromScode(MAPI_E_INTERFACE_NOT_SUPPORTED);
                goto out;
            }

            hResult = HrNewSearch( (LPMAPICONTAINER *) lppUnk,
                                   lpABC->lpABLogon,
                                   lpiid,
                                   lpABC->hLibrary,
                                   lpABC->lpAllocBuff,
                                   lpABC->lpAllocMore,
                                   lpABC->lpFreeBuff,
                                   lpABC->lpMalloc
                                  );
            break;
        }

        case PR_CONTAINER_CONTENTS:
        {
            hResult = ABC_GetContentsTable( lpABC, 0, (LPMAPITABLE *) lppUnk);
            break;
        }

        case PR_CONTAINER_HIERARCHY:
        {
            hResult = ABC_GetHierarchyTable( lpABC, 0, (LPMAPITABLE *) lppUnk);
            break;
        }

        default:
            break;
        }
    }
    else
    {
        hResult = ResultFromScode (MAPI_E_NO_SUPPORT);
    }

out:

    DebugTraceResult(ABC_OpenProperty, hResult);
    return hResult;
}



/*************************************************************************
 *
 -  ABC_GetContentsTable
 -
 *
 *  Retrieves the IMAPITable that has the contents of this container.
 *  The big one!
 */
STDMETHODIMP
ABC_GetContentsTable( LPABCNT lpABC,
                      ULONG ulFlags,
                      LPMAPITABLE * lppTable
                     )
{

    HRESULT hResult;

    /*
     *  Validate parameters
     */


    /*
     *  Check to see if it's large enough to be this object
     */
    if (IsBadReadPtr(lpABC, SIZEOF(ABCNT)))
    {
        /*
         *  Not large enough to be this object
         */
        hResult = ResultFromScode(E_INVALIDARG);
        goto out;
    }

    /*
     *  Check to see that it's the correct jump table
     */
    if (lpABC->lpVtbl != &vtblABC)
    {
        /*
         *  Not my jump table
         */
        hResult = ResultFromScode (E_INVALIDARG);
        goto out;
    }

    /*
     *  Check lppTable to validate its writability
     */
    if (IsBadWritePtr (lppTable, SIZEOF(LPMAPITABLE)))
    {
        hResult = ResultFromScode (E_INVALIDARG);
        goto out;
    }

    /*
     *  Check flags
     */
    if (ulFlags & ~(MAPI_UNICODE|MAPI_DEFERRED_ERRORS|MAPI_ASSOCIATED))
    {
        hResult = ResultFromScode(MAPI_E_UNKNOWN_FLAGS);
        goto out;
    }

    /*
     *  Certain flags are not supported
     */
    if (ulFlags & (MAPI_ASSOCIATED))
    {
        hResult = ResultFromScode(MAPI_E_NO_SUPPORT);
        goto out;
    }

    if ( ulFlags & MAPI_UNICODE )
    {
        DebugTraceArg( ABC_GetContentsTable, "Bad character width" );
        hResult = ResultFromScode( MAPI_E_BAD_CHARWIDTH );
        goto out;
    }

    /*
     *  Create the new Contents table
     */
    hResult = HrNewIVTAbc( lppTable,
                           lpABC->lpABLogon,
                           (LPABCONT) lpABC,
                           lpABC->hLibrary,
                           lpABC->lpAllocBuff,
                           lpABC->lpAllocMore,
                           lpABC->lpFreeBuff,
                           lpABC->lpMalloc
                          );

out:

    DebugTraceResult(ABC_GetContentsTable, hResult);
    return hResult;
}

/*************************************************************************
 *
 -  ABC_GetHierarchyTable
 -
 *
 *  There is no hierarchy table associated with this object.
 *
 */
STDMETHODIMP
ABC_GetHierarchyTable( LPABCNT lpABC,
                       ULONG ulFlags,
                       LPMAPITABLE * lppTable
                      )
{
    HRESULT hResult;
    /*
     *  Check to see if it has a lpVtbl object member
     */
    if (IsBadReadPtr(lpABC, offsetof(ABCNT, lpVtbl)+SIZEOF(ABC_Vtbl *)))
    {
        /*
         *  Not large enough
         */
        hResult = MakeResult(E_INVALIDARG);
        DebugTraceResult(ABC_HierarchyTable, hResult);
        return hResult;
    }

    /*
     *  Check to see that the Vtbl is large enough to include this method
     */
    if (IsBadReadPtr(lpABC->lpVtbl,
        offsetof(ABC_Vtbl, GetHierarchyTable)+SIZEOF(ABC_GetHierarchyTable_METHOD *)))
    {
        /*
         *  Jump table not derived from IUnknown
         */

        hResult = MakeResult(E_INVALIDARG);
        DebugTraceResult(ABC_HierarchyTable, hResult);
        return hResult;
    }

    /*
     *  Check to see if the method is the same
     */
    if (ABC_GetHierarchyTable != lpABC->lpVtbl->GetHierarchyTable)
    {
        /*
         *  Wrong object - the object passed doesn't have this
         *  method.
         */
        hResult = ResultFromScode(E_INVALIDARG);
        DebugTraceResult(ABC_HierarchyTable, hResult);
        return hResult;
    }

    /*
     *  See if I can set the return variable
     */
    if (IsBadWritePtr(lppTable, SIZEOF(LPMAPITABLE)))
    {
        hResult = ResultFromScode(E_INVALIDARG);
        goto out;
    }

    /*
     *  Check flags:
     *    The only valid flags are CONVENIENT_DEPTH and MAPI_DEFERRED_ERRORS
     */

    if (ulFlags & ~(CONVENIENT_DEPTH | MAPI_DEFERRED_ERRORS))
    {
        hResult = ResultFromScode(MAPI_E_UNKNOWN_FLAGS);
        goto out;
    }

    /*
     *  We don't support this method on this object
     */
    hResult = ResultFromScode(MAPI_E_NO_SUPPORT);

out:


    DebugTraceResult(ABC_GetHierarchyTable, hResult);
    return hResult;
}

/*
 *  OpenEntry() <- uses ROOT's
 */

/*
 *  SetSearchCriteria() <- uses ROOT's
 */

/*
 *  GetSearchCriteria() <- uses ROOT's
 */

/*
 *  ABC_CreateEntry() <- uses ROOT's
 */

/*
 *  ABC_CopyEntries() <- uses ROOT's
 */

/*
 *  ABC_DeleteEntries() <- uses ROOT's
 */

/**********************************************************************
 *
 *  Private functions
 */

/*
 -  HrGetDetailsDialog
 -
 *  Builds a display table for this directory.
 */

HRESULT
HrGetDetailsDialog(LPABCNT lpABC, LPMAPITABLE * lppDetailsTable)
{
    HRESULT hResult;

        /* Create a display table */
    if (!lpABC->lpTDatDetails)
    {

        /* Create a display table */
        hResult = BuildDisplayTable(
            lpABC->lpAllocBuff,
            lpABC->lpAllocMore,
            lpABC->lpFreeBuff,
            lpABC->lpMalloc,
            lpABC->hLibrary,
            SIZEOF(rgdtpageDir) / SIZEOF(DTPAGE),
            rgdtpageDir,
            0,
            lppDetailsTable,
            &lpABC->lpTDatDetails);
    }
    else
    {
        hResult = lpABC->lpTDatDetails->lpVtbl->HrGetView(
            lpABC->lpTDatDetails,
            NULL,
            NULL,
            0,
            lppDetailsTable);
    }

    DebugTraceResult(HrGetDetailsDialog, hResult);
    return hResult;
}


/*
 *  Button object for this directory's details dialog
 */

ABCBUTT_Vtbl vtblABCBUTT =
{
    ABCBUTT_QueryInterface,
    (ABCBUTT_AddRef_METHOD *) ROOT_AddRef,
    ABCBUTT_Release,
    (ABCBUTT_GetLastError_METHOD *) ROOT_GetLastError,
    ABCBUTT_Activate,
    ABCBUTT_GetState
};


HRESULT
HrNewABCButton( LPABCNT lpABC,
                ULONG ulInterfaceOptions,
                ULONG ulFlags,
                LPMAPICONTROL FAR * lppMAPICont
               )
{
    LPABCBUTT lpABCButt = NULL;
    SCODE scode;

    /*
     *  Creates a the object behind the button control in the directory
     *  details dialog...
     */

    scode = lpABC->lpAllocBuff(SIZEOF(ABCBUTT),(LPVOID *) &lpABCButt);

    if (FAILED(scode))
    {
        DebugTraceSc(HrNewABCButton, scode);
        return ResultFromScode(scode);
    }

    lpABCButt->lpVtbl = &vtblABCBUTT;
    lpABCButt->lcInit = 1;
    lpABCButt->hResult = hrSuccess;
    lpABCButt->idsLastError = 0;

    lpABCButt->hLibrary    = lpABC->hLibrary;
    lpABCButt->lpAllocBuff = lpABC->lpAllocBuff;
    lpABCButt->lpAllocMore = lpABC->lpAllocMore;
    lpABCButt->lpFreeBuff  = lpABC->lpFreeBuff;
    lpABCButt->lpMalloc    = lpABC->lpMalloc;
    lpABCButt->lpABC       = lpABC;

    lpABC->lpVtbl->AddRef(lpABC);

    InitializeCriticalSection(&lpABCButt->cs);

    *lppMAPICont = (LPMAPICONTROL) lpABCButt;

    return hrSuccess;
}

/*************************************************************************
 *
 *
 -  ABCBUTT_QueryInterface
 -
 *
 *  Allows QI'ing to IUnknown and IMAPIControl.
 *
 *
 */
STDMETHODIMP
ABCBUTT_QueryInterface( LPABCBUTT lpABCButt,
                        REFIID lpiid,
                        LPVOID FAR * lppNewObj
                       )
{

    HRESULT hResult = hrSuccess;

    /*      Minimally validate the lpABCButt parameter */

    if (IsBadReadPtr(lpABCButt, SIZEOF(ABCBUTT)))
    {
        hResult = ResultFromScode(E_INVALIDARG);
        goto out;
    }

    if (lpABCButt->lpVtbl != &vtblABCBUTT)
    {
        hResult = ResultFromScode(E_INVALIDARG);
        goto out;
    }

    /*  Check the other parameters */

    if (!lpiid || IsBadReadPtr(lpiid, (UINT) SIZEOF(IID)))
    {
        hResult = ResultFromScode(E_INVALIDARG);
        goto out;
    }

    if (IsBadWritePtr(lppNewObj, (UINT) SIZEOF(LPVOID)))
    {
        hResult = ResultFromScode(E_INVALIDARG);
        goto out;
    }

    /*      See if the requested interface is one of ours */

    if ( memcmp (lpiid, &IID_IUnknown,     SIZEOF(IID)) &&
         memcmp (lpiid, &IID_IMAPIControl, SIZEOF(IID))
        )
    {
        *lppNewObj = NULL;      /* OLE requires zeroing [out] parameter */
        hResult = ResultFromScode(E_NOINTERFACE);
        goto out;
    }

        /*      We'll do this one. Bump the usage count and return a new pointer. */

    EnterCriticalSection(&lpABCButt->cs);
    ++lpABCButt->lcInit;
    LeaveCriticalSection(&lpABCButt->cs);

    *lppNewObj = lpABCButt;

out:

    DebugTraceResult(ABCBUTT_QueryInterface, hResult);
    return hResult;
}


/*
 -  ABCBUTT_Release
 -
 *  Releases and cleans up this object
 */
STDMETHODIMP_(ULONG)
ABCBUTT_Release(LPABCBUTT lpABCButt)
{
    long lcInit;

    /*  Minimally validate the lpABCButt parameter */

    if (IsBadReadPtr(lpABCButt, SIZEOF(ABCBUTT)))
    {
        return 1;
    }

    if (lpABCButt->lpVtbl != &vtblABCBUTT)
    {
        return 1;
    }

    EnterCriticalSection(&lpABCButt->cs);
    lcInit = --lpABCButt->lcInit;
    LeaveCriticalSection(&lpABCButt->cs);

    if (lcInit == 0)
    {

        /*
         *  Release my parent
         */
        lpABCButt->lpABC->lpVtbl->Release(lpABCButt->lpABC);

        /*
         *  Destroy the critical section for this object
         */

        DeleteCriticalSection(&lpABCButt->cs);

        /*
         *  Set the Jump table to NULL.  This way the client will find out
         *  real fast if it's calling a method on a released object.  That is,
         *  the client will crash.  Hopefully, this will happen during the
         *  development stage of the client.
         */
        lpABCButt->lpVtbl = NULL;

        /*
         *  Free the object
         */

        lpABCButt->lpFreeBuff(lpABCButt);
        return 0;
    }

    return lcInit;

}

/*
 -  ABCBUTT_Activate
 -
 *
 *  Activates this control.  In this case, it brings up the common file browsing
 *  dialog and allows the user to pick a different .SAB file.
 *
 *  Note that if all is successful it sends a display table notification.  The UI
 *  will respond to this by updating the particular control that was said to have
 *  changed in the notification.
 */
STDMETHODIMP
ABCBUTT_Activate( LPABCBUTT lpABCButt,
                  ULONG     ulFlags,
                  ULONG     ulUIParam
                 )
{
    HRESULT         hResult = hrSuccess;
    OPENFILENAME    openfilename;
    TCHAR           szFileName[MAX_PATH];
    TCHAR           szDirName[MAX_PATH];
    SPropValue      sProp;
    LPSPropValue    lpspv = NULL;
    ULONG           ulCount,ich;
#ifdef UNICODE
    CHAR            szAnsiFileName[ MAX_PATH ];
#endif

    /*  Minimally validate the lpABCButt parameter */

    if (IsBadReadPtr(lpABCButt, SIZEOF(ABCBUTT)))
    {
        hResult = ResultFromScode(E_INVALIDARG);
        goto out;
    }

    if (lpABCButt->lpVtbl != &vtblABCBUTT)
    {
        hResult = ResultFromScode(E_INVALIDARG);
        goto out;
    }


    if (ulFlags)
    {
        /*
         *  No flags defined for this method
         */
        hResult = ResultFromScode(MAPI_E_UNKNOWN_FLAGS);
        goto out;
    }


    /*
     *  First, get the old FAB file name so that it shows up in the
     *  choose file dialog
     */

    hResult = lpABCButt->lpABC->lpPropData->lpVtbl->GetProps(
                  lpABCButt->lpABC->lpPropData,
                  &SPT_FAB_FILE,
                  0,              /* ansi */
                  &ulCount,
                  &lpspv);

    if (HR_FAILED(hResult))
    {
        goto out;
    }

    if (lpspv->ulPropTag != PR_FAB_FILE_TEMP_A)
    {
        /*
         *  Property wasn't there...
         */
        hResult = ResultFromScode(MAPI_E_CORRUPT_DATA);

        goto out;
    }

#ifdef UNICODE
    szFileName[0] = 0;
    MultiByteToWideChar( CP_ACP, 0, lpspv->Value.lpszA, -1, szFileName, ARRAYSIZE(szFileName) );
#else
    lstrcpy(szFileName, lpspv->Value.lpszA);
#endif

    szDirName[0] = TEXT('\0');


    /* get the path name */
    // MAPI --> NO UNICODE FOR YOU!  (These must be ANSI)
    for (ich = lstrlenA(lpspv->Value.lpszA) - 1; ich >= 0; ich--)
    {
        if (lpspv->Value.lpszA[ich] == '\\')
        {
            lpspv->Value.lpszA[ich] = '\0';
            break;
        }
        else if (lpspv->Value.lpszA[ich] == ':')
        {
            lpspv->Value.lpszA[ich + 1] = '\0';
            break;
        }
    }

#ifdef UNICODE
    szDirName[0] = 0;
    MultiByteToWideChar( CP_ACP, 0, lpspv->Value.lpszA, -1, szDirName, ARRAYSIZE(szDirName) );
#else
    lstrcpy(szDirName, lpspv->Value.lpszA);
#endif

    /*
     *  Get the user to select one
     */
    openfilename.lStructSize       = SIZEOF(OPENFILENAME);
    openfilename.hwndOwner         = (HWND) ulUIParam;
    openfilename.hInstance         = 0;     /* Ignored */
    openfilename.lpstrFilter       = TEXT("Microsoft Fax Address Book files\0*.fab\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   = szDirName;
    openfilename.lpstrTitle        = TEXT("Microsoft Fax Address Book");
    openfilename.Flags             = OFN_FILEMUSTEXIST |
                                     OFN_HIDEREADONLY  |
                                     OFN_NOCHANGEDIR;
    openfilename.nFileOffset       = 0;
    openfilename.nFileExtension    = 0;
    openfilename.lpstrDefExt       = TEXT("fab");
    openfilename.lCustData         = 0;
    openfilename.lpfnHook          = NULL;
    openfilename.lpTemplateName    = NULL;

    /*
     *  Call up the common dialog
     */
    if (!GetOpenFileName( &openfilename ))
    {
        hResult = hrSuccess;
        goto out;
    }

    /*
     *      Save FAB FileName into the container object
     */

    sProp.ulPropTag  = PR_FAB_FILE_TEMP_A;
#ifdef UNICODE
    szAnsiFileName[0] = 0;
    WideCharToMultiByte( CP_ACP, 0, szFileName, -1, szAnsiFileName, ARRAYSIZE(szAnsiFileName), NULL, NULL );
    sProp.Value.lpszA = szAnsiFileName;
#else
    sProp.Value.lpszA = szFileName;
#endif

    hResult = lpABCButt->lpABC->lpPropData->lpVtbl->SetProps(
                                    lpABCButt->lpABC->lpPropData,
                                    1,      // ansi
                                    &sProp,
                                    NULL);
    if (HR_FAILED(hResult))
    {
        goto out;
    }


    /*
     *      Notify the details table so that everyone with a view open
     *  will get notified
     */
    if (lpABCButt->lpABC->lpTDatDetails)
    {
            sProp.ulPropTag         = PR_CONTROL_ID;
            sProp.Value.bin.lpb     = (LPBYTE)&notifdata;
            sProp.Value.bin.cb      = SIZEOF(NOTIFDATA);

        hResult = lpABCButt->lpABC->lpTDatDetails->lpVtbl->HrNotify(
                          lpABCButt->lpABC->lpTDatDetails,
                          0,
                          1,
                          &sProp);
    }

out:
    lpABCButt->lpFreeBuff(lpspv);
    DebugTraceResult(ABCBUTT_Activate, hResult);
    return hResult;
}

/*
 -  ABCBUTT_GetState
 -
 *  Says whether this control should appear enabled or not at this time.
 *
 */
STDMETHODIMP
ABCBUTT_GetState( LPABCBUTT     lpABCButt,
                  ULONG         ulFlags,
                  ULONG *       lpulState
                 )
{
    HRESULT hResult = hrSuccess;

    /*  Minimally validate the lpABCButt parameter */

    if (IsBadReadPtr(lpABCButt, SIZEOF(ABCBUTT)))
    {
        hResult = ResultFromScode(E_INVALIDARG);
        goto out;
    }

    if (lpABCButt->lpVtbl != &vtblABCBUTT)
    {
        hResult = ResultFromScode(E_INVALIDARG);
        goto out;
    }

    if (IsBadWritePtr(lpulState, SIZEOF(ULONG)))
    {
        hResult = ResultFromScode(E_INVALIDARG);
        goto out;
    }

    if (ulFlags)
    {
        /*
         *  No flags defined for this method
         */
        hResult = ResultFromScode(MAPI_E_UNKNOWN_FLAGS);
        goto out;
    }

    /*
     *  Means that at this time this button should appear enabled.
     */
    *lpulState = MAPI_ENABLED;

out:
    DebugTraceResult(ABCBUTT_GetState, hResult);
    return hResult;
}

#undef _FAXAB_ABCONT