FORM.CPP

/* -------------------------------------------------------------------------- 

Basic Forms example of a custom sendable form. It is an EXE server
rather than a DLL. It implements the minimum form interface required
to launch and send a form.

Copyright (C) 1995 Microsoft Corporation

-------------------------------------------------------------------------- */

//#define ALLOW_SUBCLASS_IPM // all other forms to subclass the reply behavior
// of this form (slower, but more correct)

#include <windows.h>
#include <windowsx.h>
#include <ole2.h>
#include <initguid.h>
#include <mapiform.h>
#define INITGUID
#include <initguid.h>
#include <mapix.h>
#include <mapiutil.h>
#include <mapinls.h>
#include "dbugit.h"
#include "check.h"
#include "form.h"
#include "dlg.h"

/*
*
* Checkers form clsid. This must match the configuration file.
*
*/

DEFINE_GUID(CLSID_MyFormsClsId, 0x86174010, 0x5030, 0x0076, 0x99, 0x12, 0x00, 0xaa, 0x00, 0x38, 0x90, 0x1b);

/*
* HrStartOleAndRegisterClassFactory
*
* Purpose:
* Initialize OLE, MAPI, and the Forms Interface
* Should be called from WinMain() or InitApplication() in an SDI app
*
* This function LoadLibraries the neccessary DLLs rather than
* linking with them. This permits the form to run as a stand-
* alone executable even when MAPI and OLE are not installed.
*
* Returns:
* HRESULT
*/

#ifdef _WIN32
#define szOleDll "ole32.dll"
#define szMapiDll "mapi32.dll"
#else
#define szOleDll "compobj.dll"
#define szMapiDll "mapi.dll"
#endif

HINSTANCE hinstOle = NULL;
HINSTANCE hinstMapi = NULL;


typedef HRESULT (FAR PASCAL *LPFNCOREGISTERCLASSOBJECT)(REFCLSID rclsid,
IUnknown FAR * pUnk, DWORD dwClsContext, DWORD flags, LPDWORD lpdwRegister);
#ifdef WIN16
typedef BOOL (FAR PASCAL *LPFNISEQUALGUID)(REFGUID rguid1, REFGUID rguid2);
#undef IsEqualIID
#define IsEqualIID(riid1, riid2) (*lpfnIsEqualGUID)(riid1, riid2)
#endif
typedef HRESULT (FAR PASCAL *LPFNHRQUERYALLROWS)(LPMAPITABLE ptable,
LPSPropTagArray ptaga, LPSRestriction pres,
LPSSortOrderSet psos, LONG crowsMax,
LPSRowSet FAR *pprows);
typedef ULONG (FAR PASCAL *LPFNMAPIFREEBUFFER)(LPVOID pv);
typedef HRESULT (FAR PASCAL *LPFNMAPIINITIALIZE)(LPVOID lpvReserved);
typedef void (FAR PASCAL *LPFNMAPIUNINITIALIZE)(VOID);
typedef void (FAR PASCAL *LPFNMAPIFREEPADRLIST)(LPADRLIST);

LPFNCOREGISTERCLASSOBJECT lpfnCoRegisterClassObject;
#ifdef WIN16
LPFNISEQUALGUID lpfnIsEqualGUID;
#endif
LPFNHRQUERYALLROWS lpfnHrQueryAllRows ;
LPFNMAPIFREEBUFFER lpfnMAPIFreeBuffer ;
LPFNMAPIINITIALIZE lpfnMAPIInitialize ;
LPFNMAPIUNINITIALIZE lpfnMAPIUninitialize ;
LPFNMAPIFREEPADRLIST lpfnFreePadrlist ;

HRESULT
HrStartOleAndRegisterClassFactory(void)
{
FRMFMR * pfrmfmr = NULL;
HRESULT hr;

TraceTag(tagFormFunc,"HrStartOleAndRegisterClassFactory");

// ----- LoadLibrary the essentials
hinstOle = LoadLibrary(szOleDll);
hinstMapi = LoadLibrary(szMapiDll);
#ifdef WIN16
if (hinstOle < HINSTANCE_ERROR) hinstOle = 0;
if (hinstMapi < HINSTANCE_ERROR) hinstMapi = 0;
#endif
if (0 == hinstOle || 0 == hinstMapi)
{
return ResultFromScode(E_FAIL);
}


// ----- Setup a few function pointers
lpfnCoRegisterClassObject = (LPFNCOREGISTERCLASSOBJECT) GetProcAddress(hinstOle, "CoRegisterClassObject");
#if defined(_WIN32)
#if defined(_X86_)
lpfnHrQueryAllRows = (LPFNHRQUERYALLROWS ) GetProcAddress(hinstMapi,"HrQueryAllRows@24");
lpfnFreePadrlist = (LPFNMAPIFREEPADRLIST ) GetProcAddress(hinstMapi,"FreePadrlist@4");
#else
lpfnHrQueryAllRows = (LPFNHRQUERYALLROWS ) GetProcAddress(hinstMapi,"HrQueryAllRows");
lpfnFreePadrlist = (LPFNMAPIFREEPADRLIST ) GetProcAddress(hinstMapi,"FreePadrlist");
#endif //_X86_
#else
lpfnIsEqualGUID = (LPFNISEQUALGUID ) GetProcAddress(hinstOle, "IsEqualGUID");
lpfnHrQueryAllRows = (LPFNHRQUERYALLROWS ) GetProcAddress(hinstMapi,"HrQueryAllRows");
lpfnFreePadrlist = (LPFNMAPIFREEPADRLIST ) GetProcAddress(hinstMapi,"FreePadrlist");
#endif //_WIN32
lpfnMAPIFreeBuffer = (LPFNMAPIFREEBUFFER ) GetProcAddress(hinstMapi,"MAPIFreeBuffer");
lpfnMAPIInitialize = (LPFNMAPIINITIALIZE ) GetProcAddress(hinstMapi,"MAPIInitialize");
lpfnMAPIUninitialize = (LPFNMAPIUNINITIALIZE ) GetProcAddress(hinstMapi,"MAPIUninitialize");
AssertSz(lpfnCoRegisterClassObject ,"missing lpfnCoRegisterClassObject");
AssertSz(lpfnHrQueryAllRows ,"missing lpfnHrQueryAllRows ");
AssertSz(lpfnMAPIFreeBuffer ,"missing lpfnMAPIFreeBuffer ");
AssertSz(lpfnMAPIInitialize ,"missing lpfnMAPIInitialize ");
AssertSz(lpfnMAPIUninitialize ,"missing lpfnMAPIUninitialize ");
AssertSz(lpfnFreePadrlist ,"missing lpfnFreePadrlist ");
if (0 == lpfnCoRegisterClassObject ||
0 == lpfnHrQueryAllRows ||
0 == lpfnMAPIFreeBuffer ||
0 == lpfnMAPIInitialize ||
0 == lpfnMAPIUninitialize ||
0 == lpfnFreePadrlist)
{
AssertSz(0,"get procaddress failed");
return ResultFromScode(E_FAIL);
}

// ----- Initialize MAPI
hr = (*lpfnMAPIInitialize)(NULL);
if (S_OK != hr)
{
TraceTag(tagForm,"MapiInit failed 0x%08lx",hr);
return hr;
}

// ----- Allocate Memory for our class factory
pfrmfmr = new FRMFMR;
if (NULL == pfrmfmr)
{
TraceTag(tagForm, "RegisterClassFactory: OOM 0x%08lx",hr);
hr = ResultFromScode(E_OUTOFMEMORY);
return hr;
}

// ----- Register our class object(s)
DWORD dwRegMyForm = 0;
hr = (*lpfnCoRegisterClassObject) (CLSID_MyFormsClsId, (LPUNKNOWN)pfrmfmr,
CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE,
&dwRegMyForm); /* switch singleuse to multipleuse if you are an MDI app */
if (FAILED(hr))
{
TraceTag(tagForm,"CoRegisterClassObject() failed 0x%08lx",hr);
return hr;
}

TraceTag(tagForm,"return 0x%08lx",hr);
return hr;
}

/*
* HrStopForms
*
* Purpose:
* UnInitialize OLE, MAPI, and the Forms Interface
*
* Returns:
* HRESULT == 0
*/
HRESULT
HrStopForms(void)
{
HRESULT hr = ResultFromScode(S_OK);

TraceTag(tagFormFunc,"HrStopForms");

(*lpfnMAPIUninitialize)();

FreeLibrary(hinstOle);
FreeLibrary(hinstMapi);

return hr;
}


/*
* S a m p l e F o r m
*/

// Checkers form specific methods follow ///////////////////////////

/*
* FRM::ScGetRecipientAdrList
*
* Purpose:
* Fill the addrlist with the current recipients in the message
*
* Arguments:
* LPMESSAGE - the message (in)
* LPADRLIST - the addr list destination (out)
*
* Returns:
* SCODE Error status.
*/
SCODE
FRM::ScGetRecipientAdrlist(LPMESSAGE pmsg, LPADRLIST * ppal)
{
SCODE sc = S_OK;
HRESULT hr;
LPMAPITABLE pmt = NULL;
LPSPropTagArray ptaga = NULL;

TraceTag(tagFormFunc,"ScGetRecipientAdrlist");
AssertSz(!*ppal, "pal should be NULL on entry");
AssertSz(cRef > 0,"excuse me. are you refering to me?");

// Get the recipient table from the message
if (hr = pmsg->GetRecipientTable(MAPI_DEFERRED_ERRORS, &pmt))
{
TraceTag(tagForm,"2043 0x%08lx",hr);
goto Cleanup;
}

if (hr = pmt->QueryColumns(TBL_ALL_COLUMNS, &ptaga))
{
TraceTag(tagForm,"sdlkfj 0x%08lx",hr);
goto Cleanup;
}

#ifdef NEVER
ConvertToCorrectCharset(ptaga);
#endif

// Read in the recipients
hr = (*lpfnHrQueryAllRows)(pmt, ptaga, NULL, NULL, 0, (LPSRowSet *) ppal);
if (hr)
{
TraceTag(tagForm,"sdfhjsadjfhadkflhxxxx 0x%08lx",hr);
goto Cleanup;
}

Cleanup:
TraceTag(tagForm,"ScGetRecipientAdrlist 0x%08lx 0x%08lx", hr, sc);

(*lpfnMAPIFreeBuffer)(ptaga);
ReleaseObj(pmt);

TraceTag(tagForm,"return 0x%08lx",ResultFromScode(sc));
return sc;
}

/*
* FRM::SendForm
*
* Purpose:
* Have the message site send us
* (also tries to send the message using mapi if message site fails)
*
* Arguments:
* None.
*
* Returns:
* HRESULT Error status.
*/
HRESULT
FRM::SendForm(VOID)
{
HRESULT hr = S_OK;

TraceTag(tagFormFunc,"FRM::SendForm (this is not a standard form function)");

AssertSz(cRef > 0,"excuse me. are you refering to me?");
Assert(pMessageSite);
Assert(pMessage);

// ----- Submit message
if (hr = pMessageSite->SubmitMessage(0) )
{
TraceTag(tagForm,"failure pMessageSite->SubmitMessage 0x%08lx",hr);

#ifndef GOOD_FORM_BEHAVIOR // the following is not standard behavior

// ----- No harm in trying to send it myself
if (IDYES == MessageBox(hMainWnd, "The message site failed to send this form.\nDo you want this form to attempt sending through the message interface?", "Checkers", MB_YESNO | MB_ICONSTOP))
{
// this is often times easier to debug than the remoted
// message site interface (which does the same thing .. almost)

if (hr = Save(NULL,TRUE))
{
TraceTag(tagForm,"NO: ::Save 0x%08lx",hr);
}

HandsOffMessage();

if (hr = pMessage->SaveChanges(KEEP_OPEN_READWRITE) )
{
TraceTag(tagForm,"NO: pMessage->SaveChanges 0x%08lx",hr);
}
if (hr = pMessage->SubmitMessage(0) )
{
TraceTag(tagForm,"NO: pMessage->SubmitMessage 0x%08lx",hr);
}
}

#endif // good_form_behavior
}

// ----- advise everyone of what we just did
ADVISE(OnSubmitted)();

TraceTag(tagForm,"return 0x%08lx",hr);
return hr;
}

/*
* FRM::LaunchReplyMessage
*
* Purpose:
* Construct a reply to PR_SENDER* (note: ignoring sent representing)
* Display any form user interface on the existing form
*
* Arguments:
* HWND Parent window
*
* Returns:
* HRESULT Error status.
*/
HRESULT
FRM::LaunchReplyMessage(ULONG ulhwndParent)
{
#ifdef ALLOW_SUBCLASS_IPM
LPMAPIFORM pNewForm;
LPPERSISTMESSAGE pNewFormIPersist;
#endif

ULONG itaga;
ADRLIST al = {1,0}; /* our adrlist will have exactly one entry */
HRESULT hr = S_OK;
LPMAPIMESSAGESITE pNewMessageSite;
LPMAPIVIEWCONTEXT pNewMapiViewContext;
LPMESSAGE pNewMessage;

SizedSPropTagArray(6,tagaSender) =
{ 6,
{ PR_RECIPIENT_TYPE,
PR_SENDER_NAME,
PR_SENDER_ADDRTYPE,
PR_SENDER_ENTRYID,
PR_SENDER_EMAIL_ADDRESS,
PR_SENDER_SEARCH_KEY } };
SizedSPropTagArray(6,tagaRepliee) =
{ 6,
{ PR_RECIPIENT_TYPE,
PR_DISPLAY_NAME,
PR_ADDRTYPE,
PR_ENTRYID,
PR_EMAIL_ADDRESS,
PR_SEARCH_KEY
} };
static SizedSPropTagArray(26,tagaRemoveFromNewReply) =
{ 26,
{ // Stuff you would typically want to remove on reply
PR_MESSAGE_FLAGS, // Want unsent compose note
PR_MESSAGE_RECIPIENTS, // Will generate new recip list
PR_SENDER_ENTRYID, // Clear sender/recipient info
PR_SENDER_NAME, //
PR_RECEIVED_BY_ENTRYID, //
PR_RECEIVED_BY_NAME, //
PR_SENT_REPRESENTING_ENTRYID, // Clear delegate access stuff
PR_SENT_REPRESENTING_NAME, //
PR_SENT_REPRESENTING_ADDRTYPE, // 10961
PR_SENT_REPRESENTING_EMAIL_ADDRESS,
PR_RCVD_REPRESENTING_ENTRYID, //
PR_RCVD_REPRESENTING_NAME, //
PR_READ_RECEIPT_ENTRYID, // Clear destination overrides
PR_REPORT_ENTRYID, //
PR_REPLY_RECIPIENT_ENTRIES, //
PR_REPLY_RECIPIENT_NAMES, //
PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED, // Clear delivery receipt
PR_READ_RECEIPT_REQUESTED, // Clear read receipt
PR_CLIENT_SUBMIT_TIME, // Clear submit time
PR_MESSAGE_ATTACHMENTS, // Drop attachments on reply
PR_ORIGINAL_AUTHOR_ENTRYID, // Keep original author information
PR_ORIGINAL_AUTHOR_NAME, // on forwards
PR_ORIGINAL_SUBMIT_TIME, // Keep original time on forwards
PR_IMPORTANCE, // Lose importance on reply
PR_PRIORITY, // Lose priority on reply
PR_SENSITIVITY // Lose sensitivity on reply
} };

TraceTag(tagFormFunc,"FRM::LaunchReplyMessage");
AssertSz(cRef > 0,"excuse me. are you refering to me?");
Assert(pMessageSite);
Assert(pSession);
Assert(pMessage);

#ifdef ALLOW_SUBCLASS_IPM

/*
Since I am a single instance exe, creating a new form results in
a new process. For performance reasons, this form does not conform
to the forms API precisely. This effectively removes the ability to
subclass the reply note for IPM.Checkers. This is acceptable.

*/

// ----- open form manager
AssertSz(NULL == pFormMgr,"two form managers?");
hr = pMessageSite->GetFormManager(&pFormMgr);
if (FAILED(hr))
{
TraceTag(tagForm,"failure MAPIOpenFormMgr 0x%08lx",hr);
return hr;
}
Assert(pFormMgr);

// ----- Get form info
hr = pFormMgr->ResolveMessageClass("IPM.Checkers",0,NULL,&pFormInfo);
if (FAILED(hr))
{
TraceTag(tagForm,"failure to ResolveMessageClass 0x%08lx",hr);
return hr;
}

// ----- Create the new form
hr = pFormMgr->CreateForm(0,0,pFormInfo,IID_IMAPIForm,(LPVOID FAR*) &pNewForm);
if (FAILED(hr))
{
TraceTag(tagForm,"failure to CreateForm 0x%08lx",hr);
return hr;
}

hr = pNewForm->QueryInterface(IID_IPersistMessage, (LPVOID FAR*) &pNewFormIPersist);
if (FAILED(hr))
{
TraceTag(tagForm,"failure asking form for IPersistMessage 0x%08lx",hr);
return hr;
}
AssertSz(pNewFormIPersist,"have it?");

#endif // ALLOW_SUBCLASS_IPM

// ----- Create the reply message
hr = pMessageSite->NewMessage(0,NULL,
#ifdef ALLOW_SUBCLASS_IPM
pNewFormIPersist
#else
this
#endif // ALLOW_SUBCLASS_IPM
,&pNewMessage,&pNewMessageSite,&pNewMapiViewContext);
if (FAILED(hr))
{
TraceTag(tagForm,"failure VDOG_*NewMessage* comform...c 0x%08lx",hr);
return hr;
}
AssertSz(pNewMessage,"nothing new with you");
AssertSz(pNewMessageSite,"no new site?");
AssertSz(pNewMapiViewContext,"no new view context ... did NewMessage work at all?");

// ----- Copy current message to new message
hr = pMessage->CopyTo(0, NULL, (LPSPropTagArray)&tagaRemoveFromNewReply, 0, NULL,
(LPIID) &IID_IMessage, pNewMessage, 0, NULL);
if (FAILED(hr))
{
TraceTag(tagForm,"failure CopyTo 0x%08lx",hr);
return hr;
}

// ----- who sent this to us?
hr = pMessage->GetProps((LPSPropTagArray) &tagaSender, 0, &al.aEntries[0].cValues, &al.aEntries[0].rgPropVals);
AssertSz(ResultFromScode(MAPI_W_ERRORS_RETURNED) == hr,"mapi gave me pr_recipient_type, but should not have");

// ----- Make the sender the recipient
if (al.aEntries && al.aEntries[0].rgPropVals)
{
al.aEntries[0].rgPropVals[0].ulPropTag = PR_RECIPIENT_TYPE;
al.aEntries[0].rgPropVals[0].Value.ul = MAPI_TO;
}
else
{
AssertSz(0,"could not form reply message: al.aEntries && al.aEntries[0].rgPropVals");
return ResultFromScode(E_FAIL);
}

// ----- Set our new recipients properties to their expected property ids
itaga = 1;

TraceTag(tagForm,"0x%08lx cEntries",al.cEntries);
TraceTag(tagForm,"0x%08lx <0x%08lx> %s",al.aEntries[0].rgPropVals[itaga].ulPropTag,al.aEntries[0].rgPropVals[itaga].Value.ul,al.aEntries[0].rgPropVals[itaga].Value.LPSZ);

for (itaga = 1; itaga < tagaRepliee.cValues; itaga++)
{
al.aEntries[0].rgPropVals[itaga].ulPropTag =
PROP_TAG(PROP_TYPE(al.aEntries[0].rgPropVals[itaga].ulPropTag),
PROP_ID(tagaRepliee.aulPropTag[itaga]));

TraceTag(tagForm,"0x%08lx <0x%08lx> %d",al.aEntries[0].rgPropVals[itaga].ulPropTag,al.aEntries[0].rgPropVals[itaga].Value.ul,itaga);

AssertSz(SUCCEEDED(al.aEntries[0].rgPropVals[itaga].Value.ul),"Failure to get PR_SENDER* 10961 ");
}

// ----- Save out addresses
AssertSz(1 == al.cEntries,"we only reply to one person");
hr = pNewMessage->ModifyRecipients(0, &al);
if (FAILED(hr) )
{
TraceTag(tagForm,"pMessage->ModifyRecipients 0x%08lx",hr);
return hr;
}

// ----- Release everything the read form was remembering (if we reply inplace)
#ifndef ALLOW_SUBCLASS_IPM
Forget();
#endif

// ----- Call LoadForm (this makes the current form the new form)
hr =
#ifdef ALLOW_SUBCLASS_IPM
pNewFormIPersist->
#endif
Load(pNewMessageSite,pNewMessage,0,MSGFLAG_UNSENT);
if (FAILED(hr))
{
TraceTag(tagForm,"failure LoadForm 0x%08lx",hr);
return hr;
}

// ----- Call SetViewContext
#ifdef ALLOW_SUBCLASS_IPM
hr = pNewForm->SetViewContext(pNewMapiViewContext);
if (FAILED(hr))
{
TraceTag(tagForm,"failure SetViewContext 0x%08lx",hr);
return hr;
}
#endif

// ----- Call DoVerb So we can see the reply form
hr =
#ifdef ALLOW_SUBCLASS_IPM
pNewForm->
#endif
DoVerb(OLEIVERB_PRIMARY,NULL,ulhwndParent,NULL);
if (FAILED(hr))
{
TraceTag(tagForm,"failure DoVerb 0x%08lx",hr);
return hr;
}

// ----- release stuff
pNewMessage->Release();
pNewMessageSite->Release();
pNewMapiViewContext->Release();
(*lpfnMAPIFreeBuffer)(al.aEntries[0].rgPropVals);

// ----- Close down the read form (that's me)
#ifdef ALLOW_SUBCLASS_IPM
hr = ShutdownForm(OLECLOSE_NOSAVE);
if (FAILED(hr))
{
TraceTag(tagForm,"failure ShutdownForm'ing read form 0x%08lx",hr);
return hr;
}
#endif // ALLOW_SUBCLASS_IPM

// ----- return to caller
return hr;

}

/*
* FRM::GetCheckersData
*
* Purpose:
* Allows anyone to query the form for it's current data
*
* Arguments:
* data members (out)
*
* Returns:
* void
*/
VOID
FRM::GetCheckersData(SQUARE* out_b, int* out_turn, long* out_movenum, long* out_score)
{
Assert(out_b);
AssertSz(cRef > 0,"excuse me. are you refering to me?");

if (turn) /* set elements only if we have data to give out */
{
memcpy(out_b,b,sizeof(b));
if (out_turn) *out_turn = turn;
if (out_movenum) *out_movenum = movenum;
if (out_score) *out_score = score;
}
}

/*
* FRM::SetCheckersData
*
* Purpose:
* Allows anyone to set the forms current data members
*
* Arguments:
* data members (in)
*
* Returns:
* void
*/
VOID
FRM::SetCheckersData(SQUARE* in_b, int in_turn, long in_movenumber, long in_score)
{
AssertSz(cRef > 0,"excuse me. are you refering to me?");
Assert(in_b && in_turn);

fDirty = TRUE;

memcpy(b,in_b,sizeof(b));
turn = in_turn;
movenum = in_movenumber;
score = in_score;
}


/*
* FRM::AddressForm
*
* Purpose:
* Look at the current message addresses, and show user
* interface to address the message.
*
* Arguments:
* HWND - parent
* BOOL - true if no user interface should be presented when
* recipients are already present
*
* Returns:
* HRESULT Error Status.
*/
HRESULT
FRM::AddressForm(HWND hwnd, BOOL fDontShowIfRecipsExist)
{
LPADRBOOK pAdrBook;
ULONG ulUIParam = (ULONG) (UINT) hwnd;
SCODE sc;
HRESULT hr = S_OK;
ADRPARM ap = { 0 };
LPADRLIST pal = NULL;
BOOL fCloseForm = FALSE;

TraceTag(tagFormFunc,"FRM::AddressForm");

AssertSz(cRef > 0,"excuse me. are you refering to me?");
Assert(pMessageSite);
Assert(pSession);
Assert(pMessage);

// ----- Read in addresses from message
sc = ScGetRecipientAdrlist(pMessage, &pal);
hr = ResultFromScode(sc);
if (FAILED(hr))
{
TraceTag(tagForm,"failed to read address into pal 0x%08lx",hr);
return hr;
}

// ----- remember address book from the session
hr = pSession->OpenAddressBook(ulUIParam, NULL, 0, &pAdrBook);
if (FAILED(hr))
{
TraceTag(tagForm,"failed to get pAdrBook object 0x%08lx",hr);
goto cleanuppal;
}

// ----- Show the address book
ap.ulFlags = AB_RESOLVE | DIALOG_OPTIONS | DIALOG_MODAL | fMapiUnicode;
ap.lpszCaption = TEXT("Select Checkers Opponent");
ap.cDestFields = 1;
if (0 == fDontShowIfRecipsExist || 0 == pal->cEntries)
hr = pAdrBook->Address(&ulUIParam, &ap, &pal);
#ifdef DEBUG
if (hwnd != hMainWnd)
{
TraceTag(tagNull,"ADDRESSFORM: pAdrBook->Address changed it's out parameter even though DIALOG_MODAL");
}
#endif
if (FAILED(hr))
{
// cancel is a failed scode
TraceTag(tagForm,"cant pop up addr book 0x%08lx %d",hr,pal?pal->cEntries:0);
}

// ----- Save out addresses
AssertSz(pMessage,"pMessage said goodbye during the Address function");
hr = pMessage->ModifyRecipients(0, pal);
if (FAILED(hr) )
{
TraceTag(tagForm,"pMessage->ModifyRecipients 0x%08lx",hr);
goto cleanup;
}

// ----- Close the form if there are no recipients for this move
if (pal->cEntries <= 0) fCloseForm = TRUE;

// ----- Release the address book, adrlist, and clean up
cleanup:
Assert(pAdrBook);
pAdrBook->Release();
cleanuppal:
Assert(pal);
(*lpfnFreePadrlist)(pal);
if (fCloseForm) ShutdownForm(OLECLOSE_PROMPTSAVE);

TraceTag(tagForm,"return 0x%08lx",hr);
return hr;
}

/*
* FRM::Remember
*
* Purpose:
* Store and addref the message site, the message, and the session
* for later use
*
* Returns:
* HRESULT Error Status.
*/
HRESULT
FRM::Remember(LPMAPIMESSAGESITE pmsite, LPMESSAGE pmsg)
{
HRESULT hr;

TraceTag(tagFormFunc,"FRM::Remember");

AssertSz(cRef > 0,"excuse me. are you refering to me?");
AssertSz(pmsite,"what am I going to do without it?");
AssertSz(pmsg,"what am I going to do without it? pmsg that is");

// ----- remember our message site object
AssertSz(!pMessageSite,"who me? a message site?");
pMessageSite = pmsite;
pMessageSite->AddRef();

// ----- remember our message
AssertSz(!pMessage,"a message in my");
pMessage = pmsg;
pMessage->AddRef();

// ----- remember mapi session
AssertSz(!pSession,"another session?");
hr = pMessageSite->GetSession(&pSession);

#ifdef DEBUG
if (FAILED(hr))
{
TraceTag(tagForm,"failed to get session object %08lx",hr);
}
#endif

// ----- return result to caller
TraceTag(tagForm,"return 0x%08lx",hr);
return hr;

}

/*
* FRM::Forget
*
* Purpose:
* Release the message site, the message, and the session
*
* Returns:
* HRESULT Error Status.
*/
HRESULT
FRM::Forget(VOID)
{
TraceTag(tagFormFunc,"FRM::Forget");

if (pMessage) pMessage->Release();
if (pMessageSite) pMessageSite->Release();
if (pSession) pSession->Release();

pMessage = NULL;
pMessageSite = NULL;
pSession = NULL;

return NOERROR;
}

/*
* FRM::ShowCurrentMessage
*
* Purpose:
* Display any form user interface on a form
*
* Arguments:
* HWND Parent window
*
* Returns:
* HRESULT Error status.
*/
HRESULT
FRM::ShowCurrentMessage(ULONG ulhwndParent)
{
HRESULT hr = NOERROR;

TraceTag(tagFormFunc,"FRM::ShowCurrentMessage");

AssertSz(cRef > 0,"excuse me. are you refering to me?");
Assert(pMessageSite);
Assert(pSession);
Assert(pMessage);

// ----- Give our user access to our form interface
SendMessage(hMainWnd,EM_GIVEFORMTOHWND,0,(LPARAM) this);

// ----- Display address book modal to form if this message has not yet been addressed
FORWARD_WM_COMMAND(hMainWnd, IDM_ADDRESS, 0, 1 /* Don't Show Recips */, PostMessage);

return hr;
}

// IUnknown methods follow ///////////////////////////

/*
* FRM::QueryInterface
*
* Purpose:
* Returns a pointer to the specified interface.
*
* Arguments:
* REFIID Interface we want.
* LPUNKNOWN * Interface we return.
*
* Returns:
* HRESULT Error status.
*/
STDMETHODIMP
FRM::QueryInterface(REFIID riid, LPVOID FAR * ppvObj)
{
HRESULT hr = NOERROR;

TraceTag(tagFuncTriv,"FRM::QueryInterface %s",DumpCLSID(riid));
AssertSz(cRef > 0,"excuse me. are you refering to me?");

if (IsEqualIID(riid, IID_IUnknown))
{
AddRef();
*ppvObj = (IMAPIForm *)this;
}
else if (IsEqualIID(riid, IID_IPersistMessage))
{
AddRef();
*ppvObj = (IPersistMessage *)this;
}
else if (IsEqualIID(riid, IID_IMAPIForm))
{
AddRef();
*ppvObj = (IMAPIForm *)this;
}
else
{
hr = ResultFromScode(E_NOINTERFACE);
*ppvObj = NULL;
}

#ifdef DEBUG
if (hr != ResultFromScode(E_NOINTERFACE)) AssertSz(ppvObj,"no object pointer");
#endif

TraceTag(tagForm,"return 0x%08lx",hr);
return hr;
}


/*
* FRM::AddRef
*
* Purpose:
* Increments reference count on the sample extension.
*
* Arguments:
*
* Returns:
* ULONG New value of reference count.
*/
STDMETHODIMP_(ULONG)
FRM::AddRef(void)
{
TraceTag(tagFuncTriv,"FRM::AddRef ret %d",cRef + 1);
AssertSz(cRef > 0,"excuse me. are you refering to me?");
return ++cRef;
}


/*
* FRM::Release
*
* Purpose:
* Decrements reference count on the sample extension. If count is
* decremented to zero, the object is freed.
*
* Arguments:
*
* Returns:
* ULONG New value of reference count.
*/
STDMETHODIMP_(ULONG)
FRM::Release(void)
{
TraceTag(tagFuncTriv,"FRM::Release cRef %d",cRef);
AssertSz(cRef > 0,"excuse me. are you refering to me?");

if (!(--cRef))
{
// ----- be sure our ui is gone when we leave
#ifdef DEBUG
if (IsWindow(hMainWnd))
{
TraceTag(tagForm,"Last Release called, but IsWindow(hMainWnd). ShutdownForm called?");
}
TraceTag(tagForm,"return 0");
#endif //debug
delete this;
return 0;
}
return cRef;
}


// IPersistMessage methods follow ///////////////////////////

/*
* FRM::GetLastError
*
* Purpose:
* Get the last error
*
* Arguments:
*
* Returns:
* HRESULT NOERROR always.
*/
STDMETHODIMP
FRM::GetLastError(HRESULT hResult, ULONG ulFlags, LPMAPIERROR FAR * lppMAPIError)
{
TraceTag(tagFormFunc,"FRM::GetLastError 0x%08x",hResult);
AssertSz(cRef > 0,"excuse me. are you refering to me?");
if (lppMAPIError) *lppMAPIError = NULL;
return NOERROR;
}

/*
* FRM::GetClassID
*
* Purpose:
* Get the class ID associated with this message.

* 
* Arguments:
* LPCLSID Where to put the class ID.
*
* Returns:
* HRESULT NOERROR always.
*/
STDMETHODIMP
FRM::GetClassID(LPCLSID pclsid)
{

TraceTag(tagFormFunc,"FRM::GetClassID");
AssertSz(cRef > 0,"excuse me. are you refering to me?");

// The form only plays with things of its own class ID, so
// this is easy; it's more complicated if code supports multiple
// classes, or can do "treat as" operations
if (pclsid) *pclsid = clsid;

return NOERROR;
}


/*
* FRM::IsDirty
*
* Purpose:
* Returns whether the object has changed since the last save
*
* Arguments:
*
* Returns:
* HRESULT S_OK if dirty, S_FALSE if not dirty.
*/
STDMETHODIMP
FRM::IsDirty(void)
{
TraceTag(tagFormFunc,"FRM::IsDirty");
AssertSz(cRef > 0,"excuse me. are you refering to me?");

return fDirty ? NOERROR : ResultFromScode(S_FALSE);
}


/*
* FRM::InitNew
*
* Purpose:
* Create a new message of our message class in the provided pmsg.
*
* Arguments:
* LPMAPISESSION Session in which the message belongs.
* LPMESSAGE Message to create the new form in.
*
* Returns:
* HRESULT S_OK, or error value.
*/
STDMETHODIMP
FRM::InitNew(LPMAPIMESSAGESITE pmsite, LPMESSAGE pmsg)
{
HRESULT hr;
SPropValue prop;

TraceTag(tagFormFunc,"FRM::InitNew");
AssertSz(cRef > 0,"excuse me. are you refering to me?");

// ----- Remember our pointers and such
hr = Remember(pmsite,pmsg);
if (FAILED(hr))
{
TraceTag(tagForm,"loss of memory in initnew 0x%08lx",hr);
return hr;
}

// ----- set our message class
prop.ulPropTag = PR_MESSAGE_CLASS;
prop.Value.LPSZ = TEXT("IPM.Checkers");
hr = pMessage->SetProps(1, &prop, NULL);
if (FAILED(hr) )
{
TraceTag(tagForm,"failed setprops in initnew 0x%08lx",hr);
return hr;
}

// ----- remind ourselves that this new message could not have been sent
fSentMessage = 0;

// ----- set our special properties
prop.ulPropTag = PR_SUBJECT;
prop.Value.LPSZ = TEXT("--- CHECKERS FORM ---");
hr = pMessage->SetProps(1, &prop, NULL);
if (FAILED(hr) )
{
TraceTag(tagForm,"failed setprops in on pr_subject here 0x%08lx",hr);
return hr;
}

ADVISE(OnNewMessage)();
return hr;
}


/*
* FRM::Load
*
* Purpose:
* Attaches our object to the provided pmsg.
*
* Arguments:
* LPMAPISESSION Our session to remember.
* LPMESSAGE Our message to remember.
*
* Returns:
* HRESULT S_OK, or error value.
*/
STDMETHODIMP
FRM::Load(LPMAPIMESSAGESITE pmsite, LPMESSAGE pmsg, ULONG ulMessageStatus, ULONG ulMessageFlags)
{
ULONG cProps = 0;
#define ctagMax 10
char rgchTaga[sizeof(SPropTagArray) + (ctagMax * sizeof(ULONG))];
LPSPropTagArray ptaga = (LPSPropTagArray) rgchTaga;
LPSPropValue rgval = NULL;
LPSPropValue pval = NULL;
HRESULT hr=S_OK;

TraceTag(tagFormFunc,"FRM::Load status=0x%08lx flags=0x%08lx",ulMessageStatus,ulMessageFlags);
AssertSz(cRef > 0,"excuse me. are you refering to me?");

// ----- Remember our pointers and such
hr = Remember(pmsite,pmsg);
if (FAILED(hr))
{
TraceTag(tagForm,"loads call to remember failed 0x%08lx",hr);
return hr;
}

// ----- If this message has been sent we would like to remember that
fSentMessage = !( ulMessageFlags & MSGFLAG_UNSENT);
TraceTag(tagForm,"fSentMessage = %d",(int) fSentMessage);

// ----- Load our data out of the message like a nice form
ptaga->cValues = 0;
ptaga->aulPropTag[ptaga->cValues++] = PR_SUBJECT;
ptaga->aulPropTag[ptaga->cValues++] = PR_BODY;
ptaga->aulPropTag[ptaga->cValues++] = PR_BOARD;
ptaga->aulPropTag[ptaga->cValues++] = PR_TURN;
ptaga->aulPropTag[ptaga->cValues++] = PR_MOVENUMBER;

hr = pmsg->GetProps(ptaga,0, &cProps, &rgval);
if (FAILED(hr) )
{
TraceTag(tagForm,"failed getprops on pr_board there 0x%08lx",hr);
return hr;
}
AssertSz(ptaga->cValues <= ctagMax, "Too many properties to read!");
AssertSz(cProps == ptaga->cValues,"to mucho values");
pval = rgval;

// ----- set properties to variables
pval; // subject
pval++; // body
pval++; // board
if (pval->Value.bin.cb == sizeof(b)) /* if it's a valid board */
{
memcpy(b,pval->Value.bin.lpb,(int) pval->Value.bin.cb);
pval++; // turn
AssertSz(pval->Value.l == RED || pval->Value.l == BLACK,"cool: neither red or blacks turn according to mapi");
turn = (int) pval->Value.l;
pval++; // movenumber
}
Assert(rgval);
(*lpfnMAPIFreeBuffer)(rgval);
ADVISE(OnNewMessage)();
return hr;
}


/*
* FRM::Save
*
* Purpose:
* Writes out our information to the provided pmsg. Does NOT commit
* changes; this is the responsibility of the caller. Puts the form
* into no-scribble mode until SaveCompleted is called.
*
* Arguments:
* LPMESSAGE Message to write our changes to.
* BOOL TRUE if this is our home message, FALSE if
* this is a different message.
*
* Returns:
* HRESULT S_OK, or error value.
*/
STDMETHODIMP
FRM::Save(LPMESSAGE pmsg, ULONG fSameAsLoad)
{
SPropValue prop;
HRESULT hr = NOERROR;

TraceTag(tagFormFunc,"FRM::Save");

AssertSz(cRef > 0,"excuse me. are you refering to me?");
AssertSz(pMessage,"no pmesssg in ::Save");
#ifdef DEBUG
if (!pmsg)
{
TraceTag(tagNull,"NULL == pmsg in ::Save fsameasload==0x%08lx",fSameAsLoad);
}
#endif

// ----- If this is the same pmsg as we got back when we loaded ...
if (fSameAsLoad)
{
TraceTag(tagForm,"fSameAsLoad true");
pmsg = pMessage;
}

// ----- Put ourselves in no-scribble mode
fNoScribble = TRUE;

// ----- set our message class
prop.ulPropTag = PR_MESSAGE_CLASS;
prop.Value.LPSZ = TEXT("IPM.Checkers");
hr = pMessage->SetProps(1, &prop, NULL);
if (FAILED(hr) )
{
TraceTag(tagForm,"failed setprops in initnew 0x%08lx",hr);
return hr;
}

// ----- Write out our data
AssertSz(turn,"nobody's turn? this is not a good sign...");
prop.ulPropTag = PR_BOARD;
prop.Value.bin.lpb = (unsigned char *) b;
prop.Value.bin.cb = sizeof(b);
hr = pmsg->SetProps(1, &prop, NULL);
if (FAILED(hr) )
{
TraceTag(tagForm,"failed setprops in on pr_board here 0x%08lx",hr);
return hr;
}
prop.ulPropTag = PR_TURN;
prop.Value.l = (long) turn;
hr = pmsg->SetProps(1, &prop, NULL);
if (FAILED(hr) )
{
TraceTag(tagForm,"failed setprops in on pr_turn here 0x%08lx",hr);
return hr;
}
prop.ulPropTag = PR_MOVENUMBER;
prop.Value.l = (long) movenum;
hr = pmsg->SetProps(1, &prop, NULL);
if (FAILED(hr) )
{
TraceTag(tagForm,"failed setprops in on pr_turn here 0x%08lx",hr);
return hr;
}
prop.ulPropTag = PR_SCORINGFUNC;
prop.Value.l = (long) score;
hr = pmsg->SetProps(1, &prop, NULL);
if (FAILED(hr) )
{
TraceTag(tagForm,"failed setprops in on pr_turn here 0x%08lx",hr);
return hr;
}
prop.ulPropTag = PR_BODY;
prop.Value.lpszA = TextizeBoard(b);
TraceTag(tagForm,"Here's the board I saved:\n%s",prop.Value.lpszA);
hr = pmsg->SetProps(1, &prop, NULL);
if (FAILED(hr) )
{
TraceTag(tagForm,"failed setprops in on pr_body here 0x%08lx",hr);
return hr;
}


ADVISE(OnSaved)();
return hr;
}


/*
* FRM::SaveCompleted
*
* Purpose:
* Terminates no-scribble and hands-off modes, returning the object
* to its normal storage mode.
*
* Arguments:
* LPMESSAGE Our new home message, if we need to change.
*
* Returns:
* HRESULT S_OK, or error value.
*/
STDMETHODIMP
FRM::SaveCompleted(LPMESSAGE pmsg)
{
TraceTag(tagFormFunc,"FRM::SaveCompleted");
AssertSz(cRef > 0,"excuse me. are you refering to me?");

// Reset modes
fDirty = FALSE;
fNoScribble = FALSE;

return NOERROR;
}


/*
* FRM::HandsOffMessage
*
* Purpose:
* Releases our reference on the message so that a Save As operation
* can occur.
*
* Arguments:
*
* Returns:
* HRESULT S_OK, or error value.
*/
STDMETHODIMP
FRM::HandsOffMessage(void)
{
TraceTag(tagFormFunc,"FRM::HandsOffMessage");
AssertSz(cRef > 0,"excuse me. are you refering to me?");
return NOERROR;
}


// IMAPIForm methods follow /////////////////////////////

/*
* FRM::DoVerb
*
* Purpose:
* Performs the specified verb on the message.
*
* Arguments:
* LONG What to do.
* LPMAPIVIEWCONTEXT Our view context.
* HWND Our parent window.
* LPCRECT Where we should display ourselves given a choice.
*
* Returns:
* HRESULT S_OK, or error value.
*/
STDMETHODIMP
FRM::DoVerb(LONG iVerb, LPMAPIVIEWCONTEXT pmvc, ULONG ulhwndParent,
LPCRECT prcPosRect)
{
TraceTag(tagFormFunc,"FRM::DoVerb iVerb=%d",iVerb);
AssertSz(cRef > 0,"excuse me. are you refering to me?");

switch (iVerb)
{
default:
case OLEIVERB_HIDE:
case OLEIVERB_DISCARDUNDOSTATE:
TraceTag(tagForm,"DoVerb: not implemented iVerb");
return ResultFromScode(E_NOTIMPL);

case OLEIVERB_UIACTIVATE:
case OLEIVERB_INPLACEACTIVATE:
TraceTag(tagForm,"DoVerb: not implemented iVerb=%d",iVerb);
if (iVerb < 0)
return ResultFromScode(E_NOTIMPL);
return ResultFromScode(OLEOBJ_S_INVALIDVERB);

case OLEIVERB_SHOW:
ShowCurrentMessage(ulhwndParent);
return NOERROR;

case OLEIVERB_OPEN:
case OLEIVERB_PRIMARY:
TraceTag(tagForm,"fSentMessage = %d",(int) fSentMessage);
if (fSentMessage)
LaunchReplyMessage(ulhwndParent);
else
ShowCurrentMessage(ulhwndParent);

return NOERROR;
}
}


/*
* FRM::ShutdownForm
*
* Purpose:
* Closes down any UI associated with the form.
*
* Arguments:
* DWORD One of OLECLOSE_SAVEIFDIRTY, OLECLOSE_NOSAVE,
* or OLECLOSE_PROMPTSAVE.
*
* Returns:
* HRESULT S_OK, or error value.
*/
STDMETHODIMP
FRM::ShutdownForm(DWORD dwSaveOptions)
{
HRESULT hr = NOERROR;

TraceTag(tagFormFunc,"FRM::ShutdownForm dwSaveOptions=%d",dwSaveOptions);
TraceTag(tagForm,"pMessageSite 0x%08x",(ULONG) pMessageSite);
AssertSz(cRef > 0,"excuse me. are you refering to me?");

// ----- no way I'm closeing if I'm in a modal dialog
// especially if the modal dialog occurs in a remoted interface
if (!IsWindowEnabled(hMainWnd))
return ResultFromScode(E_ABORT);

// ----- be kind, and save ourself
switch (dwSaveOptions)
{
case OLECLOSE_NOSAVE:
break;

case OLECLOSE_SAVEIFDIRTY:
if (fDirty)
hr = pMessageSite->SaveMessage();
break;

default:
case OLECLOSE_PROMPTSAVE:
if (fDirty)
if (IDYES == MessageBox(hMainWnd, "Save changes?", "Checkers", MB_YESNO))
hr = pMessageSite->SaveMessage();
break;

}

Assert(hMainWnd && IsWindow(hMainWnd));
if (NOERROR == hr)
{
// ----- let everyone know we're shutting down
ADVISE(OnShutdown)();

// ----- Release everything we have remembered thus far
Forget();

// ----- make sure everyone has Unadvised
AssertSz(0==afAdvisee[0],"0 didn't Unadvise before ShutdownForm");
AssertSz(0==afAdvisee[1],"1 didn't Unadvise before ShutdownForm");
AssertSz(0==afAdvisee[2],"2 didn't Unadvise before ShutdownForm");
AssertSz(0==afAdvisee[3],"3 didn't Unadvise before ShutdownForm");

// ----- post a quit message to our UI
SendMessage(hMainWnd,WM_CLOSE,0,0);
}

return hr;
}

STDMETHODIMP
FRM::SetViewContext(LPMAPIVIEWCONTEXT pViewContextNew)
{
TraceTag(tagFormFunc,"FRM::SetViewContext");
AssertSz(cRef > 0,"excuse me. are you refering to me?");
AssertSz(pViewContextNew,"no view context to set");

/* View context is used for next and previous behavior
The checkers form does not do next and previous because
there is not a standard read note. It is always in
reply mode */

return NOERROR;
}

STDMETHODIMP
FRM::GetViewContext(LPMAPIVIEWCONTEXT FAR * ppViewContext)
{
TraceTag(tagFormFunc,"FRM::GetViewContext");
AssertSz(cRef > 0,"excuse me. are you refering to me?");
AssertSz(ppViewContext,"get view context to where?");

if (ppViewContext) *ppViewContext = NULL; /* not supported */

return NOERROR;
}

STDMETHODIMP
FRM::Advise(LPMAPIVIEWADVISESINK pAdvise, ULONG FAR * pdwStatus)
{
LONG i;

TraceTag(tagFormFunc,"FRM::Advise");

AssertSz(cRef > 0,"excuse me. are you refering to me?");
Assert(pdwStatus);
Assert(pAdvise);

// ----- remember who to advise
for (i=0; i<MAX_ADVISE; i++)
if (!afAdvisee[i])
{
aAdvisePtrs[i] = pAdvise;
afAdvisee[i] = 1;
*pdwStatus = i + 1; /* ulConnection of zero is not valid */
pAdvise->AddRef();
return NOERROR;
}

// ----- bad news
AssertSz(0,"out of aAdvisPtrs");
return ResultFromScode(E_FAIL);
return NOERROR;
}

STDMETHODIMP
FRM::Unadvise(ULONG ulConnection)
{
TraceTag(tagFormFunc,"FRM::Unadvise");

AssertSz(cRef > 0,"excuse me. are you refering to me?");
AssertSz(ulConnection < MAX_ADVISE && ulConnection >= 0,"testing, 123");
AssertSz(ulConnection,"a non-zero ulConnection is not valid according to OLE");

// ----- forget about advising this guy
--ulConnection; // remember, we added one in advise
AssertSz(afAdvisee[(int) ulConnection],"never wanted ::Advise in ::Unadvise?");
afAdvisee[(int) ulConnection] = 0;
aAdvisePtrs[(int) ulConnection]->Release();
return NOERROR;
}


/*
* FRM::FRM
*
* Purpose:
* Initialize or new form object
*
*/
FRM::FRM(REFCLSID clsid)
{
LONG i;

TraceTag(tagFormFunc,"FRM::FRM .................");

cRef = 1;
this->clsid = clsid;

pMessage = NULL;
pMessageSite = NULL;
pSession = NULL;

pFormMgr = NULL;
pFormInfo = NULL;

fDirty = FALSE;

for (i=0; i<MAX_ADVISE; i++)
{
aAdvisePtrs[i] = NULL;
afAdvisee[i] = 0;
}

turn = 0;
}

/*
* FRM::~FRM
*
* Purpose:
* Destroy our form object
*/
FRM::~FRM(void)
{
TraceTag(tagFormFunc,"FRM::~FRM Bye now ...");
AssertSz(0==cRef,"quit referring to this form please");
AssertSz(NULL == pMessage,"still refing the message");
AssertSz(NULL == pMessageSite,"still refing the messagesite");
AssertSz(NULL == pSession,"still refing the session");
}

/*
* S a m p l e F o r m C l a s s F a c t o r y
*
* Because we are an exe server, we must implement a class factory
* so that other viewers (like Exchange) can learn of our clsid
*
*/

/*
* FRMFAC::CreateInstance
*
* Purpose:
* Creates a new form object of the IPM.Form class.
*
* Arguments:
* LPUNKNOWN Outer object to aggregate with (not supported).
* REFIID Desired interface on new form object.
* LPVOID FAR * Where to put new form object.
*
* Returns:
* HRESULT S_OK, or one of the following errors:
* CLASS_E_NOAGGREGATION, E_OUTOFMEMORY,
* E_NOINTERFACE, E_INVALIDARG.
*/
STDMETHODIMP
FRMFAC::CreateInstance(LPUNKNOWN punkOuter, REFIID riid, LPVOID FAR * ppvObject)
{
FRM * pfrm = NULL;
HRESULT hr;

TraceTag(tagFormFunc,"FRMFAC::CreateInstance");

// ----- Initialize out parameter and check validity of parameters
if (!ppvObject)
{
hr = ResultFromScode(E_INVALIDARG);
goto Cleanup;
}
*ppvObject = NULL;

if (punkOuter)
{
hr = ResultFromScode(CLASS_E_NOAGGREGATION);
goto Cleanup;
}

// ----- Instantiate new form
if (!(pfrm = new FRM(clsid)))
{
hr = ResultFromScode(E_OUTOFMEMORY);
TraceTag(tagForm,"E_OUTOFMEMORY 0x%08lx",hr);
goto Cleanup;
}

// ----- Get the desired interface
hr = pfrm->QueryInterface(riid, ppvObject);
AssertSz(0==hr,"QueryInterface failed");

Cleanup:
ReleaseObj(pfrm);
TraceTag(tagForm,"return 0x%08lx initial reference %d",hr,cRef);
return hr;
}

/*
* FRMFAC::QueryInterface
*
* Purpose:
* Returns a pointer to the specified interface.
*
* Arguments:
* REFIID Interface we want.
* LPUNKNOWN * Interface we return.
*
* Returns:
* HRESULT Error status.
*/
STDMETHODIMP
FRMFAC::QueryInterface(REFIID riid, LPVOID FAR * ppvObj)
{
TraceTag(tagFuncTriv,"FRMFAC::QueryInterface %s",DumpCLSID(riid));
if (IsEqualIID(riid, IID_IUnknown) ||
IsEqualIID(riid, IID_IClassFactory))
{
*ppvObj = this;
AddRef();
TraceTag(tagForm,"return ok");
return NOERROR;
}

*ppvObj = NULL;
TraceTag(tagForm,"return no interface");
return ResultFromScode(E_NOINTERFACE);
}

/*
* FRMFAC::LockServer
*
* Purpose:
*
*
* Arguments:
* BOOL Whether to increment or decrement DLL reference count.
*
* Returns:
* HRESULT S_OK always.
*/
STDMETHODIMP
FRMFAC::LockServer(BOOL fLock)
{
TraceTag(tagFormFunc,"LockServer (not implemented)");
return NOERROR;
}

/*
* FRMFAC::AddRef
*
* Purpose:
* Increments reference count on the form class factory.
*
* Arguments:
*
* Returns:
* ULONG New value of reference count.
*/
STDMETHODIMP_(ULONG)
FRMFAC::AddRef(void)
{
TraceTag(tagFuncTriv,"FRMFAC::AddRef ret %d",cRef+1);
return ++cRef;
}


/*
* FRMFAC::Release
*
* Purpose:
* Decrements reference count on the form class factory.
* If count is decremented to zero, the object is freed.
*
* Arguments:
*
* Returns:
* ULONG New value of reference count.
*/
STDMETHODIMP_(ULONG)
FRMFAC::Release(void)
{
TraceTag(tagFuncTriv,"FRMFAC::Release cRef %d",cRef);
if (!(--cRef))
{
TraceTag(tagForm,"return 0");
delete this;
return 0;
}
return cRef;
}


FRMFMR::FRMFMR()
{
TraceTag(tagFuncTriv,"FRMFMR::FRMFMR");
clsid = CLSID_MyFormsClsId;
}

FRMFAC::FRMFAC()
{
TraceTag(tagFuncTriv,"FRMFAC::FRMFAC");
cRef = 1;
}

FRMFAC::~FRMFAC(void)
{
TraceTag(tagFuncTriv,"FRMFAC::~FRMFAC");
AssertSz(!cRef,"0817236");
}