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"); 
}