The right approach to get a Contact’s last communication (IItem’s PIMPR_SMARTPROP)
I've discussed about the “wrong” approach in a previous post of mine, where I also talked about why using PIMPR_SMARTPROP to retrieve the info about the last way a Windows Mobile-device user communicated with a given contact. Recently 2 MSDN Forums users asked for help about this (“How to get the information of a selected phonecall number?” and “How to read LastNumber in contact”) and therefore I wanted to invest some time for the Community, hoping this may help others as well! (and also to play with POOM, since managed APIs wrapped so many properties making POOM _quite_ obsolete…)
Before spreading the code, let me state a thing: the SmartProp property is not set for a contact until the first time the user
explicitly changes the default contact method, or it is otherwise explicitly set by an application. I noticed this when coding this sample, and later I understood that this is the expected behavior.
Ehy, remember that this is not production code: this is for testing\didactic purposes only… indeed the code is simply meant to dump out to a text-file only the First Name, Last Name and the string representing the last way user communicated with the selected contact. The code doesn’t even use the IPOutlookItemCollection::Find to get a specific contact, as this was not the goal here. Yet, I think it was worth sharing as it is, so that who wants can customize it… enjoy!!
NOTES:
- PIMPR_SMARTPROP is a property of the IItem, not of the IContact: that’s why for example the old POOM NETCF sample code didn’t contain it. So what we need is to retrieve the collection of Contacts and handle them as IItem.
- Once you have all the contacts as a collection (IPOutlookItemCollection), you can’t directly retrieve each item – even if IItem implements IDispatch, the right way is the one for example mentioned by my colleague Xiaoyun Li here, i.e. invoke IPOutlookItemCollection::Item by passing a IContact as IDispatch, then call IContact::get_Oid to retrieve the unique OID and finally use this with IPOutlookApp2::GetItemFromOidEx (see function DumpOutToText below).
- At this point you have an IItem object and can query its properties in the usual old way, considering that PIMPR_SMARTPROP will return the property id (e.g. PIMPR_MOBILE_TELEPHONE_NUMBER, PIMPR_SMS, etc); all the propIDs that can be returned as smart prop are listed in the documentation here.
- For each returned property, remember to check if it was really found ( .wFlags != CEDB_PROPNOTFOUND) and if it’s of the expected data type ( .propid == CEVT_LPWSTR).
If you know of any smarter way I’ll be more than welcome on continuing this saga about PIMPR_SMARTPROP…
#include "stdafx.h"
#include <pimstore.h>
// **************************************************************************
// Globals
IPOutlookApp2 * g_polApp = NULL;
IUnknown * g_pUnknown = NULL;
LPCTSTR g_pszFilename = TEXT("contacts.txt");
// **************************************************************************
//Functions
HRESULT InitPoom(void);
HRESULT GetPoomFolder(int nFolder, IFolder ** ppFolder);
HRESULT FillFileWithContacts(void);
HRESULT DumpOutToText(IPOutlookItemCollection * pItemCol);
HRESULT WriteItemSmartProp(IItem *pItem);
HRESULT Log(LPTSTR szLog);
HRESULT LogToFile(LPTSTR szLog, LPCTSTR pszFilename);
// **************************************************************************
//MAIN
int _tmain(int argc, _TCHAR* argv[])
{
HRESULT hr = E_FAIL;
//Initialize POOM
hr = InitPoom();
CHR(hr);
//Fill file with Contacts
hr = FillFileWithContacts();
CHR(hr);
//Success
MessageBox(NULL, TEXT("Done"), TEXT("Test"), MB_OK);
Exit:
return 0;
}
// **************************************************************************
//InitPoom
HRESULT InitPoom(void)
{
HRESULT hr = E_FAIL;
hr = CoInitializeEx(NULL, 0);
CHR(hr);
hr = CoCreateInstance(CLSID_Application, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&g_pUnknown);
CHR(hr);
hr = g_pUnknown->QueryInterface(IID_IPOutlookApp, (void**)&g_polApp);
CHR(hr);
hr = g_polApp->Logon(NULL);
CHR(hr);
//success
hr = S_OK;
Exit:
RELEASE_OBJ(g_polApp);
return hr;
}
// **************************************************************************
//FillFileWithContacts
HRESULT FillFileWithContacts(void)
{
HRESULT hr = E_FAIL;
IFolder * pCurrFldr = NULL;
IPOutlookItemCollection * pItemCol = NULL;
// Get the Contacts folder and its items
hr = GetPoomFolder(olFolderContacts, &pCurrFldr);
CHR(hr);
if (SUCCEEDED(pCurrFldr->get_Items(&pItemCol)))
{
//Dump out First Name, Last Name, SMARTPROP for each item
hr = DumpOutToText(pItemCol);
CHR(hr);
}
//success
hr = S_OK;
Exit:
RELEASE_OBJ(pItemCol);
RELEASE_OBJ(pCurrFldr);
return hr;
}
// **************************************************************************
//GetPoomFolder
HRESULT GetPoomFolder(int nFolder, IFolder ** ppFolder)
{
HRESULT hr = E_FAIL;
if (SUCCEEDED(g_polApp->GetDefaultFolder(nFolder, ppFolder)))
{
hr = S_OK;
}
return hr;
}
// **************************************************************************
//DumpOutToText
HRESULT DumpOutToText(IPOutlookItemCollection * pItemCol)
{
HRESULT hr = E_FAIL;
IContact * pContact = NULL;
IItem * pItem = NULL;
CEOID oid = 0;
int cItems = 0;
//Count contacts
pItemCol->get_Count(&cItems);
for (int i = 1; i <= cItems; i++)
{
//get the item as a IContact
if (SUCCEEDED(pItemCol->Item (i, reinterpret_cast<IDispatch**>(&pContact))))
{
//convert the IContact into a IItem...
if (SUCCEEDED(pContact->get_Oid((long*)&oid)))
{
//... by using the Oid
if (SUCCEEDED(g_polApp->GetItemFromOidEx(oid, 0, &pItem)))
{
//Write item's properties to file
hr = WriteItemSmartProp(pItem);
RELEASE_OBJ(pItem);
CHR(hr);
}
}
}
}
//success
hr = S_OK;
Exit:
return hr;
}
// **************************************************************************
//GetItemSmartProp
HRESULT WriteItemSmartProp(IItem *pItem)
{
HRESULT hr = E_FAIL;
int cProps = 20;
CEPROPID rgPropId[20] = {0};
CEPROPVAL *prgPropvalUser = NULL;
ULONG cbBuffer = 0;
// FROM https://msdn.microsoft.com/en-us/library/bb415504.aspx
// The Smart Property (PIMPR_SMARTPROP) is the Contact property that contains the property ID of the default communication mode.
rgPropId[0] = PIMPR_FIRST_NAME; //type: CEVT_LPWSTR
rgPropId[1] = PIMPR_LAST_NAME; //type: CEVT_LPWSTR
rgPropId[2] = PIMPR_SMARTPROP; //type: CEVT_UI4
//all of the following are of type: CEVT_LPWSTR
rgPropId[3] = PIMPR_IM2_ADDRESS;
rgPropId[4] = PIMPR_ASSISTANT_TELEPHONE_NUMBER;
rgPropId[5] = PIMPR_BUSINESS_TELEPHONE_NUMBER;
rgPropId[6] = PIMPR_BUSINESS2_TELEPHONE_NUMBER;
rgPropId[7] = PIMPR_CAR_TELEPHONE_NUMBER;
rgPropId[8] = PIMPR_COMPANY_TELEPHONE_NUMBER;
rgPropId[9] = PIMPR_EMAIL1_ADDRESS;
rgPropId[10] = PIMPR_EMAIL2_ADDRESS;
rgPropId[11] = PIMPR_EMAIL3_ADDRESS;
rgPropId[12] = PIMPR_HOME_TELEPHONE_NUMBER;
rgPropId[13] = PIMPR_HOME2_TELEPHONE_NUMBER;
rgPropId[14] = PIMPR_IM1_ADDRESS;
rgPropId[15] = PIMPR_IM3_ADDRESS;
rgPropId[16] = PIMPR_MOBILE_TELEPHONE_NUMBER;
rgPropId[17] = PIMPR_PAGER_NUMBER;
rgPropId[18] = PIMPR_RADIO_TELEPHONE_NUMBER;
rgPropId[19] = PIMPR_SMS;
//FROM: https://msdn.microsoft.com/en-us/library/ms859378.aspx
// Allocate memory, then get item properties
cbBuffer = 0;
hr = pItem->GetProps(rgPropId, 0, cProps, &prgPropvalUser, &cbBuffer, NULL);
if(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr)
{
prgPropvalUser = (CEPROPVAL *) LocalAlloc(0, cbBuffer);
}
// cbBuffer is set to the number of bytes required to hold the data.
hr = pItem->GetProps(rgPropId, 0, cProps, (CEPROPVAL **)&prgPropvalUser, &cbBuffer, NULL);
//better error-checking to do here...
if(FAILED(hr) || 0 == cbBuffer)
{
goto Exit;
}
if(prgPropvalUser[0].wFlags!=CEDB_PROPNOTFOUND)
{
if(LOWORD(prgPropvalUser[0].propid) == CEVT_LPWSTR)
{
Log(prgPropvalUser[0].val.lpwstr);
Log(TEXT("\t"));
}
}
if(prgPropvalUser[1].wFlags!=CEDB_PROPNOTFOUND)
{
if(LOWORD(prgPropvalUser[1].propid) == CEVT_LPWSTR)
{
Log(prgPropvalUser[1].val.lpwstr);
Log(TEXT("\t"));
}
}
if(prgPropvalUser[2].wFlags!=CEDB_PROPNOTFOUND)
{
if(LOWORD(prgPropvalUser[2].propid) == CEVT_UI4)
{
switch (prgPropvalUser[2].val.ulVal)
{
case PIMPR_IM2_ADDRESS:
if(prgPropvalUser[3].wFlags!=CEDB_PROPNOTFOUND)
{
if(LOWORD(prgPropvalUser[3].propid) == CEVT_LPWSTR)
{
Log(prgPropvalUser[3].val.lpwstr);
}
}
break;
case PIMPR_ASSISTANT_TELEPHONE_NUMBER:
if(prgPropvalUser[4].wFlags!=CEDB_PROPNOTFOUND)
{
if(LOWORD(prgPropvalUser[4].propid) == CEVT_LPWSTR)
{
Log(prgPropvalUser[4].val.lpwstr);
}
}
break;
case PIMPR_BUSINESS_TELEPHONE_NUMBER:
if(prgPropvalUser[5].wFlags!=CEDB_PROPNOTFOUND)
{
if(LOWORD(prgPropvalUser[5].propid) == CEVT_LPWSTR)
{
Log(prgPropvalUser[5].val.lpwstr);
}
}
break;
case PIMPR_BUSINESS2_TELEPHONE_NUMBER:
if(prgPropvalUser[6].wFlags!=CEDB_PROPNOTFOUND)
{
if(LOWORD(prgPropvalUser[6].propid) == CEVT_LPWSTR)
{
Log(prgPropvalUser[6].val.lpwstr);
}
}
break;
case PIMPR_CAR_TELEPHONE_NUMBER:
if(prgPropvalUser[7].wFlags!=CEDB_PROPNOTFOUND)
{
if(LOWORD(prgPropvalUser[7].propid) == CEVT_LPWSTR)
{
Log(prgPropvalUser[7].val.lpwstr);
}
}
break;
case PIMPR_COMPANY_TELEPHONE_NUMBER:
if(prgPropvalUser[8].wFlags!=CEDB_PROPNOTFOUND)
{
if(LOWORD(prgPropvalUser[8].propid) == CEVT_LPWSTR)
{
Log(prgPropvalUser[8].val.lpwstr);
}
}
break;
case PIMPR_EMAIL1_ADDRESS:
if(prgPropvalUser[9].wFlags!=CEDB_PROPNOTFOUND)
{
if(LOWORD(prgPropvalUser[9].propid) == CEVT_LPWSTR)
{
Log(prgPropvalUser[9].val.lpwstr);
}
}
break;
case PIMPR_EMAIL2_ADDRESS:
if(prgPropvalUser[10].wFlags!=CEDB_PROPNOTFOUND)
{
if(LOWORD(prgPropvalUser[10].propid) == CEVT_LPWSTR)
{
Log(prgPropvalUser[10].val.lpwstr);
}
}
break;
case PIMPR_EMAIL3_ADDRESS:
if(prgPropvalUser[11].wFlags!=CEDB_PROPNOTFOUND)
{
if(LOWORD(prgPropvalUser[11].propid) == CEVT_LPWSTR)
{
Log(prgPropvalUser[11].val.lpwstr);
}
}
break;
case PIMPR_HOME_TELEPHONE_NUMBER:
if(prgPropvalUser[12].wFlags!=CEDB_PROPNOTFOUND)
{
if(LOWORD(prgPropvalUser[12].propid) == CEVT_LPWSTR)
{
Log(prgPropvalUser[12].val.lpwstr);
}
}
break;
case PIMPR_HOME2_TELEPHONE_NUMBER:
if(prgPropvalUser[13].wFlags!=CEDB_PROPNOTFOUND)
{
if(LOWORD(prgPropvalUser[13].propid) == CEVT_LPWSTR)
{
Log(prgPropvalUser[13].val.lpwstr);
}
}
break;
case PIMPR_IM1_ADDRESS:
if(prgPropvalUser[14].wFlags!=CEDB_PROPNOTFOUND)
{
if(LOWORD(prgPropvalUser[14].propid) == CEVT_LPWSTR)
{
Log(prgPropvalUser[14].val.lpwstr);
}
}
break;
case PIMPR_IM3_ADDRESS:
if(prgPropvalUser[15].wFlags!=CEDB_PROPNOTFOUND)
{
if(LOWORD(prgPropvalUser[15].propid) == CEVT_LPWSTR)
{
Log(prgPropvalUser[15].val.lpwstr);
}
}
break;
case PIMPR_MOBILE_TELEPHONE_NUMBER:
if(prgPropvalUser[16].wFlags!=CEDB_PROPNOTFOUND)
{
if(LOWORD(prgPropvalUser[16].propid) == CEVT_LPWSTR)
{
Log(prgPropvalUser[16].val.lpwstr);
}
}
break;
case PIMPR_PAGER_NUMBER:
if(prgPropvalUser[17].wFlags!=CEDB_PROPNOTFOUND)
{
if(LOWORD(prgPropvalUser[17].propid) == CEVT_LPWSTR)
{
Log(prgPropvalUser[17].val.lpwstr);
}
}
break;
case PIMPR_RADIO_TELEPHONE_NUMBER:
if(prgPropvalUser[18].wFlags!=CEDB_PROPNOTFOUND)
{
if(LOWORD(prgPropvalUser[18].propid) == CEVT_LPWSTR)
{
Log(prgPropvalUser[18].val.lpwstr);
}
}
break;
case PIMPR_SMS:
if(prgPropvalUser[19].wFlags!=CEDB_PROPNOTFOUND)
{
if(LOWORD(prgPropvalUser[19].propid) == CEVT_LPWSTR)
{
Log(prgPropvalUser[19].val.lpwstr);
}
}
break;
} //switch
}
}
Log(TEXT("\r\n"));
//success
hr = S_OK;
Exit:
LocalFree(prgPropvalUser);
return hr;
}
// **************************************************************************
// Log
HRESULT Log(LPTSTR szLog)
{
HRESULT hr = E_FAIL;
hr = LogToFile(szLog, g_pszFilename);
CHR(hr);
Exit:
return hr;
}
// **************************************************************************
// LogToFile
// Writes szLog into the file named pszFilename
HRESULT LogToFile(LPTSTR szLog, LPCTSTR pszFilename)
{
HRESULT hr = E_FAIL;
//Open the handle to the file (and create it if it doesn't exist
HANDLE hFile = CreateFile(pszFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == hFile)
goto Exit;
//Set the pointer at the end so that we can append szLog
DWORD dwFilePointer = SetFilePointer(hFile, 0, NULL, FILE_END);
if (0xFFFFFFFF == dwFilePointer)
goto Exit;
//Write to the file
DWORD dwBytesWritten = 0;
BOOL bWriteFileRet = WriteFile(hFile, szLog, wcslen(szLog) * 2, &dwBytesWritten, NULL);
if (!bWriteFileRet)
goto Exit;
//Flush the buffer
BOOL bFlushFileBuffersRet = FlushFileBuffers(hFile);
if (!bFlushFileBuffersRet)
goto Exit;
//Success
hr = S_OK;
Exit:
if (NULL != hFile)
CloseHandle(hFile);
return hr;
}
Cheers,
~raffaele
Comments
- Anonymous
May 28, 2009
PingBack from http://www.anith.com/?p=42550