//////////////////////////////////////////////////////////////////////////////////////////////////////////// // // File: MAPIContact.cpp // Description: MAPI Contact class wrapper // // Copyright (C) 2005-2011, Noel Dillabough // // This source code is free to use and modify provided this notice remains intact and that any enhancements // or bug fixes are posted to the CodeProject page hosting this class for the community to benefit. // // Usage: see the CodeProject article at http://www.codeproject.com/internet/CMapiEx.asp // //////////////////////////////////////////////////////////////////////////////////////////////////////////// #include "MAPIExPCH.h" #include "MAPIEx.h" #define CATEGORIES_PROPERTY _T("Keywords") ///////////////////////////////////////////////////////////// // CContactAddress const ULONG ContactAddressTag[][5]={ { PR_HOME_ADDRESS_CITY, PR_HOME_ADDRESS_COUNTRY, PR_HOME_ADDRESS_STATE_OR_PROVINCE, PR_HOME_ADDRESS_STREET, PR_HOME_ADDRESS_POSTAL_CODE }, { PR_BUSINESS_ADDRESS_CITY, PR_BUSINESS_ADDRESS_COUNTRY, PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE, PR_BUSINESS_ADDRESS_STREET, PR_BUSINESS_ADDRESS_POSTAL_CODE }, { PR_OTHER_ADDRESS_CITY, PR_OTHER_ADDRESS_COUNTRY, PR_OTHER_ADDRESS_STATE_OR_PROVINCE, PR_OTHER_ADDRESS_STREET, PR_OTHER_ADDRESS_POSTAL_CODE }, }; CContactAddress::CContactAddress() { } BOOL CContactAddress::FillAddress(CMAPIContact& contact,AddressType nType) { if(nTypeOTHER) return FALSE; m_nType=nType; if(!contact.GetPropertyString(ContactAddressTag[nType][0], m_strCity)) return FALSE; if(!contact.GetPropertyString(ContactAddressTag[nType][1], m_strCountry)) return FALSE; if(!contact.GetPropertyString(ContactAddressTag[nType][2], m_strStateOrProvince)) return FALSE; if(!contact.GetPropertyString(ContactAddressTag[nType][3], m_strStreet)) return FALSE; if(!contact.GetPropertyString(ContactAddressTag[nType][4], m_strPostalCode)) return FALSE; return TRUE; } BOOL CContactAddress::SaveAddress(CMAPIContact& contact,AddressType nType) { if(nTypeOTHER) return FALSE; m_nType=nType; if(!contact.SetPropertyString(ContactAddressTag[nType][0], m_strCity)) return FALSE; if(!contact.SetPropertyString(ContactAddressTag[nType][1], m_strCountry)) return FALSE; if(!contact.SetPropertyString(ContactAddressTag[nType][2], m_strStateOrProvince)) return FALSE; if(!contact.SetPropertyString(ContactAddressTag[nType][3], m_strStreet)) return FALSE; if(!contact.SetPropertyString(ContactAddressTag[nType][4], m_strPostalCode)) return FALSE; return TRUE; } ///////////////////////////////////////////////////////////// // CMAPIContact const ULONG PhoneNumberIDs[]={ PR_PRIMARY_TELEPHONE_NUMBER, PR_BUSINESS_TELEPHONE_NUMBER, PR_HOME_TELEPHONE_NUMBER, PR_CALLBACK_TELEPHONE_NUMBER, PR_BUSINESS2_TELEPHONE_NUMBER, PR_MOBILE_TELEPHONE_NUMBER, PR_RADIO_TELEPHONE_NUMBER, PR_CAR_TELEPHONE_NUMBER, PR_OTHER_TELEPHONE_NUMBER, PR_PAGER_TELEPHONE_NUMBER, PR_PRIMARY_FAX_NUMBER, PR_BUSINESS_FAX_NUMBER, PR_HOME_FAX_NUMBER, PR_TELEX_NUMBER, PR_ISDN_NUMBER, PR_ASSISTANT_TELEPHONE_NUMBER, PR_HOME2_TELEPHONE_NUMBER, PR_TTYTDD_PHONE_NUMBER, PR_COMPANY_MAIN_PHONE_NUMBER, 0 }; const ULONG NameIDs[]={ PR_DISPLAY_NAME, PR_GIVEN_NAME, PR_MIDDLE_NAME, PR_SURNAME, 0 }; CMAPIContact::CMAPIContact() { #ifdef _WIN32_WCE m_pPOOM=NULL; m_pContact=NULL; #endif } CMAPIContact::~CMAPIContact() { Close(); } #ifdef _WIN32_WCE BOOL CMAPIContact::Open(CMAPIEx* pMAPI, CPOOM* pPOOM, IContact* pContact) { Close(); m_pMAPI=pMAPI; m_pContact=pContact; if(!m_pPOOM) m_pPOOM=pPOOM; return TRUE; } void CMAPIContact::Close() { RELEASE(m_pContact); m_pMAPI=NULL; } BOOL CMAPIContact::Save(BOOL bClose) { return (m_pContact->Save()==S_OK); } BOOL CMAPIContact::GetPropertyString(ULONG ulProperty, CString& strProperty, BOOL bStream) { return m_pPOOM ? m_pPOOM->GetProperty(m_pContact,ulProperty, strProperty) : FALSE; } BOOL CMAPIContact::SetPropertyString(ULONG ulProperty, LPCTSTR szProperty, BOOL bStream) { return m_pPOOM ? m_pPOOM->SetProperty(m_pContact,ulProperty, szProperty) : FALSE; } #endif BOOL CMAPIContact::Create(CMAPIEx* pMAPI, CMAPIFolder* pFolder) { if(!pMAPI) return FALSE; if(!pFolder) pFolder=pMAPI->GetFolder(); if(!pFolder) pFolder=pMAPI->OpenContacts(); #ifdef _WIN32_WCE if(pFolder) { m_pPOOM=pFolder->GetPOOM(); return m_pPOOM ? m_pPOOM->Create(pMAPI,*this) : FALSE; } return FALSE; #else if(!CMAPIObject::Create(pMAPI, pFolder)) return FALSE; SetMessageClass(_T("IPM.Contact")); SetImportance(IMPORTANCE_NORMAL); SetSensitivity(SENSITIVITY_NONE); return TRUE; #endif } BOOL CMAPIContact::GetName(CString& strName, ULONG ulNameID) { ULONG i=0; while(NameIDs[i]!=ulNameID && NameIDs[i]>0) i++; if(!NameIDs[i]) { strName=_T(""); return FALSE; } else { return GetPropertyString(ulNameID, strName); } } int CMAPIContact::GetOutlookEmailID(int nIndex) { ULONG ulProperty[]={ OUTLOOK_EMAIL1, OUTLOOK_EMAIL2, OUTLOOK_EMAIL3 }; if(nIndex<1 || nIndex>3) return 0; return ulProperty[nIndex-1]; } // uses the built in outlook email fields, OUTLOOK_EMAIL1 etc, minus 1 for ADDR_TYPE and +1 for EmailOriginalDisplayName BOOL CMAPIContact::GetEmail(CString& strEmail, int nIndex) { ULONG nID=GetOutlookEmailID(nIndex); if(!nID) return FALSE; #ifdef _WIN32_WCE return GetPropertyString(nID, strEmail); #else LPSPropValue pProp; if(GetOutlookProperty(OUTLOOK_DATA1, nID-1, pProp)) { CString strAddrType=CMAPIEx::GetValidString(*pProp); MAPIFreeBuffer(pProp); if(GetOutlookProperty(OUTLOOK_DATA1, nID, pProp)) { strEmail=CMAPIEx::GetValidString(*pProp); MAPIFreeBuffer(pProp); if(strAddrType==_T("EX")) { // for EX types we use the original display name (seems to contain the appropriate data) if(GetOutlookProperty(OUTLOOK_DATA1, nID+1, pProp)) { strEmail=CMAPIEx::GetValidString(*pProp); MAPIFreeBuffer(pProp); } } return TRUE; } } return FALSE; #endif } BOOL CMAPIContact::GetEmailDisplayAs(CString& strDisplayAs, int nIndex) { #ifdef _WIN32_WCE return GetEmail(strDisplayAs, nIndex); #else ULONG nID=GetOutlookEmailID(nIndex); if(!nID) return FALSE; LPSPropValue pProp; if(GetOutlookProperty(OUTLOOK_DATA1, nID-3, pProp)) { strDisplayAs=CMAPIEx::GetValidString(*pProp); MAPIFreeBuffer(pProp); return TRUE; } return FALSE; #endif } BOOL CMAPIContact::GetHomePage(CString& strHomePage, BOOL bBusiness) { return GetPropertyString(bBusiness ? PR_BUSINESS_HOME_PAGE : PR_PERSONAL_HOME_PAGE, strHomePage); } BOOL CMAPIContact::GetPhoneNumber(CString& strPhoneNumber, ULONG ulPhoneNumberID) { ULONG i=0; while(PhoneNumberIDs[i]!=ulPhoneNumberID && PhoneNumberIDs[i]>0) i++; if(!PhoneNumberIDs[i]) { strPhoneNumber=_T(""); return FALSE; } else { return GetPropertyString(ulPhoneNumberID, strPhoneNumber); } } BOOL CMAPIContact::GetAddress(CContactAddress& address, CContactAddress::AddressType nType) { return address.FillAddress(*this, nType); } BOOL CMAPIContact::GetPostalAddress(CString& strAddress) { #ifdef _WIN32_WCE return FALSE; #else return GetPropertyString(PR_POSTAL_ADDRESS, strAddress); #endif } BOOL CMAPIContact::GetIMAddress(CString& strIMAddress) { #ifndef _WIN32_WCE LPSPropValue pProp; if(GetOutlookProperty(OUTLOOK_DATA1, OUTLOOK_IM_ADDRESS, pProp)) { strIMAddress=CMAPIEx::GetValidString(*pProp); MAPIFreeBuffer(pProp); return TRUE; } #endif return FALSE; } BOOL CMAPIContact::GetFileAs(CString& strFileAs) { #ifdef _WIN32_WCE return GetPropertyString(PR_DISPLAY_NAME, strFileAs); #else LPSPropValue pProp; if(GetOutlookProperty(OUTLOOK_DATA1, OUTLOOK_FILE_AS, pProp)) { strFileAs=CMAPIEx::GetValidString(*pProp); MAPIFreeBuffer(pProp); return TRUE; } return FALSE; #endif } BOOL CMAPIContact::GetTitle(CString& strTitle) { return GetPropertyString(PR_TITLE, strTitle); } BOOL CMAPIContact::GetCompany(CString& strCompany) { return GetPropertyString(PR_COMPANY_NAME, strCompany); } BOOL CMAPIContact::GetProfession(CString& strProfession) { return GetPropertyString(PR_PROFESSION, strProfession); } BOOL CMAPIContact::GetDisplayNamePrefix(CString& strPrefix) { return GetPropertyString(PR_DISPLAY_NAME_PREFIX, strPrefix); } BOOL CMAPIContact::GetGeneration(CString& strGeneration) { return GetPropertyString(PR_GENERATION, strGeneration); } BOOL CMAPIContact::GetDepartment(CString& strDepartment) { return GetPropertyString(PR_DEPARTMENT_NAME, strDepartment); } BOOL CMAPIContact::GetOffice(CString& strOffice) { return GetPropertyString(PR_OFFICE_LOCATION, strOffice); } BOOL CMAPIContact::GetManagerName(CString& strManagerName) { return GetPropertyString(PR_MANAGER_NAME, strManagerName); } BOOL CMAPIContact::GetAssistantName(CString& strAssistantName) { return GetPropertyString(PR_ASSISTANT, strAssistantName); } BOOL CMAPIContact::GetNickName(CString& strNickName) { return GetPropertyString(PR_NICKNAME, strNickName); } BOOL CMAPIContact::GetSpouseName(CString& strSpouseName) { return GetPropertyString(PR_SPOUSE_NAME, strSpouseName); } BOOL CMAPIContact::GetBirthday(SYSTEMTIME& tmBirthday) { #ifdef _WIN32_WCE return m_pPOOM ? m_pPOOM->GetDate(m_pContact, PR_BIRTHDAY,tmBirthday) : FALSE; #else LPSPropValue pProp; if(GetProperty(PR_BIRTHDAY, pProp)==S_OK) { SYSTEMTIME tm; FileTimeToSystemTime(&pProp->Value.ft, &tm); SystemTimeToTzSpecificLocalTime(NULL, &tm, &tmBirthday); MAPIFreeBuffer(pProp); return TRUE; } return FALSE; #endif } BOOL CMAPIContact::GetBirthday(CString& strBirthday, LPCTSTR szFormat) { SYSTEMTIME tm; if(GetBirthday(tm)) { TCHAR szDate[256]; if(!szFormat) szFormat=_T("MM/dd/yyyy"); GetDateFormat(LOCALE_SYSTEM_DEFAULT, 0, &tm, szFormat, szDate, 256); strBirthday=szDate; return TRUE; } return FALSE; } BOOL CMAPIContact::GetAnniversary(SYSTEMTIME& tmAnniversary) { #ifdef _WIN32_WCE return m_pPOOM ? m_pPOOM->GetDate(m_pContact, PR_WEDDING_ANNIVERSARY,tmAnniversary) : FALSE; #else LPSPropValue pProp; if(GetProperty(PR_WEDDING_ANNIVERSARY, pProp)==S_OK) { SYSTEMTIME tm; FileTimeToSystemTime(&pProp->Value.ft, &tm); SystemTimeToTzSpecificLocalTime(NULL, &tm, &tmAnniversary); MAPIFreeBuffer(pProp); return TRUE; } return FALSE; #endif } BOOL CMAPIContact::GetAnniversary(CString& strAnniversary, LPCTSTR szFormat) { SYSTEMTIME tm; if(GetAnniversary(tm)) { TCHAR szDate[256]; if(!szFormat) szFormat=_T("MM/dd/yyyy"); GetDateFormat(LOCALE_SYSTEM_DEFAULT, 0, &tm, szFormat, szDate,256); strAnniversary=szDate; return TRUE; } return FALSE; } // Categories are stored under the multi-value named property CATEGORIES_PROPERTY BOOL CMAPIContact::GetCategories(CString& strCategories) { #ifdef _WIN32_WCE return GetPropertyString(OUTLOOK_CATEGORIES, strCategories); #else LPSPropValue pProp; if(GetNamedProperty(CATEGORIES_PROPERTY, pProp)) { strCategories=""; ULONG i; CString strCategory; for(i=0;iValue.MVi.cValues;i++) { strCategory=CMAPIEx::GetValidMVString(*pProp, i); if(strCategory.GetLength()) { if(strCategories.GetLength()) strCategories+=';'; strCategories+=strCategory; } } MAPIFreeBuffer(pProp); return TRUE; } return FALSE; #endif } BOOL CMAPIContact::SetName(LPCTSTR szName, ULONG ulNameID) { ULONG i=0; while(NameIDs[i]!=ulNameID && NameIDs[i]>0) i++; if(!NameIDs[i]) return FALSE; return SetPropertyString(ulNameID, szName); } // only supports setting SMTP Email Addresses, sets both display properties, use SetEmailDisplayAs to set the // outlook DisplayAs property to some other text. You should check that the text is a valid SMTP email address // ie name.name@domain.domain because this function will allow any string BOOL CMAPIContact::SetEmail(LPCTSTR szEmail, int nIndex) { ULONG nID=GetOutlookEmailID(nIndex); if(!nID) return FALSE; #ifdef _WIN32_WCE return m_pPOOM ? m_pPOOM->SetProperty(m_pContact, nID, szEmail) : FALSE; #else if(SetOutlookProperty(OUTLOOK_DATA1, nID-1, _T("SMTP"))) { // we set the email field and both display properties if(!SetOutlookProperty(OUTLOOK_DATA1, nID, szEmail)) return FALSE; if(!SetOutlookProperty(OUTLOOK_DATA1, nID+1, szEmail)) return FALSE; if(!SetOutlookProperty(OUTLOOK_DATA1, nID-3, szEmail)) return FALSE; return TRUE; } return FALSE; #endif } BOOL CMAPIContact::SetEmailDisplayAs(LPCTSTR szDisplayAs, int nIndex) { #ifdef _WIN32_WCE return FALSE; #endif ULONG nID=GetOutlookEmailID(nIndex); if(!nID) return FALSE; return SetOutlookProperty(OUTLOOK_DATA1, nID-3, szDisplayAs); } BOOL CMAPIContact::SetHomePage(LPCTSTR szHomePage, BOOL bBuiness) { return SetPropertyString(bBuiness ? PR_BUSINESS_HOME_PAGE : PR_PERSONAL_HOME_PAGE, szHomePage); } BOOL CMAPIContact::SetPhoneNumber(LPCTSTR szPhoneNumber, ULONG ulPhoneNumberID) { ULONG i=0; while(PhoneNumberIDs[i]!=ulPhoneNumberID && PhoneNumberIDs[i]>0) i++; if(!PhoneNumberIDs[i] || !szPhoneNumber) return FALSE; // lets do a check for North American phone numbers and change the format if so to "(code) xxx-yyyy" CString strPhoneNumber=szPhoneNumber; if(strPhoneNumber.GetLength()==12 && szPhoneNumber[3]==(TCHAR)'-' && szPhoneNumber[7]==(TCHAR)'-') { strPhoneNumber.Format(_T("(%s) %s"), strPhoneNumber.Mid(0,3), strPhoneNumber.Right(8)); } return SetPropertyString(ulPhoneNumberID, strPhoneNumber); } BOOL CMAPIContact::SetAddress(CContactAddress& address, CContactAddress::AddressType nType) { return address.SaveAddress(*this, nType); } // Sets the PR_POSTAL_ADDRESS text and checks the appropriate outlook checkbox by setting the index BOOL CMAPIContact::SetPostalAddress(CContactAddress::AddressType nType) { #ifdef _WIN32_WCE return FALSE; #else CContactAddress address; if(!GetAddress(address, nType)) return FALSE; CString strPostalAddress; strPostalAddress.Format(_T("%s\r\n%s %s %s\r\n%s"),address.m_strStreet,address.m_strCity,address.m_strStateOrProvince,address.m_strPostalCode,address.m_strCountry); if(!SetPropertyString(PR_POSTAL_ADDRESS, strPostalAddress)) return FALSE; return SetOutlookProperty(OUTLOOK_DATA1, OUTLOOK_POSTAL_ADDRESS, (int)nType+1); #endif } // updates the outlook display address; call this after changing address fields BOOL CMAPIContact::UpdateDisplayAddress(CContactAddress::AddressType nType) { #ifdef _WIN32_WCE return FALSE; #else CContactAddress address; if(!GetAddress(address, nType)) return FALSE; CString strDisplayAddress; strDisplayAddress.Format(_T("%s\r\n%s %s %s\r\n%s"),address.m_strStreet,address.m_strCity,address.m_strStateOrProvince,address.m_strPostalCode,address.m_strCountry); ULONG ulProperty=OUTLOOK_DISPLAY_ADDRESS_HOME+(int)nType; return SetOutlookProperty(OUTLOOK_DATA1,ulProperty, strDisplayAddress); #endif } BOOL CMAPIContact::SetNotes(LPCTSTR szNotes, BOOL bRTF) { #ifdef _WIN32_WCE return SetPropertyString(PR_BODY, szNotes); #else if(!Contact() || !szNotes) return FALSE; ULONG nLen=(ULONG)_tcslen(szNotes); HRESULT hr=E_FAIL; LPSTREAM pStream=NULL; if(bRTF) { if(Contact()->OpenProperty(PR_RTF_COMPRESSED, &IID_IStream, STGM_CREATE | STGM_WRITE, MAPI_MODIFY | MAPI_CREATE, (LPUNKNOWN*)&pStream)==S_OK) { IStream *pUncompressed; if(WrapCompressedRTFStream(pStream,MAPI_MODIFY, &pUncompressed)==S_OK) { hr=pUncompressed->Write(szNotes, nLen*sizeof(TCHAR), NULL); if(pUncompressed->Commit(STGC_DEFAULT)==S_OK) pStream->Commit(STGC_DEFAULT); RELEASE(pUncompressed); } } } else { if(Contact()->OpenProperty(PR_BODY, &IID_IStream, 0, MAPI_MODIFY | MAPI_CREATE, (LPUNKNOWN*)&pStream)==S_OK) { hr=pStream->Write(szNotes, (nLen+1)*sizeof(TCHAR), NULL); } } RELEASE(pStream); return (hr==S_OK); #endif } BOOL CMAPIContact::SetIMAddress(LPCTSTR szIMAddress) { #ifndef _WIN32_WCE return SetOutlookProperty(OUTLOOK_DATA1, OUTLOOK_IM_ADDRESS, szIMAddress); #else return FALSE; #endif } BOOL CMAPIContact::SetFileAs(LPCTSTR szFileAs) { #ifdef _WIN32_WCE return SetPropertyString(PR_DISPLAY_NAME, szFileAs); #else return SetOutlookProperty(OUTLOOK_DATA1, OUTLOOK_FILE_AS, szFileAs); #endif } BOOL CMAPIContact::SetTitle(LPCTSTR szTitle) { return SetPropertyString(PR_TITLE, szTitle); } BOOL CMAPIContact::SetCompany(LPCTSTR szCompany) { return SetPropertyString(PR_COMPANY_NAME, szCompany); } BOOL CMAPIContact::SetProfession(LPCTSTR szProfession) { return SetPropertyString(PR_PROFESSION, szProfession); } BOOL CMAPIContact::SetDisplayNamePrefix(LPCTSTR szPrefix) { return SetPropertyString(PR_DISPLAY_NAME_PREFIX, szPrefix); } BOOL CMAPIContact::SetGeneration(LPCTSTR szGeneration) { return SetPropertyString(PR_GENERATION, szGeneration); } // After changing any name field you should call this to update PR_INITIALS and PR_DISPLAY_NAME BOOL CMAPIContact::UpdateDisplayName() { CString strPrefix, strFirst, strMiddle, strLast, strGeneration, strFullName, strFileAs, strInitials; if(GetDisplayNamePrefix(strPrefix) && strPrefix.GetLength()) { strFullName=strPrefix; strFullName+=(TCHAR)' '; } if(GetName(strFirst, PR_GIVEN_NAME) && strFirst.GetLength()) { strFileAs+=strFirst; strInitials+=strFirst.GetAt(0); strInitials+=(TCHAR)'.'; } if(GetName(strMiddle, PR_MIDDLE_NAME) && strMiddle.GetLength()) { if(strFileAs.GetLength()) strFileAs+=(TCHAR)' '; strFileAs+=strMiddle; strInitials+=(TCHAR)strMiddle.GetAt(0); strInitials+=(TCHAR)'.'; int i=1, nLen=strMiddle.GetLength(); while(iSetDate(m_pContact, PR_BIRTHDAY,tmBirthday) : FALSE; #else SPropValue prop; prop.ulPropTag=PR_BIRTHDAY; SystemTimeToFileTime(&tmBirthday, &prop.Value.ft); return (Contact()->SetProps(1, &prop, NULL)==S_OK); #endif } BOOL CMAPIContact::SetAnniversary(SYSTEMTIME& tmAnniversary) { #ifdef _WIN32_WCE return m_pPOOM ? m_pPOOM->SetDate(m_pContact, PR_WEDDING_ANNIVERSARY,tmAnniversary) : FALSE; #else SPropValue prop; prop.ulPropTag=PR_WEDDING_ANNIVERSARY; SystemTimeToFileTime(&tmAnniversary, &prop.Value.ft); return (Contact()->SetProps(1, &prop, NULL)==S_OK); #endif } // Categories are stored under the multi-value named property CATEGORIES_PROPERTY // Pass in a string of semicolon separated category names BOOL CMAPIContact::SetCategories(LPCTSTR szCategories) { #ifdef _WIN32_WCE return SetPropertyString(OUTLOOK_CATEGORIES, szCategories); #else CString strCategories=szCategories; int nCount=0, nIndex=0; CString strCategory=strCategories.Tokenize(_T(";"), nIndex); while(strCategory.GetLength()) { nCount++; strCategory=strCategories.Tokenize(_T(";"), nIndex); } HRESULT hr=E_FAIL; if(!nCount) { if(DeleteNamedProperty(CATEGORIES_PROPERTY)) return TRUE; } else { LPTSTR* arCategories=new LPTSTR[nCount]; nCount=0; nIndex=0; int nLen=0; strCategory=strCategories.Tokenize(_T(";"), nIndex); do { nLen=strCategory.GetLength(); if(nLen>0) { arCategories[nCount]=new TCHAR[nLen+1]; memcpy(arCategories[nCount], (LPCTSTR)strCategory, nLen*sizeof(TCHAR)); arCategories[nCount++][nLen]=(TCHAR)0; strCategory=strCategories.Tokenize(_T(";"), nIndex); } } while(nLen); LPSPropValue pProp; if(SetNamedMVProperty(CATEGORIES_PROPERTY, (LPCTSTR*)arCategories, nCount, pProp)) { hr=Contact()->SetProps(1, pProp, NULL); MAPIFreeBuffer(pProp); } for(nIndex=0;nIndex0 && !DeleteAttachment()) return FALSE; BOOL bPicture=FALSE; if(szPath!=NULL) bPicture=AddAttachment(szPath, CONTACT_PICTURE, CONTACT_PICTURE); SetOutlookProperty(CMAPIContact::OUTLOOK_DATA1, CMAPIContact::OUTLOOK_PICTURE_FLAG, bPicture, PT_BOOLEAN); if(szPath==NULL) return TRUE; return bPicture; #endif }