windows-nt/Source/XPSP1/NT/printscan/fax/service/server/mapi.cpp

2125 lines
47 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
mapi.c
Abstract:
This file implements wrappers for all mapi apis.
The wrappers are necessary because mapi does not
implement unicode and this code must be non-unicode.
Author:
Wesley Witt (wesw) 13-Sept-1996
Revision History:
--*/
#undef UNICODE
#undef _UNICODE
#include <windows.h>
#include <mapiwin.h>
#include <mapix.h>
#include <mapiutil.h>
#include <stdio.h>
#include "profinfo.h"
#include "faxutil.h"
typedef ULONG (STDAPIVCALLTYPE*ULRELEASE)(LPVOID);
typedef VOID (STDAPIVCALLTYPE*FREEPADRLIST)(LPADRLIST);
typedef ULONG (STDAPIVCALLTYPE*HRQUERYALLROWS)(LPMAPITABLE,LPSPropTagArray,LPSRestriction,LPSSortOrderSet,LONG,LPSRowSet*);
typedef SCODE (STDAPIVCALLTYPE*SCDUPPROPSET)(int, LPSPropValue,LPALLOCATEBUFFER, LPSPropValue*);
static LPMAPIINITIALIZE MapiInitialize;
static LPMAPIUNINITIALIZE MapiUnInitialize;
static LPMAPILOGONEX MapiLogonEx;
static LPMAPIFREEBUFFER MapiFreeBuffer;
static LPMAPIALLOCATEBUFFER MapiAllocateBuffer;
static LPMAPIADMINPROFILES MapiAdminProfiles;
static ULRELEASE pUlRelease;
static FREEPADRLIST pFreePadrlist;
static HRQUERYALLROWS pHrQueryAllRows;
static SCDUPPROPSET pScDupPropset;
static MAPIINIT_0 MapiInit;
extern "C" BOOL MapiIsInitialized = FALSE;
extern "C" DWORD ServiceDebug;
extern "C"
LPSTR
UnicodeStringToAnsiString(
LPWSTR UnicodeString
);
extern "C"
VOID
FreeString(
LPVOID String
);
extern "C"
BOOL
InitializeMapi(
VOID
)
/*++
Routine Description:
Initializes MAPI.
Arguments:
NONE
Return Value:
TRUE if successful, FALSE if not
--*/
{
HMODULE MapiMod = NULL;
HRESULT Result;
//
// load the mapi dll
//
MapiMod = LoadLibrary( "mapi32.dll" );
if (!MapiMod) {
return FALSE;
}
//
// get the addresses of the mapi functions that we need
//
MapiInitialize = (LPMAPIINITIALIZE) GetProcAddress( MapiMod, "MAPIInitialize" );
MapiUnInitialize = (LPMAPIUNINITIALIZE) GetProcAddress( MapiMod, "MAPIUninitialize" );
MapiLogonEx = (LPMAPILOGONEX) GetProcAddress( MapiMod, "MAPILogonEx" );
MapiFreeBuffer = (LPMAPIFREEBUFFER) GetProcAddress( MapiMod, "MAPIFreeBuffer" );
MapiAllocateBuffer = (LPMAPIALLOCATEBUFFER) GetProcAddress( MapiMod, "MAPIAllocateBuffer" );
MapiAdminProfiles = (LPMAPIADMINPROFILES) GetProcAddress( MapiMod, "MAPIAdminProfiles" );
pUlRelease = (ULRELEASE) GetProcAddress( MapiMod, "UlRelease@4" );
pFreePadrlist = (FREEPADRLIST) GetProcAddress( MapiMod, "FreePadrlist@4" );
pHrQueryAllRows = (HRQUERYALLROWS) GetProcAddress( MapiMod, "HrQueryAllRows@24" );
pScDupPropset = (SCDUPPROPSET) GetProcAddress( MapiMod, "ScDupPropset@16" );
if ((!MapiInitialize) || (!MapiUnInitialize) ||
(!MapiLogonEx) || (!MapiAllocateBuffer) ||
(!MapiFreeBuffer) || (!MapiAdminProfiles) ||
(!pUlRelease) || (!pFreePadrlist) ||
(!pHrQueryAllRows) || (!pScDupPropset)) {
return FALSE;
}
MapiInit.ulFlags = MAPI_MULTITHREAD_NOTIFICATIONS;
if (!ServiceDebug) {
MapiInit.ulFlags |= MAPI_NT_SERVICE;
}
Result = MapiInitialize(&MapiInit);
if (Result != S_OK) {
return FALSE;
}
return MapiIsInitialized = TRUE;
}
VOID
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) {
MapiFreeBuffer(prows->aRow[irow].lpProps);
}
MapiFreeBuffer( prows );
}
HRESULT
HrMAPIFindInbox(
IN LPMDB lpMdb,
OUT ULONG *lpcbeid,
OUT LPENTRYID *lppeid
)
/*++
Routine Description:
Find IPM inbox folder. Copied from Exchange SDK.
Arguments:
lpMdb - pointer to message store
lpcbeid - count of bytes in entry ID
lppeid - entry ID of IPM inbox
Return value:
HRESULT (see MAPI docs)
--*/
{
HRESULT hr = NOERROR;
SCODE sc = 0;
*lpcbeid = 0;
*lppeid = NULL;
//
// Get the entry ID of the Inbox from the message store
//
hr = lpMdb->GetReceiveFolder(
NULL,
0,
lpcbeid,
lppeid,
NULL
);
return hr;
}
HRESULT
HrMAPIFindOutbox(
IN LPMDB lpMdb,
OUT ULONG *lpcbeid,
OUT LPENTRYID *lppeid
)
/*++
Routine Description:
Find IPM outbox folder. Copied from Exchange SDK.
Arguments:
lpMdb - pointer to message store
lpcbeid - count of bytes in entry ID
lppeid - entry ID of IPM inbox
Return value:
HRESULT (see MAPI docs)
--*/
{
HRESULT hr = NOERROR;
SCODE sc = 0;
ULONG cValues = 0;
LPSPropValue lpPropValue = NULL;
ULONG cbeid = 0;
SPropTagArray rgPropTag = { 1, { PR_IPM_OUTBOX_ENTRYID } };
*lpcbeid = 0;
*lppeid = NULL;
//
// Get the outbox entry ID property.
//
hr = lpMdb->GetProps(
&rgPropTag,
0,
&cValues,
&lpPropValue
);
if (hr == MAPI_W_ERRORS_RETURNED) {
goto cleanup;
}
if (FAILED(hr)) {
lpPropValue = NULL;
goto cleanup;
}
//
// Check to make sure we got the right property.
//
if (lpPropValue->ulPropTag != PR_IPM_OUTBOX_ENTRYID) {
goto cleanup;
}
cbeid = lpPropValue->Value.bin.cb;
sc = MapiAllocateBuffer( cbeid, (void **)lppeid );
if(FAILED(sc)) {
goto cleanup;
}
//
// Copy outbox Entry ID
//
CopyMemory(
*lppeid,
lpPropValue->Value.bin.lpb,
cbeid
);
*lpcbeid = cbeid;
cleanup:
MapiFreeBuffer( lpPropValue );
return hr;
}
HRESULT
HrMAPIFindDefaultMsgStore(
IN LPMAPISESSION lplhSession,
OUT ULONG *lpcbeid,
OUT LPENTRYID *lppeid
)
/*++
Routine Description:
Get the entry ID of the default message store. Copied from Exchange SDK.
Arguments:
lplhSession - session pointer
lpcbeid - count of bytes in entry ID
lppeid - entry ID default store
Return value:
HRESULT (see MAPI docs)
--*/
{
HRESULT hr = NOERROR;
SCODE sc = 0;
LPMAPITABLE lpTable = NULL;
LPSRowSet lpRows = NULL;
LPENTRYID lpeid = NULL;
ULONG cbeid = 0;
ULONG cRows = 0;
ULONG i = 0;
SizedSPropTagArray(2, rgPropTagArray) =
{
2,
{
PR_DEFAULT_STORE,
PR_ENTRYID
}
};
//
// Get the list of available message stores from MAPI
//
hr = lplhSession->GetMsgStoresTable( 0, &lpTable );
if (FAILED(hr)) {
goto cleanup;
}
//
// Get the row count for the message recipient table
//
hr = lpTable->GetRowCount( 0, &cRows );
if (FAILED(hr)) {
goto cleanup;
}
//
// Set the columns to return
//
hr = lpTable->SetColumns( (LPSPropTagArray)&rgPropTagArray, 0 );
if (FAILED(hr)) {
goto cleanup;
}
//
// Go to the beginning of the recipient table for the envelope
//
hr = lpTable->SeekRow( BOOKMARK_BEGINNING, 0, NULL );
if (FAILED(hr)) {
goto cleanup;
}
//
// Read all the rows of the table
//
hr = lpTable->QueryRows( cRows, 0, &lpRows );
if (SUCCEEDED(hr) && (lpRows != NULL) && (lpRows->cRows == 0)) {
FreeProws( lpRows );
hr = MAPI_E_NOT_FOUND;
}
if (FAILED(hr) || (lpRows == NULL)) {
goto cleanup;
}
for (i = 0; i < cRows; i++) {
if(lpRows->aRow[i].lpProps[0].Value.b == TRUE) {
cbeid = lpRows->aRow[i].lpProps[1].Value.bin.cb;
sc = MapiAllocateBuffer( cbeid, (void **)&lpeid );
if(FAILED(sc)) {
cbeid = 0;
lpeid = NULL;
goto cleanup;
}
//
// Copy entry ID of message store
//
CopyMemory(
lpeid,
lpRows->aRow[i].lpProps[1].Value.bin.lpb,
cbeid
);
break;
}
}
cleanup:
if(!lpRows) {
FreeProws( lpRows );
}
lpTable->Release();
*lpcbeid = cbeid;
*lppeid = lpeid;
return hr;
}
HRESULT
HrMAPIWriteFileToStream(
IN HANDLE hFile,
OUT LPSTREAM lpStream
)
/*++
Routine Description:
Write file to a stream given a stream pointer. Copied from Exchange SDK.
Arguments:
hFile - Handle to file
lpStream - Pointer to stream
Return value:
HRESULT (see MAPI docs)
--*/
{
HRESULT hr = NOERROR;
DWORD cBytesRead = 0;
ULONG cBytesWritten = 0;
BYTE byteBuffer[128] = {0};
BOOL fReadOk = FALSE;
for(;;) {
fReadOk = ReadFile(
hFile,
byteBuffer,
sizeof(byteBuffer),
&cBytesRead,
NULL
);
if (!fReadOk) {
break;
}
if (!cBytesRead) {
hr = NOERROR;
break;
}
hr = lpStream->Write(
byteBuffer,
cBytesRead,
&cBytesWritten
);
if (FAILED(hr)) {
break;
}
if(cBytesWritten != cBytesRead) {
break;
}
}
return hr;
}
extern "C"
VOID
DoMapiLogon(
PPROFILE_INFO ProfileInfo
)
{
HRESULT HResult = 0;
FLAGS MAPILogonFlags = MAPI_NEW_SESSION | MAPI_EXTENDED | MAPI_NO_MAIL;
LPSTR ProfileName;
LPMAPISESSION Session = NULL;
if (!MapiIsInitialized) {
ProfileInfo->Session = NULL;
SetEvent( ProfileInfo->EventHandle );
return;
}
if (!ServiceDebug) {
MAPILogonFlags |= MAPI_NT_SERVICE;
}
if (ProfileInfo->UseMail) {
MAPILogonFlags &= ~MAPI_NO_MAIL;
}
if (ProfileInfo->ProfileName[0] == 0) {
MAPILogonFlags |= MAPI_USE_DEFAULT;
}
ProfileName = UnicodeStringToAnsiString( ProfileInfo->ProfileName );
__try {
HResult = MapiLogonEx(
0,
ProfileName,
NULL,
MAPILogonFlags,
&Session
);
} __except (EXCEPTION_EXECUTE_HANDLER) {
HResult = GetExceptionCode();
}
if (HR_FAILED(HResult)) {
SetLastError( HResult );
ProfileInfo->Session = NULL;
} else {
InitializeCriticalSection( &ProfileInfo->CsSession );
ProfileInfo->Session = Session;
}
FreeString( ProfileName );
SetEvent( ProfileInfo->EventHandle );
}
extern "C"
BOOL
DoMapiLogoff(
LPMAPISESSION Session
)
{
HRESULT HResult = Session->Logoff( 0, 0, 0 );
if (HR_FAILED(HResult)) {
return FALSE;
}
return TRUE;
}
extern "C"
BOOL
StoreMapiMessage(
LPMAPISESSION Session,
LPWSTR MsgSenderNameW,
LPWSTR MsgSubjectW,
LPWSTR MsgBodyW,
LPWSTR MsgAttachmentFileNameW,
LPWSTR MsgAttachmentTitleW,
DWORD MsgImportance,
LPFILETIME MsgTime,
PULONG ResultCode
)
/*++
Routine Description:
Mails a TIFF file to the inbox in the specified profile.
Arguments:
TiffFileName - Name of TIFF file to mail
ProfileName - Profile name to use
ResultCode - The result of the failed API call
Return Value:
TRUE for success, FALSE on error
--*/
{
LPATTACH Attach = NULL;
ULONG AttachmentNum;
CHAR FileExt[_MAX_EXT];
CHAR FileName[MAX_PATH];
HRESULT HResult = 0;
LPMAPIFOLDER Inbox = NULL;
LPMESSAGE Message = NULL;
LPSTR MsgAttachmentFileName = NULL;
LPSTR MsgAttachmentTitle = NULL;
LPSTR MsgBody = NULL;
LPSTR MsgSenderName = NULL;
LPSTR MsgSubject = NULL;
DWORD RenderingPosition = 0;
LPMDB Store = NULL;
LPSTREAM Stream = NULL;
ULONG cbInEntryID = 0;
HANDLE hFile = INVALID_HANDLE_VALUE;
LPENTRYID lpInEntryID = NULL;
LPSPropProblemArray lppProblems;
ULONG lpulObjType;
SPropValue spvAttachProps[5] = { 0 };
SPropValue spvMsgProps[9] = { 0 };
FILETIME CurrentTime;
_try {
//
// get the time if the caller wants us to
//
if (!MsgTime) {
MsgTime = &CurrentTime;
GetSystemTimeAsFileTime( MsgTime );
}
//
// find the default message store
//
HResult = HrMAPIFindDefaultMsgStore( Session, &cbInEntryID, &lpInEntryID );
if(HR_FAILED(HResult)) {
_leave;
}
//
// open the message store
//
HResult = Session->OpenMsgStore(
0,
cbInEntryID,
lpInEntryID,
NULL,
MDB_NO_DIALOG | MDB_WRITE,
&Store
);
if (HR_FAILED(HResult)) {
_leave;
}
MapiFreeBuffer( lpInEntryID );
//
// find the inbox
//
HResult= HrMAPIFindInbox( Store, &cbInEntryID, &lpInEntryID );
if(HR_FAILED(HResult)) {
_leave;
}
//
// open the inbox
//
HResult = Session->OpenEntry(
cbInEntryID,
lpInEntryID,
NULL,
MAPI_MODIFY,
&lpulObjType,
(LPUNKNOWN *) &Inbox
);
if (HR_FAILED(HResult)) {
_leave;
}
//
// Create a message
//
HResult = Inbox->CreateMessage(
NULL,
0,
&Message
);
if (HR_FAILED(HResult)) {
_leave;
}
//
// convert all of the strings to ansi strings
//
MsgSenderName = UnicodeStringToAnsiString( MsgSenderNameW );
MsgSubject = UnicodeStringToAnsiString( MsgSubjectW );
MsgBody = UnicodeStringToAnsiString( MsgBodyW );
MsgAttachmentFileName = UnicodeStringToAnsiString( MsgAttachmentFileNameW );
MsgAttachmentTitle = UnicodeStringToAnsiString( MsgAttachmentTitleW );
//
// Fill in message properties and set them
//
spvMsgProps[0].ulPropTag = PR_SENDER_NAME;
spvMsgProps[1].ulPropTag = PR_SENT_REPRESENTING_NAME;
spvMsgProps[2].ulPropTag = PR_SUBJECT;
spvMsgProps[3].ulPropTag = PR_MESSAGE_CLASS;
spvMsgProps[4].ulPropTag = PR_BODY;
spvMsgProps[5].ulPropTag = PR_MESSAGE_DELIVERY_TIME;
spvMsgProps[6].ulPropTag = PR_CLIENT_SUBMIT_TIME;
spvMsgProps[7].ulPropTag = PR_MESSAGE_FLAGS;
spvMsgProps[8].ulPropTag = PR_IMPORTANCE;
spvMsgProps[0].Value.lpszA = MsgSenderName;
spvMsgProps[1].Value.lpszA = MsgSenderName;
spvMsgProps[2].Value.lpszA = MsgSubject;
spvMsgProps[3].Value.lpszA = "IPM.Note";
spvMsgProps[4].Value.lpszA = MsgBody;
spvMsgProps[5].Value.ft = *MsgTime;
spvMsgProps[6].Value.ft = *MsgTime;
spvMsgProps[7].Value.ul = 0;
spvMsgProps[8].Value.ul = MsgImportance;
HResult = Message->SetProps(
sizeof(spvMsgProps)/sizeof(SPropValue),
(LPSPropValue) spvMsgProps,
&lppProblems
);
if (HR_FAILED(HResult)) {
_leave;
}
MapiFreeBuffer( lppProblems );
if (MsgAttachmentFileName) {
//
// Create an attachment
//
HResult = Message->CreateAttach(
NULL,
0,
&AttachmentNum,
&Attach
);
if (HR_FAILED(HResult)) {
_leave;
}
_splitpath( MsgAttachmentFileName, NULL, NULL, FileName, FileExt );
strcat( FileName, FileExt );
//
// Fill in attachment properties and set them
//
if (!MsgAttachmentTitle) {
MsgAttachmentTitle = FileName;
}
RenderingPosition = strlen(MsgBody);
spvAttachProps[0].ulPropTag = PR_RENDERING_POSITION;
spvAttachProps[1].ulPropTag = PR_ATTACH_METHOD;
spvAttachProps[2].ulPropTag = PR_ATTACH_LONG_FILENAME;
spvAttachProps[3].ulPropTag = PR_DISPLAY_NAME;
spvAttachProps[4].ulPropTag = PR_ATTACH_EXTENSION;
spvAttachProps[0].Value.ul = RenderingPosition;
spvAttachProps[1].Value.ul = ATTACH_BY_VALUE;
spvAttachProps[2].Value.lpszA = MsgAttachmentTitle;
spvAttachProps[3].Value.lpszA = MsgAttachmentTitle;
spvAttachProps[4].Value.lpszA = FileExt;
HResult = Attach->SetProps(
sizeof(spvAttachProps)/sizeof(SPropValue),
(LPSPropValue) spvAttachProps,
&lppProblems
);
if (HR_FAILED(HResult)) {
_leave;
}
MapiFreeBuffer( lppProblems );
//
// Attach a data property to the attachment
//
HResult = Attach->OpenProperty(
PR_ATTACH_DATA_BIN,
&IID_IStream,
0,
MAPI_CREATE | MAPI_MODIFY,
(LPUNKNOWN *) &Stream
);
if (HR_FAILED(HResult)) {
_leave;
}
//
// open the message attachment file
//
hFile = CreateFile(
MsgAttachmentFileName,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL
);
if (hFile == INVALID_HANDLE_VALUE) {
_leave;
}
//
// Write the file to the data property
//
HResult = HrMAPIWriteFileToStream( hFile, Stream );
if (HR_FAILED(HResult)) {
_leave;
}
}
//
// Save the changes and logoff
//
HResult = Attach->SaveChanges(
FORCE_SAVE
);
if (HR_FAILED(HResult)) {
_leave;
}
HResult = Message->SaveChanges(
FORCE_SAVE
);
if (HR_FAILED(HResult)) {
_leave;
}
}
_finally {
MapiFreeBuffer( lpInEntryID );
if (Store) {
Store->Release();
}
if (Inbox) {
Inbox->Release();
}
if (Message) {
Message->Release();
}
if (Attach) {
Attach->Release();
}
if (Stream) {
Stream->Release();
}
FreeString( MsgSenderName );
FreeString( MsgSubject );
FreeString( MsgBody );
FreeString( MsgAttachmentFileName );
if (MsgAttachmentTitleW && MsgAttachmentTitle) {
FreeString( MsgAttachmentTitle );
}
CloseHandle( hFile );
}
*ResultCode = HResult;
return HResult == 0;
}
extern "C"
LONG
GetMapiProfiles(
LPWSTR *OutBuffer,
LPDWORD OutBufferSize
)
{
HMODULE MapiMod = NULL;
LPMAPITABLE pmt = NULL;
LPSRowSet prws = NULL;
LPSPropValue pval;
LPPROFADMIN lpProfAdmin;
DWORD i;
HRESULT hr;
DWORD Count;
LPWSTR Buffer;
DWORD BytesNeeded;
DWORD Offset = 0;
if (!MapiIsInitialized) {
return MAPI_E_NO_SUPPORT;
}
if (hr = MapiAdminProfiles( 0, &lpProfAdmin )) {
return hr;
}
//
// get the mapi table object
//
if (hr = lpProfAdmin->GetProfileTable( 0, &pmt )) {
goto exit;
}
//
// get the actual profile data, FINALLY
//
if (hr = pmt->QueryRows( 4000, 0, &prws )) {
goto exit;
}
//
// enumerate the profiles and put the name
// of each profile in the combo box
//
BytesNeeded = 0;
for (i=0; i<prws->cRows; i++) {
pval = prws->aRow[i].lpProps;
Count = MultiByteToWideChar(
CP_ACP,
MB_PRECOMPOSED,
pval[0].Value.lpszA,
-1,
NULL,
0
);
if (Count == 0) {
hr = GetLastError();
goto exit;
} else {
BytesNeeded += Count * sizeof(WCHAR);
}
}
BytesNeeded += sizeof(UNICODE_NULL);
Buffer = (LPWSTR) MemAlloc( BytesNeeded );
if (Buffer == NULL) {
hr = ERROR_INSUFFICIENT_BUFFER;
goto exit;
}
for (i=0; i<prws->cRows; i++) {
pval = prws->aRow[i].lpProps;
Count = MultiByteToWideChar(
CP_ACP,
MB_PRECOMPOSED,
pval[0].Value.lpszA,
-1,
&Buffer[Offset],
BytesNeeded - (Offset * sizeof(WCHAR))
);
if (Count == 0) {
hr = GetLastError();
goto exit;
} else {
Offset += Count;
}
}
Buffer[Offset] = 0;
*OutBuffer = Buffer;
*OutBufferSize = BytesNeeded;
hr = ERROR_SUCCESS;
exit:
FreeProws( prws );
if (pmt) {
pmt->Release();
}
if (lpProfAdmin) {
lpProfAdmin->Release();
}
return hr;
}
extern "C"
BOOL
GetDefaultMapiProfile(
LPWSTR ProfileName
)
{
BOOL rVal = FALSE;
LPMAPITABLE pmt = NULL;
LPSRowSet prws = NULL;
LPSPropValue pval;
LPPROFADMIN lpProfAdmin;
DWORD i;
DWORD j;
if (!MapiIsInitialized) {
goto exit;
}
if (MapiAdminProfiles( 0, &lpProfAdmin )) {
goto exit;
}
//
// get the mapi profile table object
//
if (lpProfAdmin->GetProfileTable( 0, &pmt )) {
goto exit;
}
//
// get the actual profile data, FINALLY
//
if (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
//
MultiByteToWideChar(
CP_ACP,
MB_PRECOMPOSED,
pval[0].Value.lpszA,
-1,
ProfileName,
(cchProfileNameMax + 1) * sizeof(WCHAR)
);
rVal = TRUE;
break;
}
}
}
exit:
FreeProws( prws );
if (pmt) {
pmt->Release();
}
return rVal;
}
#define IADDRTYPE 0
#define IEMAILADDR 1
#define IMAPIRECIP 2
#define IPROXYADDR 3
#define PR_EMS_AB_CONTAINERID PROP_TAG(PT_LONG, 0xFFFD)
#define PR_EMS_AB_PROXY_ADDRESSES_A PROP_TAG(PT_MV_STRING8, 0x800F)
#define MUIDEMSAB {0xDC, 0xA7, 0x40, 0xC8, 0xC0, 0x42, 0x10, 0x1A, 0xB4, 0xB9, 0x08, 0x00, 0x2B, 0x2F, 0xE1, 0x82}
#define CbNewFlagList(_cflag) (offsetof(FlagList,ulFlag) + (_cflag)*sizeof(ULONG))
HRESULT
HrMAPICreateSizedAddressList( // RETURNS: return code
IN ULONG cEntries, // count of entries in address list
OUT LPADRLIST *lppAdrList // pointer to address list pointer
)
{
HRESULT hr = NOERROR;
SCODE sc = 0;
ULONG cBytes = 0;
*lppAdrList = NULL;
cBytes = CbNewADRLIST(cEntries);
sc = MapiAllocateBuffer(cBytes, (PVOID*) lppAdrList);
if(FAILED(sc))
{
hr = E_OUTOFMEMORY;
goto cleanup;
}
// Initialize ADRLIST structure
ZeroMemory(*lppAdrList, cBytes);
(*lppAdrList)->cEntries = cEntries;
cleanup:
return hr;
}
HRESULT
HrMAPISetAddressList( // RETURNS: return code
IN ULONG iEntry, // index of address list entry
IN ULONG cProps, // count of values in address list entry
IN LPSPropValue lpPropValues, // pointer to address list entry
IN OUT LPADRLIST lpAdrList // pointer to address list pointer
)
{
HRESULT hr = NOERROR;
SCODE sc = 0;
LPSPropValue lpNewPropValues = NULL;
ULONG cBytes = 0;
if(iEntry >= lpAdrList->cEntries)
{
hr = E_FAIL;
goto cleanup;
}
sc = pScDupPropset(
cProps,
lpPropValues,
MapiAllocateBuffer,
&lpNewPropValues
);
if(FAILED(sc))
{
hr = E_FAIL;
goto cleanup;
}
if(lpAdrList->aEntries[iEntry].rgPropVals != NULL)
{
MapiFreeBuffer(lpAdrList->aEntries[iEntry].rgPropVals);
}
lpAdrList->aEntries[iEntry].cValues = cProps;
lpAdrList->aEntries[iEntry].rgPropVals = lpNewPropValues;
cleanup:
return hr;
}
HRESULT
HrCheckForTypeA( // RETURNS: return code
IN LPCSTR lpszAddrType, // pointer to address type
IN LPCSTR lpszProxy, // pointer to proxy address
OUT LPSTR * lppszAddress // pointer to address pointer
)
{
HRESULT hr = E_FAIL;
LPCSTR lpszProxyAddr = NULL;
ULONG cbAddress = 0;
SCODE sc = 0;
ULONG cchProxy = 0;
ULONG cchProxyType = 0;
// Initialize output parameter
*lppszAddress = NULL;
// find the ':' separator.
cchProxy = lstrlenA(lpszProxy);
cchProxyType = strcspn(lpszProxy, ":");
if((cchProxyType == 0) || (cchProxyType >= cchProxy))
{
hr = E_FAIL;
goto cleanup;
}
hr = MAPI_E_NOT_FOUND;
// does the address type match?
if((cchProxyType == (ULONG)lstrlenA(lpszAddrType)) &&
(_strnicmp(lpszProxy, lpszAddrType, cchProxyType) == 0))
{
// specified address type found
lpszProxyAddr = lpszProxy + cchProxyType + 1;
cbAddress = strlen(lpszProxyAddr);
// make a buffer to hold it.
sc = MapiAllocateBuffer(cbAddress, (void **)lppszAddress);
if(FAILED(sc))
{
hr = E_OUTOFMEMORY;
}
else
{
CopyMemory(*lppszAddress, lpszProxyAddr, cbAddress);
hr = NOERROR;
}
}
cleanup:
return hr;
}
HRESULT
HrFindExchangeGlobalAddressList(
IN LPADRBOOK lpAdrBook,
OUT ULONG *lpcbeid,
OUT LPENTRYID *lppeid
)
{
HRESULT hr = NOERROR;
ULONG ulObjType = 0;
ULONG i = 0;
LPMAPIPROP lpRootContainer = NULL;
LPMAPIPROP lpContainer = NULL;
LPMAPITABLE lpContainerTable = NULL;
LPSRowSet lpRows = NULL;
ULONG cbContainerEntryId = 0;
LPENTRYID lpContainerEntryId = NULL;
LPSPropValue lpCurrProp = NULL;
SRestriction SRestrictAnd[2] = {0};
SRestriction SRestrictGAL = {0};
SPropValue SPropID = {0};
SPropValue SPropProvider = {0};
BYTE muid[] = MUIDEMSAB;
SizedSPropTagArray(1, rgPropTags) =
{
1,
{
PR_ENTRYID,
}
};
*lpcbeid = 0;
*lppeid = NULL;
// Open the root container of the address book
hr = lpAdrBook->OpenEntry(
0,
NULL,
NULL,
MAPI_DEFERRED_ERRORS,
&ulObjType,
(LPUNKNOWN FAR *)&lpRootContainer
);
if(FAILED(hr))
{
goto cleanup;
}
if(ulObjType != MAPI_ABCONT)
{
hr = E_FAIL;
goto cleanup;
}
// Get the hierarchy table of the root container
hr = ((LPABCONT)lpRootContainer)->GetHierarchyTable(
MAPI_DEFERRED_ERRORS|CONVENIENT_DEPTH,
&lpContainerTable
);
if(FAILED(hr))
{
goto cleanup;
}
// Restrict the table to the global address list (GAL)
// ---------------------------------------------------
// Initialize provider restriction to only Exchange providers
SRestrictAnd[0].rt = RES_PROPERTY;
SRestrictAnd[0].res.resProperty.relop = RELOP_EQ;
SRestrictAnd[0].res.resProperty.ulPropTag = PR_AB_PROVIDER_ID;
SPropProvider.ulPropTag = PR_AB_PROVIDER_ID;
SPropProvider.Value.bin.cb = 16;
SPropProvider.Value.bin.lpb = (LPBYTE)muid;
SRestrictAnd[0].res.resProperty.lpProp = &SPropProvider;
// Initialize container ID restriction to only GAL container
SRestrictAnd[1].rt = RES_PROPERTY;
SRestrictAnd[1].res.resProperty.relop = RELOP_EQ;
SRestrictAnd[1].res.resProperty.ulPropTag = PR_EMS_AB_CONTAINERID;
SPropID.ulPropTag = PR_EMS_AB_CONTAINERID;
SPropID.Value.l = 0;
SRestrictAnd[1].res.resProperty.lpProp = &SPropID;
// Initialize AND restriction
SRestrictGAL.rt = RES_AND;
SRestrictGAL.res.resAnd.cRes = 2;
SRestrictGAL.res.resAnd.lpRes = &SRestrictAnd[0];
// Restrict the table to the GAL - only a single row should remain
// Get the row corresponding to the GAL
//
// Query all the rows
//
hr = pHrQueryAllRows(
lpContainerTable,
(LPSPropTagArray)&rgPropTags,
&SRestrictGAL,
NULL,
0,
&lpRows
);
if(FAILED(hr) || (lpRows == NULL) || (lpRows->cRows != 1))
{
hr = E_FAIL;
goto cleanup;
}
// Get the entry ID for the GAL
lpCurrProp = &(lpRows->aRow[0].lpProps[0]);
if(lpCurrProp->ulPropTag == PR_ENTRYID)
{
cbContainerEntryId = lpCurrProp->Value.bin.cb;
lpContainerEntryId = (LPENTRYID)lpCurrProp->Value.bin.lpb;
}
else
{
hr = E_FAIL;
goto cleanup;
}
hr = MapiAllocateBuffer( cbContainerEntryId, (LPVOID *)lppeid );
if(FAILED(hr))
{
*lpcbeid = 0;
*lppeid = NULL;
}
else
{
CopyMemory(
*lppeid,
lpContainerEntryId,
cbContainerEntryId);
*lpcbeid = cbContainerEntryId;
}
cleanup:
pUlRelease(lpRootContainer);
pUlRelease(lpContainerTable);
pUlRelease(lpContainer);
FreeProws( lpRows );
if(FAILED(hr)) {
MapiFreeBuffer( *lppeid );
*lpcbeid = 0;
*lppeid = NULL;
}
return hr;
}
HRESULT
HrGWResolveProxy(
IN LPADRBOOK lpAdrBook, // pointer to address book
IN ULONG cbeid, // count of bytes in the entry ID
IN LPENTRYID lpeid, // pointer to the entry ID
IN LPCSTR lpszAddrType, // pointer to the address type
OUT BOOL *lpfMapiRecip, // MAPI recipient
OUT LPSTR *lppszAddress // pointer to the address pointer
)
{
HRESULT hr = E_FAIL;
HRESULT hrT = 0;
SCODE sc = 0;
ULONG i = 0;
ULONG cbAddress = 0;
ULONG cProxy = 0;
LPSPropValue lpProps = NULL;
LPADRLIST lpAdrList = NULL;
SPropValue prop[2] = {0};
SizedSPropTagArray(4, rgPropTags) =
{
4,
{
PR_ADDRTYPE_A,
PR_EMAIL_ADDRESS_A,
PR_SEND_RICH_INFO,
PR_EMS_AB_PROXY_ADDRESSES_A
}
};
// Initialize output parameters
*lpfMapiRecip = FALSE;
*lppszAddress = NULL;
hr = HrMAPICreateSizedAddressList(1, &lpAdrList);
if(FAILED(hr))
{
goto cleanup;
}
prop[0].ulPropTag = PR_ENTRYID;
prop[0].Value.bin.cb = cbeid;
prop[0].Value.bin.lpb = (LPBYTE)lpeid;
prop[1].ulPropTag = PR_RECIPIENT_TYPE;
prop[1].Value.ul = MAPI_TO;
hr = HrMAPISetAddressList(
0,
2,
prop,
lpAdrList
);
if(FAILED(hr))
{
goto cleanup;
}
hrT = lpAdrBook->PrepareRecips(
0,
(LPSPropTagArray)&rgPropTags,
lpAdrList
);
if(FAILED(hrT))
{
goto cleanup;
}
lpProps = lpAdrList->aEntries[0].rgPropVals;
//
// Hack: detect the case where prepare recips doesn't work correctly.
// This happens when trying to look up a recipient that is in
// a replicated directory but not in the local directory.
//
if (lpAdrList->aEntries[0].cValues == 3)
{
hr = E_FAIL;
goto cleanup;
}
// If the given address type matches the PR_ADDRTYPE value,
// return the PR_EMAIL_ADDRESS value
if((PROP_TYPE(lpProps[IADDRTYPE].ulPropTag) != PT_ERROR) &&
(PROP_TYPE(lpProps[IEMAILADDR].ulPropTag) != PT_ERROR) &&
(_strcmpi(lpProps[IADDRTYPE].Value.lpszA, lpszAddrType) == 0))
{
cbAddress = strlen(lpProps[IEMAILADDR].Value.lpszA);
sc = MapiAllocateBuffer(cbAddress, (void **)lppszAddress);
if(FAILED(sc))
{
hr = E_OUTOFMEMORY;
}
else
{
CopyMemory(*lppszAddress, lpProps[IEMAILADDR].Value.lpszW, cbAddress);
hr = NOERROR;
}
goto cleanup;
}
// Search for a PR_EMS_AB_PROXY_ADDRESSES of the given type if present.
else if(PROP_TYPE(lpProps[IPROXYADDR].ulPropTag) != PT_ERROR)
{
// count of proxy addresses
cProxy = lpAdrList->aEntries[0].rgPropVals[IPROXYADDR].Value.MVszA.cValues;
for(i = 0; i < cProxy; i++)
{
hr = HrCheckForTypeA(
lpszAddrType,
lpProps[IPROXYADDR].Value.MVszA.lppszA[i],
lppszAddress
);
if(hr == MAPI_E_NOT_FOUND)
{
continue;
}
else if(FAILED(hr))
{
goto cleanup;
}
else
{
//
// Found a matching proxy address.
//
goto cleanup;
}
}
}
else
{
hr = E_FAIL;
goto cleanup;
}
cleanup:
if(SUCCEEDED(hr))
{
*lpfMapiRecip = lpAdrList->aEntries[0].rgPropVals[IMAPIRECIP].Value.b;
}
pFreePadrlist(lpAdrList);
return hr;
}
HRESULT
HrGWResolveAddress(
IN LPABCONT lpGalABCont, // pointer to GAL container
IN LPCSTR lpszAddress, // pointer to proxy address
OUT BOOL *lpfMapiRecip, // MAPI recipient
OUT ULONG *lpcbEntryID, // count of bytes in entry ID
OUT LPENTRYID *lppEntryID, // pointer to entry ID
OUT LPADRLIST *lpAdrList // address list
)
{
HRESULT hr = NOERROR;
HRESULT hrT = 0;
SCODE sc = 0;
LPFlagList lpFlagList = NULL;
SPropValue prop[2] = {0};
ULONG cbEntryID = 0;
LPENTRYID lpEntryID = NULL;
static const SizedSPropTagArray(2, rgPropTags) =
{ 2,
{
PR_ENTRYID,
PR_SEND_RICH_INFO
}
};
*lpfMapiRecip = FALSE;
*lpcbEntryID = 0;
*lppEntryID = NULL;
*lpAdrList = NULL;
sc = MapiAllocateBuffer( CbNewFlagList(1), (LPVOID*)&lpFlagList);
if(FAILED(sc))
{
hr = E_OUTOFMEMORY;
goto cleanup;
}
lpFlagList->cFlags = 1;
lpFlagList->ulFlag[0] = MAPI_UNRESOLVED;
hr = HrMAPICreateSizedAddressList(
1,
lpAdrList
);
if(FAILED(hr)) {
goto cleanup;
}
prop[0].ulPropTag = PR_DISPLAY_NAME_A;
prop[0].Value.lpszA = (LPSTR)lpszAddress;
prop[1].ulPropTag = PR_RECIPIENT_TYPE;
prop[1].Value.ul = MAPI_TO;
hr = HrMAPISetAddressList(
0,
2,
prop,
*lpAdrList
);
if(FAILED(hr)) {
goto cleanup;
}
hrT = lpGalABCont->ResolveNames(
(LPSPropTagArray)&rgPropTags,
0,
*lpAdrList,
lpFlagList
);
if(lpFlagList->ulFlag[0] != MAPI_RESOLVED)
{
if(lpFlagList->ulFlag[0] == MAPI_AMBIGUOUS)
{
hrT = MAPI_E_AMBIGUOUS_RECIP;
}
else
{
hrT = MAPI_E_NOT_FOUND;
}
}
if(FAILED(hrT))
{
if(hrT == MAPI_E_NOT_FOUND)
{
hr = MAPI_E_NOT_FOUND;
}
else
{
hr = E_FAIL;
}
goto cleanup;
}
cbEntryID = (*lpAdrList)->aEntries[0].rgPropVals[0].Value.bin.cb;
lpEntryID = (LPENTRYID)(*lpAdrList)->aEntries[0].rgPropVals[0].Value.bin.lpb;
sc = MapiAllocateBuffer( cbEntryID, (LPVOID*)lppEntryID);
if(FAILED(sc))
{
hr = E_OUTOFMEMORY;
goto cleanup;
}
CopyMemory(*lppEntryID, lpEntryID, cbEntryID);
*lpcbEntryID = cbEntryID;
*lpfMapiRecip = (*lpAdrList)->aEntries[0].rgPropVals[1].Value.b;
cleanup:
MapiFreeBuffer(lpFlagList);
return hr;
}
extern "C"
BOOL
MailMapiMessage(
LPMAPISESSION Session,
LPWSTR RecipientNameW,
LPWSTR MsgSubjectW,
LPWSTR MsgBodyW,
LPWSTR MsgAttachmentFileNameW,
LPWSTR MsgAttachmentTitleW,
DWORD MsgImportance,
PULONG ResultCode
)
/*++
Routine Description:
Mails a TIFF file to the addressbook recipient in the specified profile.
Arguments:
TiffFileName - Name of TIFF file to mail
ProfileName - Profile name to use
ResultCode - The result of the failed API call
Return Value:
TRUE for success, FALSE on error
--*/
{
ULONG cbInEntryID = 0;
LPENTRYID lpInEntryID = NULL;
LPMDB Store = NULL;
ULONG lpulObjType;
LPMAPIFOLDER Inbox = NULL;
LPMAPIFOLDER Outbox = NULL;
LPMESSAGE Message = NULL;
LPATTACH Attach = NULL;
LPSTREAM Stream = NULL;
ULONG AttachmentNum;
HRESULT HResult = 0;
LPSTR MsgAttachmentFileName = NULL;
LPSTR MsgAttachmentTitle = NULL;
LPSTR MsgBody = NULL;
LPSTR MsgSubject = NULL;
LPSTR BodyStrA = NULL;
LPSTR SubjectStrA = NULL;
LPSTR SenderStrA = NULL;
LPSTR LongFileNameA = NULL;
LPSTR RecipientName = NULL;
DWORD RenderingPosition = 0;
LPADRBOOK AddrBook;
LPADRLIST lpAddrList = NULL;
ULONG ulFlags = LOGOFF_PURGE;
LPSPropProblemArray lppProblems;
ULONG cbGalEid = 0;
LPENTRYID lpGalEid = NULL;
LPSTR lpszProxyAddr = NULL;
BOOL fMapiRecip = FALSE;
ULONG ulObjType = 0;
LPABCONT lpGalABCont = NULL;
ULONG cbEntryID = 0;
LPENTRYID lpEntryID = NULL;
SPropValue spvAttachProps[5] = { 0 };
SPropValue spvMsgProps[5] = { 0 };
CHAR FileExt[_MAX_EXT];
CHAR FileName[MAX_PATH];
HANDLE hFile = INVALID_HANDLE_VALUE;
_try {
//
// convert all of the strings to ansi strings
//
RecipientName = UnicodeStringToAnsiString( RecipientNameW );
MsgSubject = UnicodeStringToAnsiString( MsgSubjectW );
MsgBody = UnicodeStringToAnsiString( MsgBodyW );
MsgAttachmentFileName = UnicodeStringToAnsiString( MsgAttachmentFileNameW );
MsgAttachmentTitle = UnicodeStringToAnsiString( MsgAttachmentTitleW );
HResult = Session->OpenAddressBook(
0,
NULL,
AB_NO_DIALOG,
&AddrBook
);
if(HR_FAILED(HResult)) {
_leave;
}
HResult = HrFindExchangeGlobalAddressList(
AddrBook,
&cbGalEid,
&lpGalEid
);
if(HR_FAILED(HResult)) {
_leave;
}
HResult = AddrBook->OpenEntry(
cbGalEid,
lpGalEid,
NULL,
MAPI_DEFERRED_ERRORS,
&ulObjType,
(LPUNKNOWN FAR *)&lpGalABCont
);
if(HR_FAILED(HResult)) {
_leave;
}
HResult = HrGWResolveAddress(
lpGalABCont,
RecipientName,
&fMapiRecip,
&cbEntryID,
&lpEntryID,
&lpAddrList
);
if(HR_FAILED(HResult)) {
_leave;
}
//
// Find the default message store
//
HResult = HrMAPIFindDefaultMsgStore(
Session,
&cbInEntryID,
&lpInEntryID
);
if(HR_FAILED(HResult)) {
_leave;
}
//
// Open it
//
HResult = Session->OpenMsgStore(
(ULONG)0,
cbInEntryID,
lpInEntryID,
NULL,
MDB_NO_DIALOG | MDB_WRITE,
&Store
);
if(HR_FAILED(HResult)) {
_leave;
}
MapiFreeBuffer(lpInEntryID);
//
// Find the outbox
//
HResult= HrMAPIFindOutbox(
Store,
&cbInEntryID,
&lpInEntryID
);
if(HR_FAILED(HResult)) {
_leave;
}
//
// Open it
//
HResult = Store->OpenEntry(
cbInEntryID,
lpInEntryID,
NULL,
MAPI_MODIFY | MAPI_DEFERRED_ERRORS,
&lpulObjType,
(LPUNKNOWN *) &Outbox
);
if(HR_FAILED(HResult)) {
_leave;
}
//
// Create a message
//
HResult = Outbox->CreateMessage(
NULL,
0,
&Message
);
if(HR_FAILED(HResult)) {
_leave;
}
HResult = Message->ModifyRecipients(
0,
lpAddrList
);
if(HR_FAILED(HResult)) {
_leave;
}
//
// Fill in message properties and set them
//
spvMsgProps[0].ulPropTag = PR_SUBJECT;
spvMsgProps[1].ulPropTag = PR_MESSAGE_CLASS;
spvMsgProps[2].ulPropTag = PR_BODY;
spvMsgProps[3].ulPropTag = PR_IMPORTANCE;
spvMsgProps[4].ulPropTag = PR_DELETE_AFTER_SUBMIT;
spvMsgProps[0].Value.lpszA = MsgSubject;
spvMsgProps[1].Value.lpszA = "IPM.Note";
spvMsgProps[2].Value.lpszA = MsgBody;
spvMsgProps[3].Value.ul = MsgImportance;
spvMsgProps[4].Value.ul = TRUE;
HResult = Message->SetProps(
sizeof(spvMsgProps)/sizeof(SPropValue),
(LPSPropValue) spvMsgProps,
&lppProblems
);
if (HR_FAILED(HResult)) {
_leave;
}
MapiFreeBuffer( lppProblems );
if (MsgAttachmentFileName) {
//
// Create an attachment
//
HResult = Message->CreateAttach(
NULL,
0,
&AttachmentNum,
&Attach
);
if (HR_FAILED(HResult)) {
_leave;
}
_splitpath( MsgAttachmentFileName, NULL, NULL, FileName, FileExt );
strcat( FileName, FileExt );
//
// Fill in attachment properties and set them
//
if (!MsgAttachmentTitle) {
MsgAttachmentTitle = FileName;
}
RenderingPosition = strlen(MsgBody);
spvAttachProps[0].ulPropTag = PR_RENDERING_POSITION;
spvAttachProps[1].ulPropTag = PR_ATTACH_METHOD;
spvAttachProps[2].ulPropTag = PR_ATTACH_LONG_FILENAME;
spvAttachProps[3].ulPropTag = PR_DISPLAY_NAME;
spvAttachProps[4].ulPropTag = PR_ATTACH_EXTENSION;
spvAttachProps[0].Value.ul = RenderingPosition;
spvAttachProps[1].Value.ul = ATTACH_BY_VALUE;
spvAttachProps[2].Value.lpszA = MsgAttachmentTitle;
spvAttachProps[3].Value.lpszA = MsgAttachmentTitle;
spvAttachProps[4].Value.lpszA = FileExt;
HResult = Attach->SetProps(
sizeof(spvAttachProps)/sizeof(SPropValue),
(LPSPropValue) spvAttachProps,
&lppProblems
);
if (HR_FAILED(HResult)) {
_leave;
}
MapiFreeBuffer( lppProblems );
//
// Attach a data property to the attachment
//
HResult = Attach->OpenProperty(
PR_ATTACH_DATA_BIN,
&IID_IStream,
0,
MAPI_CREATE | MAPI_MODIFY,
(LPUNKNOWN *) &Stream
);
if (HR_FAILED(HResult)) {
_leave;
}
//
// open the message attachment file
//
hFile = CreateFile(
MsgAttachmentFileName,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL
);
if (hFile == INVALID_HANDLE_VALUE) {
_leave;
}
//
// Write the file to the data property
//
HResult = HrMAPIWriteFileToStream( hFile, Stream );
if (HR_FAILED(HResult)) {
_leave;
}
HResult = Attach->SaveChanges(
FORCE_SAVE
);
if (HR_FAILED(HResult)) {
_leave;
}
}
//
// mail the message
//
HResult = Message->SubmitMessage(
0
);
if(HR_FAILED(HResult)) {
_leave;
}
HResult = Store->StoreLogoff(
&ulFlags
);
if(HR_FAILED(HResult)) {
_leave;
}
}
_finally {
if (Store) {
Store->Release();
}
if (Inbox) {
Inbox->Release();
}
if (Message) {
Message->Release();
}
if (Attach) {
Attach->Release();
}
if (Stream) {
Stream->Release();
}
if (AddrBook) {
AddrBook->Release();
}
if (lpAddrList) {
pFreePadrlist( lpAddrList );
}
if (lpEntryID) {
MapiFreeBuffer( lpEntryID );
}
if (lpszProxyAddr) {
MapiFreeBuffer( lpszProxyAddr );
}
if (lpInEntryID) {
MapiFreeBuffer( lpInEntryID );
}
FreeString( MsgSubject );
FreeString( MsgBody );
FreeString( MsgAttachmentFileName );
if (MsgAttachmentTitleW && MsgAttachmentTitle) {
FreeString( MsgAttachmentTitle );
}
CloseHandle( hFile );
goto exit;
}
exit:
*ResultCode = HResult;
return HResult == 0;
}