EVNTEXT.CPP

/////////////////////////////////////////////////////////////////////////////// 
//
// File Name
// EVNTEXT.CPP
//
// Description
// This is an implementation of a message event extension example. The
// extension calculates the checksum of a message that is submitted by
// The Exchange client and then signs the message with the checksum result
// to be used upon reading. The sample includes a Tools Options property
// page to enable or disable signing.
//
// IExchExt interface methods:
// MyExchExt::QueryInterface()
// MyExchExt::AddRef()
// MyExchExt::Release()
//
// MyExchExt::Install()
//
// IExchExtPropertySheets interface methods:
// IExchExtPropertySheets::QueryInterface()
// IExchExtPropertySheets::AddRef()
// IExchExtPropertySheets::Release()
//
// IExchExtPropertySheets::GetMaxPageCount()
// IExchExtPropertySheets::GetPages()
// IExchExtPropertySheets::FreePages()
//
// IExchExtMessageEvents interface methods:
// IExchExtMessageEvents::QueryInterface()
// IExchExtMessageEvents::AddRef()
// IExchExtMessageEvents::Release()
//
// IExchExtMessageEvents::OnCheckNames()
// IExchExtMessageEvents::OnCheckNamesComplete()
// IExchExtMessageEvents::OnRead()
// IExchExtMessageEvents::OnReadComplete()
// IExchExtMessageEvents::OnSubmit()
// IExchExtMessageEvents::OnSubmitComplete()
// IExchExtMessageEvents::OnWrite()
// IExchExtMessageEvents::OnWriteComplete()
//
// Author
// Gary Peluso
//
// Revision: 1.01
//
// History
// 9/21/95 - fixed "signature" property sheet bug (two sheets incorrectly shown)
// 9/21/95 - changed QI for MyExchExtMessageEvents and MyExchExtPropertySheets
// to return pointer to MyExchExt when IID_IUNKNOWN is requested
//
// Written for Microsoft Windows Developer Support
// Copyright (c) 1992-1995 Microsoft Corporation. All rights reserved.
//
#define INITGUID
#define USES_IID_IExchExt
#define USES_IID_IExchExtAdvancedCriteria
#define USES_IID_IExchExtAttachedFileEvents
#define USES_IID_IExchExtCommands
#define USES_IID_IExchExtMessageEvents
#define USES_IID_IExchExtPropertySheets
#define USES_IID_IExchExtSessionEvents
#define USES_IID_IExchExtUserEvents
#define USES_IID_IMessage
#define USES_PS_MAPI
#define USES_PS_PUBLIC_STRINGS

#include "EVNTEXT.H"

//#include <INITGUID.H>
#include <MAPIGUID.H>


///////////////////////////////////////////////////////////////////////////////
// global data that supports extension functionality
BOOL bSignatureOn = TRUE;


///////////////////////////////////////////////////////////////////////////////
// global data for DLL
static HINSTANCE ghInstDLL = NULL; // instance handle of DLL

///////////////////////////////////////////////////////////////////////////////
// FUNCTION: DLLMain()
//
// Purpose
// Do initilization processesing
//
// Return Value
// TRUE - DLL successfully loads and LoadLibrary will succeed.
// FALSE - will cause an Exchange error message saying it cannot locate
// the extension DLL.
//
// Comments
// We only need to get a copy of the DLL's HINSTANCE.
//
BOOL WINAPI DllMain(
HINSTANCE hinstDLL,
DWORD fdwReason,
LPVOID lpvReserved)
{
if (DLL_PROCESS_ATTACH == fdwReason)
{
ghInstDLL = hinstDLL;

}
return TRUE;
}


///////////////////////////////////////////////////////////////////////////////
// FUNCTION: ExchEntryPoint
//
// Parameters - none
//
// Purpose
// The entry point called by Exchange.
//
// Return Value
// Pointer to Exchange Extension (IExchExt) interface
//
// Comments
// Exchange Client calls this for each context entry.
//

LPEXCHEXT CALLBACK ExchEntryPoint(void)
{
return new MyExchExt;
}


///////////////////////////////////////////////////////////////////////////////
// MyExchExt::MyExchExt()
//
// Parameters - none
//
// Purpose
// Comstructor. Called during instantiation of MyExchExt object.
//
//

MyExchExt::MyExchExt()
{
m_cRef = 1;

m_pExchExtPropertySheets = new MyExchExtPropertySheets(this);
m_pExchExtMessageEvents = new MyExchExtMessageEvents(this);

};



///////////////////////////////////////////////////////////////////////////////
// IExchExt virtual member functions implementation
//

///////////////////////////////////////////////////////////////////////////////
// MyExchExtPropertySheets::Release()
//
// Parameters - none
//
// Purpose
// Frees memory when interface is not referenced any more
//
// Return value
// reference count of interface
//

STDMETHODIMP_(ULONG) MyExchExt::Release()
{
ULONG ulCount = --m_cRef;

if (!ulCount)
{
delete this;
}

return ulCount;

}

///////////////////////////////////////////////////////////////////////////////
// MyExchExt::QueryInterface()
//
// Parameters
// riid -- Interface ID.
// ppvObj -- address of interface object pointer.
//
// Purpose
// Returns a pointer to an interface object that is requested by ID.
//
// Comments
// The interfaces are requested everytime a new context is entered. The
// IID_IExchExt* interfaces are ignored if not specified in the Exchange
// extensions registry.
//
// If an interface pointer is returned for more than one context, that
// interface is used by the client for each of those contexts. Check the
// current context to verify if it is appropriate to pass back an interface
// pointer.
//
// Return Value - none
//

STDMETHODIMP MyExchExt::QueryInterface(REFIID riid, LPVOID FAR * ppvObj)
{
HRESULT hResult = S_OK;

*ppvObj = NULL;

if (( IID_IUnknown == riid) || ( IID_IExchExt == riid) )
{
*ppvObj = (LPUNKNOWN)this;
}
else if (IID_IExchExtPropertySheets == riid)
{
// if we are in the read or send context, do not return
// propertysheet interface
if ( (m_context == EECONTEXT_SENDNOTEMESSAGE) ||
(m_context == EECONTEXT_SENDPOSTMESSAGE) ||
(m_context == EECONTEXT_SENDRESENDMESSAGE) ||
(m_context == EECONTEXT_READNOTEMESSAGE) ||
(m_context == EECONTEXT_READPOSTMESSAGE) ||
(m_context == EECONTEXT_READREPORTMESSAGE) )
return E_NOINTERFACE;

// otherwise return the interface
*ppvObj = (LPUNKNOWN) m_pExchExtPropertySheets;
}
else if (IID_IExchExtMessageEvents == riid)
{
*ppvObj = (LPUNKNOWN) m_pExchExtMessageEvents;
}
else
hResult = E_NOINTERFACE;

if (NULL != *ppvObj)
((LPUNKNOWN)*ppvObj)->AddRef();

return hResult;
}



///////////////////////////////////////////////////////////////////////////////
// MyExchExt::Install()
//
// Parameters
// peecb -- pointer to Exchange Extension callback function
// eecontext -- context code at time of being called.
//
// Purpose
// Called once for each new context that is entered.
//
// Return Value
// S_OK - the installation succeeded for the context
// S_FALSE - deny the installation fo the extension for the context
//
STDMETHODIMP MyExchExt::Install(LPEXCHEXTCALLBACK peecb, ULONG eecontext, ULONG ulFlags)
{
ULONG ulBuildVersion;
HRESULT hr;

m_context = eecontext;

// make sure this is the right version
peecb->GetVersion(&ulBuildVersion, EECBGV_GETBUILDVERSION);
if (EECBGV_BUILDVERSION_MAJOR != (ulBuildVersion &
EECBGV_BUILDVERSION_MAJOR_MASK))
return S_FALSE;


switch (eecontext)
{
case EECONTEXT_PROPERTYSHEETS:
case EECONTEXT_SENDNOTEMESSAGE:
case EECONTEXT_SENDPOSTMESSAGE:
case EECONTEXT_SENDRESENDMESSAGE:
case EECONTEXT_READNOTEMESSAGE:
case EECONTEXT_READPOSTMESSAGE:
case EECONTEXT_READREPORTMESSAGE:
hr = S_OK;
break;

default:
hr = S_FALSE;
break;
}


return hr;

}

///////////////////////////////////////////////////////////////////////////////
// IExchExtPropertySheets virtual member functions implementation
//

///////////////////////////////////////////////////////////////////////////////
// MyExchExtPropertySheets::QueryInterface()
//
// Parameters
// riid -- Interface ID.
// ppvObj -- address of interface object pointer.
//
// Purpose
// Return interface object upon request
//
// Return Value - none
//
// Comments
// Currently the Exchange client does not call QueryInterface from any object
// except for IExchExt. This is implemented in case features are added to
// Exchange to require QueryInterface from any object. Also, as a "rule of
// OLE COM" this is the proper implementation of QueryInterface.
//

STDMETHODIMP MyExchExtPropertySheets::QueryInterface(REFIID riid, LPVOID FAR * ppvObj)
{

*ppvObj = NULL;
if (riid == IID_IExchExtPropertySheets)
{
*ppvObj = (LPVOID)this;
// Increase usage count of this object
AddRef();
return S_OK;
}
if (riid == IID_IUnknown)
{
*ppvObj = (LPVOID)m_pExchExt; // return parent interface
m_pExchExt->AddRef();
return S_OK;
}

return E_NOINTERFACE;

}


///////////////////////////////////////////////////////////////////////////////
// MyExchExtPropertySheets::GetMaxPageCount()
//
// Parameters
// ulFlags -- a bitmask indicating what type of property sheet is being
// displayed
//
// Purpose
// Returns the number of property pages which are to be added.
//
// Return Value - maximum number of custom pages for the property sheet
//
// Exchange calls this to know how many PROPSHEETPAGE buffers it needs
// to allocate.
//

STDMETHODIMP_ (ULONG) MyExchExtPropertySheets::GetMaxPageCount(ULONG ulFlags)
{
ULONG ulNumExtSheets;

switch (ulFlags)
{
// ignore these objects.
case EEPS_FOLDER:
case EEPS_STORE:
case EEPS_MESSAGE:
ulNumExtSheets = 0;
break;

case EEPS_TOOLSOPTIONS:
ulNumExtSheets = 1; // adding one propery page
break;

default:
ulNumExtSheets = 0;
break;
}

return ulNumExtSheets;
}


///////////////////////////////////////////////////////////////////////////////
// MyExchExtPropertySheets::GetPages()
//
// Parameters
// peecb -- pointer to Exchange callback interface
// ulFlags -- a bitmask indicating what type of property sheet is being
// displayed
// ppsp -- output parm pointing to pointer to list of property sheets
// pcpsp -- output parm pointing to buffer contaiing number of property
// sheets actually used.
//
// Purpose
// Fills the PROPSHEETPAGE members for the custom property page.
//
// Return Value
// S_FALSE - do not add a new page
// S_OK - use the ppsp information for new pages.
//
// Comments
// Exchange calls this method to gather information for any custom
// property pages to be added to the sheet. Here we are only adding
// one. ppsp may be an array of PROPSHEETPAGE structures to allow you
// to all multiple property pages.
//

STDMETHODIMP MyExchExtPropertySheets::GetPages(LPEXCHEXTCALLBACK peecb,
ULONG ulFlags, LPPROPSHEETPAGE ppsp, ULONG FAR * pcpsp)
{
LPMDB pMDB = NULL;
LPMESSAGE pItem = NULL;

*pcpsp = 0;


// fill out members for the property page
ppsp[0].dwSize = sizeof (PROPSHEETPAGE);
ppsp[0].dwFlags = PSP_DEFAULT | PSP_HASHELP;
ppsp[0].hInstance = ghInstDLL;
ppsp[0].pszTemplate = MAKEINTRESOURCE(IDD_SIGNATURE);
ppsp[0].hIcon = NULL; // not used in this sample
ppsp[0].pszTitle = NULL; // not used in this sample
ppsp[0].pfnDlgProc = (DLGPROC)SignatureOptionsDlgProc;
ppsp[0].lParam = 0;
ppsp[0].pfnCallback = NULL;
ppsp[0].pcRefParent = NULL; // not used in this sample

*pcpsp = 1;

return S_OK;
}


///////////////////////////////////////////////////////////////////////////////
// MyExchExtPropertySheets::FreePages()
//
// Parameters
// ppsp -- pointer to a pointer to the first of a list of property pages
// cpsp -- number of custom property pages in the list
// ulFlags -- type of property page
//
// Purpose
// Free any memory associated to the property sheet.
//
// Return Value - none
//
// Comments
// No parameters are used in this example but the function is used as
// a signal that the property sheet is going away and so memory may
// be freed.
//

STDMETHODIMP_ (VOID) MyExchExtPropertySheets::FreePages(LPPROPSHEETPAGE ppsp, ULONG ulFlags,
ULONG cpsp)
{
// not used in this sample
}


///////////////////////////////////////////////////////////////////////////////
// IExchExtMessageEvents virtual member functions implementation
//

///////////////////////////////////////////////////////////////////////////////
// MyExchExtMessageEvents::QueryInterface()
//
// Parameters
// riid -- Interface ID.
// ppvObj -- address of interface object pointer.
//
// Purpose
// Return interface object upon request
//
// Return Value - none
//
// Comments
// Currently the Exchange client does not call QueryInterface from any object
// except for IExchExt. This is implemented in case features are added to
// Exchange to require QueryInterface from any object. Also, as a "rule of
// OLE COM" this is the proper implementation of QueryInterface.
//

STDMETHODIMP MyExchExtMessageEvents::QueryInterface(REFIID riid, LPVOID FAR * ppvObj)
{

*ppvObj = NULL;
if (riid == IID_IExchExtMessageEvents)
{
*ppvObj = (LPVOID)this;
// Increase usage count of this object
AddRef();
return S_OK;
}
if (riid == IID_IUnknown)
{
*ppvObj = (LPVOID)m_pExchExt; // return parent interface
m_pExchExt->AddRef();
return S_OK;
}
return E_NOINTERFACE;

}



///////////////////////////////////////////////////////////////////////////////
// MyExchExtMessageEvents::OnRead()
//
// Parameters
// lpeecb -- pointer to IExchExtCallback interface
//
// Purpose
// To extend or inhibit Exchange when displaying the send or read note form.
//
// Return Value
// S_OK signals Exchange to not continue calling extensions
// S_FALSE signals Exchange to continue calling extensions
// Other MAPI Code errors will abort the send or read note form.
//
//

STDMETHODIMP MyExchExtMessageEvents::OnRead(LPEXCHEXTCALLBACK lpeecb)
{
HRESULT hr;
LPMESSAGE pMessage = NULL;
LPMDB pMDB = NULL;
ULONG ulCheckSum = 0;
LPSPropTagArray pNamedPropTags = NULL;
LPSPropValue pPropValues = NULL;
HWND hWnd;
HCURSOR hOldCursor = NULL;
MAPINAMEID NamedID;
LPMAPINAMEID pnmid = &NamedID;


m_hrOnReadComplete = S_FALSE; // tell OnReadComplete to show its dialog

if (!bSignatureOn) // user has turned off signatures
{
goto error_return; // return and do nothing
}


hr = lpeecb->GetObject(&pMDB, (LPMAPIPROP *)&pMessage);
if (FAILED(hr))
{
goto error_return;
}


lpeecb->GetWindow(&hWnd);
hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));

// check to see if MsgChecksum exists
// use a named property for the message body checksum
NamedID.lpguid = (LPGUID) &PS_PUBLIC_STRINGS;
NamedID.ulKind = MNID_STRING;
NamedID.Kind.lpwstrName = L"MsgChecksum";

hr = pMessage->GetIDsFromNames(1, &pnmid, 0, &pNamedPropTags);
//here we can get away with checking for hr!=0, because we retrieve only one
//PropTag. If more then one PropTag requested and GetIDsFromName can't find
// some of them, a warning MAPI_W_ERRORS_RETURNED is returned and each PropTag
//has to be checked separatly. (See Docs for details).
if (hr) // likely the message is not signed
{
goto error_return;
}

pNamedPropTags->aulPropTag[0] = PROP_TAG(PT_LONG, PROP_ID(pNamedPropTags->aulPropTag[0]));

hr = HrGetOneProp(pMessage, pNamedPropTags->aulPropTag[0], &pPropValues);
if (hr)
{
goto error_return;
}

// we have a signed message, continue with checking validity through checksum
hr = CalcULONGCheckSum(pMessage, &ulCheckSum);
if (FAILED(hr))
{
ErrorMessageBox(hWnd, hr, "OnRead",
"An error occured while calculating\n"
"the message body checksum.");
goto error_return;
}

if (pPropValues[0].Value.ul == ulCheckSum)
{
MessageBox(hWnd, "Signed Message Verified.\n"
"Message has authentic contents.", "Event Extension", MB_OK);
}
else
{
int nRet = MessageBox(hWnd, "Signed Message was altered.\n"
"Message may not have authentic contents.\n"
"Do you wish to view the message anyway?", "Event Extension", MB_YESNO);

if (nRet == IDNO)
// this will tell OnReadComplete to not display the read note UI at all.
m_hrOnReadComplete = MAPI_E_CALL_FAILED;
}

error_return:

hr = S_FALSE;

UlRelease(pMDB);

UlRelease(pMessage);

MAPIFreeBuffer(pNamedPropTags);

MAPIFreeBuffer(pPropValues);

if(hOldCursor)
SetCursor(hOldCursor);

return hr;
}

///////////////////////////////////////////////////////////////////////////////
// MyExchExtMessageEvents::OnReadComplete()
//
// Parameters
// lpeecb -- pointer to IExchExtCallback interface
//
// Purpose
// To do processing after message has been read.
//
// Return Value
// S_OK signals Exchange to not continue calling extensions
// S_FALSE signals Exchange to continue calling extensions
// Some MAPI Code error indicates a problem and will not display the send
// or read note form.
//
// Comments.
// If an error code, such as MAPI_E_CALL_FAILED, is returned, Exchange will
// call OnReadComplete again with the ulFlags parameter set to
// EEME_COMPLETE_FAILED. Returning the error code again will cause Exchange
// to not display the UI.
//

STDMETHODIMP MyExchExtMessageEvents::OnReadComplete(LPEXCHEXTCALLBACK lpeecb, ULONG ulFlags)
{

return m_hrOnReadComplete;
}

///////////////////////////////////////////////////////////////////////////////
// MyExchExtMessageEvents::OnWrite()
//
// Parameters
// lpeecb -- pointer to IExchExtCallback interface
//
// Purpose
// This method is called when a message is about to be written. The message
// only has default properties at this point. It does not contain
// properties which the user has added by way of recipients, subject,
// message text, or attachments.
// This method is called when the user Sends or Saves a message
//
// Return Value
// S_OK signals Exchange to not continue calling extensions
// S_FALSE signals Exchange to continue calling extensions
//
//

STDMETHODIMP MyExchExtMessageEvents::OnWrite(LPEXCHEXTCALLBACK lpeecb)
{
HRESULT hr;

hr = S_FALSE;

return hr;
}

///////////////////////////////////////////////////////////////////////////////
// MyExchExtMessageEvents::OnWriteComplete()
//
// Parameters
// lpeecb -- pointer to IExchExtCallback interface
//
// Purpose
// This method is called when the data (recipients, attachments, body,
// subject, etc.) has been written to the message. This sample will
// calculate the checksum on the PR_BODY property.
//
// Return Value
// S_OK signals Exchange to not continue calling extensions
// S_FALSE signals Exchange to continue calling extensions
//
//

STDMETHODIMP MyExchExtMessageEvents::OnWriteComplete(LPEXCHEXTCALLBACK lpeecb, ULONG ulFlags)
{
HRESULT hr;
LPMESSAGE pMessage = NULL;
LPMDB pMDB = NULL;
ULONG ulCheckSum = 0;
SPropValue valChksumProp;
LPSPropTagArray pNamedPropTags = NULL;
HWND hWnd;
HCURSOR hOldCursor;

MAPINAMEID NamedID;

LPMAPINAMEID pnmid = &NamedID;

if (!bSignatureOn) // user has turned off signatures
{
return S_FALSE; // return and do nothing
}

if (!m_bInSubmitState) // means user is just "saving the message"
{
return S_FALSE;
}

hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));


hr = lpeecb->GetObject(&pMDB, (LPMAPIPROP *)&pMessage);
if (FAILED(hr))
{
goto error_return;
}

hr = CalcULONGCheckSum(pMessage, &ulCheckSum);
if (FAILED(hr))
{
lpeecb->GetWindow(&hWnd);

ErrorMessageBox(hWnd, hr, "OnWriteComplete",
"An error occured while calculating\n"
"the message body checksum.");
goto error_return;
}

// use a named property for the message body checksum
NamedID.lpguid = (LPGUID) &PS_PUBLIC_STRINGS;
NamedID.ulKind = MNID_STRING;
NamedID.Kind.lpwstrName = L"MsgChecksum";

hr = pMessage->GetIDsFromNames(1, &pnmid, MAPI_CREATE, &pNamedPropTags);
//here we can get away with checking for hr!=0, because we retrieve only one
//PropTag. If more then one PropTag requested and GetIDsFromName can't find
// some of them, a warning MAPI_W_ERRORS_RETURNED is returned and each PropTag
//has to be checked separatly. (See Docs for details).
if(hr)
{
goto error_return;
}

valChksumProp.ulPropTag = PROP_TAG(PT_LONG, PROP_ID(pNamedPropTags->aulPropTag[0]));
valChksumProp.dwAlignPad = 0L;
valChksumProp.Value.ul = ulCheckSum;

hr = HrSetOneProp(pMessage, &valChksumProp);
if (hr)
{
goto error_return;
}


// everything succeeded. return S_FALSE to tell Exchange to continue calling
// extensions.
hr = S_FALSE;

error_return:


UlRelease(pMDB);

UlRelease(pMessage);

MAPIFreeBuffer(pNamedPropTags);


SetCursor(hOldCursor);

return hr;
}

///////////////////////////////////////////////////////////////////////////////
// MyExchExtMessageEvents::OnCheckNames()
//
// Parameters
// lpeecb -- pointer to IExchExtCallback interface
//
// Purpose
// Called when user selects the Check Names button and just before message
// is submitted to MAPI.
//
// Return Value
// S_OK signals Exchange to not continue calling extensions
// S_FALSE signals Exchange to continue calling extensions
//

STDMETHODIMP MyExchExtMessageEvents::OnCheckNames(LPEXCHEXTCALLBACK lpeecb)
{
// Not used by the sample.
return S_FALSE;
}

///////////////////////////////////////////////////////////////////////////////
// MyExchExtMessageEvents::OnCheckNamesComplete()
//
// Parameters
// lpeecb -- pointer to IExchExtCallback interface
//
// Purpose
// Called after exchange has completed resolving names in the message
// recipients table.
//
// Return Value
// S_OK signals Exchange to not continue calling extensions
// S_FALSE signals Exchange to continue calling extensions
//

STDMETHODIMP MyExchExtMessageEvents::OnCheckNamesComplete(LPEXCHEXTCALLBACK lpeecb, ULONG ulFlags)
{
// Not used by the sample
return S_FALSE;
}

///////////////////////////////////////////////////////////////////////////////
// MyExchExtMessageEvents::OnSubmit()
//
// Parameters
// lpeecb -- pointer to IExchExtCallback interface
//
// Purpose
// Called before message data has been written and is
// is submitted to MAPI.
//
// Return Value
// S_OK signals Exchange to not continue calling extensions
// S_FALSE signals Exchange to continue calling extensions
//
// Set a member function to show that submit has been called
// to indicate to OnWriteComplete that the user has hit the
// Send button and is not just saving the message.
//

STDMETHODIMP MyExchExtMessageEvents::OnSubmit(LPEXCHEXTCALLBACK lpeecb)
{
HRESULT hr;

hr = S_FALSE;

m_bInSubmitState = TRUE; // submit is called


return hr;
}

///////////////////////////////////////////////////////////////////////////////
// MyExchExtMessageEvents::OnSubmitComplete()
//
// Parameters
// lpeecb -- pointer to IExchExtCallback interface
//
// Purpose
// Called after message has been submitted to MAPI.
//
// Return Value - none
//
// A flag is cleared to indicate to other methods that Exchange is
// finished submitting the message.
//

STDMETHODIMP_ (VOID) MyExchExtMessageEvents::OnSubmitComplete(LPEXCHEXTCALLBACK lpeecb, ULONG ulFlags)
{

m_bInSubmitState = FALSE; // out of submit state

}



///////////////////////////////////////////////////////////////////////////////
// Helper Functions
//

///////////////////////////////////////////////////////////////////////////////
// ErrorMessageBox()
//
// Parameters
// hWnd -- parent window
// hr -- HRESULT value
// szFunction -- function name in which the error occurred
// szMessage -- error message
//
// Purpose
// Displays an error message using MessageBox
//
// Return Value - none
//
// Comments
// Pass 0 for hr to not display an error number. Pass NULL for
// szFunction to not display a function name. Use these options
// to display error messages for public consumption. Use hr and
// function name for internal errors and for debugging. szMessage
// is mandatory.
//
void ErrorMessageBox(HWND hWnd, HRESULT hr, LPSTR szFunction, LPSTR szMessage)
{
static char szError[256];

if (szMessage == NULL)
{
MessageBox(hWnd,
"An unknown error occured in\nSample property sheet extension",
"Sample Property Sheet Extension", MB_ICONEXCLAMATION | MB_OK);
return;
}

if ((hr == 0) && (szFunction == NULL))
{
MessageBox(hWnd, szMessage, "Sample Extension Error", MB_ICONEXCLAMATION | MB_OK);
return;
}


if (szFunction != NULL)
{
wsprintf(szError, "Error %08X in %s\n%s", hr, szFunction, szMessage);
MessageBox(hWnd, szError, "Sample Extension Error", MB_ICONEXCLAMATION | MB_OK);
}

}



///////////////////////////////////////////////////////////////////////////////
// CalcULONGCheckSum(pMessage, &ulCheckSum)()
//
// Parameters
// pMessage -- pointer to message object
// *pulCheckSum -- pointer to buffer to contain calculated checksum
//
// Purpose
// Calculates a checksum on the PR_BODY property of a message
//
// Return Value
// HRESULT value:
// S_OK -- succeeded
// anything means some internal call failed
//
// Comments
// Checksum is calculated by adding the ascii values for 4 bytes of data
// concatenated together at a time. If the message does not have an even
// multiple of 4 characters in the PR_BODY, the remain 1 2 or 3 characters
// are concatenated to make a ULONG with leading zeros.
//

HRESULT CalcULONGCheckSum(LPMESSAGE pMessage, ULONG *pulCheckSum)
{
HRESULT hr;
LPSTREAM pStreamBody = NULL;
ULONG ulValue;
ULONG ulRead;
LARGE_INTEGER large_int;
ULARGE_INTEGER ularge_int;

if ( (pMessage == NULL) ||
(pulCheckSum == NULL) )
{
hr = MAPI_E_INVALID_PARAMETER;
goto error_return;
}

hr = pMessage->OpenProperty(PR_BODY, &IID_IStream, STGM_DIRECT | STGM_READ, 0,
(LPUNKNOWN *) &pStreamBody);

if(GetScode(hr) == MAPI_E_NOT_FOUND) //no body in the message
{
*pulCheckSum = 0;
return hrSuccess;
}
if (FAILED(hr))
{
goto error_return;
}

large_int.LowPart = 0;
large_int.HighPart = 0;

hr = pStreamBody->Seek(large_int, STREAM_SEEK_SET, &ularge_int);
if (FAILED(hr))
{
goto error_return;
}


(*pulCheckSum) = 0;
ulValue = 0;
while ( (S_OK == (hr = pStreamBody->Read((LPVOID)&ulValue, 4, &ulRead))) &&
(ulRead > 0) )
{
(*pulCheckSum) += ulValue;
ulValue = 0;
}


error_return:

if (pStreamBody != NULL)
pStreamBody->Release();

return hr; 

}