CLIENT.C

/* 
- C L I E N T . C
-
* Purpose:
* Sample routing mail client for the MAPI SDK.
* Exclusively uses the Extended MAPI interface.
*
* Copyright 1986-1996, Microsoft Corporation. All Rights Reserved.
*
*/

#include <string.h>
#include <stdlib.h>
#include <windows.h>
#include <windowsx.h>
#ifdef _WIN32
#include <objerror.h>
#include <objbase.h>
#endif
#ifdef WIN16
#include <compobj.h>
#endif
#include <mapiutil.h>
#include <mapidbg.h>
#include <mapix.h>
#include <mapiwin.h>
#include <pdkver.h>
#include <mapiform.h>
#include <ole2.h>
#include <wrap3d.h>

#define USES_IID_IMAPIStatus 1
#define USES_IID_IMessage 1
#include <mapiguid.h>

#include "client.h"
#include "bitmap.h"
#include "route.h"
#ifdef _WIN32
#include "chsfld.h"
#endif

#ifdef WIN16
#define GWL_USERDATA DWL_USER
#endif


/* Application instance */
HANDLE hInst;

/* Static Data */

static ULONG cbeidFolderToView;
static LPENTRYID lpeidFolderToView = NULL;

LPADRBOOK pabAddrB = NULL;
LPMAPISESSION pses = NULL;
LPMDB pmdb = NULL;
LPMAPIFOLDER pfldOutBox = NULL;
LPSPropValue pvalSentMailEID = NULL;
HCURSOR hWaitCur;

LPVOID lpCtl3d = NULL; /* 3D control context */

#ifdef _WIN32
/* Choose folder stuff */
HMODULE g_hChsFldDll;
HRPICKFOLDER g_lpfnHrPickFolder;
ULONG cbCFDState = 0;
LPBYTE pbCFDState = NULL;
#endif /*_WIN32 */

int PASCAL
WinMain (HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpszCmd, int nCmdShow)
{
MSG msg;

if (!hPrevInst)
if (!InitApplication (hInstance))
return (FALSE);

if (!InitInstance (hInstance, nCmdShow))
return (FALSE);

while (GetMessage (&msg, 0, 0, 0))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}

DeinitApplication ();

return (msg.wParam);
}

/*
- InitApplication
-
* Purpose:
* Initialize the application.
*
* Parameters:
* hInstance - Instance handle
*
* Returns:
* True/False
*
*/

BOOL
InitApplication (HANDLE hInstance)
{
WNDCLASS wc;

wc.style = 0;
wc.lpfnWndProc = MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon (hInstance, "NoMail");
wc.hCursor = LoadCursor (0, IDC_ARROW);
wc.hbrBackground = GetStockObject (WHITE_BRUSH);
wc.lpszMenuName = "MailMenu";
wc.lpszClassName = "RoutingSample";

return (RegisterClass (&wc));
}

/*
- InitInstance
-
* Purpose:
* Initialize this instance.
*
* Parameters:
* hInstance - Instance handle
* nCmdShow - Do we show the window?
*
* Returns:
* True/False
*
*/

BOOL
InitInstance (HANDLE hInstance, int nCmdShow)
{
HWND hWnd;
BOOL fInit;
BOOL f;

hInst = hInstance;

fInit = InitMAPI(0);

if (!lpCtl3d)
{
lpCtl3d = CTL3D_Initialize(hInstance);
CTL3D_AutoSubclass(lpCtl3d, hInstance, &f);
}

hWnd = CreateWindow ("RoutingSample", "Routing Sample", WS_OVERLAPPEDWINDOW,
5, 5, 550, 75, 0, 0, hInst, NULL);

if (!hWnd)
return (FALSE);

ShowWindow (hWnd, nCmdShow);
UpdateWindow (hWnd);

hWaitCur = LoadCursor(0, IDC_WAIT);

if (fInit)
{
if (ClientLogon (hWnd))
ToggleMenuState (hWnd, TRUE);
}

return (fInit);
}

BOOL
InitMAPI (HWND hWnd)
{
HRESULT hr;

hr = MAPIInitialize(NULL);
if(hr)
{
MakeMessageBox(hWnd, GetScode(hr), IDS_MAPIINIF, NULL, MBS_ERROR);
return FALSE;
}
return TRUE;
}

void
DeinitApplication ()
{
DeinitMAPI ();

CTL3D_Uninitialize(lpCtl3d);
lpCtl3d = NULL;

#ifdef _WIN32
if(g_hChsFldDll)
FreeLibrary(g_hChsFldDll);

#endif /* _WIN32 */
}

void
DeinitMAPI ()
{
MAPIUninitialize();

}

/*
* Log on to MAPI
*
* Error messages are in subroutings.
*
* Globals:
* pses MAPI session handle
* pmdb MAPI message store object
* pabAddrB MAPI address book object
* pfldOutBox Out folder
* pvalSentMailEID EntryID of the "Sent mail" folder
*/

BOOL ClientLogon (HWND hWnd)
{
HRESULT hr;
/* We should not yet be logged on*/
Assert(pses == NULL);
Assert(pmdb == NULL);


/* MAPILogon might yield control to Windows. So to prevent the user
from clicking "logon" while we are in the process of loggin on we
have to disable it*/

SecureMenu(hWnd, TRUE);

/* Create a MAPI session*/
hr = MAPILogonEx((ULONG) hWnd, NULL, NULL,
MAPI_EXTENDED | MAPI_EXPLICIT_PROFILE | MAPI_LOGON_UI |
MAPI_NEW_SESSION, &pses);
if(hr)
{
SecureMenu(hWnd, FALSE);
pses = NULL;
if (GetScode(hr) != MAPI_E_USER_CANCEL)
MakeMessageBox (hWnd, GetScode(hr), IDS_LOGONFAIL, NULL, MBS_ERROR);
return FALSE;
}


pmdb = OpenDefaultStore(hWnd);
if (!pmdb) goto err;

pabAddrB = OpenAddressBook(hWnd);
if (!pabAddrB) goto err;

if(!OpenOutFolder(hWnd, &pfldOutBox)) goto err;

/* retrieve the EntryID of the sentmail folder and change the property tag
so that it is ready to use on a message*/
hr = HrGetOneProp((LPMAPIPROP)pmdb, PR_IPM_SENTMAIL_ENTRYID, &pvalSentMailEID);
if(hr)
{
goto err;
}
pvalSentMailEID->ulPropTag = PR_SENTMAIL_ENTRYID;

return TRUE;

err:

ClientLogoff(hWnd);
SecureMenu(hWnd, FALSE);

return FALSE;
}

/*
* Releases the global objects and logs off MAPI.
*
* Globals:
* pses Extended-MAPI session object
* pmdb Extended-MAPI message store object
* pabAddrB Address Book
* pfldOutBox out folder
* pvalSetmailEID contains EID of the "Sent" folder
*/
VOID
ClientLogoff (HWND hWnd)
{

#ifdef _WIN32
MAPIFreeBuffer(pbCFDState);
pbCFDState = NULL;
cbCFDState = 0;
#endif

UlRelease(pfldOutBox);
pfldOutBox = NULL;

UlRelease(pmdb);
pmdb = NULL;

UlRelease(pabAddrB);
pabAddrB = NULL;

MAPIFreeBuffer(pvalSentMailEID);
pvalSentMailEID = NULL;

pses->lpVtbl->Logoff(pses, (ULONG)hWnd, MAPI_LOGOFF_UI, 0);
UlRelease(pses);
pses = NULL;

SetWindowText(hWnd, "Routing Sample");
}

LPMDB
OpenDefaultStore(HWND hWnd)
{
HRESULT hr;
LPMDB lpmdb = NULL;
LPMAPITABLE ptable = NULL;
LPSRowSet prows = NULL;
LPSPropValue pvalProp = NULL;
static SizedSPropTagArray(2, columns) =
{ 2, { PR_DEFAULT_STORE, PR_ENTRYID} };
SPropValue valDefStore;
SPropertyRestriction restpropDefStore;
SRestriction restDefStore;


valDefStore.ulPropTag = PR_DEFAULT_STORE;
valDefStore.dwAlignPad = 0;
valDefStore.Value.b = TRUE;

restpropDefStore.relop = RELOP_EQ;
restpropDefStore.ulPropTag = PR_DEFAULT_STORE;
restpropDefStore.lpProp = &valDefStore;

restDefStore.rt = RES_PROPERTY;
restDefStore.res.resProperty = restpropDefStore;

hr = pses->lpVtbl->GetMsgStoresTable(pses, 0, &ptable);
if (HR_FAILED(hr))
{
MakeMessageBox (hWnd, GetScode(hr), IDS_STORETBLFAIL, NULL, MBS_ERROR);
goto ret;
}


hr = HrQueryAllRows(ptable, (LPSPropTagArray) &columns, &restDefStore, NULL, 0, &prows);
if (HR_FAILED(hr))
{
MakeMessageBox (hWnd, GetScode(hr), IDS_QUERYROWFAIL, NULL, MBS_ERROR);
goto ret;
}

if (prows == NULL || prows->cRows == 0
|| prows->aRow[0].lpProps[1].ulPropTag != PR_ENTRYID)
{
MakeMessageBox (hWnd, 0L, IDS_NODEFAULTSTORE, NULL, MBS_ERROR);
goto ret;
}

Assert(prows->cRows == 1);

hr = pses->lpVtbl->OpenMsgStore(pses, (ULONG)hWnd,
prows->aRow[0].lpProps[1].Value.bin.cb,
(LPENTRYID)prows->aRow[0].lpProps[1].Value.bin.lpb,
NULL, MDB_WRITE | MAPI_DEFERRED_ERRORS, &lpmdb);
if (HR_FAILED(hr))
{
if (GetScode(hr) != MAPI_E_USER_CANCEL)
MakeMessageBox (hWnd, GetScode(hr), IDS_OPENSTOREFAIL, NULL, MBS_ERROR);
Assert(lpmdb == NULL);
goto ret;
}
if(hr) /*if we have a warning, display it and succeed */
{
LPMAPIERROR perr = NULL;

pses->lpVtbl->GetLastError(pses, hr, 0, &perr);
MakeMessageBox(hWnd, GetScode(hr), IDS_OPENSTOREWARN, perr, MBS_ERROR);
MAPIFreeBuffer(perr);
}


Assert(lpmdb != NULL);

hr = HrGetOneProp((LPMAPIPROP)lpmdb, PR_DISPLAY_NAME, &pvalProp);
if(!hr)
{
char buf[128];

wsprintf(buf, "Routing Sample: %s", pvalProp->Value.lpszA);

SetWindowText(hWnd, buf);
MAPIFreeBuffer(pvalProp);
}

ret:
FreeProws(prows);
UlRelease(ptable);

return lpmdb;
}


#ifdef _WIN32
BOOL FGetFoldChooser(void)
{
UINT uiErrMode;

if(g_lpfnHrPickFolder)
return TRUE;

Assert(!g_hChsFldDll);

uiErrMode = SetErrorMode(SEM_NOOPENFILEERRORBOX);

g_hChsFldDll = LoadLibrary(szChsFldDllName);

SetErrorMode(uiErrMode);

if(g_hChsFldDll)
{
if((g_lpfnHrPickFolder = (HRPICKFOLDER)GetProcAddress(g_hChsFldDll,
szChsFldFnName)))
{
return TRUE;
}

DebugTrace("route.cli: GetProcAddress for %s failed", szChsFldFnName);

FreeLibrary(g_hChsFldDll);
g_hChsFldDll = NULL;
}
else
{
DebugTrace("smpfrm: failed to load choose folder dll\n");
}

return FALSE;
}
#endif /* _WIN32 */

/*
- MainWndProc
-
* Purpose:
* Main Window Procedure.
* Handles the menu bar and standard window messages.
*
* Parameters:
* hWnd
* message
* wParam
* lParam
*
* Returns:
*
*
*/

LONG FAR PASCAL
MainWndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
HANDLE_MSG(hWnd, WM_COMMAND, MAIN_OnCommand);

case WM_ENDSESSION:
DestroyWindow (hWnd);
break;

case WM_CLOSE:
case WM_DESTROY:
if (pses)
ClientLogoff (hWnd);

PostQuitMessage (0);
break;

default:
return (DefWindowProc (hWnd, msg, wParam, lParam));
}
return FALSE;
}


LONG MAIN_OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify)
{
HRESULT hr;
#ifndef _WIN32
LPDIALOGDATA pDialogData;
#endif
LPMESSAGE pmsgOutgoing = NULL;
ULONG ulMsgToken = 0;

switch (id)
{
case IDM_NEWFORM:
{
LPMAPIFORMMGR pfmmgr = NULL;
LPMAPIFORMINFO pfminfo = NULL;
LPSPropValue pvalMsgClass = NULL;
LPMESSAGE pmsgForm = NULL;

hr = MAPIOpenFormMgr(pses, &pfmmgr);
if(!hr)
{
hr = pfmmgr->lpVtbl->SelectForm(pfmmgr, (ULONG) hWnd, 0, NULL, NULL, &pfminfo);

if(!hr)
{

/*Get the form's msg class */
hr = HrGetOneProp((LPMAPIPROP)pfminfo, PR_MESSAGE_CLASS, &pvalMsgClass);
DebugTraceResult(HrGetOneProp, hr);

if(!hr)
{
/*create new message*/
if(CreateOutMessage(&pmsgForm))
{
hr = pses->lpVtbl->PrepareForm(pses, NULL, pmsgForm, (LPULONG) &ulMsgToken);
if(S_OK != GetScode(hr))
{
DebugTrace("Client: PrepareForm failed");
break;
}
UlRelease(pmsgForm);
pmsgForm = NULL;

hr = pses->lpVtbl->ShowForm(pses, (ULONG) hWnd, pmdb, pfldOutBox,
NULL, ulMsgToken, NULL, MAPI_NEW_MESSAGE,
0, MSGFLAG_UNSENT | MSGFLAG_READ, 0,
pvalMsgClass->Value.lpszA );
if(S_OK != GetScode(hr))
MakeMessageBox(hWnd, GetScode(hr), IDS_SHOWFORM, NULL, MBS_ERROR);
}
else
MakeMessageBox(hWnd, 1, IDS_CRTOUTMSG, NULL, MBS_ERROR);
}

MAPIFreeBuffer(pvalMsgClass);
UlRelease(pfminfo);
}

UlRelease(pfmmgr);
}
}

break;


case IDM_LOGON:
if (!pses)
{
if (ClientLogon (hWnd))
ToggleMenuState (hWnd, TRUE);
}
break;

case IDM_LOGOFF:
if (pses)
{
ClientLogoff (hWnd);
ToggleMenuState (hWnd, FALSE);
}
break;

case IDM_HIER:
Assert(pses);
//on win32 use the sample Choose Folder Dialog
#ifdef _WIN32
{
HRESULT hr;
LPMAPIFOLDER pfld = NULL;
LPMDB pmdbNew = NULL;

if(!FGetFoldChooser())
return TRUE;

hr = (*g_lpfnHrPickFolder)(NULL, hWnd, pses, &pfld, &pmdbNew,
&cbCFDState, &pbCFDState);
if(HR_SUCCEEDED(hr))
{
LPSPropValue pval = NULL;

UlRelease(pmdb);
pmdb = pmdbNew;
pmdbNew = NULL;

hr = HrGetOneProp((LPMAPIPROP)pmdb, PR_DISPLAY_NAME, &pval);
if(!hr)
{
char buf[128];

wsprintf(buf, "Routing Sample: %s", pval->Value.lpszA);

SetWindowText(hWnd, buf);
MAPIFreeBuffer(pval);
pval = NULL;
}


hr = HrGetOneProp((LPMAPIPROP)pfld, PR_ENTRYID, &pval);
if(!hr)
{
cbeidFolderToView = pval->Value.bin.cb;
lpeidFolderToView = (LPENTRYID)pval->Value.bin.lpb;

DialogBox (hInst, "InBox", hWnd, InBoxDlgProc);

cbeidFolderToView = 0;
lpeidFolderToView = NULL;

MAPIFreeBuffer(pval);
}

UlRelease(pfld);
}
}

#else


Assert(pmdb);
if (pDialogData = CreateDialogData (iHierarchy))
DialogBoxParam (hInst, "HierarchyTable", hWnd, CommonDlgProc, (LPARAM)pDialogData);
#endif /* _WIN32 */
break;

#ifndef _WIN32
case IDM_OPEN:
Assert(pses);
if (pDialogData = CreateDialogData (iStores))
DialogBoxParam (hInst, "OpenStore", hWnd, CommonDlgProc, (LPARAM)pDialogData);
break;
#endif /* _WIN32 */

case IDM_ROUTE:
Assert(pses);
DialogBoxParam(hInst, "RouteNote", hWnd, RouteNoteDlgProc, (LPARAM) NULL );
break;

case IDM_READ:
Assert(pses);
Assert(pmdb);
Assert(lpeidFolderToView == NULL);

{
/* Get the entry ID of the Inbox from the message store. */
if ((hr = pmdb->lpVtbl->GetReceiveFolder(pmdb,
"IPM", 0,
&cbeidFolderToView, &lpeidFolderToView, NULL))
== hrSuccess)
{
DialogBox (hInst, "InBox", hWnd, InBoxDlgProc);
MAPIFreeBuffer(lpeidFolderToView);
lpeidFolderToView = NULL;
}
else
MakeMessageBox (hWnd, GetScode(hr), IDS_GETRCVFAIL, NULL, MBS_ERROR);
}
break;

case IDM_SEND:
Assert(pses);
{
if(CreateOutMessage(&pmsgOutgoing))
{
hr = pses->lpVtbl->PrepareForm(pses, NULL, pmsgOutgoing, (LPULONG) &ulMsgToken);
if(S_OK != GetScode(hr))
{
DebugTrace("Client: PrepareForm failed");
break;
}
UlRelease(pmsgOutgoing);
pmsgOutgoing = NULL;

hr = pses->lpVtbl->ShowForm(pses, (ULONG) hWnd, pmdb, pfldOutBox, NULL, ulMsgToken,
NULL, MAPI_NEW_MESSAGE, 0, MSGFLAG_UNSENT | MSGFLAG_READ, 0, "IPM.Note");
if(S_OK != GetScode(hr))
MakeMessageBox(hWnd, GetScode(hr), IDS_SHOWFORM, NULL, MBS_ERROR);
}
else
MakeMessageBox(hWnd, 1, IDS_CRTOUTMSG, NULL, MBS_ERROR);

}
break;

case IDM_ABOUT:
DialogBox (hInst, "AboutBox", hWnd, AboutDlgProc);
break;


case IDM_EXIT:
if (pses)
ClientLogoff (hWnd);

PostQuitMessage (0);
break;

default:
return TRUE;
}

return FALSE;
}


/*
* Displays an About dialog for the sample client.
*/

BOOL CALLBACK
AboutDlgProc (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
char rgchVersion[80];

switch (msg)
{
case WM_INITDIALOG:
wsprintf(rgchVersion, "Version %d.%d.%d (%s)", rmj, rmm, rup,
szVerName && *szVerName ? szVerName : "BUDDY");
SetDlgItemText(hDlg, IDC_VERSION, rgchVersion);
return TRUE;

case WM_COMMAND:
if (wParam == IDOK || wParam == IDCANCEL)
{
EndDialog (hDlg, TRUE);
return TRUE;
}
break;
}
return FALSE;
}



/*
* Handles the Inbox list view, its command buttons.
* The main window is a listbox presenting a summary line for
* each message in the Inbox. There are command buttons for
* refreshing the list, displaying a message, and deleting a
* message.
*
* All operations are on single messages; multiple selection is
* not supported.
*
* The EntryID of the folder to examine is passed in using
* the global lpeidFolderToView.
*/

BOOL CALLBACK
InBoxDlgProc (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
LPMSGID lpMsgIdList = NULL;
HCURSOR hOldCur;
LPMESSAGE pmsgRead = NULL;
ULONG ulObjType = 0;
ULONG ulMsgToken = 0;
LPINBOXDATA pIBData = NULL;

switch (msg)
{
case WM_INITDIALOG:
if(MAPIAllocateBuffer(sizeof(INBOXDATA), &pIBData))
{
EndDialog(hDlg, FALSE);
return TRUE;
}

hOldCur = SetCursor(hWaitCur);

ZeroMemory(pIBData, sizeof(INBOXDATA));

InitBmps(hDlg, IDC_MSG);

/* Populate List Box with all messages in InBox. */
PopulateMessages(hDlg, pIBData);

SetCursor(hOldCur);
SetFocus (GetDlgItem (hDlg, IDC_MSG));
SetWindowLong(hDlg, GWL_USERDATA, (LONG)pIBData);
return TRUE;
break;

case WM_SETFOCUS:
SetFocus (GetDlgItem (hDlg, IDC_MSG));
break;

case WM_MEASUREITEM:
/* Sets the height of the owner-drawn List-Box */
MeasureItem(hDlg, (MEASUREITEMSTRUCT *)lParam);
break;

case WM_DRAWITEM:
DrawItem((DRAWITEMSTRUCT *)lParam);
break;

case WM_CHARTOITEM: /* don't select item by character*/
return -2;

/* Handled by IDC_DELETE */
case WM_DELETEITEM:
return TRUE;
HANDLE_MSG(hDlg, WM_COMMAND, INBOX_OnCommand);
break;
}

return FALSE;
}

void INBOX_OnCommand(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify)
{
LPMSGID lpMsgNode = NULL;
LPINBOXDATA pibData = NULL;
HCURSOR hOldCur;
UINT nIndex;
RECT Rect;
LPMESSAGE pmsgRead = NULL;
ULONG ulObjType = 0;
HRESULT hr;
ULONG ulMsgToken = 0;
ULONG cProps = 0;
LPSPropValue pvalProps = NULL;
LONG lStat = 0, lFlags = 0, lAccess = 0;
LPSTR lpszMsgClass = "IPM.Note";

enum { PRSTAT, PRFLAGS, PRACCESS, PRCLASS, PRDIM}; /* used to retrieve props*/
SizedSPropTagArray(PRDIM, sptaSFProps) = /*required for ShowForm call*/
{ PRDIM, {PR_MSG_STATUS, PR_MESSAGE_FLAGS, PR_ACCESS_LEVEL, PR_MESSAGE_CLASS} };


switch (id)
{

case IDC_FLUSH:
hr = DeliverNow(hDlg);
if(hr)
break;
/*fall through if the flush was succesfull*/
case IDC_NEW:

pibData = (LPINBOXDATA)GetWindowLong(hDlg, GWL_USERDATA);
if(!pibData)
{
DebugTrace("Client: userdata == 0 (inboxdlgproc)");
}
hOldCur = SetCursor(hWaitCur);

/* Destroy the old message list. */
FreeMsgList (pibData->lpMsgIdList);
pibData->lpMsgIdList = NULL;

/* Populate List Box with all messages in InBox. */
PopulateMessages(hDlg, pibData);

SetCursor(hOldCur);
break;

case IDC_MSG:
if(codeNotify != LBN_DBLCLK)
break;
/* FALL THROUGH to read the double-clicked message */

case IDC_READ:
nIndex = (UINT)ListBox_GetCurSel(GetDlgItem(hDlg, IDC_MSG));
if (nIndex == LB_ERR)
break;

lpMsgNode = (LPMSGID)ListBox_GetItemData(GetDlgItem(hDlg, IDC_MSG), nIndex);
if (lpMsgNode)
{
hr = pmdb->lpVtbl->OpenEntry(pmdb, lpMsgNode->cbEID, lpMsgNode->lpEID,
NULL, MAPI_BEST_ACCESS | MAPI_DEFERRED_ERRORS,
&ulObjType, (LPUNKNOWN FAR *) &pmsgRead);

if(S_OK != GetScode(hr))
{
LPMAPIERROR perr = NULL;

pmdb->lpVtbl->GetLastError(pmdb, hr, 0, &perr);
MakeMessageBox(hDlg, GetScode(hr), IDS_READFAIL, perr, MBS_ERROR);
MAPIFreeBuffer(perr);
DebugTrace("Client: OpenEntry failed");
break;
}
else
{
Assert(ulObjType == MAPI_MESSAGE);

lpMsgNode->fUnRead = FALSE;

/* get all the props in one call */
hr = pmsgRead->lpVtbl->
GetProps(pmsgRead, (LPSPropTagArray)&sptaSFProps, 0,
&cProps, &pvalProps);
if(HR_SUCCEEDED(hr))
{
if(pvalProps[PRSTAT].ulPropTag == PR_MSG_STATUS)
lStat = pvalProps[PRSTAT].Value.l;
if(pvalProps[PRFLAGS].ulPropTag == PR_MESSAGE_FLAGS)
lFlags = pvalProps[PRFLAGS].Value.l;
if(pvalProps[PRACCESS].ulPropTag == PR_ACCESS_LEVEL)
lAccess = pvalProps[PRACCESS].Value.l;
if(pvalProps[PRCLASS].ulPropTag == PR_MESSAGE_CLASS)
lpszMsgClass = pvalProps[PRCLASS].Value.lpszA;
}

else
{
DebugTrace("Client: GetProps (for ShowForm) failed");
break;
}

if (!lstrcmpi(lpszMsgClass, lpszSmplRTMsgClass))
{
DialogBoxParam(hInst, "RouteNote", hDlg, RouteNoteDlgProc, (LPARAM) pmsgRead);
/* RouteNoteDlgPropc will release pmsgRead*/
}
else
{
hr = pses->lpVtbl->PrepareForm(pses, NULL, pmsgRead, (LPULONG) &ulMsgToken);
if(S_OK != GetScode(hr))
{
DebugTrace("Client: PrepareForm failed");
MAPIFreeBuffer(pvalProps);
break;
}
UlRelease(pmsgRead);
pmsgRead = NULL;
hr = pses->lpVtbl->ShowForm(pses, (ULONG) hDlg, pmdb, pfldOutBox, NULL, ulMsgToken,
NULL, 0, lStat, lFlags, lAccess, lpszMsgClass);

}
}

}

MAPIFreeBuffer(pvalProps);
pvalProps = NULL;
/* Update the Messages List-Box with new icon */
lpMsgNode = NULL;

ListBox_GetItemRect(GetDlgItem(hDlg, IDC_MSG),nIndex, (LPARAM) &Rect);
InvalidateRect(GetDlgItem(hDlg, IDC_MSG), &Rect, FALSE);

break;

case IDC_DELETE:
{
ENTRYLIST el;
SBinary sb;

nIndex = (UINT)ListBox_GetCurSel(GetDlgItem(hDlg, IDC_MSG));
if (nIndex == LB_ERR)
break;

pibData = (LPINBOXDATA)GetWindowLong(hDlg, GWL_USERDATA);
if(!pibData)
{
DebugTrace("Client: userdata == 0 (inboxdlgproc)");
}

lpMsgNode = (LPMSGID) ListBox_GetItemData(GetDlgItem(hDlg, IDC_MSG),
nIndex);
if (lpMsgNode)
{
sb.cb = lpMsgNode->cbEID;
sb.lpb = (LPBYTE)lpMsgNode->lpEID;
el.cValues = 1;
el.lpbin = &sb;

hr = pibData->pfld->lpVtbl->
DeleteMessages(pibData->pfld, &el, 0, NULL, 0);
DeleteMsgNode (lpMsgNode, &pibData->lpMsgIdList);
}

ListBox_DeleteString(GetDlgItem(hDlg, IDC_MSG), nIndex);
}
break;

case IDC_CLOSE:
case IDCANCEL:
pibData = (LPINBOXDATA)GetWindowLong(hDlg, GWL_USERDATA);
if(!pibData)
{
DebugTrace("Client: userdata == 0 (inboxdlgproc)");

}

FreeMsgList (pibData->lpMsgIdList);
pibData->lpMsgIdList = NULL;
UlRelease(pibData->pfld);
pibData->pfld = NULL;
MAPIFreeBuffer(pibData);
pibData = NULL;
DeInitBmps();
EndDialog (hDlg, TRUE);
break;

default:
break;
}
}

/*
* DeliverNow flushes outbound and inboud queues.
*
*/
HRESULT DeliverNow(HWND hWnd)
{
HRESULT hr;
LPMAPISTATUS pstatSpooler = NULL;
ULONG ulObjType = 0;
LPMAPITABLE ptblStatus = NULL;
SizedSPropTagArray(1, columns) =
{ 1, { PR_ENTRYID} };
SPropValue valSpooler;
SPropertyRestriction restpropSpooler;
SRestriction restSpooler;
LPSRowSet prows = NULL;

/*Build property restriction (PR_RESOURCE_TYPE == MAPI_SPOOLER)*/
valSpooler.ulPropTag = PR_RESOURCE_TYPE;
valSpooler.dwAlignPad = 0;
valSpooler.Value.l = MAPI_SPOOLER;

restpropSpooler.relop = RELOP_EQ;
restpropSpooler.ulPropTag = PR_RESOURCE_TYPE;
restpropSpooler.lpProp = &valSpooler;

restSpooler.rt = RES_PROPERTY;
restSpooler.res.resProperty = restpropSpooler;

/*open session status table*/
hr = pses->lpVtbl->GetStatusTable(pses, 0, &ptblStatus);
if(hr)
{
DebugTraceResult(GetStatusTable, hr);
goto err;
}

/*find a row corresponding to the spooler*/
hr = HrQueryAllRows(ptblStatus, (LPSPropTagArray) &columns, &restSpooler, NULL, 0, &prows);
if (HR_FAILED(hr))
{
DebugTraceResult(HrQueryAllRows, hr);
goto err;
}

Assert(prows && prows->cRows == 1); /*hope the spooler is always there */
Assert(prows->aRow[0].lpProps[0].ulPropTag == PR_ENTRYID);


/*open spooler as a status object*/
hr = pses->lpVtbl->
OpenEntry(pses, prows->aRow->lpProps->Value.bin.cb,
(LPENTRYID)prows->aRow->lpProps->Value.bin.lpb,
&IID_IMAPIStatus, MAPI_BEST_ACCESS, &ulObjType,
(LPUNKNOWN FAR *) &pstatSpooler);
if(hr)
{
DebugTraceResult(OpenEntry, hr);
goto err;
}

Assert(ulObjType == MAPI_STATUS);

/*call FlushQueues()*/
hr = pstatSpooler->lpVtbl->FlushQueues(pstatSpooler, (ULONG) hWnd, 0, NULL,
FLUSH_DOWNLOAD | FLUSH_UPLOAD);
if(hr)
{
DebugTraceResult(FlushQueues, hr);
goto err;
}

/*release all used objects*/

err:
UlRelease(ptblStatus);
FreeProws(prows);
UlRelease(pstatSpooler);

return hr;
}

/*
- MakeMessageBox
-
* Purpose:
* Gets resource string and displays an error message box.
*
* Parameters:
* hWnd - Handle to parent window
* sc - SCODE
* idString - Resource ID of message in StringTable
* perr - pointer to MAPIERROR from last GetLastError
* fStyle - style for MessageBox
*
* Returns:
* Void
*
*/

void
MakeMessageBox (HWND hWnd, SCODE sc, UINT idString, LPMAPIERROR perr, UINT fStyle)
{
char szMessage[512];
char szbuf[256];

if(!LoadString (hInst, idString, szMessage, 255))
return;

if(perr)
{
wsprintf(szbuf, "\n%s\n%s\nLowLevelError: 0x%08lx context: %ld ", (perr->lpszError ? perr->lpszError:""),
perr->lpszComponent ? perr->lpszComponent:"", perr->ulLowLevelError, perr->ulContext);
lstrcat(szMessage, szbuf);
}
if (sc)
{
wsprintf (szbuf, "\nReturn Code: 0x%08lx", sc);
lstrcat (szMessage, szbuf);
}

MessageBox (hWnd, szMessage, "Sample Routing Form", fStyle);
}


/*
* Common Dialog Proc for store and folder listboxes
*
*/
BOOL CALLBACK
CommonDlgProc (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
MEASUREITEMSTRUCT *pmis;
DRAWITEMSTRUCT *pdis;
HCURSOR hOldCur;
LPDIALOGDATA pdd = (LPDIALOGDATA) GetWindowLong(hDlg, DWL_USER);

switch (msg)
{
case WM_INITDIALOG:
/* Remember the address of our dialog data*/
SetWindowLong(hDlg, DWL_USER, lParam);
pdd = (LPDIALOGDATA)lParam;
Assert(pdd->poarHead == NULL);

/*/ Load up the rows of the dialog*/
hOldCur = SetCursor(hWaitCur);
PopulateStores (hDlg, &pdd->poarHead, pdd->iDlgType,
pdd->cbEntryID, pdd->lpEntryID);
SetCursor(hOldCur);

SetFocus (GetDlgItem (hDlg, IDC_MSG));
return TRUE;

case WM_DESTROY:
/* Discard our dialog data*/
FreeOarList(&pdd->poarHead);
MAPIFreeBuffer(pdd);
return TRUE;

case WM_SETFOCUS:
SetFocus (GetDlgItem (hDlg, IDC_MSG));
break;

case WM_MEASUREITEM:
/* Sets the height of the owner-drawn List-Box */
pmis = (MEASUREITEMSTRUCT *) lParam;
pmis->itemHeight = 15;
break;

case WM_DRAWITEM:
pdis = (DRAWITEMSTRUCT *) lParam;
DrawOarItem (pdis, pdd->iDlgType);
break;

case WM_CHARTOITEM: /* don't select item by character*/
return -2;

/* Handled by IDC_DELETE */
case WM_DELETEITEM:
return TRUE;

// HANDLE_MSG(hDlg, WM_COMMAND, Common_OnCommand);
case WM_COMMAND:
return ((Common_OnCommand)((hDlg), (int)(wParam), (HWND)(LOWORD(lParam)), 0L));

} /* switch (msg) */

return FALSE;
}


BOOL Common_OnCommand(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify)
{
LPDIALOGDATA pdd = (LPDIALOGDATA) GetWindowLong(hDlg, DWL_USER);

switch (id)
{
case IDC_MSG:
if(codeNotify != LBN_DBLCLK)
break;
/* FALL THROUGH to read the double-clicked store */

case IDC_DOWN:
case IDC_READ:
/*/ open a Hierarchy or Store*/
{
/*/ Open one of the items in the list*/
LPMDB pmdbTemp;
UINT nIndex;
LPOAR poar;
LPSPropValue pProp;
LPSPropValue pvalProp = NULL;
HRESULT hr;

nIndex = (UINT)ListBox_GetCurSel(GetDlgItem(hDlg, IDC_MSG));

if (nIndex == LB_ERR)
return TRUE;

poar = (LPOAR) ListBox_GetItemData(GetDlgItem(hDlg, IDC_MSG), nIndex);

if (!poar)
return TRUE;

/* PropFindProp from mapiutil.h */
pProp = PpropFindProp(poar->lpProps, poar->cValues, PR_ENTRYID);

Assert(pProp);
/*/ Every row should have an EntryID */

if (pdd->iDlgType == iStores)
{
hr = pses->lpVtbl->OpenMsgStore(pses,
(ULONG)hDlg,
pProp->Value.bin.cb,
(LPENTRYID)pProp->Value.bin.lpb,
NULL, MDB_WRITE, &pmdbTemp);

if (HR_FAILED(hr))
{
if (GetScode(hr) != MAPI_E_USER_CANCEL)
MakeMessageBox (hDlg, GetScode(hr), IDS_OPENSTOREFAIL, NULL, MBS_ERROR);
return TRUE;
}
if(hr) /*if we have a warning*/
{
LPMAPIERROR perr = NULL;

pses->lpVtbl->GetLastError(pses, hr, 0, &perr);
MakeMessageBox(hDlg, GetScode(hr), IDS_OPENSTOREWARN, perr, MBS_ERROR);
MAPIFreeBuffer(perr);
}


Assert(hr == hrSuccess); /* no warnings (for now?)*/

UlRelease(pmdb);
pmdb = pmdbTemp;

/*Change the caption of the main window */
hr = HrGetOneProp((LPMAPIPROP)pmdb, PR_DISPLAY_NAME, &pvalProp);
if(!hr)
{
char buf[128];

wsprintf(buf, "Routing Sample: %s", pvalProp->Value.lpszA);
SetWindowText(GetParent(hDlg), buf);

MAPIFreeBuffer(pvalProp);
pvalProp = NULL;
}

EndDialog (hDlg, TRUE);
return TRUE;
}

Assert (pdd->iDlgType == iHierarchy);

if (id == IDC_DOWN)
{
LPDIALOGDATA pDialogData;
if (pDialogData = CreateDialogData (iHierarchy))
{
pDialogData->cbEntryID = pProp->Value.bin.cb;
pDialogData->lpEntryID = (LPENTRYID)pProp->Value.bin.lpb;
DialogBoxParam (hInst, "HierarchyTable", hDlg, CommonDlgProc, (LPARAM)pDialogData);
}

}
else /* IDC_READ */
{
/* OPEN a contents table*/
Assert(lpeidFolderToView == NULL);
cbeidFolderToView = pProp->Value.bin.cb;
lpeidFolderToView = (LPENTRYID)pProp->Value.bin.lpb;
DialogBox (hInst, "InBox", hDlg, InBoxDlgProc);
lpeidFolderToView = NULL;
}
}
return TRUE;

case IDC_CLOSE:
case IDCANCEL:
EndDialog (hDlg, TRUE);
return TRUE;

}

return TRUE;
}

/*
- PopulateStores
-
* Accumulate all the rows of a table onto the OAR list.
* idlgType indicates iStores, iHierarchy.
*
* If iHierarchy, the EntryID of the folder is in cb/lpeid.
*/

VOID
PopulateStores ( HWND hDlg, LPOAR FAR * ppoarHead, int idlgType,
ULONG cb, LPENTRYID lpeid)
{
HRESULT hr;
SCODE sc;
LPMAPIFOLDER pfld = NULL;
LPMAPITABLE ptable = NULL;
LPSRowSet prows = NULL;
LPSPropValue pvalProp = NULL;
UINT idx;

/* Get the list of available message stores or folderes from MAPI*/
Assert(pses);

switch (idlgType)
{
case iStores:
if (hr = pses->lpVtbl->GetMsgStoresTable(pses, 0, &ptable))
{
MakeMessageBox (hDlg, GetScode(hr), IDS_STORETBLFAIL, NULL, MBS_ERROR);
goto ret;
}
break;
case iHierarchy:
{
ULONG ulObjType;
if (hr = pmdb->lpVtbl->OpenEntry(pmdb, cb, lpeid, NULL,
MAPI_DEFERRED_ERRORS,
&ulObjType, (LPUNKNOWN FAR *) &pfld))
{
MakeMessageBox (hDlg, GetScode(hr), IDS_OPENFOLDERFAIL, NULL, MBS_ERROR);
goto ret;
}
Assert(ulObjType == MAPI_FOLDER);
if (hr = pfld->lpVtbl->GetHierarchyTable(pfld, MAPI_DEFERRED_ERRORS,
&ptable))
{
MakeMessageBox (hDlg, GetScode(hr), IDS_STORETBLFAIL, NULL, MBS_ERROR);
goto ret;
}
hr = HrGetOneProp((LPMAPIPROP)pfld, PR_DISPLAY_NAME, &pvalProp);
if(!hr)
{
if(*pvalProp->Value.lpszA)
SetWindowText(hDlg, pvalProp->Value.lpszA);
MAPIFreeBuffer(pvalProp);
pvalProp = NULL;
}

}
break;
default:
Assert(0);
}

if(hr = HrQueryAllRows(ptable, NULL, NULL, NULL, 1000l, &prows))
{
MakeMessageBox (hDlg, GetScode(hr), IDS_QUERYROWFAIL, NULL, MBS_ERROR);
goto ret;
}
for(idx = 0; idx < prows->cRows; ++idx)
{
LPOAR poar = NULL;
sc = MAPIAllocateBuffer(sizeof(OAR), &poar);
if (sc)
{
hr = ResultFromScode(sc);
FreeProws(prows); /* free ENTIRE row*/
break;
}
/*/ Transfer the data of the row to our OAR structure.*/
poar->cValues = prows->aRow[idx].cValues;
poar->lpProps = prows->aRow[idx].lpProps;

/* Put OAR at head of the list*/
if (*ppoarHead)
(*ppoarHead)->lpPrev = poar;
poar->lpPrev = NULL;
poar->lpNext = (*ppoarHead);
*ppoarHead = poar;

ListBox_AddString(GetDlgItem(hDlg, IDC_MSG), (LONG) poar);
}

MAPIFreeBuffer(prows); /* free OUTER buffer only*/

ret:
UlRelease(ptable);
UlRelease(pfld);
}

/*
- FreeOarList
-
* Free the memory of the rows on the screen.
* Zero the pointer passed in.
*/
VOID
FreeOarList (LPOAR FAR *ppoarHead)
{
LPOAR poar = *ppoarHead;

while (poar)
{
LPOAR poarTemp = poar;

poar = poarTemp->lpNext;
MAPIFreeBuffer(poarTemp->lpProps);
MAPIFreeBuffer(poarTemp);
}

*ppoarHead = NULL;
}

/*
- DrawOarItem
-
* Purpose:
* Paint the client area of the owner-drawn listbox.
*
* Parameters:
* pdis - Pointer to a DRAWITEMSTRUCT
*
* Returns:
* void
*
*/

VOID
DrawOarItem (DRAWITEMSTRUCT FAR * pdis, int idlgType)
{
HBRUSH hSolidBrush = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
HBRUSH hOldBrush = SelectObject(pdis->hDC, hSolidBrush);

if (ODA_DRAWENTIRE & pdis->itemAction)
{
LPOAR poar;
LPSPropValue pProp;
UINT c;

/* Clear the item Rectangle */

PatBlt (pdis->hDC, pdis->rcItem.left, pdis->rcItem.top,
pdis->rcItem.right - pdis->rcItem.left,
pdis->rcItem.bottom - pdis->rcItem.top, PATCOPY);

/* Draw the item */

poar = (LPOAR) pdis->itemData;

for (pProp = poar->lpProps, c = (UINT)poar->cValues;
c > 0;
c-- , pProp++)
{
/* Identify the Default Store*/
if (idlgType == iStores
&& pProp->ulPropTag == PR_DEFAULT_STORE && pProp->Value.b)
TextOut (pdis->hDC, pdis->rcItem.left + 10, pdis->rcItem.top+2,
TEXT(">"), 1);

if (pProp->ulPropTag == PR_DISPLAY_NAME)
TextOut (pdis->hDC, pdis->rcItem.left + 20, pdis->rcItem.top+2,
pProp->Value.LPSZ,
lstrlen (pProp->Value.LPSZ));

}

/* Invert item rectangle if item is selected */

if (ODS_SELECTED & pdis->itemState)
PatBlt (pdis->hDC, pdis->rcItem.left, pdis->rcItem.top,
pdis->rcItem.right - pdis->rcItem.left,
pdis->rcItem.bottom - pdis->rcItem.top, DSTINVERT);

/* Draw a focus rectangle if item has focus */

if (ODS_FOCUS & pdis->itemState)
DrawFocusRect (pdis->hDC, &pdis->rcItem);
}
else
{
/* Invert the item if the selection state is changing */

if (ODA_SELECT & pdis->itemAction)
PatBlt (pdis->hDC, pdis->rcItem.left, pdis->rcItem.top,
pdis->rcItem.right - pdis->rcItem.left,
pdis->rcItem.bottom - pdis->rcItem.top, DSTINVERT);

/* Draw a focus if the focus state is changing */

if (ODA_FOCUS & pdis->itemAction)
DrawFocusRect (pdis->hDC, &pdis->rcItem);
}

SelectObject(pdis->hDC, hOldBrush);
DeleteObject(hSolidBrush);
}


/* Fill the lppMsgIdList with info about the messages in the specified folder. */
enum { E_EID=0, E_SUBJECT, E_SENDER_NAME, E_MSG_DEL_TIME, E_FLAGS, E_PRIORITY, E_CONVERS_KEY,
E_SEARCH_KEY, E_CLASS, E_RECORD_KEY, E_PTINBOXDIM};
SizedSPropTagArray(E_PTINBOXDIM, ptInbox) =
{
E_PTINBOXDIM,
{
PR_ENTRYID,
PR_SUBJECT,
PR_SENDER_NAME,
PR_MESSAGE_DELIVERY_TIME,
PR_MESSAGE_FLAGS,
PR_PRIORITY,
PR_CONVERSATION_KEY,
PR_SEARCH_KEY,
PR_MESSAGE_CLASS,
PR_RECORD_KEY
}
};

VOID
PopulateMessages( HWND hDlg, LPINBOXDATA pibData )
{
LPMAPIFOLDER pfld = pibData->pfld;
LPMAPITABLE ptable = NULL;
LPSRowSet prows = NULL;
HRESULT hr;
ULONG ulType;
SizedSSortOrderSet(1, sos) =
{ 1, 0, 0, { PR_MESSAGE_DELIVERY_TIME, TABLE_SORT_ASCEND } };
UINT nIndex;
LPMSGID lpMsgNode;
LPSPropValue pvalProp = NULL;

ListBox_ResetContent(GetDlgItem(hDlg, IDC_MSG));

if(!pfld)
{
/* Open the right folder, and get the list of messages. */
hr = pmdb->lpVtbl->OpenEntry(pmdb,cbeidFolderToView, lpeidFolderToView,
NULL, MAPI_BEST_ACCESS | MAPI_DEFERRED_ERRORS,
&ulType, (LPUNKNOWN FAR *)&pfld);
if(GetScode(hr) != S_OK) goto ret;

Assert(ulType == MAPI_FOLDER);
pibData->pfld = pfld;
hr = HrGetOneProp((LPMAPIPROP)pfld, PR_DISPLAY_NAME, &pvalProp);
if(!hr)
{
if(*pvalProp->Value.lpszA)
SetWindowText(hDlg, pvalProp->Value.lpszA);
MAPIFreeBuffer(pvalProp);
pvalProp = NULL;
}
}
hr = pfld->lpVtbl->GetContentsTable(pfld, MAPI_DEFERRED_ERRORS, &ptable);
if (hr)
goto ret;

if (hr = HrQueryAllRows(ptable, (LPSPropTagArray) &ptInbox, NULL,
(LPSSortOrderSet) &sos, 0, &prows))
{
MakeMessageBox (hDlg, GetScode(hr), IDS_QUERYROWFAIL, NULL, MBS_ERROR);
goto ret;
}

for (nIndex = 0; nIndex < prows->cRows; ++nIndex)
{
lpMsgNode = MakeMsgNode(prows->aRow + nIndex);

if (lpMsgNode)
{
InsertMsgNode(lpMsgNode, &pibData->lpMsgIdList);

ListBox_AddString(GetDlgItem(hDlg, IDC_MSG),(LONG) lpMsgNode);
}
}
FreeProws(prows);



ret:
UlRelease(ptable);
}

/*
- MakeMsgNode
-
* Purpose:
* Allocate memory for a new MSGID node and initialize its
* data members to the values passed in.
* A separate allocation is used for each property which is pretty
* wastefull. This can be changed to a smarter allocation scheme.
*
* Parameters:
*
* Return:
* lpMsgNode - Pointer to new node
*/

LPMSGID
MakeMsgNode (LPSRow prow)
{
LPMSGID lpMsgNode = NULL;

if (!prow)
goto err;

if (MAPIAllocateBuffer (sizeof (MSGID), (LPVOID far *) & lpMsgNode))
goto err;

ZeroMemory(lpMsgNode, sizeof (MSGID));

if(prow->lpProps[E_FLAGS].ulPropTag == PR_MESSAGE_FLAGS)
{
lpMsgNode->fHasAttach = !!(prow->lpProps[E_FLAGS].Value.l & MSGFLAG_HASATTACH);
lpMsgNode->fUnRead = !(prow->lpProps[E_FLAGS].Value.l & MSGFLAG_READ);
}

if(prow->lpProps[E_EID].ulPropTag == PR_ENTRYID)
{
if (MAPIAllocateMore(prow->lpProps[E_EID].Value.bin.cb, lpMsgNode,
(LPVOID FAR *)&lpMsgNode->lpEID))
goto err;
CopyMemory(lpMsgNode->lpEID, prow->lpProps[E_EID].Value.bin.lpb, prow->lpProps[E_EID].Value.bin.cb);
lpMsgNode->cbEID = prow->lpProps[E_EID].Value.bin.cb;
}

if(prow->lpProps[E_SENDER_NAME].ulPropTag == PR_SENDER_NAME)
{
if (MAPIAllocateMore (lstrlen (prow->lpProps[E_SENDER_NAME].Value.LPSZ) + 1,
lpMsgNode, (LPVOID far *) & lpMsgNode->lpszFrom))
goto err;
lstrcpy (lpMsgNode->lpszFrom, prow->lpProps[E_SENDER_NAME].Value.LPSZ);
}

if(prow->lpProps[E_SUBJECT].ulPropTag == PR_SUBJECT)
{
if (MAPIAllocateMore (lstrlen (prow->lpProps[E_SUBJECT].Value.LPSZ) + 1, lpMsgNode,
(LPVOID far *) & lpMsgNode->lpszSubject))
goto err;
lstrcpy (lpMsgNode->lpszSubject, prow->lpProps[E_SUBJECT].Value.LPSZ);
}

if(prow->lpProps[E_MSG_DEL_TIME].ulPropTag == PR_MESSAGE_DELIVERY_TIME)
{
if (MAPIAllocateMore (32, lpMsgNode,(LPVOID far *) & lpMsgNode->lpszDateRec))
goto err;
FormatFILETIME (&prow->lpProps[E_MSG_DEL_TIME].Value.ft, lpMsgNode->lpszDateRec);
}

return lpMsgNode;

err:
MAPIFreeBuffer (lpMsgNode);
return NULL;
}

/*
- InsertMsgNode
-
* Purpose:
* We insert the nodes
* at the beginning of the list. This can be
* replaced with a routine that inserts sorted on
* different criteria, like DateReceived, From, or
* Subject.
*
* Parameters:
* lpMsgNode - Pointer to a MSGID node
* lppMsgHead - Pointer to the head of the list
*
* Return:
* Void.
*/

void
InsertMsgNode (LPMSGID lpMsgNode, LPMSGID * lppMsgHead)
{
if (*lppMsgHead)
{
lpMsgNode->lpNext = *lppMsgHead;
(*lppMsgHead)->lpPrev = lpMsgNode;
}
else
lpMsgNode->lpNext = NULL;

/* The next 2 assignments are here in case the node came from somewhere */
/* other than a call to MakeMsgNode () in which case we aren't sure */
/* they're already NULL. */

lpMsgNode->lpPrev = NULL;
*lppMsgHead = lpMsgNode;
}

/*
- DeleteMsgNode
-
* Purpose:
* Removes the node passed in from the list. This
* may seem like a strange way to do this but it's
* not, because the Owner-Drawn List Box gives us
* direct access to elements in the list that makes
* it easier to do things this way.
*
* Parameters:
* lpMsgNode - Pointer to the MSGID node to delete
* lppMsgHead - Pointer to the head of the list
*
* Return:
* Void.
*/

void
DeleteMsgNode (LPMSGID lpMsgNode, LPMSGID * lppMsgHead)
{
if (!lpMsgNode)
return;

if (lpMsgNode->lpPrev)
{
/* Adjust Previous node to point to Next*/
Assert(*lppMsgHead != lpMsgNode);
lpMsgNode->lpPrev->lpNext = lpMsgNode->lpNext;
}
else
{
/*/ Adjust Head to point to Next*/
Assert(*lppMsgHead == lpMsgNode);
*lppMsgHead = lpMsgNode->lpNext;
}

/* Adjust next node to point to Previous*/

if (lpMsgNode->lpNext)
lpMsgNode->lpNext->lpPrev = lpMsgNode->lpPrev;

MAPIFreeBuffer (lpMsgNode);
return;
}



/*
- FindNode
-
* Purpose:
* Returns a pointer to the node with EntryID equal to *pEntryID.
* Returns NULL if node doesn't exist.
*
* Parameters:
* lpMsgHead - Pointer to the head of the list
* pEntryID + cbEntryID - Message ID to search for
*
* Return:
* lpMsgNode - Pointer to the node returned
*/

LPMSGID
FindNode (LPMSGID lpMsgHead, LPENTRYID pEntryID, ULONG cbEntryID)
{
ULONG fl;
HRESULT hr;

Assert(pmdb);

while (lpMsgHead)
{
hr = pmdb->lpVtbl->CompareEntryIDs(pmdb, cbEntryID, pEntryID, lpMsgHead->cbEID, lpMsgHead->lpEID,
0, &fl);
if(S_OK != GetScode(hr))
return NULL;
if(fl)
break;

lpMsgHead = lpMsgHead->lpNext;
}

return lpMsgHead;
}

/*
- FreeMsgList
-
* Purpose:
* Walks down the MsgList and frees each node.
*
* Parameters:
* lpMsgHead - Pointer to the head of the list
*
* Return:
* Void.
*/

void
FreeMsgList (LPMSGID lpMsgHead)
{
LPMSGID lpT;

while (lpMsgHead)
{
lpT = lpMsgHead;
lpMsgHead = lpMsgHead->lpNext;
MAPIFreeBuffer (lpT);
}
}

/*
- ToggleMenuState
-
* Purpose:
* Enables/Disables menu items depending on the session state.
*
* Parameters:
* hWnd - handle to the window/dialog who called us
* fLoggedOn - TRUE if logged on, FALSE if logged off
*
* Return:
* Void.
*/

void ToggleMenuState(HWND hWnd, BOOL fLoggedOn)
{
EnableMenuItem (GetMenu (hWnd), IDM_HIER, !fLoggedOn);
EnableMenuItem (GetMenu (hWnd), IDM_OPEN, !fLoggedOn);
EnableMenuItem (GetMenu (hWnd), IDM_LOGOFF, !fLoggedOn);
EnableMenuItem (GetMenu (hWnd), IDM_ROUTE, !fLoggedOn);
EnableMenuItem (GetMenu (hWnd), IDM_READ, !fLoggedOn);
EnableMenuItem (GetMenu (hWnd), IDM_SEND, !fLoggedOn);
EnableMenuItem (GetMenu (hWnd), IDM_NEWFORM, !fLoggedOn);
EnableMenuItem (GetMenu (hWnd), IDM_LOGON, fLoggedOn);
EnableMenuItem (GetMenu (hWnd), IDM_EXIT, FALSE);
}

//
// SecureMenu
//
// Purpose:
// Enables/Disables Logon and Exit menu items.
// CMCLogon might yield control to Windows, so the user might be able to
// access the window menu (for example click Logon) after we call
// MAPILogon, but before it returns.
//
// Parameters:
// hWnd - handle to the window/dialog who called us
// fBeforeLogon - TRUE when this function is called when we are about
// to call MAPILogon, FALSE if called after logon (failed)
// if Logon succeddes ToggleMenuState is called instead of
// this function.
//
// Return:
// Void.
//


void SecureMenu(HWND hWnd, BOOL fBeforeLogon)
{
EnableMenuItem (GetMenu (hWnd), IDM_LOGON, fBeforeLogon);
EnableMenuItem (GetMenu (hWnd), IDM_EXIT, fBeforeLogon);
}


/*
* Formats a Win32 file time as a MAPI date/time string.
* NOTE: converts from GMT to local time.
*/
void FormatFILETIME(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);
}

/*
* Create a data block used in CommonDlgProc to remember
* the entire chain of rows in the scrolling list box.
*/
LPDIALOGDATA
CreateDialogData (int iDlgType)
{
LPDIALOGDATA pDialogData;

SCODE sc;

sc = MAPIAllocateBuffer(sizeof(DIALOGDATA), &pDialogData);
if (sc)
{
MakeMessageBox (0, MAPI_E_NOT_ENOUGH_MEMORY, IDS_OPERATION, NULL, MBS_ERROR);
return NULL;
}

pDialogData->iDlgType = iDlgType;
pDialogData->poarHead = NULL;
pDialogData->cbEntryID = 0;
pDialogData->lpEntryID = NULL;
return pDialogData;
}