/*++ Copyright (c) 1997 Microsoft Corporation Module Name: cwabobj.cpp Abstract: Interface to the windows address book. Environment: Fax send wizard Revision History: 10/23/97 -GeorgeJe- Created it. mm/dd/yy -author- description --*/ #include #include #include #include #include "faxui.h" #include "cwabobj.h" static LPWSTR DupUnicodeString( LPWSTR pStr ); static LPWSTR DupStringAnsiToUnicode( LPSTR pAnsiStr ); static LPSPropValue FindProp( LPSPropValue rgprop, ULONG cprop, ULONG ulPropTag ); static AddRecipient( PRECIPIENT *ppNewRecip, LPWSTR DisplayName, LPWSTR FaxNumber ); static SizedSPropTagArray(5, sPropTags) = { 5, { PR_DISPLAY_NAME_A, PR_PRIMARY_FAX_NUMBER_A, PR_HOME_FAX_NUMBER_A, PR_BUSINESS_FAX_NUMBER_A, PR_OBJECT_TYPE } }; CWabObj::CWabObj( HINSTANCE hInstance ) /*++ Routine Description: Constructor for CWabObj class Arguments: hInstance - Instance handle Return Value: NONE --*/ { m_Initialized = FALSE; m_lpAdrList = NULL; m_hInstance = hInstance; } BOOL CWabObj::Initialize( VOID ) /*++ Routine Description: intialization function for CWabObj class Arguments: NONE Return Value: TRUE if the object is initialized successfully, else FALSE --*/ { TCHAR szDllPath[MAX_PATH]; HKEY hKey = NULL; LONG rVal; DWORD dwType; DWORD cbData = MAX_PATH * sizeof(TCHAR); HRESULT hr; PCTSTR szDefaultPath = TEXT("%CommonProgramFiles%\\System\\wab32.dll"); m_Initialized = TRUE; // // get the path to wab32.dll // rVal = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REGVAL_WABPATH, 0, KEY_READ, &hKey ); if (rVal == ERROR_SUCCESS) { rVal = RegQueryValueEx( hKey, TEXT(""), NULL, &dwType, (LPBYTE) szDllPath, &cbData ); RegCloseKey( hKey ); } if (rVal != ERROR_SUCCESS) { ExpandEnvironmentStrings(szDefaultPath,szDllPath,sizeof(szDllPath)/sizeof(TCHAR)); } m_hWab = LoadLibrary( szDllPath ); if (m_hWab != NULL) { m_lpWabOpen = (LPWABOPEN) GetProcAddress( m_hWab , "WABOpen" ); } else { m_lpWabOpen = (LPWABOPEN) NULL; } if (m_lpWabOpen == NULL) { hr = E_FAIL; goto exit; } // // open the wab // hr = m_lpWabOpen( &m_lpAdrBook, &m_lpWABObject, 0, 0 ); exit: if (HR_FAILED(hr)) { m_lpAdrBook = NULL; m_lpWABObject = NULL; m_Initialized = FALSE; if (m_hWab != NULL) { FreeLibrary( m_hWab ); } m_hWab = NULL; } return(m_Initialized); } CWabObj::~CWabObj() /*++ Routine Description: Destructor for CWabObj class Arguments: NONE Return Value: NONE --*/ { if (m_lpAdrBook) { m_lpAdrBook->Release(); } if (m_lpWABObject) { m_lpWABObject->Release(); } if ( m_hWab ) { FreeLibrary( m_hWab ); } } BOOL CWabObj::Address( HWND hWnd, PRECIPIENT pRecipients, PRECIPIENT * ppNewRecip ) /*++ Routine Description: Bring up the address book UI. Prepopulate the to box with the entries in pRecipient. Return the modified entries in ppNewRecip. Arguments: hWnd - window handle to parent window pRecipients - list of recipients to look up ppNewRecipients - list of new/modified recipients Return Value: TRUE if all recipients had a fax number. FALSE if one or more of them didn't. --*/ { ADRPARM AdrParms = { 0 }; LPADRLIST tmp; HRESULT hr; DWORD i; DWORD nRecips; PRECIPIENT tmpRecipient; ULONG DestComps[3] = { MAPI_TO, MAPI_CC, MAPI_BCC }; DWORD cDropped; nRecips = 0; tmpRecipient = pRecipients; m_hWnd = hWnd; m_PickNumber = 0; // // count recipients and set up initial address list // while (tmpRecipient) { nRecips++; tmpRecipient = (PRECIPIENT) tmpRecipient->pNext; } if (nRecips > 0) { hr = m_lpWABObject->AllocateBuffer( CbNewADRLIST( nRecips ), (LPVOID *) &m_lpAdrList ); m_lpAdrList->cEntries = nRecips; } else { m_lpAdrList = NULL; } for (i = 0, tmpRecipient = pRecipients; i < nRecips; i++, tmpRecipient = (PRECIPIENT) tmpRecipient->pNext) { LPADRENTRY lpAdrEntry = &m_lpAdrList->aEntries[i]; lpAdrEntry->cValues = 3; hr = m_lpWABObject->AllocateBuffer( sizeof( SPropValue ) * 3, (LPVOID *) &lpAdrEntry->rgPropVals ); ZeroMemory( lpAdrEntry->rgPropVals, sizeof( SPropValue ) * 3 ); lpAdrEntry->rgPropVals[0].ulPropTag = PR_DISPLAY_NAME_A; lpAdrEntry->rgPropVals[0].Value.lpszA = DupStringUnicodeToAnsi( lpAdrEntry->rgPropVals, tmpRecipient->pName ); lpAdrEntry->rgPropVals[1].ulPropTag = PR_RECIPIENT_TYPE; lpAdrEntry->rgPropVals[1].Value.l = MAPI_TO; lpAdrEntry->rgPropVals[2].ulPropTag = PR_PRIMARY_FAX_NUMBER_A; lpAdrEntry->rgPropVals[2].Value.lpszA = DupStringUnicodeToAnsi( lpAdrEntry->rgPropVals, tmpRecipient->pAddress ); } tmp = m_lpAdrList; AdrParms.cDestFields = 1; AdrParms.ulFlags = DIALOG_MODAL; AdrParms.nDestFieldFocus = 0; AdrParms.lpulDestComps = DestComps; AdrParms.lpszCaption = TEXT( "" ); // // Bring up the address book UI // hr = m_lpAdrBook->Address( (ULONG *) &hWnd, &AdrParms, &m_lpAdrList ); if (FAILED (hr) || !m_lpAdrList || m_lpAdrList->cEntries == 0) { // // in this case the user pressed cancel, so we skip resolving any of our addresses that aren't listed in the // WAB // cDropped = 0; goto skipresolve; } // // Resolve names // hr = m_lpAdrBook->ResolveName ((ULONG_PTR)hWnd, 0, NULL, m_lpAdrList); skipresolve: tmp = m_lpAdrList; if (m_lpAdrList) { for (i = cDropped = 0; i < m_lpAdrList->cEntries; i++) { LPADRENTRY lpAdrEntry = &m_lpAdrList->aEntries[i]; if (!InterpretAddress( lpAdrEntry->rgPropVals, lpAdrEntry->cValues, ppNewRecip )){ cDropped++; } } // // Clean up // for (ULONG iEntry = 0; iEntry < m_lpAdrList->cEntries; ++iEntry) { if(m_lpAdrList->aEntries[iEntry].rgPropVals) m_lpWABObject->FreeBuffer(m_lpAdrList->aEntries[iEntry].rgPropVals); } m_lpWABObject->FreeBuffer(m_lpAdrList); m_lpAdrList = NULL; } m_hWnd = NULL; return cDropped == 0; } BOOL CWabObj::InterpretAddress( LPSPropValue SPropVal, ULONG cValues, PRECIPIENT *ppNewRecip ) /*++ Routine Description: Interpret the address book entry represented by SPropVal. Arguments: SPropVal - Property values for address book entry. cValues - number of property values ppNewRecip - new recipient list Return Value: TRUE if all of the entries have a fax number. FALSE otherwise. --*/ { LPSPropValue lpSPropVal; LPWSTR FaxNumber, DisplayName; BOOL rVal = FALSE; // // get the object type // lpSPropVal = FindProp( SPropVal, cValues, PR_OBJECT_TYPE ); if (lpSPropVal) { // // If the object is a mail user, get the fax numbers and add the recipient // to the list. If the object is a distribtion list, process it. // switch (lpSPropVal->Value.l) { case MAPI_MAILUSER: if(GetRecipientInfo( SPropVal, cValues, &FaxNumber, &DisplayName )) { AddRecipient( ppNewRecip, DisplayName, FaxNumber ); rVal = TRUE; } break; case MAPI_DISTLIST: rVal = InterpretDistList( SPropVal, cValues, ppNewRecip ); } return rVal; } else { // // If there is no object type then this is valid entry that we queried on that went unresolved. // We know that there is a fax number so add it. // if(GetRecipientInfo( SPropVal, cValues, &FaxNumber, &DisplayName )) { AddRecipient( ppNewRecip, DisplayName, FaxNumber ); rVal = TRUE; } } return rVal; } BOOL CWabObj::InterpretDistList( LPSPropValue SPropVal, ULONG cValues, PRECIPIENT * ppNewRecip ) /*++ Routine Description: Process a distribution list. Arguments: SPropVal - Property values for distribution list. cValues - Number of properties. ppNewRecip - New recipient list. Return Value: TRUE if all of the entries have a fax number. FALSE otherwise. --*/ #define EXIT_IF_FAILED(hr) { if (FAILED(hr)) goto ExitDistList; } { LPSPropValue lpPropVals; LPSRowSet pRows = NULL; LPDISTLIST lpMailDistList = NULL; LPMAPITABLE pMapiTable = NULL; ULONG ulObjType, cRows; HRESULT hr; BOOL rVal = FALSE; lpPropVals = FindProp( SPropVal, cValues, PR_ENTRYID ); if (lpPropVals) { LPENTRYID lpEntryId = (LPENTRYID) lpPropVals->Value.bin.lpb; DWORD cbEntryId = lpPropVals->Value.bin.cb; // // Open the recipient entry // hr = m_lpAdrBook->OpenEntry( cbEntryId, lpEntryId, (LPCIID) NULL, 0, &ulObjType, (LPUNKNOWN *) &lpMailDistList ); EXIT_IF_FAILED( hr ); // // Get the contents table of the address entry // hr = lpMailDistList->GetContentsTable( MAPI_DEFERRED_ERRORS, &pMapiTable ); EXIT_IF_FAILED(hr); // // Limit the query to only the properties we're interested in // hr = pMapiTable->SetColumns((LPSPropTagArray) &sPropTags, 0); EXIT_IF_FAILED(hr); // // Get the total number of rows // hr = pMapiTable->GetRowCount(0, &cRows); EXIT_IF_FAILED(hr); // // Get the individual entries of the distribution list // hr = pMapiTable->SeekRow(BOOKMARK_BEGINNING, 0, NULL); EXIT_IF_FAILED(hr); hr = pMapiTable->QueryRows(cRows, 0, &pRows); EXIT_IF_FAILED(hr); hr = S_OK; if (pRows && pRows->cRows) { // // Handle each entry of the distribution list in turn: // for simple entries, call InterpretAddress // for embedded distribution list, call this function recursively // for (cRows = 0; cRows < pRows->cRows; cRows++) { LPSPropValue lpProps = pRows->aRow[cRows].lpProps; ULONG cRowValues = pRows->aRow[cRows].cValues; lpPropVals = FindProp( lpProps, cRowValues, PR_OBJECT_TYPE ); if (lpPropVals) { switch (lpPropVals->Value.l) { case MAPI_MAILUSER: rVal = InterpretAddress( lpProps, cRowValues, ppNewRecip ); break; case MAPI_DISTLIST: rVal = InterpretDistList( lpProps, cRowValues, ppNewRecip ); } } } } } ExitDistList: // // Perform necessary clean up before returning to caller // if (pRows) { for (cRows = 0; cRows < pRows->cRows; cRows++) { m_lpWABObject->FreeBuffer(pRows->aRow[cRows].lpProps); } m_lpWABObject->FreeBuffer(pRows); } if (pMapiTable) pMapiTable->Release(); if (lpMailDistList) lpMailDistList->Release(); return rVal; } INT_PTR CALLBACK ChooseFaxNumberDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) /*++ Routine Description: Dialog proc for choose fax number dialog. Arguments: lParam - pointer to PickFax structure. Return Value: Control id of selection. --*/ { PPICKFAX pPickFax = (PPICKFAX) lParam; TCHAR buffer[MAX_TITLE_LEN]; switch (uMsg) { case WM_INITDIALOG: SetDlgItemText(hDlg, IDC_DISPLAY_NAME, pPickFax->DisplayName); buffer[0] = 0; GetDlgItemText(hDlg, IDC_BUSINESS_FAX, buffer, MAX_TITLE_LEN); _tcscat(buffer, pPickFax->BusinessFax); SetDlgItemText(hDlg, IDC_BUSINESS_FAX, buffer); buffer[0] = 0; GetDlgItemText(hDlg, IDC_HOME_FAX, buffer, MAX_TITLE_LEN); _tcscat(buffer, pPickFax->HomeFax); SetDlgItemText(hDlg, IDC_HOME_FAX, buffer); CheckDlgButton(hDlg, IDC_BUSINESS_FAX, BST_CHECKED); return TRUE; case WM_COMMAND: switch(LOWORD( wParam )){ case IDOK: if (IsDlgButtonChecked(hDlg, IDC_BUSINESS_FAX) == BST_CHECKED) { if (IsDlgButtonChecked(hDlg, IDC_ALWAYS_OPTION) == BST_CHECKED) { EndDialog(hDlg, IDC_ALLBUS); } else { EndDialog(hDlg, IDC_BUSINESS_FAX); } } else if (IsDlgButtonChecked(hDlg, IDC_HOME_FAX) == BST_CHECKED) { if (IsDlgButtonChecked(hDlg, IDC_ALWAYS_OPTION) == BST_CHECKED) { EndDialog(hDlg, IDC_ALLHOME); } else { EndDialog(hDlg, IDC_HOME_FAX); } } break;; } break; default: return FALSE; } return FALSE; } #define StrPropOk( strprop ) ((strprop) && (strprop)->Value.lpszA && *(strprop)->Value.lpszA) BOOL CWabObj::GetRecipientInfo( LPSPropValue SPropVals, ULONG cValues, LPWSTR * FaxNumber, LPWSTR * DisplayName ) /*++ Routine Description: Get the fax number and display name properties. Arguments: SPropVal - Property values for distribution list. cValues - Number of properties. FaxNumber - pointer to pointer to string to hold the fax number. DisplayName - pointer to pointer to string to hold the display name. Return Value: TRUE if there is a fax number and display name. FALSE otherwise. --*/ { LPSPropValue lpPropVals; LPSPropValue lpPropArray; BOOL Result = FALSE; PICKFAX PickFax = { 0 }; *FaxNumber = *DisplayName = NULL; // // Get the entryid and open the entry. // lpPropVals = FindProp( SPropVals, cValues, PR_ENTRYID ); if (lpPropVals) { ULONG lpulObjType; LPMAILUSER lpMailUser = NULL; LPENTRYID lpEntryId = (LPENTRYID) lpPropVals->Value.bin.lpb; DWORD cbEntryId = lpPropVals->Value.bin.cb; HRESULT hr; ULONG countValues; hr = m_lpAdrBook->OpenEntry( cbEntryId, lpEntryId, (LPCIID) NULL, 0, &lpulObjType, (LPUNKNOWN *) &lpMailUser ); if (HR_SUCCEEDED(hr)) { // // Get the properties. // hr = ((IMailUser *) lpMailUser)->GetProps( (LPSPropTagArray) &sPropTags, 0, &countValues, &lpPropArray ); if (HR_SUCCEEDED(hr)) { lpPropVals = FindProp( lpPropArray, countValues, PR_BUSINESS_FAX_NUMBER_A ); if (StrPropOk( lpPropVals )) { PickFax.BusinessFax = DupStringAnsiToUnicode( lpPropVals->Value.lpszA ); } lpPropVals = FindProp( lpPropArray, countValues, PR_HOME_FAX_NUMBER_A ); if (StrPropOk( lpPropVals )) { PickFax.HomeFax = DupStringAnsiToUnicode( lpPropVals->Value.lpszA ); } lpPropVals = FindProp( lpPropArray, countValues, PR_DISPLAY_NAME_A ); if (StrPropOk( lpPropVals )) { *DisplayName = PickFax.DisplayName = DupStringAnsiToUnicode( lpPropVals->Value.lpszA ); } // // If there are two fax numbers, ask the user to pick one. // if (PickFax.BusinessFax && PickFax.HomeFax) { int dlgResult; if (m_PickNumber != 0) { dlgResult = m_PickNumber; } else { dlgResult = (int)DialogBoxParam( (HINSTANCE) m_hInstance, MAKEINTRESOURCE( IDD_CHOOSE_FAXNUMBER ), m_hWnd, ChooseFaxNumberDlgProc, (LPARAM) &PickFax ); } switch( dlgResult ) { case IDC_ALLBUS: m_PickNumber = IDC_BUSINESS_FAX; // fall through case IDC_BUSINESS_FAX: MemFree( PickFax.HomeFax ); *FaxNumber = PickFax.BusinessFax; break; case IDC_ALLHOME: m_PickNumber = IDC_HOME_FAX; // fall through case IDC_HOME_FAX: MemFree( PickFax.BusinessFax ); *FaxNumber = PickFax.HomeFax; break; } } else if (PickFax.BusinessFax) { *FaxNumber = PickFax.BusinessFax; } else if (PickFax.HomeFax) { *FaxNumber = PickFax.HomeFax; } } m_lpWABObject->FreeBuffer( lpPropArray ); } if (lpMailUser) { lpMailUser->Release(); } } else { // If there is no entryid, then this is a valid entry that we queried on that went unresolved // add if anyway. In this case we know that PR_PRIMARY_FAX_NUMBER_A and PR_DISPLAY_NAME_A will be // present. lpPropVals = FindProp( SPropVals, cValues, PR_PRIMARY_FAX_NUMBER_A ); if (lpPropVals) { *FaxNumber = DupStringAnsiToUnicode( lpPropVals->Value.lpszA ); } lpPropVals = FindProp( SPropVals, cValues, PR_DISPLAY_NAME_A ); if (lpPropVals) { *DisplayName = DupStringAnsiToUnicode( lpPropVals->Value.lpszA ); } } if (FaxNumber && DisplayName) { return (*FaxNumber != 0 && *DisplayName != 0); } else { return FALSE; } } LPSPropValue FindProp( LPSPropValue rgprop, ULONG cprop, ULONG ulPropTag ) /*++ Routine Description: Searches for a given property tag in a propset. If the given property tag has type PT_UNSPECIFIED, matches only on the property ID; otherwise, matches on the entire tag. Arguments: rgprop - Property values. cprop - Number of properties. ulPropTag - Property to search for. Return Value: Pointer to property desired property value or NULL. --*/ { BOOL f = PROP_TYPE(ulPropTag) == PT_UNSPECIFIED; LPSPropValue pprop = rgprop; if (!cprop || !rgprop) return NULL; while (cprop--) { if (pprop->ulPropTag == ulPropTag || (f && PROP_ID(pprop->ulPropTag) == PROP_ID(ulPropTag))) return pprop; ++pprop; } return NULL; } LPSTR CWabObj::DupStringUnicodeToAnsi( LPVOID lpObject, LPWSTR pUnicodeStr ) /*++ Routine Description: Convert a Unicode string to a multi-byte string Arguments: pUnicodeStr - Pointer to the Unicode string to be duplicated Return Value: Pointer to the duplicated multi-byte string NOTE: This is only need because the WAB is not Unicode enabled on NT. This uses the WAB memory allocator so it must be freed with FreeBuffer. --*/ { INT nChar; LPSTR pAnsiStr; // // Figure out how much memory to allocate for the multi-byte string // if (! (nChar = WideCharToMultiByte(CP_ACP, 0, pUnicodeStr, -1, NULL, 0, NULL, NULL)) || ! HR_SUCCEEDED( m_lpWABObject->AllocateMore( nChar, lpObject, (LPVOID *) &pAnsiStr ))) { return NULL; } // // Convert Unicode string to multi-byte string // WideCharToMultiByte(CP_ACP, 0, pUnicodeStr, -1, pAnsiStr, nChar, NULL, NULL); return pAnsiStr; } LPWSTR DupStringAnsiToUnicode( LPSTR pAnsiStr ) /*++ Routine Description: Convert a multi-byte string to a Unicode string Arguments: pAnsiStr - Pointer to the Ansi string to be duplicated Return Value: Pointer to the duplicated Unicode string NOTE: This is only need because MAPI is not Unicode enabled on NT. This routine uses MemAlloc to allocate memory so the caller needs to use MemFree. --*/ { INT nChar; LPWSTR pUnicodeStr; // // Figure out how much memory to allocate for the Unicode string // if (! (nChar = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pAnsiStr, -1, NULL, 0)) || ! ( pUnicodeStr = (LPWSTR) MemAlloc( nChar * sizeof(WCHAR) ) )) { return NULL; } // // Convert Unicode string to multi-byte string // MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pAnsiStr, -1, pUnicodeStr, nChar); return pUnicodeStr; } LPWSTR DupUnicodeString( LPWSTR pStr ) /*++ Routine Description: Duplicate a Unicode string. Arguments: pStr - pointer to string to duplicate. Return Value: pointer to duplicated string. --*/ { LPWSTR NewStr; NewStr = (LPWSTR) MemAlloc( (wcslen( pStr ) + 1) * sizeof (WCHAR)); wcscpy( NewStr, pStr ); return NewStr; } AddRecipient( PRECIPIENT *ppNewRecip, LPWSTR DisplayName, LPWSTR FaxNumber ) /*++ Routine Description: Add a recipient to the recipient list. Arguments: ppNewRecip - pointer to pointer to list to add item to. DisplayName - recipient name. FaxNumber - recipient fax number. Return Value: NA --*/ { PRECIPIENT NewRecip; NewRecip = (PRECIPIENT) MemAllocZ( sizeof( RECIPIENT ) ); if (NewRecip) { NewRecip->pName = DisplayName; NewRecip->pAddress = FaxNumber; NewRecip->pNext = (LPVOID) *ppNewRecip; *ppNewRecip = NewRecip; } return 0; } extern "C" BOOL CallWabAddress( HWND hDlg, PUSERMEM pUserMem, PRECIPIENT * ppNewRecipient ) /*++ Routine Description: C wrapper for CWabObj->Address Arguments: hDlg - parent window handle. pUserMem - pointer to USERMEM structure ppNewRecipient - list to add new recipients to. Return Value: TRUE if all of the entries have a fax number. FALSE otherwise. --*/ { LPWABOBJ lpCWabObj = (LPWABOBJ) pUserMem->lpWabInit; return lpCWabObj->Address( hDlg, pUserMem->pRecipients, ppNewRecipient ); } extern "C" LPVOID InitializeWAB( HINSTANCE hInstance ) /*++ Routine Description: Initialize the WAB. Arguments: hInstance - instance handle. Return Value: NONE --*/ { LPWABOBJ lpWabObj = new CWabObj( hInstance ); if (lpWabObj) { if (!lpWabObj->Initialize()) { delete (lpWabObj); lpWabObj = NULL; } } return (LPVOID) lpWabObj; } extern "C" VOID UnInitializeWAB( LPVOID lpVoid ) /*++ Routine Description: UnInitialize the WAB. Arguments: NONE Return Value: NONE --*/ { LPWABOBJ lpWabObj = (LPWABOBJ) lpVoid; delete lpWabObj; }