|
|
/*++
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 <windows.h>
#include <prsht.h>
#include <tchar.h>
#include <wab.h>
#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; }
|