// IForms.h : Declaration of the CIntelliForms class

#ifndef __IFORMS_H_
#define __IFORMS_H_

#include "iforms.h"

const TCHAR c_szRegKeySMIEM[] = TEXT("Software\\Microsoft\\Internet Explorer\\Main");
const TCHAR c_szRegKeyIntelliForms[] = TEXT("Software\\Microsoft\\Internet Explorer\\IntelliForms");
const WCHAR c_wszRegKeyIntelliFormsSPW[] = TEXT("Software\\Microsoft\\Internet Explorer\\IntelliForms\\SPW");
const TCHAR c_szRegKeyRestrict[] = TEXT("Software\\Policies\\Microsoft\\Internet Explorer\\Control Panel");

const TCHAR c_szRegValUseFormSuggest[] = TEXT("Use FormSuggest");
const TCHAR c_szRegValFormSuggestRestrict[] = TEXT("FormSuggest");
const TCHAR c_szRegValSavePasswords[] = TEXT("FormSuggest Passwords");
const TCHAR c_szRegValAskPasswords[] = TEXT("FormSuggest PW Ask");
const TCHAR c_szRegValAskUser[] = TEXT("AskUser");

interface IAutoComplete2;
interface IAutoCompleteDropDown;
class CStringList;

#define IF_CHAR             WM_APP  + 0x08
#define IF_KEYDOWN          WM_APP  + 0x09
#define IF_IME_COMPOSITION  WM_APP  + 0x0A

/////////////////////////////////////////////////////////////////////////////
// CIntelliForms
class CEventSinkCallback
{
public:
    typedef enum
    {
        EVENT_BOGUS     = 100,
        EVENT_KEYDOWN   = 0,
        EVENT_KEYPRESS,
        EVENT_MOUSEDOWN,
        EVENT_DBLCLICK,
        EVENT_FOCUS,
        EVENT_BLUR,
        EVENT_SUBMIT,
        EVENT_SCROLL,

        EVENT_COMPOSITION,
        EVENT_NOTIFY,
    }
    EVENTS;

    typedef struct
    {
        EVENTS                      Event;
        LPCWSTR                     pwszEventSubscribe;
        LPCWSTR                     pwszEventName;
    }
    EventSinkEntry;

    virtual HRESULT HandleEvent(IHTMLElement *pEle, EVENTS Event, IHTMLEventObj *pEventObj) = 0;

    static EventSinkEntry EventsToSink[];
};

class CEditEventSinkCallback
{
public:
    virtual HRESULT PreHandleEvent(DISPID inEvtDispId, IHTMLEventObj* pIEventObj) = 0;
};

class CIntelliForms : 
    public CEventSinkCallback,
    public CEditEventSinkCallback
{
    long    m_cRef;

public:
    class CEventSink;
    class CEditEventSink;
    class CAutoSuggest;
    friend CAutoSuggest;

    CIntelliForms();
    ~CIntelliForms();

public:
    // IUnknown
    STDMETHODIMP QueryInterface(REFIID, void **);
    STDMETHODIMP_(ULONG) AddRef(void);
    STDMETHODIMP_(ULONG) Release(void);


    // CEventSinkCallback
    HRESULT HandleEvent(IHTMLElement *pEle, EVENTS Event, IHTMLEventObj *pEventObj);

    // CEditEventSinkCallback
    HRESULT PreHandleEvent(DISPID inEvtDispId, IHTMLEventObj* pIEventObj);

public:
    HRESULT Init(CIEFrameAuto::COmWindow *pOmWindow, IHTMLDocument2 *pDoc2, HWND hwnd);
    HRESULT UnInit();

    LPCWSTR GetUrl();

    HRESULT UserInput(IHTMLInputTextElement *pTextEle);

    HRESULT WriteToStore(LPCWSTR pwszName, CStringList *psl);
    HRESULT ReadFromStore(LPCWSTR pwszName, CStringList **ppsl, BOOL fPasswordList=FALSE);
    HRESULT DeleteFromStore(LPCWSTR pwszName);
    HRESULT ClearStore(DWORD dwClear);

    BOOL    IsRestricted() { return m_fRestricted; }
    BOOL    IsRestrictedPW() { return m_fRestrictedPW; }

    IUnknown *GetDocument() { return m_punkDoc2; }

    HRESULT ScriptSubmit(IHTMLFormElement *pForm);
    HRESULT HandleFormSubmit(IHTMLFormElement *pForm);

    // for CEnumString
    HRESULT GetPasswordStringList(CStringList **ppslPasswords);
    // for IntelliFormsSaveForm
    CIntelliForms *GetNext() { return m_pNext; }

    BOOL    IsEnabledForPage();

    static HRESULT GetName(IHTMLInputTextElement *pTextEle, BSTR *pbstrName);

    // Default to disabled, since we need to ask the user before enabling it
    static BOOL    IsEnabledInCPL() {
                        return IsEnabledInRegistry(c_szRegKeySMIEM, c_szRegValUseFormSuggest, FALSE); }
    // Default to enabled, since we prompt before saving passwords anyway
    static BOOL    IsEnabledRestorePW() {
                        return IsEnabledInRegistry(c_szRegKeySMIEM, c_szRegValSavePasswords, TRUE); }
    static BOOL    IsEnabledAskPW() {
                        return IsEnabledRestorePW() &&
                            IsEnabledInRegistry(c_szRegKeySMIEM, c_szRegValAskPasswords, TRUE); }

    static BOOL    IsAdminRestricted(LPCTSTR pszRegVal);

    BOOL AskedUserToEnable();
    
    typedef HRESULT (*PFN_ENUM_CALLBACK)(IDispatch *pDispEle, DWORD_PTR dwCBData);
    HRESULT ActiveElementChanged(IHTMLElement * pHTMLElement);

protected:
    enum { LIST_DATA_PASSWORD = 1 };    // Flag to indicate a password list in store

    HRESULT AddToElementList(IHTMLInputTextElement *pITE);
    HRESULT FindInElementList(IHTMLInputTextElement *pITE);
    void    FreeElementList();

    HRESULT AddToFormList(IHTMLFormElement *pFormEle);
    HRESULT FindInFormList(IHTMLFormElement *pFormEle);
    void    FreeFormList();

    static BOOL IsElementEnabled(IHTMLElement *pEle);
    static HRESULT ShouldAttachToElement(IUnknown *, BOOL fCheckForm, 
                                IHTMLElement2**, IHTMLInputTextElement**, IHTMLFormElement**, BOOL *pfPassword);
    HRESULT GetBodyEle(IHTMLElement2 **ppEle2);

    HRESULT SubmitElement(IHTMLInputTextElement *pITE, FILETIME ft, BOOL fEnabledInCPL);

    LPCWSTR GetUrlHash();
    
    BOOL ArePasswordsSaved();
    BOOL LoadPasswords();
    void SavePasswords();
    HRESULT FindPasswordEntry(LPCWSTR pwszValue, int *piIndex);
    void SetPasswordsAreSaved(BOOL fSaved);
    HRESULT AutoFillPassword(IHTMLInputTextElement *pTextEle, LPCWSTR pwszUsername);
    HRESULT SavePassword(IHTMLFormElement *pFormEle, FILETIME ftSubmit, IHTMLInputTextElement *pFirstEle);
    HRESULT DeletePassword(LPCWSTR pwszUsername);

    HRESULT AttachToForm(IHTMLFormElement *pFormEle);

    HRESULT CreatePStore();
    HRESULT CreatePStoreAndType();
    void ReleasePStore();

    static BOOL IsEnabledInRegistry(LPCTSTR pszKey, LPCTSTR pszValue, BOOL fDefault);

    inline void EnterModalDialog();
    inline void LeaveModalDialog();


private:
    // CIntelliForms member variables
    CEventSink  *m_pSink;
    CEditEventSink *m_pEditSink;
    CAutoSuggest *m_pAutoSuggest;   // Can attach to one edit control at a time

    HINSTANCE   m_hinstPStore;
    IPStore     *m_pPStore;
    BOOL        m_fPStoreTypeInit : 1;  // Our types initialized

    HDPA        m_hdpaElements;         // Elements user has modified
    HDPA        m_hdpaForms;            // Forms we are sinked to

    BOOL        m_fCheckedIfEnabled : 1; // Checked if we're enabled for this page?
    BOOL        m_fEnabledForPage : 1;   // We're enabled for this page (non-SSL)?
    BOOL        m_fHitPWField : 1;      // Went to a password field?
    BOOL        m_fCheckedPW  : 1;      // Checked if we have a password for this URL?
    CStringList *m_pslPasswords;        // Usernames && Passwords for page, if any
    int         m_iRestoredIndex;       // Index of restored password in m_pslPasswords (-1=none)
    BOOL        m_fRestricted : 1;      // Are we restricted for normal Intelliforms?
    BOOL        m_fRestrictedPW : 1;    // Are save passwords restricted?

    // Lifetime management - see Enter/LeaveModalDialog
    BOOL        m_fInModalDialog : 1;   // Are we in a dialog?
    BOOL        m_fUninitCalled : 1;    // Was Uninit called during dialog?

    // Useful stuff for the attached document
    HWND            m_hwndBrowser;
    IHTMLDocument2 *m_pDoc2;
    IUnknown       *m_punkDoc2;
    
    CIEFrameAuto::COmWindow   *m_pOmWindow;

    BSTR        m_bstrFullUrl;          // Full url if https: protocol (security check)
    BSTR        m_bstrUrl;              // Full url with anchor/query string stripped
    LPCWSTR     m_pwszUrlHash;          // String based on UrlHash(m_bstrUrl)

    // Linked list of objects, to find CIntelliForms object for IHTMLDocument2
    CIntelliForms *m_pNext;

public:
    // GUID to use for subtype of PStore - identity GUID or c_PStoreType
    GUID        m_guidUserId;

public:
    // Helper classes
    template <class TYPE> class CEnumCollection
    {
    public:
        static HRESULT EnumCollection(TYPE *pCollection, PFN_ENUM_CALLBACK pfnCB, DWORD_PTR dwCBData);
    };

    // Sinks regular Trident events. Calls back via CEventSinkCallback
    class CEventSink : public IDispatch
    {
        ULONG   m_cRef;

    public:

        CEventSink(CEventSinkCallback *pParent);
        ~CEventSink();

        HRESULT SinkEvents(IHTMLElement2 *pEle2, int iNum, EVENTS *pEvents);
        HRESULT UnSinkEvents(IHTMLElement2 *pEle2, int iNum, EVENTS *pEvents);
        HRESULT SinkEvents(IHTMLWindow3 *pWin3, int iNum, EVENTS *pEvents);
        HRESULT UnSinkEvents(IHTMLWindow3 *pWin3, int iNum, EVENTS *pEvents);

        void SetParent(CEventSinkCallback *pParent) { m_pParent = pParent; }

        STDMETHODIMP QueryInterface(REFIID, void **);
        STDMETHODIMP_(ULONG) AddRef(void);
        STDMETHODIMP_(ULONG) Release(void);

        // IDispatch
        STDMETHODIMP GetTypeInfoCount(UINT* pctinfo);
        STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo);
        STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames,
            LCID lcid, DISPID *rgDispId);
        STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid,
            LCID lcid, WORD wFlags, DISPPARAMS  *pDispParams, VARIANT  *pVarResult,
            EXCEPINFO *pExcepInfo, UINT *puArgErr);

    private:
        CEventSinkCallback *m_pParent;
    };

    // Sinks editing Trident events. Required for IME events. Callback CEditEventSinkCallback
    class CEditEventSink : public IHTMLEditDesigner
    {
        ULONG   m_cRef;

    public:
        CEditEventSink(CEditEventSinkCallback *pParent);
        ~CEditEventSink();

        HRESULT Attach(IUnknown *punkElement);  // Attach(NULL) to detach
        void SetParent(CEditEventSinkCallback *pParent) { m_pParent = pParent; }

        STDMETHODIMP QueryInterface(REFIID, void **);
        STDMETHODIMP_(ULONG) AddRef(void);
        STDMETHODIMP_(ULONG) Release(void);

        // IHTMLEditDesigner
        STDMETHODIMP PreHandleEvent(DISPID inEvtDispId, IHTMLEventObj *pIEventObj);
        STDMETHODIMP PostHandleEvent(DISPID inEvtDispId, IHTMLEventObj *pIEventObj);
        STDMETHODIMP TranslateAccelerator(DISPID inEvtDispId, IHTMLEventObj *pIEventObj);
        STDMETHODIMP PostEditorEventNotify(DISPID inEvtDispId, IHTMLEventObj *pIEventObj) {return S_FALSE;}

    private:
        CEditEventSinkCallback  *m_pParent;
        IHTMLEditServices       *m_pEditServices;   // we keep a ref so we can unsink
    };

    class CAutoSuggest : public CEventSinkCallback
    {
        class CEnumString;

    public:
        CAutoSuggest(CIntelliForms *pParent, BOOL fEnabled, BOOL fEnabledSPW);
        ~CAutoSuggest();

        void SetParent(CIntelliForms *pParent) { m_pParent = pParent; }

        HRESULT AttachToInput(IHTMLInputTextElement *pTextEle);
        HRESULT DetachFromInput();

        static LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

        static EVENTS s_EventsToSink[];

    protected:
        // Called by window to perform requests by CAutoComplete to MSHTML
        HRESULT GetText(int cchTextMax, LPWSTR pszTextOut, LRESULT *lcchCopied);
        HRESULT GetTextLength(int *pcch);
        HRESULT SetText(LPCWSTR pszTextIn);

        void CheckAutoFillPassword(LPCWSTR pwszUsername);

        inline void MarkDirty();

    public:
        // Called to pass on events from MSHTML to CAutoComplete
        HRESULT HandleEvent(IHTMLElement *pEle, EVENTS Event, IHTMLEventObj *pEventObj);
        HRESULT UpdateDropdownPosition();

        IHTMLInputTextElement *AttachedElement() { return m_pTextEle; }
        
    private:
        HRESULT CreateAutoComplete();
        
        HRESULT CleanUp();

        CIntelliForms  *m_pParent;          // No refcount
        CEventSink     *m_pEventSink;
        IAutoComplete2 *m_pAutoComplete;
        IAutoCompleteDropDown *m_pAutoCompleteDD;
        HWND            m_hwndEdit;
        IHTMLInputTextElement *m_pTextEle;
        CEnumString    *m_pEnumString;
        long        m_lCancelKeyPress;

        BOOL        m_fAddedToDirtyList : 1;        // Add to list once they hit a key

        BOOL        m_fAllowAutoFillPW : 1;         // Call AutoFillPassword?
        BSTR        m_bstrLastUsername;             // Last Username we called AutoFillPassword for

        BOOL        m_fInitAutoComplete : 1;        // Initialized Auto Complete?

        BOOL        m_fEnabled : 1;                 // Regular intelliforms enabled?
        BOOL        m_fEnabledPW : 1;               // Restore passwords enabled?

        BOOL        m_fEscapeHit : 1;               // Escape key used to dismiss dropdown?

        UINT        m_uMsgItemActivate;             // registered message from autocomplete
        static BOOL s_fRegisteredWndClass;

        // This object is thread-safed because AutoComplete calls on second thread
        class CEnumString : public IEnumString
        {
            long    m_cRef;

        public:
            CEnumString();
            ~CEnumString();

            HRESULT Init(IHTMLInputTextElement *pInputEle, CIntelliForms *pIForms);
            void UnInit();

            HRESULT ResetEnum();

            STDMETHODIMP QueryInterface(REFIID, void **);
            STDMETHODIMP_(ULONG) AddRef(void);
            STDMETHODIMP_(ULONG) Release(void);

            // IEnumString
            virtual STDMETHODIMP Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched);
            virtual STDMETHODIMP Skip(ULONG celt) { return E_NOTIMPL; }
            virtual STDMETHODIMP Reset();
            virtual STDMETHODIMP Clone(IEnumString **ppenum) { return E_NOTIMPL; }

        protected:
            HRESULT FillEnumerator();       // called on secondary thread

            CRITICAL_SECTION m_crit;
            CStringList *m_pslMain;
            BSTR    m_bstrName;         // name of input field
            LPWSTR  m_pszOpsValue;      // value from profile assistant
            CIntelliForms *m_pIntelliForms;

            int     m_iPtr;

            BOOL    m_fFilledStrings : 1;
            BOOL    m_fInit : 1;
        };
    };
};

template <class TYPE>
HRESULT CIntelliForms::CEnumCollection<TYPE>::EnumCollection(
            TYPE                   *pCollection,
            PFN_ENUM_CALLBACK       pfnCB,
            DWORD_PTR               dwCBData)
{
    IDispatch       *pDispItem;

    HRESULT hr;
    long    l, lCount;
    VARIANT vIndex, vEmpty;

    VariantInit(&vEmpty);
    VariantInit(&vIndex);

    hr = pCollection->get_length(&lCount);

    if (FAILED(hr))
        lCount = 0;

    for (l=0; l<lCount; l++)
    {
        vIndex.vt = VT_I4;
        vIndex.lVal = l;

        hr = pCollection->item(vIndex, vEmpty, &pDispItem);

        if (SUCCEEDED(hr) && pDispItem)
        {
            hr = pfnCB(pDispItem, dwCBData);

            pDispItem->Release();
        }

        if (E_ABORT == hr)
        {
            break;
        }
    }

    return hr;
}

inline void CIntelliForms::CAutoSuggest::MarkDirty()
{
    if (!m_fAddedToDirtyList && m_pParent)
    {
        m_fAddedToDirtyList = TRUE;
        m_pParent->UserInput(m_pTextEle);
    }
}

// These wrap modal dialogs, keeping us alive and attached to the document
//  even if something weird happens while our dlgbox messageloop is alive
inline void CIntelliForms::EnterModalDialog()
{
    ASSERT(!m_fInModalDialog);  // Don't support nested Enter/Leave
    ASSERT(!m_fUninitCalled);

    m_fInModalDialog = TRUE;    // Keep us attached to document
    
    AddRef();                   // Keep us alive
}

inline void CIntelliForms::LeaveModalDialog()
{
    ASSERT(m_fInModalDialog);

    m_fInModalDialog = FALSE;
    
    if (m_fUninitCalled)
    {
        UnInit();           // Detach from document
    }

    Release();
}

// HKCU/S/MS/Win/CV/IForms/Names  /[name]/ SIndex | SData

// CStringList is optimized for appending arbitrary amounts of strings and converting to and
//  from blobs. It is not optimized for deleting or inserting strings.
class CStringList
{
protected:
    CStringList();

public:
    ~CStringList();

    friend static HRESULT CStringList_New(CStringList **ppNew, BOOL fAutoDelete=TRUE);

    // E_FAIL, S_FALSE (duplicate), S_OK
    HRESULT AddString(LPCWSTR lpwstr, int *piNum = NULL);
    HRESULT AddString(LPCWSTR lpwstr, FILETIME ft, int *piNum = NULL);

    // E_FAIL, S_OK   Doesn't check for duplicates
    HRESULT AppendString(LPCWSTR lpwstr, int *piNum = NULL);
    HRESULT AppendString(LPCWSTR lpwstr, FILETIME ft, int *piNum = NULL);

    // iLen must be length in characters of string, not counting null term.
    // -1 if unknown. *piNum filled in with index if specified
    HRESULT FindString(LPCWSTR lpwstr, int iLen/*=-1*/, int *piNum/*=NULL*/, BOOL fCaseSensitive);

    inline int      NumStrings();
    inline LPCWSTR  GetString(int iIndex);
    inline LPWSTR   GetStringPtr(int iIndex);
    inline DWORD    GetStringLen(int iIndex);
    inline HRESULT  GetStringTime(int iIndex, FILETIME *ft);
    inline HRESULT  SetStringTime(int iIndex, FILETIME ft);
    inline HRESULT  UpdateStringTime(int iIndex, FILETIME ft);
//  inline HRESULT  GetStringData(int iIndex, DWORD *pdwData);
//  inline HRESULT  SetStringData(int iIndex, DWORD dwData);

    HRESULT GetBSTR(int iIndex, BSTR *pbstrRet);
    HRESULT GetTaskAllocString(int iIndex, LPOLESTR *pRet);

    inline HRESULT  GetListData(INT64 *piData);
    inline HRESULT  SetListData(INT64 iData);

    // If set to TRUE, CStringList will delete old strings when full
    void SetAutoScavenge(BOOL fAutoScavenge) { m_fAutoScavenge=fAutoScavenge; }

    HRESULT DeleteString(int iIndex);
    HRESULT InsertString(int iIndex, LPCWSTR lpwstr);
    HRESULT ReplaceString(int iIndex, LPCWSTR lpwstr);

    // Functions to read/write to the store; converts to and from BLOBs
    // For efficiencies sake these take and return heap alloced blobs
    HRESULT WriteToBlobs(LPBYTE *ppBlob1, DWORD *pcbBlob1, LPBYTE *ppBlob2, DWORD *pcbBlob2);
    HRESULT ReadFromBlobs(LPBYTE *ppBlob1, DWORD cbBlob1, LPBYTE *ppBlob2, DWORD cbBlob2);

    static HRESULT GetFlagsFromIndex(LPBYTE pBlob1, INT64 *piFlags);

    // Warning: Don't set max strings past the MAX_STRINGS constant our ReadFromBlobs will fail
    //  if you save/restore the string list
    void SetMaxStrings(DWORD dwMaxStrings) { m_dwMaxStrings = dwMaxStrings; }
    DWORD GetMaxStrings() { return m_dwMaxStrings; }
    
    enum { MAX_STRINGS = 200 };
    
protected:
    enum { INDEX_SIGNATURE=0x4B434957 };        // WICK
    enum { INIT_BUF_SIZE=1024 };

#pragma warning (disable: 4200)     // zero-sized array warning
typedef struct 
{
    DWORD   dwSignature;    // Offset: 00
    DWORD   cbSize;         // Offset: 04 (up to not including first StringEntry)
    DWORD   dwNumStrings;   // Offset: 08 (Num of StringEntry present)
                            // Offset: 0C (--PAD--)
    INT64   iData;          // Offset: 10 (Extra data for string list user)

    struct tagStringEntry
    {
        DWORD       dwStringPtr;        // Offset: 18 (Offset of string in buffer)
        FILETIME    ftLastSubmitted;    // Offset: 1C (filetime of last submit) (unaligned)
        DWORD       dwStringLen;        // Offset: 24 (Length of this string)
    }
    StringEntry[];

} StringIndex;
#pragma warning (default: 4200)

// Value for cbSize in StringIndex
#define STRINGINDEX_CBSIZE PtrToUlong(&((StringIndex*)NULL)->StringEntry)
#define STRINGENTRY_SIZE (PtrToUlong(&((StringIndex*)NULL)->StringEntry[1]) - STRINGINDEX_CBSIZE )
// Size of StringIndex for given number of strings 
#define INDEX_SIZE(n) (STRINGINDEX_CBSIZE + (n)*STRINGENTRY_SIZE)


    void    CleanUp();
    HRESULT Init(DWORD dwBufSize=0);
    HRESULT Validate();

    HRESULT EnsureBuffer(DWORD dwSizeNeeded);
    HRESULT EnsureIndex(DWORD dwNumStringsNeeded);

    HRESULT _AddString(LPCWSTR lpwstr, BOOL fCheckDuplicates, int *piNum);

private:
    StringIndex *m_psiIndex;            // Index of strings
    DWORD   m_dwIndexSize;              // size in bytes of m_psiIndex

    LPBYTE  m_pBuffer;                  // Holds all character data
    DWORD   m_dwBufEnd;                 // Last byte used in buffer
    DWORD   m_dwBufSize;                // Size of buffer in bytes

    DWORD   m_dwMaxStrings;             // Max # strings

    BOOL    m_fAutoScavenge:1;          // Automatically remove old strings when full?
};

// We really only use this for comparing to 0, so this method works just as well and does not require alignment.
#define FILETIME_TO_INT(ft) (ft.dwLowDateTime | ft.dwHighDateTime)

inline int     CStringList::NumStrings()
{
    if (!m_psiIndex) return 0;
    return m_psiIndex->dwNumStrings;
}

inline LPCWSTR CStringList::GetString(int iIndex)
{
    if (!m_psiIndex) return NULL;
    ASSERT((DWORD)iIndex < m_psiIndex->dwNumStrings);
    return (LPCWSTR) (m_pBuffer + m_psiIndex->StringEntry[iIndex].dwStringPtr);
}

inline LPWSTR CStringList::GetStringPtr(int iIndex)
{
    if (!m_psiIndex) return NULL;
    ASSERT((DWORD)iIndex < m_psiIndex->dwNumStrings);
    return (LPWSTR) (m_pBuffer + m_psiIndex->StringEntry[iIndex].dwStringPtr);
}

inline DWORD CStringList::GetStringLen(int iIndex)
{ 
    if (!m_psiIndex) return E_FAIL;
    ASSERT((DWORD)iIndex < m_psiIndex->dwNumStrings);
    return m_psiIndex->StringEntry[iIndex].dwStringLen;
}

inline HRESULT CStringList::GetStringTime(int iIndex, FILETIME *ft)
{
    if (!m_psiIndex) return E_FAIL;
    ASSERT((DWORD)iIndex < m_psiIndex->dwNumStrings);
    *ft = m_psiIndex->StringEntry[iIndex].ftLastSubmitted;
    return S_OK;
}

inline HRESULT CStringList::SetStringTime(int iIndex, FILETIME ft)
{
    if (!m_psiIndex) return E_FAIL;
    ASSERT((DWORD)iIndex < m_psiIndex->dwNumStrings);
    ASSERT(-1 != CompareFileTime(&ft, &m_psiIndex->StringEntry[iIndex].ftLastSubmitted));
    m_psiIndex->StringEntry[iIndex].ftLastSubmitted = ft;
    return S_OK;
}

inline HRESULT CStringList::UpdateStringTime(int iIndex, FILETIME ft)
{
    if (!m_psiIndex) return E_FAIL;
    ASSERT((DWORD)iIndex < m_psiIndex->dwNumStrings);
    if (1 == CompareFileTime(&ft, &m_psiIndex->StringEntry[iIndex].ftLastSubmitted))
    {
        m_psiIndex->StringEntry[iIndex].ftLastSubmitted = ft;
        return S_OK;
    }
    return S_FALSE;
}
inline HRESULT CStringList::GetListData(INT64 *piData)
{
    if (m_psiIndex)
    {
        *piData = m_psiIndex->iData;
        return S_OK;
    }
    return E_FAIL;
}
inline HRESULT CStringList::SetListData(INT64 iData)
{
    if (!m_psiIndex && FAILED(Init()))
        return E_FAIL;

    m_psiIndex->iData = iData;
    return S_OK;
}
/*
inline HRESULT CStringList::GetStringData(int iIndex, DWORD *pdwData)
{
    if (!m_psiIndex) return E_FAIL;
    ASSERT((DWORD)iIndex < m_psiIndex->dwNumStrings);
    *pdwData = m_psiIndex->StringEntry[iIndex].dwData;
    return S_OK;
}

inline HRESULT CStringList::SetStringData(int iIndex, DWORD dwData)
{
    if (!m_psiIndex) return E_FAIL;
    ASSERT((DWORD)iIndex < m_psiIndex->dwNumStrings);
    m_psiIndex->StringEntry[iIndex].dwData = dwData;
    return S_OK;
}
*/
#endif //__IFORMS_H_
