You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
778 lines
16 KiB
778 lines
16 KiB
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
cmapiabobj.cpp
|
|
|
|
Abstract:
|
|
|
|
Interface to the MAPI address book.
|
|
|
|
Environment:
|
|
|
|
Fax send wizard
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include <windows.h>
|
|
#include <prsht.h>
|
|
#include <tchar.h>
|
|
|
|
#include <mapiwin.h>
|
|
#include <mapix.h>
|
|
#include <mapiutil.h>
|
|
#include <mapi.h>
|
|
|
|
#include "faxui.h"
|
|
#include "mapiabobj.h"
|
|
#include <mapitags.h>
|
|
#include "edkmdb.h"
|
|
|
|
#define STRSAFE_NO_DEPRECATE
|
|
#include <strsafe.h>
|
|
|
|
|
|
// initialize static variables
|
|
HINSTANCE CMAPIabObj::m_hInstMapi = NULL;
|
|
LPMAPISESSION CMAPIabObj::m_lpMapiSession = NULL;
|
|
LPMAPILOGONEX CMAPIabObj::m_lpfnMAPILogonEx = NULL;
|
|
LPMAPILOGOFF CMAPIabObj::m_lpfnMAPILogoff = NULL;
|
|
LPMAPIADDRESS CMAPIabObj::m_lpfnMAPIAddress = NULL;
|
|
LPMAPIFREEBUFFER CMAPIabObj::m_lpfnMAPIFreeBuffer = NULL;
|
|
LPMAPIINITIALIZE CMAPIabObj::m_lpfnMAPIInitialize = NULL;
|
|
LPMAPIUNINITIALIZE CMAPIabObj::m_lpfnMAPIUninitialize = NULL;
|
|
LPMAPIALLOCATEBUFFER CMAPIabObj::m_lpfnMAPIAllocateBuffer = NULL;
|
|
LPMAPIALLOCATEMORE CMAPIabObj::m_lpfnMAPIAllocateMore = NULL;
|
|
LPMAPIADMINPROFILES CMAPIabObj::m_lpfnMAPIAdminProfiles = NULL;
|
|
LPHrQueryAllRows CMAPIabObj::m_lpfnHrQueryAllRows = NULL;
|
|
|
|
BOOL CMAPIabObj::m_Initialized = FALSE;
|
|
|
|
|
|
CMAPIabObj::CMAPIabObj(
|
|
HINSTANCE hInstance,
|
|
HWND hDlg
|
|
) : CCommonAbObj(hInstance)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Constructor for CMAPIabObj class
|
|
|
|
Arguments:
|
|
|
|
hInstance - Instance handle
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
m_Initialized = InitMapiService(hDlg);
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
CMAPIabObj::FreeProws(
|
|
LPSRowSet prows
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destroy SRowSet structure. Copied from MAPI.
|
|
|
|
Arguments:
|
|
|
|
hFile - Pointer to SRowSet
|
|
|
|
Return value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG irow;
|
|
|
|
if (!prows) {
|
|
return;
|
|
}
|
|
|
|
for (irow = 0; irow < prows->cRows; ++irow) {
|
|
m_lpfnMAPIFreeBuffer(prows->aRow[irow].lpProps);
|
|
}
|
|
|
|
m_lpfnMAPIFreeBuffer( prows );
|
|
}
|
|
|
|
BOOL
|
|
CMAPIabObj::GetDefaultMapiProfile(
|
|
OUT LPSTR ProfileName,
|
|
IN UINT cchProfileName
|
|
)
|
|
{
|
|
BOOL rVal = FALSE;
|
|
LPMAPITABLE pmt = NULL;
|
|
LPSRowSet prws = NULL;
|
|
LPSPropValue pval;
|
|
LPPROFADMIN lpProfAdmin;
|
|
DWORD i;
|
|
DWORD j;
|
|
|
|
if (FAILED(m_lpfnMAPIAdminProfiles(0, &lpProfAdmin )))
|
|
{
|
|
goto exit;
|
|
}
|
|
//
|
|
// get the mapi profile table object
|
|
//
|
|
if (FAILED(lpProfAdmin->GetProfileTable(0, &pmt )))
|
|
{
|
|
goto exit;
|
|
}
|
|
//
|
|
// get the actual profile data, FINALLY
|
|
//
|
|
if (FAILED(pmt->QueryRows( 4000, 0, &prws )))
|
|
{
|
|
goto exit;
|
|
}
|
|
//
|
|
// enumerate the profiles looking for the default profile
|
|
//
|
|
for (i=0; i<prws->cRows; i++)
|
|
{
|
|
pval = prws->aRow[i].lpProps;
|
|
for (j = 0; j < 2; j++)
|
|
{
|
|
if (pval[j].ulPropTag == PR_DEFAULT_PROFILE && pval[j].Value.b)
|
|
{
|
|
//
|
|
// this is the default profile
|
|
//
|
|
if(FAILED(StringCchCopyA(ProfileName, cchProfileName, pval[0].Value.lpszA)))
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
rVal = TRUE;
|
|
break;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
exit:
|
|
FreeProws( prws );
|
|
|
|
if (pmt)
|
|
{
|
|
pmt->Release();
|
|
}
|
|
|
|
if (lpProfAdmin)
|
|
{
|
|
lpProfAdmin->Release();
|
|
}
|
|
return rVal;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CMAPIabObj::DoMapiLogon(
|
|
HWND hDlg
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Logon MAPI to in order to access address book
|
|
|
|
Arguments:
|
|
|
|
hDlg - Handle to the send fax wizard window
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
CHAR strProfileName[MAX_PROFILE_NAME];
|
|
HRESULT status;
|
|
|
|
|
|
status = m_lpfnMAPIInitialize(NULL);
|
|
|
|
if (status != SUCCESS_SUCCESS)
|
|
return FALSE;
|
|
|
|
|
|
if (!GetDefaultMapiProfile(strProfileName, ARR_SIZE(strProfileName)))
|
|
{
|
|
m_lpfnMAPIUninitialize();
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
status = m_lpfnMAPILogonEx((ULONG_PTR) hDlg,
|
|
(LPTSTR)strProfileName,
|
|
NULL,
|
|
MAPI_USE_DEFAULT,
|
|
&m_lpMapiSession);
|
|
if (status != SUCCESS_SUCCESS || !m_lpMapiSession )
|
|
{
|
|
m_lpfnMAPIUninitialize();
|
|
return FALSE;
|
|
}
|
|
|
|
OpenExchangeStore(); // If it fails it might just indicate that exchagne is not here.
|
|
// We don't fail the function in this case.
|
|
|
|
status = m_lpMapiSession->OpenAddressBook((ULONG_PTR) hDlg,
|
|
NULL,
|
|
0,
|
|
&m_lpAdrBook);
|
|
if (HR_FAILED(status) || !m_lpAdrBook)
|
|
{
|
|
if (m_lpMapiSession)
|
|
{
|
|
m_lpMapiSession->Logoff(0,0,0);
|
|
m_lpMapiSession->Release();
|
|
m_lpIMsgStore->Release();
|
|
}
|
|
m_lpIMsgStore=NULL;
|
|
m_lpMapiSession = NULL;
|
|
m_lpfnMAPIUninitialize();
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BOOL CMAPIabObj::OpenExchangeStore()
|
|
{
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Open the Exchange message store and place a pointer to the IMsgStore interface
|
|
in CMAPIabObj::m_lpIMsgStore.
|
|
This is done to keep the store loaded as long as we have the address book opened.
|
|
This resolves an exchange issue where sometimes the address book is released
|
|
alrhough we still have reference count on its interfaces.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
--*/
|
|
|
|
ULONG ulRow=0;
|
|
HRESULT hr = S_OK;
|
|
LPMAPITABLE lpStoreTable = NULL;
|
|
LPSRowSet lprowsStores = NULL;
|
|
SizedSPropTagArray(3, propStoreProperties)=
|
|
{
|
|
3,
|
|
{PR_DISPLAY_NAME_A, PR_ENTRYID, PR_MDB_PROVIDER}
|
|
|
|
};
|
|
|
|
|
|
hr = m_lpMapiSession->GetMsgStoresTable(
|
|
0, // NO UNICODE
|
|
&lpStoreTable);
|
|
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
Error(("IMAPISession::GetMsgStoresTable failed. hr = 0x%08X", hr));
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Now we have a pointer to the message store table. Lets find the exchange store.
|
|
//
|
|
|
|
Assert(NULL!=lpStoreTable);
|
|
hr = m_lpfnHrQueryAllRows
|
|
(
|
|
lpStoreTable, // pointer to the table being queried
|
|
(LPSPropTagArray)&propStoreProperties, // properties to return in each row
|
|
NULL, // no restrictions get the entire table
|
|
NULL, // use default sort order
|
|
0, // No limit on the number of rows retrieved
|
|
&lprowsStores
|
|
);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
Error(("HrQueryAllRows on the stores table failed. hr = 0x%08X", hr));
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Go over the rows and look for the one with PR_MDB_PROVIDER = pbExchangeProviderPrimaryUserGuid
|
|
//
|
|
|
|
for (ulRow=0;ulRow<lprowsStores->cRows;ulRow++)
|
|
{
|
|
|
|
if ( (sizeof(pbExchangeProviderPrimaryUserGuid)-1 == lprowsStores->aRow[ulRow].lpProps[2].Value.bin.cb))
|
|
{
|
|
|
|
if (!memcmp(lprowsStores->aRow[ulRow].lpProps[2].Value.bin.lpb, pbExchangeProviderPrimaryUserGuid, lprowsStores->aRow[ulRow].lpProps[2].Value.bin.cb))
|
|
|
|
{
|
|
|
|
//
|
|
// If this is the Exchange store.
|
|
// Open the information store using the value of PR_ENTRYID
|
|
//
|
|
hr = m_lpMapiSession->OpenMsgStore(
|
|
NULL,
|
|
lprowsStores->aRow[ulRow].lpProps[1].Value.bin.cb,
|
|
(LPENTRYID)lprowsStores->aRow[ulRow].lpProps[1].Value.bin.lpb,
|
|
NULL, // get the standard interface IMsgStore
|
|
MAPI_BEST_ACCESS,
|
|
&m_lpIMsgStore);
|
|
{
|
|
if (FAILED(hr))
|
|
{
|
|
Error(("OpenMsgStore failed for store [%s]. hr = 0x%08X",
|
|
lprowsStores->aRow[ulRow].lpProps[0].Value.lpszA,
|
|
hr));
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (lpStoreTable)
|
|
{
|
|
lpStoreTable->Release();
|
|
lpStoreTable = NULL;
|
|
}
|
|
|
|
if (lprowsStores)
|
|
{
|
|
FreeProws(lprowsStores);
|
|
lprowsStores = NULL;
|
|
}
|
|
|
|
return SUCCEEDED(hr);
|
|
}
|
|
|
|
|
|
BOOL
|
|
CMAPIabObj::InitMapiService(
|
|
HWND hDlg
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize Simple MAPI services
|
|
|
|
Arguments:
|
|
|
|
hDlg - Handle to the send fax wizard window
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE otherwise
|
|
|
|
NOTE:
|
|
|
|
Every successful call to this function must be balanced
|
|
by a call to DeinitMapiService.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL result = FALSE;
|
|
|
|
m_lpIMsgStore=NULL;
|
|
if(!IsOutlookDefaultClient())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Load MAPI32.DLL into memory if necessary
|
|
//
|
|
|
|
if ((m_hInstMapi == NULL) &&
|
|
(m_hInstMapi = LoadLibrary(TEXT("MAPI32.DLL"))))
|
|
{
|
|
//
|
|
// Get pointers to various Simple MAPI functions
|
|
//
|
|
|
|
m_lpfnMAPILogonEx = (LPMAPILOGONEX) GetProcAddress(m_hInstMapi, "MAPILogonEx");
|
|
m_lpfnMAPILogoff = (LPMAPILOGOFF) GetProcAddress(m_hInstMapi, "MAPILogoff");
|
|
m_lpfnMAPIAddress = (LPMAPIADDRESS) GetProcAddress(m_hInstMapi, "MAPIAddress");
|
|
m_lpfnMAPIFreeBuffer = (LPMAPIFREEBUFFER) GetProcAddress(m_hInstMapi, "MAPIFreeBuffer");
|
|
m_lpfnMAPIInitialize = (LPMAPIINITIALIZE) GetProcAddress(m_hInstMapi, "MAPIInitialize");
|
|
m_lpfnMAPIUninitialize = (LPMAPIUNINITIALIZE) GetProcAddress(m_hInstMapi, "MAPIUninitialize");
|
|
m_lpfnMAPIAllocateBuffer = (LPMAPIALLOCATEBUFFER) GetProcAddress(m_hInstMapi, "MAPIAllocateBuffer");
|
|
m_lpfnMAPIAllocateMore = (LPMAPIALLOCATEMORE) GetProcAddress(m_hInstMapi, "MAPIAllocateMore");
|
|
m_lpfnMAPIAdminProfiles = (LPMAPIADMINPROFILES)GetProcAddress( m_hInstMapi,"MAPIAdminProfiles" );
|
|
m_lpfnHrQueryAllRows = (LPHrQueryAllRows)GetProcAddress(m_hInstMapi,"HrQueryAllRows@24");
|
|
|
|
|
|
//
|
|
// Begins a simple MAPI session and obtain session handle and pointer
|
|
//
|
|
|
|
if (m_lpfnMAPILogonEx == NULL ||
|
|
m_lpfnMAPILogoff == NULL ||
|
|
m_lpfnMAPIAddress == NULL ||
|
|
m_lpfnMAPIFreeBuffer == NULL ||
|
|
m_lpfnMAPIInitialize == NULL ||
|
|
m_lpfnMAPIUninitialize == NULL ||
|
|
m_lpfnMAPIAllocateBuffer == NULL ||
|
|
m_lpfnMAPIAllocateMore == NULL ||
|
|
m_lpfnMAPIAdminProfiles == NULL ||
|
|
m_lpfnHrQueryAllRows == NULL ||
|
|
!DoMapiLogon(hDlg))
|
|
{
|
|
//
|
|
// Clean up properly in case of error
|
|
//
|
|
|
|
m_lpMapiSession = NULL;
|
|
FreeLibrary(m_hInstMapi);
|
|
m_hInstMapi = NULL;
|
|
}
|
|
else
|
|
{
|
|
result = TRUE;
|
|
}
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
//
|
|
// The Outlook Address Book supports Unicode since version 10 (Office XP)
|
|
// So we check the version
|
|
//
|
|
HKEY hRegKey = NULL;
|
|
|
|
hRegKey = OpenRegistryKey(HKEY_LOCAL_MACHINE, REGKEY_OUTLOOK_CUR_VER, FALSE, KEY_READ);
|
|
if(!hRegKey)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
LPWSTR szCurVer = NULL;
|
|
szCurVer = GetRegistryString(hRegKey, TEXT(""), TEXT(""));
|
|
RegCloseKey(hRegKey);
|
|
|
|
if(!szCurVer)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// The Outlook version string is Outlook.Application.X
|
|
// Where is X the version
|
|
//
|
|
LPWSTR ptr = wcsrchr(szCurVer, TEXT('.'));
|
|
if(ptr)
|
|
{
|
|
ptr = _wcsinc(ptr);
|
|
DWORD dwVer = _wtoi(ptr);
|
|
|
|
if(dwVer >= 10)
|
|
{
|
|
m_bUnicode = TRUE;
|
|
}
|
|
}
|
|
|
|
MemFree (szCurVer);
|
|
|
|
#endif // UNICODE
|
|
|
|
return result;
|
|
}
|
|
|
|
VOID
|
|
CMAPIabObj::DeinitMapiService(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deinitialize Simple MAPI services if necessary
|
|
|
|
Arguments:
|
|
|
|
NONE
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
if (m_hInstMapi != NULL)
|
|
{
|
|
if (m_lpAdrBook)
|
|
{
|
|
m_lpAdrBook->Release();
|
|
m_lpAdrBook = NULL;
|
|
}
|
|
if (m_lpIMsgStore)
|
|
{
|
|
m_lpIMsgStore->Release();
|
|
m_lpIMsgStore = NULL;
|
|
}
|
|
if (m_lpMapiSession)
|
|
{
|
|
m_lpMapiSession->Logoff(0,0,0);
|
|
m_lpMapiSession->Release();
|
|
m_lpMapiSession = NULL;
|
|
}
|
|
|
|
m_lpfnMAPIUninitialize();
|
|
FreeLibrary(m_hInstMapi);
|
|
m_hInstMapi = NULL;
|
|
}
|
|
}
|
|
|
|
CMAPIabObj::~CMAPIabObj()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destructor for CMAPIabObj class
|
|
|
|
Arguments:
|
|
|
|
NONE
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
{
|
|
DeinitMapiService();
|
|
}
|
|
|
|
HRESULT
|
|
CMAPIabObj::ABAllocateBuffer(
|
|
ULONG cbSize,
|
|
LPVOID FAR * lppBuffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
--*/
|
|
|
|
{
|
|
return m_lpfnMAPIAllocateBuffer( cbSize, lppBuffer );
|
|
}
|
|
|
|
|
|
ULONG
|
|
CMAPIabObj::ABFreeBuffer(
|
|
LPVOID lpBuffer
|
|
)
|
|
{
|
|
return m_lpfnMAPIFreeBuffer(lpBuffer);
|
|
}
|
|
|
|
|
|
extern "C"
|
|
VOID
|
|
FreeMapiEntryID(
|
|
PWIZARDUSERMEM pWizardUserMem,
|
|
LPENTRYID lpEntryId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
C wrapper for MAPI Free
|
|
|
|
Arguments:
|
|
|
|
pWizardUserMem - pointer to WIZARDUSERMEM structure
|
|
lpEntryID - pointer to EntryId
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
{
|
|
CMAPIabObj * lpCMAPIabObj = (CMAPIabObj *) pWizardUserMem->lpMAPIabInit;
|
|
lpCMAPIabObj->ABFreeBuffer(lpEntryId);
|
|
}
|
|
|
|
extern "C"
|
|
BOOL
|
|
CallMAPIabAddress(
|
|
HWND hDlg,
|
|
PWIZARDUSERMEM pWizardUserMem,
|
|
PRECIPIENT * ppNewRecipient
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
C wrapper for CMAPIabObj->Address
|
|
|
|
Arguments:
|
|
|
|
hDlg - parent window handle.
|
|
pWizardUserMem - pointer to WIZARDUSERMEM structure
|
|
ppNewRecipient - list to add new recipients to.
|
|
|
|
Return Value:
|
|
|
|
TRUE if all of the entries have a fax number.
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
CMAPIabObj * lpCMAPIabObj = (CMAPIabObj *) pWizardUserMem->lpMAPIabInit;
|
|
|
|
return lpCMAPIabObj->Address(
|
|
hDlg,
|
|
pWizardUserMem->pRecipients,
|
|
ppNewRecipient
|
|
);
|
|
|
|
}
|
|
|
|
extern "C"
|
|
LPTSTR
|
|
CallMAPIabAddressEmail(
|
|
HWND hDlg,
|
|
PWIZARDUSERMEM pWizardUserMem
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
C wrapper for CMAPIabObj->AddressEmail
|
|
|
|
Arguments:
|
|
|
|
hDlg - parent window handle.
|
|
pWizardUserMem - pointer to WIZARDUSERMEM structure
|
|
|
|
Return Value:
|
|
|
|
TRUE if found one appropriate E-mail
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
CMAPIabObj * lpCMAPIabObj = (CMAPIabObj *) pWizardUserMem->lpMAPIabInit;
|
|
|
|
return lpCMAPIabObj->AddressEmail(
|
|
hDlg
|
|
);
|
|
|
|
}
|
|
|
|
extern "C"
|
|
LPVOID
|
|
InitializeMAPIAB(
|
|
HINSTANCE hInstance,
|
|
HWND hDlg
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the MAPI.
|
|
|
|
Arguments:
|
|
|
|
hInstance - instance handle.
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
--*/
|
|
|
|
{
|
|
CMAPIabObj * lpCMAPIabObj = new (std::nothrow) CMAPIabObj ( hInstance, hDlg );
|
|
|
|
if ((lpCMAPIabObj!=NULL) && (!lpCMAPIabObj->isInitialized())) // constructor failed
|
|
{
|
|
delete lpCMAPIabObj;
|
|
lpCMAPIabObj = NULL;
|
|
}
|
|
|
|
return (LPVOID) lpCMAPIabObj ;
|
|
}
|
|
|
|
extern "C"
|
|
VOID
|
|
UnInitializeMAPIAB(
|
|
LPVOID lpVoid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
UnInitialize the MAPI.
|
|
|
|
Arguments:
|
|
|
|
NONE
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
--*/
|
|
|
|
{
|
|
CMAPIabObj * lpCMAPIabObj = (CMAPIabObj *) lpVoid;
|
|
|
|
delete lpCMAPIabObj;
|
|
}
|