//////////////////////////////////////////////////////////////////////////////
//
// FILE: FORMBASE.CPP
//
//
//
// Copyright (c) 1986-1996, Microsoft Corporation.
// All rights reserved.
//
//--
#include "precomp.h"
#include <cindex.h>
static BOOL g_FModalUp = FALSE;
static BOOL g_FMBoxUp = FALSE;
static HWND g_hwndUp = NULL;
BOOL CALLBACK FormDlgProcSend(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK FormDlgProcRead(HWND, UINT, WPARAM, LPARAM);
SizedSPropTagArray(cpropMsg, tagaRead) = MESSAGE_TAGS;
//szRE_PREFIX and szFW_PREFIX have to have the same length
char szRE_PREFIX[] = "RE: ";
char szFW_PREFIX[] = "FW: ";
#define EXCLUDED_PROPS_ON_REPLY 32
SizedSPropTagArray (EXCLUDED_PROPS_ON_REPLY, sptExcludedProps) =
{
EXCLUDED_PROPS_ON_REPLY,
{
PR_SENDER_NAME,
PR_SENDER_ENTRYID,
PR_SENDER_SEARCH_KEY,
PR_SENDER_EMAIL_ADDRESS,
PR_SENDER_ADDRTYPE,
PR_RECEIVED_BY_NAME,
PR_RECEIVED_BY_ENTRYID,
PR_RECEIVED_BY_SEARCH_KEY,
PR_SENT_REPRESENTING_NAME,
PR_SENT_REPRESENTING_ENTRYID,
PR_SENT_REPRESENTING_SEARCH_KEY,
PR_SENT_REPRESENTING_EMAIL_ADDRESS,
PR_SENT_REPRESENTING_ADDRTYPE,
PR_RCVD_REPRESENTING_NAME,
PR_RCVD_REPRESENTING_ENTRYID,
PR_RCVD_REPRESENTING_SEARCH_KEY,
PR_MESSAGE_FLAGS,
PR_MESSAGE_RECIPIENTS,
PR_READ_RECEIPT_ENTRYID,
PR_REPORT_ENTRYID,
PR_REPLY_RECIPIENT_ENTRIES,
PR_REPLY_RECIPIENT_NAMES,
PR_PARENT_KEY,
PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED,
PR_READ_RECEIPT_REQUESTED,
PR_CLIENT_SUBMIT_TIME,
PR_MESSAGE_DELIVERY_TIME,
PR_MESSAGE_DOWNLOAD_TIME,
PR_BODY,
PR_SUBJECT,
PR_SUBJECT_PREFIX,
PR_MESSAGE_ATTACHMENTS
}
};
//// CBaseForm::CBaseForm
//
CBaseForm::CBaseForm(CClassFactory * pClassFactory)
: m_lsterr(g_szFormName)
{
m_pClassFactory = pClassFactory;
m_pClassFactory->AddRef();
m_ulViewStatus = 0;
m_ulSiteStatus = 0;
m_cRef = 1;
m_pviewctxOverride = NULL;
m_pviewctx = NULL;
m_pmsgsite = NULL;
m_pmsg = NULL;
m_hwnd = NULL;
m_hwndDialog = NULL;
m_pses = NULL;
m_pab = NULL;
m_pval = NULL;
m_padrlist = NULL;
m_fRecipientsDirty = FALSE;
m_fDirty = FALSE;
m_fSameAsLoaded = FALSE;
//
// Add self to the head of the linked list of forms
//
m_pfrmNext = g_PfrmFirst;
g_PfrmFirst = this;
m_cbConvIdx = 0;
m_lpbConvIdx = NULL;
m_fConvTopicSet = FALSE;
m_hChsFldDll = NULL;
m_lpfnHrPickFolder = NULL;
m_cbCFDState = 0;
m_pbCFDState = NULL;
m_state = stateUninit;
}
//// CBaseForm::~CBaseForm
//
CBaseForm::~CBaseForm()
{
CBaseForm * pfrm;
//
// Remove ourselves from the form link list
//
if (g_PfrmFirst == this)
{
g_PfrmFirst = m_pfrmNext;
}
else
{
for (pfrm = g_PfrmFirst; pfrm != NULL; pfrm = pfrm->GetNext())
{
if (pfrm->m_pfrmNext == this)
{
pfrm->m_pfrmNext = m_pfrmNext;
break;
}
}
Assert(pfrm != NULL);
}
MAPIFreeBuffer(m_pval);
FreePadrlist(m_padrlist);
MAPIFreeBuffer(m_lpbConvIdx);
if(m_hChsFldDll)
FreeLibrary(m_hChsFldDll);
MAPIFreeBuffer(m_pbCFDState);
//
// Let the class factory know we are gone
//
UlRelease(m_pClassFactory);
}
//// CBaseForm::DeInitObjects
//
void CBaseForm::DeInitObjects()
{
if (m_hwnd != NULL)
{
DestroyWindow(m_hwnd); // Destroy the frame window
}
Assert(m_pmsg == NULL);
Assert(m_pmsgsite == NULL);
Assert(m_pviewctx == NULL);
Assert(m_pviewctxOverride == NULL);
Assert(m_pab == NULL);
Assert(m_pses == NULL);
m_state = stateDead;
}
///////////////////////////////////////////////////////////////////////////////
//
// IUnknown interface
//
///////////////////////////////////////////////////////////////////////////////
//// CBaseForm::QueryInterface
//
//
// DESCRIPTION: The form supports IMAPIform,
// IPersistMessage and IAMPIFormAdviseSink
STDMETHODIMP CBaseForm::QueryInterface(REFIID riid, LPVOID * ppvObj)
{
if (riid == IID_IUnknown || riid == IID_IMAPIForm)
{
*ppvObj = (LPVOID) (IMAPIForm *) this;
}
else if (riid == IID_IPersistMessage)
{
*ppvObj = (LPVOID) (IPersistMessage *) this;
}
else if (riid == IID_IMAPIFormAdviseSink)
{
*ppvObj = (LPVOID) (IMAPIFormAdviseSink *) this;
}
else
{
*ppvObj = NULL;
return m_lsterr.HrSetLastError(ResultFromScode(E_NOINTERFACE));
}
AddRef();
return hrSuccess;
}
//// CBaseForm::AddRef
//
// Description:
//
STDMETHODIMP_(ULONG) CBaseForm::AddRef ()
{
m_cRef += 1;
return m_cRef;
}
//// CBaseForm::Release
//
STDMETHODIMP_(ULONG) CBaseForm::Release ()
{
ULONG cRef = -- m_cRef;
if (cRef == 0)
{
// Let the class factory now we are gone
m_pClassFactory->ObjDestroyedCallback();
delete this;
}
return cRef;
}
///////////////////////////////////////////////////////////////////////////////
//
// IMAPIForm interface
//
///////////////////////////////////////////////////////////////////////////////
//// IMAPIForm::SetViewContext
//
//
STDMETHODIMP CBaseForm::SetViewContext(IN IMAPIViewContext * pvc)
{
//
// If we currently have a view context, then release it
//
if (m_pviewctx != NULL)
{
m_pviewctx->SetAdviseSink(NULL);
m_pviewctx->Release();
}
//
// Accept the new view context.
//
m_pviewctx = pvc;
//
// If the new view context is non-null, then save it away, setup
// the advise sink back to check for things and get the current set
// of status flags
//
m_ulViewStatus = 0;
if (pvc != NULL)
{
m_pviewctx->AddRef ();
m_pviewctx->SetAdviseSink (this);
m_pviewctx->GetViewStatus(&m_ulViewStatus);
}
ConfigWinMenu();
return hrSuccess;
}
//// IMAPIForm::GetViewContext
//
STDMETHODIMP CBaseForm::GetViewContext(OUT IMAPIViewContext * FAR * ppvc)
{
Assert(ppvc);
*ppvc = m_pviewctx;
if(m_pviewctx != NULL)
{
m_pviewctx->AddRef();
return hrSuccess;
}
else
return ResultFromScode(S_FALSE);
}
//// IMAPIForm::ShutdownForm
//
// Description:
// This routine is called to shut down the form and if necessary
// to cause save changes to the form.
//
STDMETHODIMP CBaseForm::ShutdownForm(DWORD dwSaveOptions)
{
HRESULT hr;
//
// Check for valid state to make the call
//
switch( m_state )
{
default:
case stateDead:
m_viewnotify.OnShutdown ();
return m_lsterr.HrSetLastError(ResultFromScode(E_UNEXPECTED));
case stateUninit:
case stateNormal:
case stateNoScribble:
case stateHandsOffFromSave:
case stateHandsOffFromNormal:
break;
}
hr = HrQuerySave(dwSaveOptions);
if(HR_FAILED(hr))
return hr;
//
// Save us from ourselfs by add-refing the object
//
AddRef();
//
// Release the view context
//
if (m_pviewctx != NULL)
{
m_pviewctx->SetAdviseSink(NULL);
m_pviewctx->Release();
m_pviewctx = NULL;
}
//
// We need to notify anyone who has an advise on us that we are
// shutting down. We want to do this in such a manner as to
// protect ourselves since we are referencing the data structure
// internally. Thus the AddRef/Release pair.
//
m_viewnotify.OnShutdown ();
//
// Release message objects if we have them
//
if(g_FModalUp)
m_pviewctxOverride = NULL;
UlRelease(m_pmsg);
m_pmsg = NULL;
UlRelease(m_pmsgsite);
m_pmsgsite = NULL;
UlRelease(m_pab);
m_pab = NULL;
UlRelease(m_pses);
m_pses = NULL;
//
// Tell all objects to be closed and de-initialized, only IUnknown
// calls are legal after this.
//
DeInitObjects();
//
// We are now all done -- release our internal reference
//
Release();
return hrSuccess;
}
//// IMAPIForm::DoVerb
//
STDMETHODIMP CBaseForm::DoVerb(LONG iVerb, LPMAPIVIEWCONTEXT pviewctx,
ULONG hwndParent, LPCRECT lprcPosRect)
{
HRESULT hr;
//
// If a view context was passed in, then we need to get the
// status bits from this view context. Also we are going to save
// the current view context and use this view context for the
// duration of the verb execution.
//
if (pviewctx != NULL)
{
m_pviewctxOverride = pviewctx;
pviewctx->GetViewStatus(&m_ulViewStatus);
}
//
// Execute the requested verb. If we do not understand the verb
// or we do not support the verb then we return NO SUPPORT and let
// the viewer deal with this.
//
switch (iVerb)
{
case EXCHIVERB_OPEN:
hr = HrOpenForm((HWND) hwndParent, lprcPosRect, m_ulViewStatus);
break;
case EXCHIVERB_REPLYTOSENDER:
hr = HrReply(eREPLY, (HWND) hwndParent, lprcPosRect);
if(HR_SUCCEEDED(hr))
{
m_pviewctxOverride = NULL;
ShutdownForm(SAVEOPTS_NOSAVE);
}
break;
case EXCHIVERB_FORWARD:
hr = HrReply(eFORWARD, (HWND) hwndParent, lprcPosRect);
if(HR_SUCCEEDED(hr))
{
m_pviewctxOverride = NULL;
ShutdownForm(SAVEOPTS_NOSAVE);
}
break;
case EXCHIVERB_PRINT:
case EXCHIVERB_REPLYTOALL:
case EXCHIVERB_SAVEAS:
case EXCHIVERB_REPLYTOFOLDER:
//the viewer should not call us here
//(see Value in extensions section of smpfrm.cfg)
Assert(FALSE);
default:
hr = m_lsterr.HrSetLastError(ResultFromScode(MAPI_E_NO_SUPPORT));
break;
}
//
// If we moved to a different view context, then switch back to
// the one we started with.
//
m_pviewctxOverride = NULL;
if(m_pviewctx != NULL)
{
m_ulViewStatus =0;
m_pviewctx->GetViewStatus(&m_ulViewStatus);
ConfigWinMenu();
}
return hr;
}
//// IMAPIForm::Advise
//
STDMETHODIMP CBaseForm::Advise (IN IMAPIViewAdviseSink * pViewAdvise,
OUT ULONG FAR * pulConnection)
{
HRESULT hr;
hr = m_viewnotify.Advise (pViewAdvise, pulConnection);
if (FAILED(hr))
{
hr = m_lsterr.HrSetLastError(hr);
}
return hr;
}
//// IMAPIForm::Unadvise
//
STDMETHODIMP CBaseForm::Unadvise(ULONG ulConnection)
{
HRESULT hr;
hr = m_viewnotify.Unadvise(ulConnection);
if (FAILED(hr))
{
hr = m_lsterr.HrSetLastError(hr);
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
//
// IPersistMessage interface
//
///////////////////////////////////////////////////////////////////////////////
//// IPersistMessage::GetClassID
STDMETHODIMP CBaseForm::GetClassID(LPCLSID lpClassID)
{
*lpClassID = CLSID_IPM_NOTE_SAMPLE;
return hrSuccess;
}
//// IPersistMessage::GetLastError
//
// Description: This routine is used to get back a string giving more
// information about the last error in the form.
//
STDMETHODIMP CBaseForm::GetLastError(HRESULT hr, ULONG ulFlags,
LPMAPIERROR FAR * lppMAPIError)
{
return m_lsterr.HrGetLastError(hr, ulFlags, lppMAPIError);
}
//// IPersistMessage::IsDirty
//
STDMETHODIMP CBaseForm::IsDirty ()
{
if(m_fDirty)
return ResultFromScode(S_OK);
if(NULL == m_hwndDialog)
{
m_fDirty = FALSE;
}
else if(m_eFormType == eformRead)
{
m_fDirty = (m_fRecipientsDirty ||
Edit_GetModify(GetDlgItem(m_hwndDialog, ID_BODY)));
}
else
{
m_fDirty = (m_fRecipientsDirty ||
Edit_GetModify(GetDlgItem(m_hwndDialog, ID_BODY)) ||
Edit_GetModify(GetDlgItem(m_hwndDialog, ID_SUBJECT)) ||
Edit_GetModify(GetDlgItem(m_hwndDialog, ID_TO)) ||
Edit_GetModify(GetDlgItem(m_hwndDialog, ID_CC)));
}
return ResultFromScode ((m_fDirty ? S_OK : S_FALSE));
}
//// IPersistMessage::InitNew
//
// Description: This function is called in the case of composing a new
// message. There is a small set of properties which are set by
// the constructor of the message, however in general it can be
// assumed the message is clean.
//
STDMETHODIMP CBaseForm::InitNew(LPMAPIMESSAGESITE pmsgsite, LPMESSAGE pmsg)
{
//
// Ensure we are in a state where we can accept this call
//
switch(m_state)
{
case stateUninit:
case stateHandsOffFromSave:
case stateHandsOffFromNormal:
break;
default:
return m_lsterr.HrSetLastError(ResultFromScode(E_UNEXPECTED));
}
//
// If we currently have a message site, then release it as we
// will no longer be using it.
//
UlRelease(m_pmsgsite);
m_pmsgsite = NULL;
//
// Save away the pointers to the message and message site
//
m_pmsgsite = pmsgsite;
pmsgsite->AddRef();
m_ulSiteStatus = 0;
m_pmsgsite->GetSiteStatus(&m_ulSiteStatus);
m_pmsg = pmsg;
pmsg->AddRef();
//
// Make an assumption on the message flags and status
//
m_ulMsgStatus = 0;
m_ulMsgFlags = MSGFLAG_UNSENT;
if(m_hwnd)
DisplayMessage();
//
// We succeeded in doing the InitNew so move to the normal state
//
m_state = stateNormal;
//
// Tell everybody who cares that we just loaded a new message
//
m_viewnotify.OnNewMessage();
return hrSuccess;
}
//// IPersistMessage::Load
//
// Description: This routine is called as part of loading an existing
// message into the form.
//
STDMETHODIMP CBaseForm::Load(LPMAPIMESSAGESITE pmsgsite, LPMESSAGE pmsg,
ULONG ulMsgStatus, ULONG ulMsgFlags)
{
//
// Ensure we are in a state where we can accept this call
//
switch(m_state)
{
case stateUninit:
case stateHandsOffFromSave:
case stateHandsOffFromNormal:
break;
default:
return m_lsterr.HrSetLastError(ResultFromScode(E_UNEXPECTED));
}
//
// If we currently have a message site, then release it as we
// will no longer be using it.
//
UlRelease(m_pmsgsite);
m_pmsgsite = NULL;
UlRelease(m_pmsg);
m_pmsg = NULL;
HRESULT hr = HrGetMsgDataFromMsg(pmsg, ulMsgFlags);
if(HR_FAILED(hr))
goto err;
//
// Save away the message and message site which are passed in.
//
m_pmsg = pmsg;
pmsg->AddRef();
m_pmsgsite = pmsgsite;
pmsgsite->AddRef();
//
// Get the site status flags for disabling buttons & menus
//
m_ulSiteStatus = 0;
m_pmsgsite->GetSiteStatus(&m_ulSiteStatus);
//
// Save away these properties
//
m_ulMsgStatus = ulMsgStatus;
m_ulMsgFlags = ulMsgFlags;
//
// Put us into the normal state
//
m_state = stateNormal;
//
// if our form is up, display the message
//
if(m_hwnd)
DisplayMessage();
//
// Tell everybody who cares that we just loaded a new message
//
m_viewnotify.OnNewMessage();
return hrSuccess;
err:
return hr;
}
//// IPersistMessage::Save
//
// Description:
// This function will be called whenever a save operation of the
// information into the form should be done. We should only make
// modifications to the message in this function.
//
STDMETHODIMP CBaseForm::Save(IN LPMESSAGE pmsg, IN ULONG fSameAsLoad)
{
HRESULT hr;
//
// Check that we are in a state where we are willing to accept
// this call. Must have a message.
//
switch( m_state )
{
default:
Assert(FALSE);
case stateDead:
case stateUninit:
case stateNoScribble:
case stateHandsOffFromSave:
case stateHandsOffFromNormal:
return m_lsterr.HrSetLastError(ResultFromScode(E_UNEXPECTED));
case stateNormal:
break;
}
if (fSameAsLoad)
{
//
// Its the same message interface as was loaded into us. We can
// assume that the pmsg passed in is either NULL or an interface
// on the same object as the message we already have loaded
//
hr = HrSaveInto(m_pmsg);
}
else
{
//
// We need to copy everything into the new message as we are going
// to clone ourselves into it.
//
hr = m_pmsg->CopyTo(0, NULL, NULL, 0, NULL, &IID_IMessage, pmsg, 0, NULL);
if (FAILED(hr))
{
m_lsterr.HrSetLastError(hr, m_pmsg);
return hr;
}
//
// Now make all of the incremental changes
//
hr = HrSaveInto(pmsg);
}
if (FAILED(hr))
{
return hr;
}
m_state = stateNoScribble;
m_fSameAsLoaded = fSameAsLoad;
return S_OK;
}
//// IPersistMessage::SaveCompleted
//
//
STDMETHODIMP CBaseForm::SaveCompleted(IN LPMESSAGE pmsg)
{
switch( m_state )
{
case stateHandsOffFromNormal:
case stateHandsOffFromSave:
case stateNoScribble:
break;
default:
return m_lsterr.HrSetLastError(ResultFromScode(E_UNEXPECTED));
}
if((stateHandsOffFromNormal == m_state ||
stateHandsOffFromSave == m_state) && NULL == pmsg)
{
DebugTrace("smpfrm: SaveCompleted called in handsOff state with pmsg==NULL\r\n");
return m_lsterr.HrSetLastError(ResultFromScode(E_INVALIDARG));
}
ULONG ulOldState = m_state;
m_state = stateNormal;
//state == NoScribble , pmsg == NULL
if(NULL == pmsg)
{
if(m_fSameAsLoaded)
{
ClearDirty();
m_viewnotify.OnSaved();
}
return hrSuccess;
}
//state == handsOffFromNormal, pmsg != NULL
if(stateHandsOffFromNormal == ulOldState)
{
UlRelease(m_pmsg);
m_pmsg = pmsg;
pmsg->AddRef();
return hrSuccess;
}
//state == handsOffFromSave || NoScribble, pmsg != NULL
if(stateNoScribble == ulOldState)
{
UlRelease(m_pmsg);
m_pmsg = pmsg;
pmsg->AddRef();
}
m_viewnotify.OnSaved();
ClearDirty();
return hrSuccess;
}
//// IPersistMessage::HandsOffMessage
//
// Description: store, folder and message objects has to be released
// in this method.
//
//
STDMETHODIMP CBaseForm::HandsOffMessage ()
{
switch( m_state )
{
case stateNormal:
case stateNoScribble:
break;
default:
return m_lsterr.HrSetLastError(ResultFromScode(E_UNEXPECTED));
}
if(stateNormal == m_state)
m_state = stateHandsOffFromNormal;
else
m_state = stateHandsOffFromSave;
//
// We must have a message
//
Assert(m_pmsg != NULL);
m_pmsg->Release();
m_pmsg = NULL;
return hrSuccess;
}
///////////////////////////////////////////////////////////////////////////////
//
// IMAPIFormAdviseSink interfaces
//
///////////////////////////////////////////////////////////////////////////////
//// IMAPIFormAdviseSink::OnChange
//
// Description: called to notify about changes in viewctx status
STDMETHODIMP CBaseForm::OnChange(ULONG ulflag)
{
if(m_pviewctxOverride == NULL)
{
m_ulViewStatus = ulflag;
ConfigWinMenu();
}
return hrSuccess;
}
//// CBaseForm::OnActivateNext
//
// Description: We only say that we will handle the next message if
// it is the exact same message class as the current message.
// If the next message has the same "unsentness" will reuse the
// current object, otherwise ask our ClassFactory for a new one.
//
STDMETHODIMP CBaseForm::OnActivateNext(LPCSTR lpszMessageClass, ULONG ulMessageStatus,
ULONG ulMessageFlags,
LPPERSISTMESSAGE FAR * ppPersistMessage)
{
HRESULT hr;
*ppPersistMessage = NULL;
Assert(m_pval);
if(PR_MESSAGE_CLASS == m_pval[irtClass].ulPropTag)
{
//the message class comparison has to be case insensitive
if((lstrcmpi(m_pval[irtClass].Value.LPSZ, lpszMessageClass) != 0) &&
lstrcmpi(FormClassName, lpszMessageClass) != 0)
{
return ResultFromScode(S_FALSE);
}
}
else
{
if(lstrcmpi(FormClassName, lpszMessageClass) != 0)
{
return ResultFromScode(S_FALSE);
}
}
if((m_ulMsgFlags & MSGFLAG_UNSENT) == (ulMessageFlags & MSGFLAG_UNSENT))
//tell the viewer to reuse our object
return ResultFromScode(S_OK);
//Get a new object from our class factory
hr = m_pClassFactory->CreateInstance(NULL, IID_IPersistMessage, (LPVOID FAR *)ppPersistMessage);
if(hr)
return ResultFromScode (S_FALSE);
else
return ResultFromScode(S_OK);
}
///////////////////////////////////////////////////////////////////////////////
//
// Non-IMAPIinterface functions
//
///////////////////////////////////////////////////////////////////////////////
/// CBaseForm::HrGetMsgDataFromMsg
//
// fills in m_pval and m_padrlist (for unsent msgs only)
// with the info from pmsg
HRESULT CBaseForm::HrGetMsgDataFromMsg(LPMESSAGE pmsg, ULONG ulMsgFlags)
{
Assert(pmsg);
FreePadrlist(m_padrlist);
m_padrlist = NULL;
ULONG cValues = 0;
MAPIFreeBuffer(m_pval);
m_pval = NULL;
MAPIFreeBuffer(m_lpbConvIdx);
m_lpbConvIdx = NULL;
HRESULT hr = pmsg->GetProps((LPSPropTagArray) &tagaRead, 0,
&cValues, &m_pval);
if (HR_FAILED(hr))
{
m_lsterr.HrSetLastError(hr, pmsg);
goto err;
}
if(PROP_TYPE(m_pval[irtBody].ulPropTag) == PT_ERROR &&
GetScode(m_pval[irtBody].Value.l) == MAPI_E_NOT_ENOUGH_MEMORY)
{
hr = HrStreamInMsgBody(pmsg, m_pval, &m_pval[irtBody].Value.LPSZ, &m_lsterr);
if(hr)
{
goto err;
}
else
{
m_pval[irtBody].ulPropTag = PR_BODY;
}
}
Assert(cValues == cpropMsg);
if(PR_CONVERSATION_INDEX == m_pval[irtConvIdx].ulPropTag)
{
LPSPropValue pval = &m_pval[irtConvIdx];
m_cbConvIdx = pval->Value.bin.cb;
if(MAPIAllocateBuffer(m_cbConvIdx, (LPVOID *)&m_lpbConvIdx))
{
m_lpbConvIdx = NULL;
m_cbConvIdx = 0;
}
else
{
CopyMemory(m_lpbConvIdx, pval->Value.bin.lpb, m_cbConvIdx);
}
}
else
{
m_lpbConvIdx = NULL;
m_cbConvIdx = 0;
}
m_fConvTopicSet = (PR_CONVERSATION_TOPIC == m_pval[irtConvTopic].ulPropTag);
if(ulMsgFlags & MSGFLAG_UNSENT)
{
hr = GetMsgAdrlist (pmsg, &m_padrlist, &m_lsterr);
if(HR_FAILED(hr))
{
goto err;
}
}
return hrSuccess;
err:
MAPIFreeBuffer(m_pval);
m_pval = NULL;
FreePadrlist(m_padrlist);
m_padrlist = NULL;
return hr;
}
/// CBaseForm::ClearDirty
//
// Clears dirty state
void CBaseForm::ClearDirty(void)
{
m_fDirty = FALSE;
m_fRecipientsDirty = FALSE;
if(m_eFormType == eformSend)
{
Edit_SetModify(GetDlgItem(m_hwndDialog, ID_BODY), FALSE);
Edit_SetModify(GetDlgItem(m_hwndDialog, ID_SUBJECT), FALSE);
Edit_SetModify(GetDlgItem(m_hwndDialog, ID_TO), FALSE);
Edit_SetModify(GetDlgItem(m_hwndDialog, ID_CC), FALSE);
}
else
{
Edit_SetModify(GetDlgItem(m_hwndDialog, ID_BODY), FALSE);
}
}
//// CBaseForm::Address
//
// Description:
// This function is used to address the form.
// The parameter determines which button in the address
// dialog has the focus.
//
void CBaseForm::Address(int id)
{
Assert( ID_TO_BUTTON == id || ID_CC_BUTTON == id);
HRESULT hr;
if (m_pses == NULL)
{
hr = m_pmsgsite->GetSession(&m_pses);
if(hr)
{
m_lsterr.HrSetLastError(hr, m_pmsgsite);
ShowError();
return;
}
}
Assert(m_pses != NULL);
if (m_pab == NULL)
{
hr = m_pses->OpenAddressBook((ULONG) m_hwnd, NULL, 0, &m_pab);
if(hr)
{
m_lsterr.HrSetLastError(hr, m_pses);
ShowError();
}
if(HR_FAILED(hr)) //if it's a real error (not a warning)
return;
}
Assert(m_pab != NULL);
ADRPARM adrparm = { 0, NULL, AB_RESOLVE | DIALOG_MODAL, NULL, 0L,
NULL, NULL, NULL, NULL, "Address Book", NULL,
"Send Note To", 2, (id == ID_TO_BUTTON ? 0:1),
NULL, NULL, NULL, NULL };
ULONG ulHwndAddr = (ULONG) m_hwnd;
hr = m_pab->Address(&ulHwndAddr, &adrparm, &m_padrlist);
if (!hr)
{
m_fRecipientsDirty = TRUE;
UpdateRecipientsDisplay();
}
else if(GetScode(hr) != MAPI_E_USER_CANCEL)
{
m_lsterr.HrSetLastError(hr, m_pab);
ShowError();
}
}
//// CBaseForm::ClearWindow
//
// Description:
// This routine is called when the window for this form object is
// destroyed. We clear out pointer to the window and the windows
// pointer to us.
//
void CBaseForm::ClearWindow()
{
Assert(m_hwnd != NULL);
//
// Clear the back pointer to us and remove the reference count for it.
//
SetWindowLong(m_hwnd, 0, 0);
Release();
//
// Clear out pointer to the window since it is now dead
//
m_hwnd = NULL;
m_hwndDialog = NULL;
}
//// CBaseForm::HrOpenForm
//
// Description: This is the internal routine which is called from the
// open/display verb. It will cause UI to appear if there is none
// and force the window to the foreground if there is already UI.
//
HRESULT CBaseForm::HrOpenForm(HWND hwndParent, LPCRECT lprcPosRect,
ULONG ulViewFlags)
{
if (lprcPosRect == NULL)
return m_lsterr.HrSetLastError(ResultFromScode(E_INVALIDARG));
//
// If any modal forms are visible then do not do anything
//
Assert( g_FModalUp && g_hwndUp || !g_FModalUp && !g_hwndUp);
if (g_FMBoxUp || (g_FModalUp && hwndParent != g_hwndUp))
return m_lsterr.HrSetLastError(
ResultFromScode(OLEOBJ_S_CANNOT_DOVERB_NOW));
if (!(ulViewFlags & VCSTATUS_MODAL))
{
// If we are not modal then don't do anything relative to the parent
hwndParent = NULL;
}
//
// Check to see if we have a window up
//
if (m_hwnd != 0)
{
MoveWindow(m_hwnd, lprcPosRect->left, lprcPosRect->top,
lprcPosRect->right - lprcPosRect->left,
lprcPosRect->bottom - lprcPosRect->top,
TRUE);
SetForegroundWindow(m_hwnd);
return S_OK;
}
m_hwnd = CreateWindow((m_ulMsgFlags & MSGFLAG_UNSENT) ?
g_szSendWinClass : g_szReadWinClass,
g_szWindowCaption, WS_OVERLAPPEDWINDOW,
0, 0, 10, 10, hwndParent, NULL, g_hinst, NULL);
if (m_hwnd == NULL)
{
return m_lsterr.HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
}
//
// Put the pointer to this object into the window
//
SetWindowLong(m_hwnd, 0, (long) (void FAR *) this);
//we just created another pointer to ourself, so addref
AddRef();
//
// Create the dialog as a child of this window
//
if (m_ulMsgFlags & MSGFLAG_UNSENT)
{
m_eFormType = eformSend;
m_hwndDialog = CreateDialog(g_hinst, MAKEINTRESOURCE(IDR_SEND_FORM),
m_hwnd, &FormDlgProcSend);
m_HAccelTable = LoadAccelerators(g_hinst, MAKEINTRESOURCE(IDR_SEND_FORM));
}
else
{
m_eFormType = eformRead;
m_hwndDialog = CreateDialog(g_hinst, MAKEINTRESOURCE(IDR_READ_FORM),
m_hwnd, &FormDlgProcRead);
m_HAccelTable = LoadAccelerators(g_hinst, MAKEINTRESOURCE(IDR_READ_FORM));
}
DisplayMessage();
//
// Position the window where it is suppose to be
//
MoveWindow(m_hwnd, lprcPosRect->left, lprcPosRect->top,
lprcPosRect->right - lprcPosRect->left,
lprcPosRect->bottom - lprcPosRect->top,
TRUE);
ShowWindow(m_hwnd, SW_SHOW);
SetForegroundWindow(m_hwnd);
//
// If we are modal, then we loop until the form is closed
//
if (ulViewFlags & VCSTATUS_MODAL)
{
MSG msg;
BOOL fOldModalUp = g_FModalUp;
HWND hwndOldUp = g_hwndUp;
g_FModalUp = TRUE;
g_hwndUp = m_hwnd;
while ((m_hwnd != NULL) && (GetMessage(&msg, m_hwnd, 0, 0)))
{
//first call our method and see if this message makes sense to us.
//if not, let WIN API care about it.
if (!TranslateMessage(msg))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
g_FModalUp = fOldModalUp;
g_hwndUp = hwndOldUp;
}
return hrSuccess;
}
//// CBaseForm::HrSaveInto
//
// Description:
// This routine gives one central location which save all modified
// properties into a message.
//
HRESULT CBaseForm::HrSaveInto(LPMESSAGE pmsg)
{
ULONG cval = 0;
HRESULT hr;
//
// First, write out the recipient table
//
if(m_padrlist && m_fRecipientsDirty)
{
hr = pmsg->ModifyRecipients(0, m_padrlist);
if (FAILED(hr))
{
m_lsterr.HrSetLastError(hr, pmsg);
return hr;
}
}
//
// Next set up and save the rest of the info
//
HrGetMsgDataFromUI(m_hwndDialog);
//PR_DISPLAY_TO AND PR_DISPLAY_CC can't be set
ULONG ulToTag = m_pval[irtTo].ulPropTag;
m_pval[irtTo].ulPropTag = PR_NULL;
ULONG ulCcTag = m_pval[irtCc].ulPropTag;
m_pval[irtCc].ulPropTag = PR_NULL;
ULONG ulNormSubjectTag = m_pval[irtNormSubject].ulPropTag;
m_pval[irtNormSubject].ulPropTag = PR_NULL;
m_pval[irtClass].ulPropTag = PR_MESSAGE_CLASS;
m_pval[irtClass].Value.lpszA = FormClassName;
/*
*If the message didn't have PR_CONVERSATION_TOPIC when we loaded it, we'll
* set it every time we save the message. Otherwise we don't touch it
*/
if(!m_fConvTopicSet)
{
m_pval[irtConvTopic].ulPropTag = PR_CONVERSATION_TOPIC;
if(PR_SUBJECT == m_pval[irtSubject].ulPropTag)
{
m_pval[irtConvTopic].Value.LPSZ = m_pval[irtSubject].Value.LPSZ;
}
else
{
m_pval[irtConvTopic].Value.LPSZ = "";
}
}
else
{
m_pval[irtConvTopic].ulPropTag = PR_NULL;
}
/*
* if the message doesn't have a PR_CONVERSATION_INDEX, create and set it
*
*/
if(m_cbConvIdx == 0)
{
if(!ScAddConversationIndex(0, NULL, &m_cbConvIdx, &m_lpbConvIdx))
{
m_pval[irtConvIdx].ulPropTag = PR_CONVERSATION_INDEX;
m_pval[irtConvIdx].Value.bin.lpb = m_lpbConvIdx;
m_pval[irtConvIdx].Value.bin.cb = m_cbConvIdx;
}
else
{
m_pval[irtConvIdx].ulPropTag = PR_NULL;
}
}
else
{
m_pval[irtConvIdx].ulPropTag = PR_NULL;
}
LPSPropProblemArray pProblems = NULL;
hr = pmsg->SetProps(cpropMsg, m_pval, &pProblems);
if (hr)
{
m_lsterr.HrSetLastError(hr, pmsg);
goto err;
}
if(pProblems)
{
for(UINT ind = 0; ind < pProblems->cProblem; ++ind)
{
if(PR_BODY == pProblems->aProblem[ind].ulPropTag &&
MAPI_E_NOT_ENOUGH_MEMORY == pProblems->aProblem[ind].scode)
{
hr = HrStreamOutMsgBody(pmsg, m_pval[irtBody].Value.LPSZ, &m_lsterr);
if(hr)
{
MAPIFreeBuffer(pProblems);
pProblems = NULL;
goto err;
}
break;
}
}
MAPIFreeBuffer(pProblems);
pProblems = NULL;
}
err:
m_pval[irtTo].ulPropTag = ulToTag;
m_pval[irtCc].ulPropTag = ulCcTag;
m_pval[irtNormSubject].ulPropTag = ulNormSubjectTag;
return hr;
}
BOOL CBaseForm::TranslateMessage(MSG& msg)
{
//
// We translate accelerators before the dialog message so that we
// can get our accelerators to override the dialog's.
//
if(msg.hwnd == m_hwnd || IsChild(m_hwnd, msg.hwnd))
{
if (::TranslateAccelerator(m_hwnd, m_HAccelTable, &msg))
{
return TRUE;
}
}
if ((m_hwndDialog != NULL) && ::IsDialogMessage(m_hwndDialog, &msg))
{
return TRUE;
}
return FALSE;
}
/// CBaseForm::UpdateRecipientsDisplay
// go through m_padrlist and display TO and CC recipients in the
// edit boxes.
// Called only for send form. The Read form can use PR_DISPLAY_*
void CBaseForm::UpdateRecipientsDisplay(void)
{
Assert(m_hwndDialog);
Assert(m_eFormType == eformSend);
HWND hwTo = GetDlgItem(m_hwndDialog, ID_TO);
HWND hwCC = GetDlgItem(m_hwndDialog, ID_CC);
Edit_SetText(hwTo, "");
Edit_SetText(hwCC, "");
if(m_padrlist == NULL || m_padrlist->cEntries == 0) return;
#define ADRTEXTLEN 512
char szTo [ADRTEXTLEN] = "";
char szCC [ADRTEXTLEN] = "";
BOOL fToFull = FALSE;
BOOL fCCFull = FALSE;
LPADRENTRY pae;
for(pae = m_padrlist->aEntries;
pae < m_padrlist->aEntries + m_padrlist->cEntries;
++pae)
{
if( NULL == pae->rgPropVals) continue;
LPSPropValue lpsprop;
LPSTR szDisplay = NULL;
LPSTR szName = NULL;
BOOL * pfFull = NULL;
for(lpsprop = pae->rgPropVals;
lpsprop < pae->rgPropVals + pae->cValues;
++lpsprop)
{
if(lpsprop->ulPropTag == PR_RECIPIENT_TYPE)
{
if(lpsprop->Value.l == MAPI_TO)
{
szDisplay = szTo;
pfFull = &fToFull;
}
else if(lpsprop->Value.l == MAPI_CC)
{
szDisplay = szCC;
pfFull = &fCCFull;
}
}
else if(lpsprop->ulPropTag == PR_DISPLAY_NAME)
{
szName = lpsprop->Value.LPSZ;
}
if(NULL != szName && NULL != szDisplay)
{
Assert(pfFull);
if(*pfFull) break;
if(lstrlen(szDisplay) + lstrlen(szName) + 7 < ADRTEXTLEN)
{
lstrcat(szDisplay, szName);
lstrcat(szDisplay, "; ");
}
else
{
lstrcat(szDisplay, "...");
*pfFull = TRUE;
}
break;
}
}
}
//get rid of the "; " after the last recipient
if(lstrlen(szTo) > 0 && !fToFull)
szTo[lstrlen(szTo)-2] = 0;
if(lstrlen(szCC) > 0 && !fCCFull)
szCC[lstrlen(szCC)-2] = 0;
Edit_SetText(hwTo, szTo);
Edit_SetText(hwCC, szCC);
}
/// CBaseForm::DisplayMessage
//
// display the info from m_pval in the dialog
void CBaseForm::DisplayMessage(void)
{
char sz[256];
Assert(m_hwnd);
Assert(m_hwndDialog);
if(NULL == m_pval) return;
if (m_pval[irtSubject].ulPropTag == PR_SUBJECT)
{
SetDlgItemText(m_hwndDialog, ID_SUBJECT, m_pval[irtSubject].Value.LPSZ);
lstrcpyn(sz, m_pval[irtSubject].Value.LPSZ, 200);
sz[200] = 0;
lstrcat(sz, " - ");
lstrcat(sz, g_szWindowCaption);
SetWindowText(m_hwnd, sz);
}
else
{
SetWindowText(m_hwnd, g_szWindowCaption);
}
if (m_pval[irtBody].ulPropTag == PR_BODY)
SetDlgItemText(m_hwndDialog, ID_BODY, m_pval[irtBody].Value.LPSZ);
if(m_eFormType == eformRead)
{
if (m_pval[irtFrom].ulPropTag == PR_SENDER_NAME)
SetDlgItemText(m_hwndDialog, ID_FROM, m_pval[irtFrom].Value.LPSZ);
if (m_pval[irtTime].ulPropTag == PR_CLIENT_SUBMIT_TIME) {
FormatTime(&m_pval[irtTime].Value.ft, sz);
SetDlgItemText(m_hwndDialog, ID_SENT, sz);
}
if (m_pval[irtTo].ulPropTag == PR_DISPLAY_TO)
SetDlgItemText(m_hwndDialog, ID_TO, m_pval[irtTo].Value.LPSZ);
Edit_SetModify(GetDlgItem(m_hwndDialog, ID_BODY), FALSE);
}
else if (m_eFormType == eformSend)
{
UpdateRecipientsDisplay();
Edit_SetModify(GetDlgItem(m_hwndDialog, ID_BODY), FALSE);
Edit_SetModify(GetDlgItem(m_hwndDialog, ID_SUBJECT), FALSE);
Edit_SetModify(GetDlgItem(m_hwndDialog, ID_TO), FALSE);
Edit_SetModify(GetDlgItem(m_hwndDialog, ID_CC), FALSE);
}
else
{
Assert(FALSE);
}
}
/// CBaseForm::HrGetMsgDataFromUI
//
// save the message info from the dialog into the m_pval
HRESULT CBaseForm::HrGetMsgDataFromUI(HWND hDlg)
{
LONG cb = 0;
HRESULT hr = hrSuccess;
//have to call IsDirty() to make sure m_fDirty is current
if(!m_fDirty)
IsDirty();
if(m_eFormType == eformRead)
{
Assert(m_pval);
if(!m_fDirty)
return hrSuccess;
//everything is read-only except for the body
cb = GetWindowTextLength(GetDlgItem(hDlg, ID_BODY));
if(m_pval[irtBody].ulPropTag == PR_BODY &&
cb <= lstrlen(m_pval[irtBody].Value.LPSZ))
{
GetWindowText(GetDlgItem(hDlg, ID_BODY), m_pval[irtBody].Value.LPSZ, cb+1);
}
else
{
if(hr = MAPIAllocateMore(cb+1, m_pval, (LPVOID FAR *)&m_pval[irtBody].Value.LPSZ))
{
return m_lsterr.HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
}
GetWindowText(GetDlgItem(hDlg, ID_BODY), m_pval[irtBody].Value.LPSZ, cb+1);
}
m_pval[irtBody].ulPropTag = PR_BODY;
}
else if (m_eFormType == eformSend)
{
if(!m_fDirty && m_pval != NULL)
return hrSuccess;
if(NULL != m_pval)
{
MAPIFreeBuffer(m_pval);
m_pval = NULL;
}
if(MAPIAllocateBuffer(sizeof(SPropValue)* cpropMsg, (LPVOID FAR *) &m_pval))
{
return m_lsterr.HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
}
ZeroMemory(m_pval, sizeof(SPropValue)* cpropMsg);
m_pval[irtTime].ulPropTag = PR_NULL;
m_pval[irtFrom].ulPropTag = PR_NULL;
cb = GetWindowTextLength(GetDlgItem(hDlg, ID_SUBJECT)) + 1;
/*if(cb > 0)
{*/
if(MAPIAllocateMore(cb+1, m_pval, (LPVOID FAR *)&m_pval[irtSubject].Value.LPSZ))
{
return m_lsterr.HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
}
cb = GetWindowText(GetDlgItem(hDlg, ID_SUBJECT), m_pval[irtSubject].Value.LPSZ, cb+1);
m_pval[irtSubject].ulPropTag = PR_SUBJECT;
/*}
else
{ //no subject
m_pval[irtSubject].ulPropTag = PR_NULL;
} */
cb = GetWindowTextLength(GetDlgItem(hDlg, ID_BODY)) +1 ;
/*if(cb > 0)
{ */
if(MAPIAllocateMore(cb+1, m_pval, (LPVOID FAR *)&m_pval[irtBody].Value.LPSZ))
{
return m_lsterr.HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
}
cb = GetWindowText(GetDlgItem(hDlg, ID_BODY), m_pval[irtBody].Value.LPSZ, cb+1);
m_pval[irtBody].ulPropTag = PR_BODY;
/*}
else
{ //no body
m_pval[irtBody].ulPropTag = PR_NULL;
} */
cb = GetWindowTextLength(GetDlgItem(hDlg, ID_TO));
if(cb > 0)
{
if(hr = MAPIAllocateMore(cb+1, m_pval, (LPVOID FAR *)&m_pval[irtTo].Value.LPSZ))
{
return m_lsterr.HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
}
GetWindowText(GetDlgItem(hDlg, ID_TO), m_pval[irtTo].Value.LPSZ, cb+1);
m_pval[irtTo].ulPropTag = PR_DISPLAY_TO;
}
else
{ //no to
m_pval[irtTo].ulPropTag = PR_NULL;
}
cb = GetWindowTextLength(GetDlgItem(hDlg, ID_CC));
if(cb > 0)
{
if(hr = MAPIAllocateMore(cb+1, m_pval, (LPVOID FAR *)&m_pval[irtCc].Value.LPSZ))
{
return m_lsterr.HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
}
GetWindowText(GetDlgItem(hDlg, ID_CC), m_pval[irtCc].Value.LPSZ, cb+1);
m_pval[irtTo].ulPropTag = PR_DISPLAY_CC;
}
else
{ //no cc
m_pval[irtCc].ulPropTag = PR_NULL;
}
}
else
{
Assert(FALSE);
}
return hr;
}
/// CBaseForm::IsAddressed
//
//Does m_padrlist contain a recipient?
BOOL CBaseForm::IsAddressed(void)
{
Assert(m_eFormType == eformSend) ;
if(NULL == m_padrlist || m_padrlist->cEntries == 0)
return FALSE;
for(LPADRENTRY pae = m_padrlist->aEntries;
pae < m_padrlist->aEntries + m_padrlist->cEntries; ++pae)
{
if(pae->rgPropVals)
return TRUE;
}
return FALSE;
}
/// CBaseForm::ConfigMenu
//Enable/disable menu commands based on the values of m_ulSiteStatus
// and m_ulViewStatus
void CBaseForm::ConfigMenu(HMENU hMenu)
{
if(m_eFormType == eformRead)
{
EnableMenuItem(hMenu, IDC_MESSAGE_SAVE,
MF_BYCOMMAND|((m_ulSiteStatus & VCSTATUS_SAVE)? MF_ENABLED:MF_GRAYED));
EnableMenuItem(hMenu, IDC_MESSAGE_DELETE,
MF_BYCOMMAND| (!(m_ulViewStatus & VCSTATUS_READONLY) &&
(m_ulSiteStatus & VCSTATUS_DELETE) ? MF_ENABLED:MF_GRAYED));
EnableMenuItem(hMenu, IDC_MESSAGE_COPY,
MF_BYCOMMAND| (!(m_ulViewStatus & VCSTATUS_READONLY) &&
(m_ulSiteStatus & VCSTATUS_COPY) ? MF_ENABLED:MF_GRAYED));
EnableMenuItem(hMenu, IDC_MESSAGE_MOVE,
MF_BYCOMMAND| (!(m_ulViewStatus & VCSTATUS_READONLY) &&
(m_ulSiteStatus & VCSTATUS_MOVE) ? MF_ENABLED:MF_GRAYED));
EnableMenuItem(hMenu, IDC_VIEW_ITEMABOVE,
MF_BYCOMMAND|(m_ulViewStatus & VCSTATUS_PREV ? MF_ENABLED:MF_GRAYED));
EnableMenuItem(hMenu, IDC_VIEW_ITEMBELOW,
MF_BYCOMMAND|(m_ulViewStatus & VCSTATUS_NEXT ? MF_ENABLED:MF_GRAYED));
}
else
{
EnableMenuItem(hMenu, IDC_MESSAGE_SUBMIT,
MF_BYCOMMAND|((m_ulSiteStatus &VCSTATUS_SUBMIT) ? MF_ENABLED:MF_GRAYED));
EnableMenuItem(hMenu, IDC_MESSAGE_SAVE,
MF_BYCOMMAND|((m_ulSiteStatus & VCSTATUS_SAVE)? MF_ENABLED:MF_GRAYED));
EnableMenuItem(hMenu, IDC_MESSAGE_DELETE,
MF_BYCOMMAND| (!(m_ulViewStatus & VCSTATUS_READONLY) &&
(m_ulSiteStatus & VCSTATUS_DELETE) ? MF_ENABLED:MF_GRAYED));
EnableMenuItem(hMenu, IDC_MESSAGE_COPY,
MF_BYCOMMAND| (!(m_ulViewStatus & VCSTATUS_READONLY) &&
(m_ulSiteStatus & VCSTATUS_COPY) ? MF_ENABLED:MF_GRAYED));
EnableMenuItem(hMenu, IDC_MESSAGE_MOVE,
MF_BYCOMMAND| (!(m_ulViewStatus & VCSTATUS_READONLY) &&
(m_ulSiteStatus & VCSTATUS_MOVE) ? MF_ENABLED:MF_GRAYED));
}
}
///CBaseForm::HrReply
//
//
HRESULT CBaseForm::HrReply(eREPLYTYPE eReplyType, HWND hwndParent, LPCRECT prect)
{
//reply all is not implemented
Assert(eREPLY == eReplyType || eFORWARD == eReplyType);
HRESULT hr;
LONG cb;
char * szBody = NULL;
char * szSubject = NULL;
SPropValue val[3] = {0};
enum { eName, eAddrType, eEID, eRecipType, eDim};
SizedSPropTagArray(eDim, sptSender) =
{eDim, {PR_SENDER_NAME, PR_SENDER_ADDRTYPE,
PR_SENDER_ENTRYID, PR_NULL}};
LPSPropProblemArray pProblems = NULL;
LPMAPIFORM pfrmReply = NULL;
LPPERSISTMESSAGE ppermsg = NULL;
LPMAPIMESSAGESITE pmsgsite = NULL;
LPMAPIVIEWCONTEXT pviewctx = NULL;
LPMESSAGE pmsg = NULL;
ULONG cbNewConvIdx = 0;
LPBYTE lpbNewConvIdx = NULL;
Assert(m_pmsg);
hr = m_pClassFactory->CreateInstance(NULL, IID_IMAPIForm, (LPVOID FAR *) &pfrmReply);
if(hr)
{
m_lsterr.HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
goto err;
}
hr = pfrmReply->QueryInterface(IID_IPersistMessage, (LPVOID *) &ppermsg);
if(hr)
{
m_lsterr.HrSetLastError(hr, pfrmReply);
goto err;
}
hr = m_pmsgsite->NewMessage(FALSE, NULL, ppermsg, &pmsg, &pmsgsite, &pviewctx);
if(hr)
{
m_lsterr.HrSetLastError(hr, m_pmsgsite);
goto err;
}
hr = m_pmsg->CopyTo(0, NULL, (LPSPropTagArray)&sptExcludedProps,
0, NULL, &IID_IMessage, pmsg, 0, &pProblems);
if(hr)
{
m_lsterr.HrSetLastError(hr, m_pmsg);
goto err;
}
if(pProblems)
{
DebugTraceProblems("SmplForm: CopyTo returned ...", pProblems);
// if any of the errors is other than MAPI_E_COMPUTED, fail
for(UINT ind = 0; ind < pProblems->cProblem; ++ind)
{
if(MAPI_E_COMPUTED != pProblems->aProblem[ind].scode)
{
hr = m_lsterr.HrSetLastError(
ResultFromScode(pProblems->aProblem[ind].scode));
MAPIFreeBuffer(pProblems);
pProblems = NULL;
goto err;
}
}
MAPIFreeBuffer(pProblems);
pProblems = NULL;
}
if(m_pval && m_pval[irtNormSubject].ulPropTag == PR_NORMALIZED_SUBJECT)
cb = lstrlen(m_pval[irtNormSubject].Value.LPSZ);
else
cb = 0;
if(hr = MAPIAllocateBuffer(cb+lstrlen(szRE_PREFIX)+1, (LPVOID FAR *) &szSubject))
{
m_lsterr.HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
goto err;
}
*szSubject = '\0';
if(eREPLY == eReplyType)
{
lstrcat(szSubject, szRE_PREFIX);
}
else
{
lstrcat(szSubject, szFW_PREFIX);
}
if(cb > 0)
{
lstrcat(szSubject, m_pval[irtNormSubject].Value.LPSZ);
}
val[1].Value.lpszA = szSubject;
val[1].ulPropTag = PR_SUBJECT;
HrSaveToString(&szBody);
if(szBody)
{
val[0].Value.LPSZ = szBody;
val[0].ulPropTag = PR_BODY;
}
else
val[0].ulPropTag = PR_NULL;
/*
* Create a conversation index for the reply msg based on that of ours
*
*/
if(!ScAddConversationIndex(m_cbConvIdx, m_lpbConvIdx,
&cbNewConvIdx, &lpbNewConvIdx))
{
val[2].ulPropTag = PR_CONVERSATION_INDEX;
val[2].Value.bin.cb = cbNewConvIdx;
val[2].Value.bin.lpb = lpbNewConvIdx;
}
else
{
val[2].ulPropTag = PR_NULL;
}
hr = pmsg->SetProps(3, val, &pProblems);
MAPIFreeBuffer(lpbNewConvIdx);
lpbNewConvIdx = NULL;
MAPIFreeBuffer(szSubject);
szSubject = NULL;
if(!hr)
{
if(pProblems)
{
for(UINT ind = 0; ind < pProblems->cProblem; ++ind)
{
if(PR_BODY == pProblems->aProblem[ind].ulPropTag &&
MAPI_E_NOT_ENOUGH_MEMORY == pProblems->aProblem[ind].scode)
{
hr = HrStreamOutMsgBody(pmsg, szBody, &m_lsterr);
if(hr)
{
MAPIFreeBuffer(pProblems);
pProblems = NULL;
goto err;
}
break;
}
}
MAPIFreeBuffer(pProblems);
pProblems = NULL;
}
}
else
{
m_lsterr.HrSetLastError(hr, m_pmsg);
goto err;
}
// if it's a reply, set the addressee
if(eREPLY == eReplyType)
{
LPADRLIST pal = NULL;
ULONG cVal = 0;
LPSPropValue pval = NULL;
hr = MAPIAllocateBuffer(CbNewADRLIST(1), (LPVOID FAR *)&pal);
if(hr)
{
m_lsterr.HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
goto err;
}
hr = m_pmsg->GetProps((LPSPropTagArray) &sptSender, 0, &cVal, &pval);
if(hr) //treat warnings as an error, 'cause the props we ask for are required
{
m_lsterr.HrSetLastError(hr, m_pmsg);
MAPIFreeBuffer(pal);
goto err;
}
Assert(cVal == eDim);
pval[eRecipType].ulPropTag = PR_RECIPIENT_TYPE;
pval[eRecipType].Value.l = MAPI_TO;
Assert(pval[eName].ulPropTag == PR_SENDER_NAME);
pval[eName].ulPropTag = PR_DISPLAY_NAME;
Assert(pval[eAddrType].ulPropTag == PR_SENDER_ADDRTYPE);
pval[eAddrType].ulPropTag = PR_ADDRTYPE;
Assert(pval[eEID].ulPropTag == PR_SENDER_ENTRYID);
pval[eEID].ulPropTag = PR_ENTRYID;
pal->aEntries[0].rgPropVals = pval;
pal->cEntries = 1;
pal->aEntries[0].cValues = eDim;
hr = pmsg->ModifyRecipients(0, pal);
FreePadrlist(pal); //this will also free pval
pal = NULL;
pval = NULL;
if(hr)
{
m_lsterr.HrSetLastError(hr, pmsg);
goto err;
}
}
hr = ppermsg->Load(pmsgsite, pmsg, 0, MSGFLAG_UNSENT );
if(hr)
{
m_lsterr.HrSetLastError(hr, ppermsg);
goto err;
}
hr = pfrmReply->DoVerb(EXCHIVERB_OPEN, pviewctx, (ULONG)hwndParent, prect);
if(hr)
{
m_lsterr.HrSetLastError(hr, pfrmReply);
pfrmReply->ShutdownForm(SAVEOPTS_NOSAVE);
goto err;
}
err:
UlRelease(pfrmReply);
UlRelease(ppermsg);
UlRelease(pmsgsite);
UlRelease(pviewctx);
UlRelease(pmsg);
delete [] szBody;
return hr;
}
/// CBaseForm::HrSaveToString
//
// The returned string has to be freed using delete
HRESULT CBaseForm::HrSaveToString(LPSTR * pszMessage)
{
Assert(m_pval);
Assert(pszMessage);
ostrstream ostrBody;
char *szMsg = NULL;
ostrBody << "\r\n-----------------------------";
ostrBody << "\r\nFrom:\t";
if(m_pval[irtFrom].ulPropTag == PR_SENDER_NAME)
{
ostrBody << m_pval[irtFrom].Value.LPSZ;
}
else
{
ostrBody << "Unknown";
}
if(m_pval[irtTime].ulPropTag == PR_CLIENT_SUBMIT_TIME)
{
char sz[30];
FormatTime(&m_pval[irtTime].Value.ft, sz);
ostrBody << "\r\nSent:\t";
ostrBody << sz;
}
if(m_pval[irtTo].ulPropTag == PR_DISPLAY_TO)
{
ostrBody << "\r\nTo:\t";
ostrBody << m_pval[irtTo].Value.LPSZ;
}
if(m_pval[irtSubject].ulPropTag == PR_SUBJECT)
{
ostrBody << "\r\nSubject:\t";
ostrBody << m_pval[irtSubject].Value.LPSZ;
}
ostrBody << "\r\n";
if(m_pval && m_pval[irtBody].ulPropTag == PR_BODY)
ostrBody << m_pval[irtBody].Value.LPSZ;
ostrBody << ends;
szMsg = ostrBody.str();
if(szMsg != NULL)
{
*pszMessage = szMsg;
return hrSuccess;
}
else
{
*pszMessage = NULL;
return m_lsterr.HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
}
}
/// CBaseForm::HrQuerySave
//
//
HRESULT CBaseForm::HrQuerySave (DWORD ulSaveOptions)
{
HRESULT hr = hrSuccess;
UINT ui;
//
// Check to see if we are marked as dirty. If we are clean then
// we can just return without doing any work
//
if(GetScode(IsDirty()) != S_OK)
return hrSuccess;
switch( ulSaveOptions )
{
default:
Assert(FALSE);
case SAVEOPTS_SAVEIFDIRTY:
break;
case SAVEOPTS_NOSAVE:
return hrSuccess;
case SAVEOPTS_PROMPTSAVE:
ui = ShowMessageBox (m_hwnd, "Would you like to save changes?",
g_szFormName, MB_ICONEXCLAMATION | MB_YESNOCANCEL);
switch (ui)
{
case IDYES:
break;
default:
Assert(FALSE);
case IDNO:
return hrSuccess;
case IDCANCEL:
return MAPI_E_USER_CANCEL;
}
}
hr = m_pmsgsite->SaveMessage();
if(hr)
m_lsterr.HrSetLastError(hr, m_pmsgsite);
return hr;
}
/// CBaseForm::DoDelete
//
// called only from our UI
void CBaseForm::DoDelete(void)
{
HRESULT hr;
RECT rect;
GetWindowRect(m_hwnd, &rect);
hr = m_pmsgsite->DeleteMessage(m_pviewctx, &rect);
if(HR_FAILED(hr))
{
m_lsterr.HrSetLastError(hr, m_pmsgsite);
ShowError();
}
if(NULL == m_pmsg)
{
ShutdownForm(SAVEOPTS_NOSAVE);
}
}
/// CBaseForm::DoSave
//
// called only from our UI
void CBaseForm::DoSave(void)
{
HRESULT hr;
hr = m_pmsgsite->SaveMessage();
if (FAILED(hr))
{
m_lsterr.HrSetLastError(hr, m_pmsgsite);
ShowError();
}
}
/// CBaseForm::DoSubmit
//
// called only from our UI
void CBaseForm::DoSubmit(void)
{
HRESULT hr;
if(!IsAddressed())
{
ShowMessageBox(m_hwndDialog, "No recipients", g_szFormName, MB_OK);
return;
}
hr = m_pmsgsite->SubmitMessage(0);
if (FAILED(hr))
{
m_lsterr.HrSetLastError(hr, m_pmsgsite);
ShowError();
}
else
{
m_viewnotify.OnSubmitted();
}
if (m_pmsg == NULL)
ShutdownForm(SAVEOPTS_NOSAVE);
}
/// CBaseForm::DoNext
//
// called only from our UI
void CBaseForm::DoNext(ULONG ulDir)
{
Assert(VCDIR_NEXT == ulDir || VCDIR_PREV == ulDir);
HRESULT hr;
hr = HrQuerySave(SAVEOPTS_PROMPTSAVE);
if(hr)
{
if(hr != MAPI_E_USER_CANCEL)
ShowError();
return;
}
RECT rect;
GetWindowRect(m_hwnd, &rect);
hr = ViewCtx()->ActivateNext(ulDir, &rect);
if(NULL == m_pmsg)
{
ShutdownForm(SAVEOPTS_NOSAVE);
}
}
/// CBaseForm::DoReply
//
// called only from our UI
void CBaseForm::DoReply(eREPLYTYPE eType)
{
HRESULT hr;
hr = HrQuerySave(SAVEOPTS_PROMPTSAVE);
if(hr)
{
if(hr != MAPI_E_USER_CANCEL)
ShowError();
return;
}
RECT rect;
GetWindowRect(m_hwnd, &rect);
int iOffset = GetSystemMetrics(SM_CYCAPTION);
OffsetRect(&rect, iOffset, iOffset);
hr = HrReply(eType, m_hwnd, &rect);
if(!hr)
{
ShutdownForm(SAVEOPTS_NOSAVE);
}
else
{
ShowError();
}
}
/// CBaseForm::DoCopy
//
// called only from our UI
void CBaseForm::DoCopy(void)
{
HRESULT hr;
LPMAPIFOLDER pfld = NULL;
LPMDB pmdb = NULL;
if(!FGetFoldChooser())
{
ShowMessageBox(m_hwnd, "Can't copy", g_szFormName, MB_OK | MB_ICONSTOP);
return;
}
Assert(m_lpfnHrPickFolder && m_hChsFldDll);
if (m_pses == NULL)
{
hr = m_pmsgsite->GetSession(&m_pses);
if(hr)
{
m_lsterr.HrSetLastError(hr, m_pmsgsite);
ShowError();
return;
}
}
BOOL fOldModalUp = g_FModalUp;
g_FModalUp = TRUE;
hr = (*m_lpfnHrPickFolder)(g_hinst, m_hwnd, m_pses, &pfld, &pmdb,
&m_cbCFDState, &m_pbCFDState);
g_FModalUp = fOldModalUp;
if(hr)
{
if(GetScode(hr) != MAPI_E_USER_CANCEL)
ShowMessageBox(m_hwnd, "Can't copy", g_szFormName, MB_OK | MB_ICONSTOP);
return;
}
Assert(m_pmsgsite);
Assert(pfld);
Assert(pmdb);
hr = m_pmsgsite->CopyMessage(pfld);
pfld->Release();
pmdb->Release();
if(hr)
{
m_lsterr.HrSetLastError(hr, m_pmsgsite);
ShowError();
return;
}
}
/// CBaseForm::DoMove
//
// called only from our UI
void CBaseForm::DoMove(void)
{
HRESULT hr;
LPMAPIFOLDER pfld = NULL;
LPMDB pmdb = NULL;
if(!FGetFoldChooser())
{
ShowMessageBox(m_hwnd, "Can't move", g_szFormName, MB_OK | MB_ICONSTOP);
return;
}
Assert(m_lpfnHrPickFolder && m_hChsFldDll);
if (m_pses == NULL)
{
hr = m_pmsgsite->GetSession(&m_pses);
if(hr)
{
m_lsterr.HrSetLastError(hr, m_pmsgsite);
ShowError();
return;
}
}
BOOL fOldModalUp = g_FModalUp;
g_FModalUp = TRUE;
hr = (*m_lpfnHrPickFolder)(g_hinst, m_hwnd, m_pses, &pfld, &pmdb,
//&m_cbCFDState, &m_pbCFDState);
NULL, NULL);
g_FModalUp = fOldModalUp;
if(hr)
{
if(GetScode(hr) != MAPI_E_USER_CANCEL)
ShowMessageBox(m_hwnd, "Can't move", g_szFormName, MB_OK | MB_ICONSTOP);
return;
}
Assert(m_pmsgsite);
Assert(pfld);
Assert(pmdb);
RECT rect;
GetWindowRect(m_hwnd, &rect);
hr = m_pmsgsite->MoveMessage(pfld, ViewCtx(), &rect);
pfld->Release();
pmdb->Release();
if(HR_FAILED(hr))
{
m_lsterr.HrSetLastError(hr, m_pmsgsite);
ShowError();
return;
}
if(NULL == m_pmsg)
{
ShutdownForm(SAVEOPTS_NOSAVE);
}
}
//wraper for MessageBox()
int CBaseForm::ShowMessageBox(HWND hwnd, LPCTSTR lpszText, LPCTSTR lpszTitle, UINT uiStyle)
{
int iret;
BOOL fOldModalUp = g_FMBoxUp;
g_FMBoxUp = TRUE;
iret = MessageBox(hwnd, lpszText, lpszTitle, uiStyle);
g_FMBoxUp = fOldModalUp;
return iret;
}
//wraper for m_lsterr.ShowError()
void CBaseForm::ShowError(void)
{
int iret;
BOOL fOldModalUp = g_FMBoxUp;
g_FMBoxUp = TRUE;
iret = m_lsterr.ShowError(m_hwnd);
g_FMBoxUp = fOldModalUp;
}
BOOL CBaseForm::FGetFoldChooser(void)
{
if(m_lpfnHrPickFolder)
return TRUE;
Assert(!m_hChsFldDll);
UINT uiErrMode = SetErrorMode(SEM_NOOPENFILEERRORBOX);
m_hChsFldDll = LoadLibrary(szChsFldDllName);
SetErrorMode(uiErrMode);
if(m_hChsFldDll)
{
if((m_lpfnHrPickFolder = (HRPICKFOLDER)GetProcAddress(m_hChsFldDll,
szChsFldFnName)))
{
return TRUE;
}
DebugTrace("smpfrm: GetProcAddress for %s failed", szChsFldFnName);
FreeLibrary(m_hChsFldDll);
m_hChsFldDll = NULL;
}
else
{
DebugTrace("smpfrm: failed to load choose folder dll\n");
}
return FALSE;
}
//
//if the body is to large for GetProps, have to use IStream
//
HRESULT HrStreamInMsgBody(LPMESSAGE pmsg, LPVOID pbase,
LPSTR * pszBody, CLastError * plasterror)
{
Assert(pmsg);
Assert(pszBody);
Assert(plasterror);
HRESULT hr;
LPSTREAM lpstreamBody = NULL;
STATSTG statstg;
LPSTR szRet = NULL;
ULONG cb = 0;
Assert(pszBody);
*pszBody = NULL;
hr = pmsg->OpenProperty(PR_BODY, &IID_IStream,
STGM_READ, 0, (LPUNKNOWN FAR *) &lpstreamBody);
if(S_OK != GetScode(hr))
{
plasterror->HrSetLastError(hr, pmsg);
goto err;
}
hr = lpstreamBody->Stat(&statstg, STATFLAG_NONAME);
if(S_OK != GetScode(hr))
{
plasterror->HrSetLastError(hr, lpstreamBody);
goto err;
}
Assert(statstg.cbSize.HighPart == 0);
//if p base is not null, link the new buffer to it
if(pbase)
{
if(MAPIAllocateMore(statstg.cbSize.LowPart + 1, pbase, (LPVOID FAR *) &szRet))
{
plasterror->HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
goto err;
}
}
else
{
if(MAPIAllocateBuffer(statstg.cbSize.LowPart + 1, (LPVOID FAR *) &szRet))
{
plasterror->HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
goto err;
}
}
hr = lpstreamBody->Read(szRet, statstg.cbSize.LowPart, &cb);
if(S_OK != GetScode(hr))
{
plasterror->HrSetLastError(hr);
goto err;
}
szRet[statstg.cbSize.LowPart] = '\0';
err:
UlRelease(lpstreamBody);
lpstreamBody = NULL;
if(hr)
{
if(!pbase)
MAPIFreeBuffer(szRet);
szRet = NULL;
}
*pszBody = szRet;
return hr;
}
//
//if the body is to large for SetProps, have to use IStream
//
HRESULT HrStreamOutMsgBody(LPMESSAGE pmsg, LPSTR szBody, CLastError * plasterror)
{
Assert(pmsg);
Assert(szBody);
Assert(plasterror);
HRESULT hr;
LPSTREAM lpstreamBody = NULL;
ULONG cb = 0;
Assert(szBody);
hr = pmsg->OpenProperty(PR_BODY, &IID_IStream,
STGM_READWRITE, 0, (LPUNKNOWN FAR *) &lpstreamBody);
if(S_OK != GetScode(hr))
{
plasterror->HrSetLastError(hr, pmsg);
goto err;
}
hr = lpstreamBody->Write(szBody, lstrlen(szBody)+1, NULL);
if(hr)
plasterror->HrSetLastError(hr);
err:
UlRelease(lpstreamBody);
lpstreamBody = NULL;
return hr;
}
/*
* Formats a Win32 file time as a MAPI date/time string.
* NOTE: converts from GMT to local time.
*/
void FormatTime(FILETIME *pft, LPSTR szTime)
{
FILETIME ft;
SYSTEMTIME systime;
FileTimeToLocalFileTime(pft, &ft);
FileTimeToSystemTime(&ft, &systime);
wsprintf(szTime,
"%04.4d/%02.2d/%02.2d %02.2d:%02.2d",
systime.wYear, systime.wMonth, systime.wDay,
systime.wHour, systime.wMinute);
}
/// GetMsgAdrlist
// retrieves recipients adrlist of a message
HRESULT GetMsgAdrlist (LPMESSAGE pmsg, LPADRLIST * ppAdrList, CLastError * plasterror)
{
*ppAdrList = NULL;
LPMAPITABLE pTable = NULL;
HRESULT hr;
hr = pmsg->GetRecipientTable (0, &pTable);
if(!hr)
{
hr = HrQueryAllRows(pTable, NULL, NULL, NULL, 0, (LPSRowSet *)ppAdrList);
if(hr)
{
plasterror->HrSetLastError(hr, pTable);
}
}
else
{
plasterror->HrSetLastError(hr, pmsg);
}
pTable->Release();
return hr;
}