|
|
// 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 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, 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);
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_
|