home *** CD-ROM | disk | FTP | other *** search
/ Power GUI Programming with VisualAge C++ / powergui.iso / trialva / ibmcppw / sdk / mapi / win16 / dev / chkrform / form.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-11  |  47.1 KB  |  1,719 lines

  1. /* --------------------------------------------------------------------------
  2.  
  3. Basic Forms example of a custom sendable form.  It is an EXE server
  4. rather than a DLL.  It implements the minimum form interface required
  5. to launch and send a form.
  6.  
  7. Copyright (C) 1995 Microsoft Corporation
  8.  
  9. -------------------------------------------------------------------------- */
  10.  
  11. //#define ALLOW_SUBCLASS_IPM // all other forms to subclass the reply behavior
  12.                              // of this form (slower, but more correct)
  13.  
  14. #include <windows.h>
  15. #include <windowsx.h>
  16. #include <ole2.h>
  17. #include <initguid.h>
  18. #include <mapiform.h>
  19. #define INITGUID
  20. #include <initguid.h>
  21. #include <mapix.h>
  22. #include <mapiutil.h>
  23. #include <mapinls.h>
  24. #include "dbugit.h"
  25. #include "check.h"
  26. #include "form.h"
  27. #include "dlg.h"
  28.  
  29. /*
  30.  *  
  31.  * Checkers form clsid.  This must match the configuration file.
  32.  *  
  33.  */
  34.  
  35. DEFINE_GUID(CLSID_MyFormsClsId,  0x86174010, 0x5030, 0x0076, 0x99, 0x12, 0x00, 0xaa, 0x00, 0x38, 0x90, 0x1b);
  36.  
  37. /*
  38.  *  HrStartOleAndRegisterClassFactory
  39.  *
  40.  *  Purpose:
  41.  *      Initialize OLE, MAPI, and the Forms Interface
  42.  *      Should be called from WinMain() or InitApplication() in an SDI app
  43.  *
  44.  *      This function LoadLibraries the neccessary DLLs rather than
  45.  *      linking with them.  This permits the form to run as a stand-
  46.  *      alone executable even when MAPI and OLE are not installed.
  47.  *
  48.  *  Returns:
  49.  *      HRESULT
  50.  */
  51.  
  52. #ifdef WIN32
  53. #define szOleDll  "ole32.dll"
  54. #define szMapiDll "mapi32.dll"
  55. #else
  56. #define szOleDll  "compobj.dll"
  57. #define szMapiDll "mapi.dll"
  58. #endif
  59.  
  60. HINSTANCE hinstOle   = NULL;
  61. HINSTANCE hinstMapi  = NULL;
  62.  
  63.  
  64. typedef HRESULT (FAR PASCAL *LPFNCOREGISTERCLASSOBJECT)(REFCLSID rclsid,
  65.     IUnknown FAR * pUnk, DWORD dwClsContext, DWORD flags, LPDWORD lpdwRegister);
  66. #ifdef WIN16
  67. typedef BOOL (FAR PASCAL *LPFNISEQUALGUID)(REFGUID rguid1, REFGUID rguid2);
  68. #undef IsEqualIID
  69. #define IsEqualIID(riid1, riid2) (*lpfnIsEqualGUID)(riid1, riid2)
  70. #endif
  71. typedef HRESULT (FAR PASCAL *LPFNHRQUERYALLROWS)(LPMAPITABLE ptable, 
  72.                         LPSPropTagArray ptaga, LPSRestriction pres,
  73.                         LPSSortOrderSet psos, LONG crowsMax,
  74.                         LPSRowSet FAR *pprows);
  75. typedef ULONG   (FAR PASCAL *LPFNMAPIFREEBUFFER)(LPVOID pv);
  76. typedef HRESULT (FAR PASCAL *LPFNMAPIINITIALIZE)(LPVOID lpvReserved);
  77. typedef void    (FAR PASCAL *LPFNMAPIUNINITIALIZE)(VOID);
  78.  
  79. LPFNCOREGISTERCLASSOBJECT lpfnCoRegisterClassObject;
  80. #ifdef WIN16
  81. LPFNISEQUALGUID           lpfnIsEqualGUID;
  82. #endif
  83. LPFNHRQUERYALLROWS        lpfnHrQueryAllRows       ;
  84. LPFNMAPIFREEBUFFER        lpfnMAPIFreeBuffer       ;
  85. LPFNMAPIINITIALIZE        lpfnMAPIInitialize       ;
  86. LPFNMAPIUNINITIALIZE      lpfnMAPIUninitialize     ;
  87.  
  88. HRESULT
  89. HrStartOleAndRegisterClassFactory(void)
  90. {
  91.     FRMFMR *    pfrmfmr = NULL;
  92.     HRESULT     hr;
  93.  
  94.     TraceTag(tagFormFunc,"HrStartOleAndRegisterClassFactory");
  95.  
  96.     // ----- LoadLibrary the essentials
  97.     hinstOle   = LoadLibrary(szOleDll);
  98.     hinstMapi  = LoadLibrary(szMapiDll);
  99.     #ifdef WIN16
  100.     if (hinstOle   < HINSTANCE_ERROR) hinstOle   = 0;
  101.     if (hinstMapi  < HINSTANCE_ERROR) hinstMapi  = 0;
  102.     #endif
  103.     if (0 == hinstOle || 0 == hinstMapi)
  104.         {
  105.         return ResultFromScode(E_FAIL);
  106.         }
  107.         
  108.  
  109.     // ----- Setup a few function pointers
  110.     lpfnCoRegisterClassObject = (LPFNCOREGISTERCLASSOBJECT) GetProcAddress(hinstOle,  "CoRegisterClassObject");
  111.     #ifdef WIN32
  112.     lpfnHrQueryAllRows        = (LPFNHRQUERYALLROWS       ) GetProcAddress(hinstMapi,"HrQueryAllRows@24");
  113.     #else
  114.     lpfnIsEqualGUID           = (LPFNISEQUALGUID          ) GetProcAddress(hinstOle,  "IsEqualGUID");
  115.     lpfnHrQueryAllRows        = (LPFNHRQUERYALLROWS       ) GetProcAddress(hinstMapi,"HrQueryAllRows");
  116.     #endif
  117.     lpfnMAPIFreeBuffer        = (LPFNMAPIFREEBUFFER       ) GetProcAddress(hinstMapi,"MAPIFreeBuffer");
  118.     lpfnMAPIInitialize        = (LPFNMAPIINITIALIZE       ) GetProcAddress(hinstMapi,"MAPIInitialize");
  119.     lpfnMAPIUninitialize      = (LPFNMAPIUNINITIALIZE     ) GetProcAddress(hinstMapi,"MAPIUninitialize");
  120.     AssertSz(lpfnCoRegisterClassObject ,"missing lpfnCoRegisterClassObject");
  121.     AssertSz(lpfnHrQueryAllRows        ,"missing lpfnHrQueryAllRows       ");
  122.     AssertSz(lpfnMAPIFreeBuffer        ,"missing lpfnMAPIFreeBuffer       ");
  123.     AssertSz(lpfnMAPIInitialize        ,"missing lpfnMAPIInitialize       ");
  124.     AssertSz(lpfnMAPIUninitialize      ,"missing lpfnMAPIUninitialize     ");
  125.     if (0 == lpfnCoRegisterClassObject ||
  126.         0 == lpfnHrQueryAllRows        ||
  127.         0 == lpfnMAPIFreeBuffer        ||
  128.         0 == lpfnMAPIInitialize        ||
  129.         0 == lpfnMAPIUninitialize      )
  130.         {
  131.         AssertSz(0,"get procaddress failed");
  132.         return ResultFromScode(E_FAIL);
  133.         }    
  134.  
  135.     // ----- Initialize MAPI
  136.     hr = (*lpfnMAPIInitialize)(NULL);
  137.     if (S_OK != hr)
  138.         {
  139.         TraceTag(tagForm,"MapiInit failed 0x%08lx",hr);
  140.         return hr;
  141.         }
  142.  
  143.     // ----- Allocate Memory for our class factory
  144.     pfrmfmr = new FRMFMR;
  145.     if (NULL == pfrmfmr)
  146.         {
  147.         TraceTag(tagForm, "RegisterClassFactory: OOM 0x%08lx",hr);
  148.         hr = ResultFromScode(E_OUTOFMEMORY);
  149.         return hr;
  150.         }
  151.  
  152.     // ----- Register our class object(s)
  153.     DWORD dwRegMyForm = 0;
  154.     hr = (*lpfnCoRegisterClassObject) (CLSID_MyFormsClsId, (LPUNKNOWN)pfrmfmr,
  155.             CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE,
  156.             &dwRegMyForm); /* switch singleuse to multipleuse if you are an MDI app */
  157.     if (FAILED(hr))
  158.         {
  159.         TraceTag(tagForm,"CoRegisterClassObject() failed 0x%08lx",hr);
  160.         return hr;
  161.         }
  162.  
  163.     TraceTag(tagForm,"return 0x%08lx",hr);
  164.     return hr;
  165. }
  166.  
  167. /*
  168.  *  HrStopForms
  169.  *
  170.  *  Purpose:
  171.  *      UnInitialize OLE, MAPI, and the Forms Interface
  172.  *
  173.  *  Returns:
  174.  *      HRESULT == 0
  175.  */
  176. HRESULT
  177. HrStopForms(void)
  178. {
  179.     HRESULT hr = ResultFromScode(S_OK);
  180.  
  181.     TraceTag(tagFormFunc,"HrStopForms");
  182.  
  183.     (*lpfnMAPIUninitialize)();
  184.  
  185.     FreeLibrary(hinstOle);
  186.     FreeLibrary(hinstMapi);
  187.  
  188.     return hr;
  189. }
  190.  
  191.  
  192. /*
  193.  *  S a m p l e   F o r m
  194.  */
  195.  
  196. // Checkers form specific methods follow ///////////////////////////
  197.  
  198. /*
  199.  *  FRM::ScGetRecipientAdrList
  200.  *
  201.  *  Purpose:
  202.  *      Fill the addrlist with the current recipients in the message
  203.  *
  204.  *  Arguments:
  205.  *      LPMESSAGE - the message (in)
  206.  *      LPADRLIST - the addr list destination (out)
  207.  *
  208.  *  Returns:
  209.  *      SCODE       Error status.
  210.  */
  211. SCODE
  212. FRM::ScGetRecipientAdrlist(LPMESSAGE pmsg, LPADRLIST * ppal)
  213. {
  214.     SCODE           sc      = S_OK;
  215.     HRESULT         hr;
  216.     LPMAPITABLE     pmt     = NULL;
  217.     LPSPropTagArray ptaga   = NULL;
  218.  
  219.     TraceTag(tagFormFunc,"ScGetRecipientAdrlist");
  220.     AssertSz(!*ppal, "pal should be NULL on entry");
  221.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  222.  
  223.     // Get the recipient table from the message
  224.     if (hr = pmsg->GetRecipientTable(MAPI_DEFERRED_ERRORS, &pmt))
  225.     {
  226.         TraceTag(tagForm,"2043 0x%08lx",hr);
  227.         goto Cleanup;
  228.     }
  229.  
  230.     if (hr = pmt->QueryColumns(TBL_ALL_COLUMNS, &ptaga))
  231.     {
  232.         TraceTag(tagForm,"sdlkfj 0x%08lx",hr);
  233.         goto Cleanup;
  234.     }
  235.  
  236. #ifdef NEVER
  237.     ConvertToCorrectCharset(ptaga);
  238. #endif
  239.  
  240.     // Read in the recipients
  241.     hr = (*lpfnHrQueryAllRows)(pmt, ptaga, NULL, NULL, 0, (LPSRowSet *) ppal);
  242.     if (hr)
  243.     {
  244.         TraceTag(tagForm,"sdfhjsadjfhadkflhxxxx 0x%08lx",hr);
  245.         goto Cleanup;
  246.     }
  247.  
  248. Cleanup:
  249.     TraceTag(tagForm,"ScGetRecipientAdrlist 0x%08lx 0x%08lx", hr, sc);
  250.  
  251.     (*lpfnMAPIFreeBuffer)(ptaga);
  252.     ReleaseObj(pmt);
  253.  
  254.     TraceTag(tagForm,"return 0x%08lx",ResultFromScode(sc));
  255.     return sc;
  256. }
  257.  
  258. /*
  259.  *  FRM::SendForm
  260.  *
  261.  *  Purpose:
  262.  *      Have the message site send us
  263.  *      (also tries to send the message using mapi if message site fails)
  264.  *
  265.  *  Arguments:
  266.  *      None.
  267.  *
  268.  *  Returns:
  269.  *      HRESULT             Error status.
  270.  */
  271. HRESULT
  272. FRM::SendForm(VOID)
  273. {
  274.     HRESULT hr = S_OK;
  275.  
  276.     TraceTag(tagFormFunc,"FRM::SendForm (this is not a standard form function)");
  277.  
  278.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  279.     Assert(pMessageSite);
  280.     Assert(pMessage);
  281.  
  282.     // ----- Submit message
  283.     if (hr = pMessageSite->SubmitMessage(0) )
  284.     {
  285.         TraceTag(tagForm,"failure pMessageSite->SubmitMessage 0x%08lx",hr);
  286.  
  287.         #ifndef GOOD_FORM_BEHAVIOR // the following is not standard behavior
  288.  
  289.         // ----- No harm in trying to send it myself
  290.         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))
  291.         {
  292.             // this is often times easier to debug than the remoted
  293.             // message site interface (which does the same thing .. almost)
  294.  
  295.             if (hr = Save(NULL,TRUE))
  296.                 {
  297.                 TraceTag(tagForm,"NO: ::Save 0x%08lx",hr);
  298.                 }
  299.  
  300.             HandsOffMessage();
  301.  
  302.             if (hr = pMessage->SaveChanges(KEEP_OPEN_READWRITE) )
  303.                 {
  304.                 TraceTag(tagForm,"NO: pMessage->SaveChanges 0x%08lx",hr);
  305.                 }
  306.             if (hr = pMessage->SubmitMessage(0) )
  307.                 {
  308.                 TraceTag(tagForm,"NO: pMessage->SubmitMessage 0x%08lx",hr);
  309.                 }
  310.         }
  311.  
  312.         #endif // good_form_behavior
  313.     }
  314.  
  315.     // ----- advise everyone of what we just did
  316.     ADVISE(OnSubmitted)();
  317.  
  318.     TraceTag(tagForm,"return 0x%08lx",hr);
  319.     return hr;
  320. }
  321.  
  322. /*
  323.  *  FRM::LaunchReplyMessage
  324.  *
  325.  *  Purpose:
  326.  *      Construct a reply to PR_SENDER* (note: ignoring sent representing)
  327.  *      Display any form user interface on the existing form
  328.  *
  329.  *  Arguments:
  330.  *      HWND                Parent window
  331.  *
  332.  *  Returns:
  333.  *      HRESULT             Error status.
  334.  */
  335. HRESULT
  336. FRM::LaunchReplyMessage(ULONG ulhwndParent)
  337. {
  338.     #ifdef ALLOW_SUBCLASS_IPM
  339.     LPMAPIFORM pNewForm;
  340.     LPPERSISTMESSAGE pNewFormIPersist;
  341.     #endif
  342.  
  343.     ULONG itaga;
  344.     ADRLIST al = {1,0}; /* our adrlist will have exactly one entry */
  345.     HRESULT hr = S_OK;
  346.     LPMAPIMESSAGESITE pNewMessageSite;
  347.     LPMAPIVIEWCONTEXT pNewMapiViewContext;
  348.     LPMESSAGE pNewMessage;
  349.  
  350.     SizedSPropTagArray(6,tagaSender) =
  351.         { 6,
  352.         { PR_RECIPIENT_TYPE,
  353.             PR_SENDER_NAME,
  354.             PR_SENDER_ADDRTYPE,
  355.             PR_SENDER_ENTRYID,
  356.             PR_SENDER_EMAIL_ADDRESS,
  357.             PR_SENDER_SEARCH_KEY } };
  358.     SizedSPropTagArray(6,tagaRepliee) =
  359.         { 6,
  360.         { PR_RECIPIENT_TYPE,
  361.             PR_DISPLAY_NAME,
  362.             PR_ADDRTYPE,
  363.             PR_ENTRYID,
  364.             PR_EMAIL_ADDRESS,
  365.             PR_SEARCH_KEY
  366.         } };
  367.     static SizedSPropTagArray(26,tagaRemoveFromNewReply) =
  368.         { 26,
  369.         {   // Stuff you would typically want to remove on reply
  370.             PR_MESSAGE_FLAGS,               // Want unsent compose note
  371.             PR_MESSAGE_RECIPIENTS,          // Will generate new recip list
  372.             PR_SENDER_ENTRYID,              // Clear sender/recipient info
  373.             PR_SENDER_NAME,                 //
  374.             PR_RECEIVED_BY_ENTRYID,         //
  375.             PR_RECEIVED_BY_NAME,            //
  376.             PR_SENT_REPRESENTING_ENTRYID,   // Clear delegate access stuff
  377.             PR_SENT_REPRESENTING_NAME,      //
  378.             PR_SENT_REPRESENTING_ADDRTYPE,  // 10961
  379.             PR_SENT_REPRESENTING_EMAIL_ADDRESS,
  380.             PR_RCVD_REPRESENTING_ENTRYID,   // 
  381.             PR_RCVD_REPRESENTING_NAME,      //
  382.             PR_READ_RECEIPT_ENTRYID,        // Clear destination overrides
  383.             PR_REPORT_ENTRYID,              //
  384.             PR_REPLY_RECIPIENT_ENTRIES,     //
  385.             PR_REPLY_RECIPIENT_NAMES,       //
  386.             PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED, // Clear delivery receipt
  387.             PR_READ_RECEIPT_REQUESTED,      // Clear read receipt
  388.             PR_CLIENT_SUBMIT_TIME,          // Clear submit time
  389.             PR_MESSAGE_ATTACHMENTS,         // Drop attachments on reply
  390.             PR_ORIGINAL_AUTHOR_ENTRYID,     // Keep original author information
  391.             PR_ORIGINAL_AUTHOR_NAME,        //  on forwards
  392.             PR_ORIGINAL_SUBMIT_TIME,        // Keep original time on forwards
  393.             PR_IMPORTANCE,                  // Lose importance on reply
  394.             PR_PRIORITY,                    // Lose priority on reply
  395.             PR_SENSITIVITY                  // Lose sensitivity on reply
  396.         } };
  397.  
  398.     TraceTag(tagFormFunc,"FRM::LaunchReplyMessage");
  399.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  400.     Assert(pMessageSite);
  401.     Assert(pSession);
  402.     Assert(pMessage);
  403.  
  404.     #ifdef ALLOW_SUBCLASS_IPM
  405.  
  406.     /*
  407.        Since I am a single instance exe, creating a new form results in
  408.        a new process.  For performance reasons, this form does not conform
  409.        to the forms API precisely. This effectively removes the ability to
  410.        subclass the reply note for IPM.Checkers.  This is acceptable.
  411.  
  412.     */
  413.  
  414.     // ----- open form manager
  415.     AssertSz(NULL == pFormMgr,"two form managers?");
  416.     hr = pMessageSite->GetFormManager(&pFormMgr);
  417.     if (FAILED(hr))
  418.         {
  419.         TraceTag(tagForm,"failure MAPIOpenFormMgr 0x%08lx",hr);
  420.         return hr;
  421.         }
  422.     Assert(pFormMgr);
  423.  
  424.     // ----- Get form info
  425.     hr = pFormMgr->ResolveMessageClass("IPM.Checkers",0,NULL,&pFormInfo);
  426.     if (FAILED(hr))
  427.         {
  428.         TraceTag(tagForm,"failure to ResolveMessageClass 0x%08lx",hr);
  429.         return hr;
  430.         }
  431.  
  432.     // ----- Create the new form
  433.     hr = pFormMgr->CreateForm(0,0,pFormInfo,IID_IMAPIForm,(LPVOID FAR*) &pNewForm);
  434.     if (FAILED(hr))
  435.         {
  436.         TraceTag(tagForm,"failure to CreateForm 0x%08lx",hr);
  437.         return hr;
  438.         }
  439.  
  440.     hr = pNewForm->QueryInterface(IID_IPersistMessage, (LPVOID FAR*) &pNewFormIPersist);
  441.     if (FAILED(hr))
  442.         {
  443.         TraceTag(tagForm,"failure asking form for IPersistMessage 0x%08lx",hr);
  444.         return hr;
  445.         }
  446.     AssertSz(pNewFormIPersist,"have it?");
  447.  
  448.     #endif // ALLOW_SUBCLASS_IPM
  449.  
  450.     // ----- Create the reply message
  451.     hr = pMessageSite->NewMessage(0,NULL,
  452.         #ifdef ALLOW_SUBCLASS_IPM
  453.         pNewFormIPersist
  454.         #else
  455.         this
  456.         #endif // ALLOW_SUBCLASS_IPM
  457.         ,&pNewMessage,&pNewMessageSite,&pNewMapiViewContext);
  458.     if (FAILED(hr))
  459.         {
  460.         TraceTag(tagForm,"failure VDOG_*NewMessage* comform...c 0x%08lx",hr);
  461.         return hr;
  462.         }
  463.     AssertSz(pNewMessage,"nothing new with you");
  464.     AssertSz(pNewMessageSite,"no new site?");
  465.     AssertSz(pNewMapiViewContext,"no new view context ... did NewMessage work at all?");
  466.  
  467.     // ----- Copy current message to new message
  468.     hr = pMessage->CopyTo(0, NULL, (LPSPropTagArray)&tagaRemoveFromNewReply, 0, NULL,
  469.                                     (LPIID) &IID_IMessage, pNewMessage, 0, NULL);
  470.     if (FAILED(hr))
  471.         {
  472.         TraceTag(tagForm,"failure CopyTo 0x%08lx",hr);
  473.         return hr;
  474.         }
  475.  
  476.     // ----- who sent this to us?
  477.     hr = pMessage->GetProps((LPSPropTagArray) &tagaSender, 0, &al.aEntries[0].cValues, &al.aEntries[0].rgPropVals);
  478.     AssertSz(ResultFromScode(MAPI_W_ERRORS_RETURNED) == hr,"mapi gave me pr_recipient_type, but should not have");
  479.  
  480.     // ----- Make the sender the recipient
  481.     if (al.aEntries && al.aEntries[0].rgPropVals)
  482.     {
  483.         al.aEntries[0].rgPropVals[0].ulPropTag = PR_RECIPIENT_TYPE;
  484.         al.aEntries[0].rgPropVals[0].Value.ul = MAPI_TO;
  485.     }
  486.     else
  487.     {
  488.         AssertSz(0,"could not form reply message: al.aEntries && al.aEntries[0].rgPropVals");
  489.         return ResultFromScode(E_FAIL);
  490.     }
  491.  
  492.     // ----- Set our new recipients properties to their expected property ids
  493.     itaga = 1;
  494.  
  495.     TraceTag(tagForm,"0x%08lx cEntries",al.cEntries);
  496.     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);
  497.  
  498.     for (itaga = 1; itaga < tagaRepliee.cValues; itaga++)
  499.         {
  500.         al.aEntries[0].rgPropVals[itaga].ulPropTag =
  501.             PROP_TAG(PROP_TYPE(al.aEntries[0].rgPropVals[itaga].ulPropTag),
  502.                 PROP_ID(tagaRepliee.aulPropTag[itaga]));
  503.  
  504.         TraceTag(tagForm,"0x%08lx <0x%08lx> %d",al.aEntries[0].rgPropVals[itaga].ulPropTag,al.aEntries[0].rgPropVals[itaga].Value.ul,itaga);
  505.  
  506.         AssertSz(SUCCEEDED(al.aEntries[0].rgPropVals[itaga].Value.ul),"Failure to get PR_SENDER* 10961 ");
  507.         }
  508.  
  509.     // ----- Save out addresses
  510.     AssertSz(1 == al.cEntries,"we only reply to one person");
  511.     hr = pNewMessage->ModifyRecipients(0, &al);
  512.     if (FAILED(hr) )
  513.         {
  514.         TraceTag(tagForm,"pMessage->ModifyRecipients 0x%08lx",hr);
  515.         return hr;
  516.         }
  517.  
  518.     // ----- Release everything the read form was remembering (if we reply inplace)
  519.     #ifndef ALLOW_SUBCLASS_IPM
  520.     Forget();
  521.     #endif
  522.  
  523.     // ----- Call LoadForm (this makes the current form the new form)
  524.     hr =
  525.     #ifdef ALLOW_SUBCLASS_IPM
  526.     pNewFormIPersist->
  527.     #endif
  528.     Load(pNewMessageSite,pNewMessage,0,MSGFLAG_UNSENT);
  529.     if (FAILED(hr))
  530.         {
  531.         TraceTag(tagForm,"failure LoadForm 0x%08lx",hr);
  532.         return hr;
  533.         }
  534.  
  535.     // ----- Call SetViewContext
  536.     #ifdef ALLOW_SUBCLASS_IPM
  537.     hr = pNewForm->SetViewContext(pNewMapiViewContext);
  538.     if (FAILED(hr))
  539.         {
  540.         TraceTag(tagForm,"failure SetViewContext 0x%08lx",hr);
  541.         return hr;
  542.         }
  543.     #endif
  544.  
  545.     // ----- Call DoVerb So we can see the reply form
  546.     hr = 
  547.     #ifdef ALLOW_SUBCLASS_IPM
  548.     pNewForm->
  549.     #endif
  550.     DoVerb(OLEIVERB_PRIMARY,NULL,ulhwndParent,NULL);
  551.     if (FAILED(hr))
  552.         {
  553.         TraceTag(tagForm,"failure DoVerb 0x%08lx",hr);
  554.         return hr;
  555.         }
  556.  
  557.     // ----- release stuff
  558.     pNewMessage->Release();
  559.     pNewMessageSite->Release();
  560.     pNewMapiViewContext->Release();
  561.     (*lpfnMAPIFreeBuffer)(al.aEntries[0].rgPropVals);
  562.  
  563.     // ----- Close down the read form (that's me)
  564.     #ifdef ALLOW_SUBCLASS_IPM
  565.     hr = ShutdownForm(OLECLOSE_NOSAVE);
  566.     if (FAILED(hr))
  567.         {
  568.         TraceTag(tagForm,"failure ShutdownForm'ing read form 0x%08lx",hr);
  569.         return hr;
  570.         }
  571.     #endif // ALLOW_SUBCLASS_IPM
  572.  
  573.     // ----- return to caller
  574.     return hr;
  575.  
  576. }
  577.  
  578. /*
  579.  *  FRM::GetCheckersData
  580.  *
  581.  *  Purpose:
  582.  *      Allows anyone to query the form for it's current data
  583.  *
  584.  *  Arguments:
  585.  *      data members (out)
  586.  *
  587.  *  Returns:
  588.  *      void
  589.  */
  590. VOID
  591. FRM::GetCheckersData(SQUARE* out_b, int* out_turn, long* out_movenum, long* out_score)
  592. {
  593.     Assert(out_b);
  594.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  595.  
  596.     if (turn) /* set elements only if we have data to give out */
  597.     {
  598.         memcpy(out_b,b,sizeof(b));
  599.         if (out_turn) *out_turn = turn;
  600.         if (out_movenum) *out_movenum = movenum;
  601.         if (out_score) *out_score = score;
  602.     }
  603. }
  604.  
  605. /*
  606.  *  FRM::SetCheckersData
  607.  *
  608.  *  Purpose:
  609.  *      Allows anyone to set the forms current data members
  610.  *
  611.  *  Arguments:
  612.  *      data members (in)
  613.  *
  614.  *  Returns:
  615.  *      void
  616.  */
  617. VOID
  618. FRM::SetCheckersData(SQUARE* in_b, int in_turn, long in_movenumber, long in_score)
  619. {
  620.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  621.     Assert(in_b && in_turn);
  622.  
  623.     fDirty = TRUE;
  624.  
  625.     memcpy(b,in_b,sizeof(b));
  626.     turn    = in_turn;
  627.     movenum = in_movenumber;
  628.     score   = in_score;
  629. }
  630.  
  631.  
  632. /*
  633.  *  FRM::AddressForm
  634.  *
  635.  *  Purpose:
  636.  *      Look at the current message addresses, and show user
  637.  *      interface to address the message.
  638.  *
  639.  *  Arguments:
  640.  *      HWND - parent
  641.  *      BOOL - true if no user interface should be presented when
  642.  *             recipients are already present
  643.  *
  644.  *  Returns:
  645.  *      HRESULT Error Status.
  646.  */
  647. HRESULT
  648. FRM::AddressForm(HWND hwnd, BOOL fDontShowIfRecipsExist)
  649. {
  650.     LPADRBOOK   pAdrBook;
  651.     ULONG       ulUIParam = (ULONG) (UINT) hwnd;
  652.     SCODE       sc;
  653.     HRESULT     hr = S_OK;
  654.     ADRPARM     ap = { 0 };
  655.     LPADRLIST   pal = NULL;
  656.     BOOL        fCloseForm = FALSE;
  657.  
  658.     TraceTag(tagFormFunc,"FRM::AddressForm");
  659.  
  660.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  661.     Assert(pMessageSite);
  662.     Assert(pSession);
  663.     Assert(pMessage);
  664.  
  665.     // ----- Read in addresses from message
  666.     sc = ScGetRecipientAdrlist(pMessage, &pal);
  667.     hr = ResultFromScode(sc);
  668.     if (FAILED(hr))
  669.         {
  670.         TraceTag(tagForm,"failed to read address into pal 0x%08lx",hr);
  671.         return hr;
  672.         }
  673.  
  674.     // ----- remember address book from the session
  675.     hr = pSession->OpenAddressBook(ulUIParam, NULL, 0, &pAdrBook);
  676.     if (FAILED(hr))
  677.         {
  678.         TraceTag(tagForm,"failed to get pAdrBook object 0x%08lx",hr);
  679.         goto cleanuppal;
  680.         }
  681.  
  682.     // ----- Show the address book
  683.     ap.ulFlags = AB_RESOLVE | DIALOG_OPTIONS | DIALOG_MODAL | fMapiUnicode;
  684.     ap.lpszCaption = TEXT("Select Checkers Opponent");
  685.     ap.cDestFields = 1;
  686.     if (0 == fDontShowIfRecipsExist || 0 == pal->cEntries)
  687.         hr = pAdrBook->Address(&ulUIParam, &ap, &pal);
  688.     #ifdef DEBUG
  689.     if (hwnd != hMainWnd)
  690.     {
  691.         TraceTag(tagNull,"ADDRESSFORM: pAdrBook->Address changed it's out parameter even though DIALOG_MODAL");
  692.     }
  693.     #endif
  694.     if (FAILED(hr))
  695.     {
  696.         // cancel is a failed scode
  697.         TraceTag(tagForm,"cant pop up addr book 0x%08lx %d",hr,pal?pal->cEntries:0);
  698.     }
  699.  
  700.     // ----- Save out addresses
  701.     AssertSz(pMessage,"pMessage said goodbye during the Address function");
  702.     hr = pMessage->ModifyRecipients(0, pal);
  703.     if (FAILED(hr) )
  704.         {
  705.         TraceTag(tagForm,"pMessage->ModifyRecipients 0x%08lx",hr);
  706.         goto cleanup;
  707.         }
  708.  
  709.     // ----- Close the form if there are no recipients for this move
  710.     if (pal->cEntries <= 0) fCloseForm = TRUE;
  711.  
  712.     // ----- Release the address book, adrlist, and clean up
  713. cleanup:
  714.     Assert(pAdrBook);
  715.     pAdrBook->Release();
  716. cleanuppal:
  717.     Assert(pal);
  718.     (*lpfnMAPIFreeBuffer)( (LPSRowSet) pal);
  719.     if (fCloseForm) ShutdownForm(OLECLOSE_PROMPTSAVE);
  720.  
  721.     TraceTag(tagForm,"return 0x%08lx",hr);
  722.     return hr;
  723. }
  724.  
  725. /*
  726.  *  FRM::Remember
  727.  *
  728.  *  Purpose:
  729.  *      Store and addref the message site, the message, and the session
  730.  *      for later use
  731.  *
  732.  *  Returns:
  733.  *      HRESULT Error Status.
  734.  */
  735. HRESULT
  736. FRM::Remember(LPMAPIMESSAGESITE pmsite, LPMESSAGE pmsg)
  737. {
  738.     HRESULT hr;
  739.  
  740.     TraceTag(tagFormFunc,"FRM::Remember");
  741.  
  742.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  743.     AssertSz(pmsite,"what am I going to do without it?");
  744.     AssertSz(pmsg,"what am I going to do without it? pmsg that is");
  745.  
  746.     // ----- remember our message site object
  747.     AssertSz(!pMessageSite,"who me? a message site?");
  748.     pMessageSite = pmsite;
  749.     pMessageSite->AddRef();
  750.  
  751.     // ----- remember our message
  752.     AssertSz(!pMessage,"a message in my");
  753.     pMessage = pmsg;
  754.     pMessage->AddRef();
  755.  
  756.     // ----- remember mapi session
  757.     AssertSz(!pSession,"another session?");
  758.     hr = pMessageSite->GetSession(&pSession);
  759.  
  760.     #ifdef DEBUG    
  761.     if (FAILED(hr))
  762.     {
  763.         TraceTag(tagForm,"failed to get session object %08lx",hr);
  764.     }    
  765.     #endif
  766.  
  767.     // ----- return result to caller
  768.     TraceTag(tagForm,"return 0x%08lx",hr);
  769.     return hr;
  770.  
  771. }
  772.  
  773. /*
  774.  *  FRM::Forget
  775.  *
  776.  *  Purpose:
  777.  *      Release the message site, the message, and the session
  778.  *
  779.  *  Returns:
  780.  *      HRESULT Error Status.
  781.  */
  782. HRESULT
  783. FRM::Forget(VOID)
  784. {
  785.     TraceTag(tagFormFunc,"FRM::Forget");
  786.  
  787.     if (pMessage) pMessage->Release();
  788.     if (pMessageSite) pMessageSite->Release();
  789.     if (pSession) pSession->Release();
  790.  
  791.     pMessage = NULL;
  792.     pMessageSite = NULL;
  793.     pSession = NULL;
  794.  
  795.     return NOERROR;
  796. }
  797.  
  798. /*
  799.  *  FRM::ShowCurrentMessage
  800.  *
  801.  *  Purpose:
  802.  *      Display any form user interface on a form
  803.  *
  804.  *  Arguments:
  805.  *      HWND                Parent window
  806.  *
  807.  *  Returns:
  808.  *      HRESULT             Error status.
  809.  */
  810. HRESULT
  811. FRM::ShowCurrentMessage(ULONG ulhwndParent)
  812. {
  813.     HRESULT hr = NOERROR;
  814.  
  815.     TraceTag(tagFormFunc,"FRM::ShowCurrentMessage");
  816.  
  817.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  818.     Assert(pMessageSite);
  819.     Assert(pSession);
  820.     Assert(pMessage);
  821.  
  822.     // ----- Give our user access to our form interface
  823.     SendMessage(hMainWnd,EM_GIVEFORMTOHWND,0,(LPARAM) this);
  824.  
  825.     // ----- Display address book modal to form if this message has not yet been addressed
  826.     FORWARD_WM_COMMAND(hMainWnd, IDM_ADDRESS, 0, 1 /* Don't Show Recips */, PostMessage);
  827.  
  828.     return hr;
  829. }
  830.  
  831. // IUnknown methods follow ///////////////////////////
  832.  
  833. /*
  834.  *  FRM::QueryInterface
  835.  *
  836.  *  Purpose:
  837.  *      Returns a pointer to the specified interface.
  838.  *
  839.  *  Arguments:
  840.  *      REFIID              Interface we want.
  841.  *      LPUNKNOWN *         Interface we return.
  842.  *
  843.  *  Returns:
  844.  *      HRESULT             Error status.
  845.  */
  846. STDMETHODIMP
  847. FRM::QueryInterface(REFIID riid, LPVOID FAR * ppvObj)
  848. {
  849.     HRESULT hr = NOERROR;
  850.  
  851.     TraceTag(tagFuncTriv,"FRM::QueryInterface %s",DumpCLSID(riid));
  852.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  853.  
  854.     if (IsEqualIID(riid, IID_IUnknown))
  855.         {
  856.         AddRef();
  857.         *ppvObj = (IMAPIForm *)this;
  858.         }
  859.     else if (IsEqualIID(riid, IID_IPersistMessage))
  860.         {
  861.         AddRef();
  862.         *ppvObj = (IPersistMessage *)this;
  863.         }
  864.     else if (IsEqualIID(riid, IID_IMAPIForm))
  865.         {
  866.         AddRef();
  867.         *ppvObj = (IMAPIForm *)this;
  868.         }
  869.     else
  870.         {
  871.         hr = ResultFromScode(E_NOINTERFACE);
  872.         *ppvObj = NULL;
  873.         }
  874.     
  875.     #ifdef DEBUG
  876.     if (hr != ResultFromScode(E_NOINTERFACE)) AssertSz(ppvObj,"no object pointer");
  877.     #endif
  878.  
  879.     TraceTag(tagForm,"return 0x%08lx",hr);
  880.     return hr;
  881. }
  882.  
  883.  
  884. /*
  885.  *  FRM::AddRef
  886.  *
  887.  *  Purpose:
  888.  *      Increments reference count on the sample extension.
  889.  *
  890.  *  Arguments:
  891.  *
  892.  *  Returns:
  893.  *      ULONG               New value of reference count.
  894.  */
  895. STDMETHODIMP_(ULONG)
  896. FRM::AddRef(void)
  897. {
  898.     TraceTag(tagFuncTriv,"FRM::AddRef ret %d",cRef + 1);
  899.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  900.     return ++cRef;
  901. }
  902.  
  903.  
  904. /*
  905.  *  FRM::Release
  906.  *
  907.  *  Purpose:
  908.  *      Decrements reference count on the sample extension.  If count is
  909.  *      decremented to zero, the object is freed.
  910.  *
  911.  *  Arguments:
  912.  *
  913.  *  Returns:
  914.  *      ULONG               New value of reference count.
  915.  */
  916. STDMETHODIMP_(ULONG)
  917. FRM::Release(void)
  918. {
  919.     TraceTag(tagFuncTriv,"FRM::Release cRef %d",cRef);
  920.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  921.  
  922.     if (!(--cRef))
  923.         {
  924.         // ----- be sure our ui is gone when we leave
  925.         #ifdef DEBUG
  926.         if (IsWindow(hMainWnd))
  927.             {
  928.             TraceTag(tagForm,"Last Release called, but IsWindow(hMainWnd).  ShutdownForm called?");
  929.             }
  930.         TraceTag(tagForm,"return 0");
  931.         #endif //debug
  932.         delete this;
  933.         return 0;
  934.         }
  935.     return cRef;
  936. }
  937.  
  938.  
  939. // IPersistMessage methods follow ///////////////////////////
  940.  
  941. /*
  942.  *  FRM::GetLastError
  943.  *
  944.  *  Purpose:
  945.  *      Get the last error
  946.  *
  947.  *  Arguments:
  948.  *
  949.  *  Returns:
  950.  *      HRESULT             NOERROR always.
  951.  */
  952. STDMETHODIMP
  953. FRM::GetLastError(HRESULT hResult, ULONG ulFlags, LPMAPIERROR FAR * lppMAPIError)
  954. {
  955.     TraceTag(tagFormFunc,"FRM::GetLastError 0x%08x",hResult);
  956.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  957.     if (lppMAPIError) *lppMAPIError = NULL;
  958.     return NOERROR;
  959. }
  960.  
  961. /*
  962.  *  FRM::GetClassID
  963.  *
  964.  *  Purpose:
  965.  *      Get the class ID associated with this message.
  966.  *
  967.  *  Arguments:
  968.  *      LPCLSID             Where to put the class ID.
  969.  *
  970.  *  Returns:
  971.  *      HRESULT             NOERROR always.
  972.  */
  973. STDMETHODIMP
  974. FRM::GetClassID(LPCLSID pclsid)
  975. {
  976.     
  977.     TraceTag(tagFormFunc,"FRM::GetClassID");
  978.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  979.  
  980.     // The form only plays with things of its own class ID, so
  981.     // this is easy; it's more complicated if code supports multiple
  982.     // classes, or can do "treat as" operations
  983.     if (pclsid) *pclsid = clsid;
  984.  
  985.     return NOERROR;
  986. }
  987.  
  988.  
  989. /*
  990.  *  FRM::IsDirty
  991.  *
  992.  *  Purpose:
  993.  *      Returns whether the object has changed since the last save
  994.  *
  995.  *  Arguments:
  996.  *
  997.  *  Returns:
  998.  *      HRESULT             S_OK if dirty, S_FALSE if not dirty.
  999.  */
  1000. STDMETHODIMP
  1001. FRM::IsDirty(void)
  1002. {
  1003.     TraceTag(tagFormFunc,"FRM::IsDirty");
  1004.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  1005.     
  1006.     return fDirty ? NOERROR : ResultFromScode(S_FALSE);
  1007. }
  1008.  
  1009.  
  1010. /*
  1011.  *  FRM::InitNew
  1012.  *
  1013.  *  Purpose:
  1014.  *      Create a new message of our message class in the provided pmsg.
  1015.  *
  1016.  *  Arguments:
  1017.  *      LPMAPISESSION       Session in which the message belongs.
  1018.  *      LPMESSAGE           Message to create the new form in.
  1019.  *
  1020.  *  Returns:
  1021.  *      HRESULT             S_OK, or error value.
  1022.  */
  1023. STDMETHODIMP
  1024. FRM::InitNew(LPMAPIMESSAGESITE pmsite, LPMESSAGE pmsg)
  1025. {
  1026.     HRESULT             hr;
  1027.     SPropValue          prop;
  1028.  
  1029.     TraceTag(tagFormFunc,"FRM::InitNew");
  1030.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  1031.  
  1032.     // ----- Remember our pointers and such
  1033.     hr = Remember(pmsite,pmsg);
  1034.     if (FAILED(hr))
  1035.     {
  1036.         TraceTag(tagForm,"loss of memory in initnew 0x%08lx",hr);
  1037.         return hr;
  1038.     }
  1039.  
  1040.     // ----- set our message class
  1041.     prop.ulPropTag  = PR_MESSAGE_CLASS;
  1042.     prop.Value.LPSZ = TEXT("IPM.Checkers");
  1043.     hr = pMessage->SetProps(1, &prop, NULL);
  1044.     if (FAILED(hr) )
  1045.         {
  1046.         TraceTag(tagForm,"failed setprops in initnew 0x%08lx",hr);
  1047.         return hr;
  1048.         }
  1049.  
  1050.     // ----- remind ourselves that this new message could not have been sent
  1051.     fSentMessage = 0;
  1052.  
  1053.     // ----- set our special properties
  1054.     prop.ulPropTag  = PR_SUBJECT;
  1055.     prop.Value.LPSZ = TEXT("--- CHECKERS FORM ---");
  1056.     hr = pMessage->SetProps(1, &prop, NULL);
  1057.     if (FAILED(hr) )
  1058.         {
  1059.         TraceTag(tagForm,"failed setprops in on pr_subject here 0x%08lx",hr);
  1060.         return hr;
  1061.         }
  1062.  
  1063.     ADVISE(OnNewMessage)();
  1064.     return hr;
  1065. }
  1066.  
  1067.  
  1068. /*
  1069.  *  FRM::Load
  1070.  *
  1071.  *  Purpose:
  1072.  *      Attaches our object to the provided pmsg.
  1073.  *
  1074.  *  Arguments:
  1075.  *      LPMAPISESSION       Our session to remember.
  1076.  *      LPMESSAGE           Our message to remember.
  1077.  *
  1078.  *  Returns:
  1079.  *      HRESULT             S_OK, or error value.
  1080.  */
  1081. STDMETHODIMP
  1082. FRM::Load(LPMAPIMESSAGESITE pmsite, LPMESSAGE pmsg, ULONG ulMessageStatus, ULONG ulMessageFlags)
  1083. {
  1084.     ULONG cProps = 0;
  1085.     #define ctagMax 10
  1086.     char rgchTaga[sizeof(SPropTagArray) + (ctagMax * sizeof(ULONG))];
  1087.     LPSPropTagArray ptaga = (LPSPropTagArray) rgchTaga;
  1088.     LPSPropValue rgval = NULL;
  1089.     LPSPropValue pval = NULL;
  1090.     HRESULT         hr=S_OK;
  1091.  
  1092.     TraceTag(tagFormFunc,"FRM::Load status=0x%08lx flags=0x%08lx",ulMessageStatus,ulMessageFlags);
  1093.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  1094.  
  1095.     // ----- Remember our pointers and such
  1096.     hr = Remember(pmsite,pmsg);
  1097.     if (FAILED(hr))
  1098.         {
  1099.         TraceTag(tagForm,"loads call to remember failed 0x%08lx",hr);
  1100.         return hr;
  1101.         }
  1102.  
  1103.     // ----- If this message has been sent we would like to remember that
  1104.     fSentMessage = !( ulMessageFlags & MSGFLAG_UNSENT);
  1105.     TraceTag(tagForm,"fSentMessage = %d",(int) fSentMessage);
  1106.  
  1107.     // ----- Load our data out of the message like a nice form
  1108.     ptaga->cValues = 0;
  1109.     ptaga->aulPropTag[ptaga->cValues++] = PR_SUBJECT;
  1110.     ptaga->aulPropTag[ptaga->cValues++] = PR_BODY;
  1111.     ptaga->aulPropTag[ptaga->cValues++] = PR_BOARD;
  1112.     ptaga->aulPropTag[ptaga->cValues++] = PR_TURN;
  1113.     ptaga->aulPropTag[ptaga->cValues++] = PR_MOVENUMBER;
  1114.  
  1115.     hr = pmsg->GetProps(ptaga,0, &cProps, &rgval);
  1116.     if (FAILED(hr) )
  1117.         {
  1118.         TraceTag(tagForm,"failed getprops on pr_board there 0x%08lx",hr);
  1119.         return hr;
  1120.         }
  1121.     AssertSz(ptaga->cValues <= ctagMax, "Too many properties to read!");
  1122.     AssertSz(cProps == ptaga->cValues,"to mucho values");
  1123.     pval = rgval;
  1124.  
  1125.     // ----- set properties to variables
  1126.     pval;   // subject
  1127.     pval++; // body
  1128.     pval++; // board
  1129.     if (pval->Value.bin.cb == sizeof(b)) /* if it's a valid board */
  1130.         {
  1131.         memcpy(b,pval->Value.bin.lpb,(int) pval->Value.bin.cb);
  1132.         pval++; // turn
  1133.         AssertSz(pval->Value.l == RED || pval->Value.l == BLACK,"cool: neither red or blacks turn according to mapi");
  1134.         turn = (int) pval->Value.l;
  1135.         pval++; // movenumber
  1136.         }
  1137.     Assert(rgval);
  1138.     (*lpfnMAPIFreeBuffer)(rgval);
  1139.     ADVISE(OnNewMessage)();
  1140.     return hr;
  1141. }
  1142.  
  1143.  
  1144. /*
  1145.  *  FRM::Save
  1146.  *
  1147.  *  Purpose:
  1148.  *      Writes out our information to the provided pmsg. Does NOT commit
  1149.  *      changes; this is the responsibility of the caller. Puts the form
  1150.  *      into no-scribble mode until SaveCompleted is called.
  1151.  *
  1152.  *  Arguments:
  1153.  *      LPMESSAGE           Message to write our changes to.
  1154.  *      BOOL                TRUE if this is our home message, FALSE if
  1155.  *                          this is a different message.
  1156.  *
  1157.  *  Returns:
  1158.  *      HRESULT             S_OK, or error value.
  1159.  */
  1160. STDMETHODIMP
  1161. FRM::Save(LPMESSAGE pmsg, ULONG fSameAsLoad)
  1162. {
  1163.     SPropValue prop;
  1164.     HRESULT hr = NOERROR;
  1165.  
  1166.     TraceTag(tagFormFunc,"FRM::Save");
  1167.  
  1168.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  1169.     AssertSz(pMessage,"no pmesssg in ::Save");
  1170.     #ifdef DEBUG
  1171.     if (!pmsg)
  1172.     {
  1173.         TraceTag(tagNull,"NULL == pmsg in ::Save fsameasload==0x%08lx",fSameAsLoad);
  1174.     }
  1175.     #endif
  1176.  
  1177.     // ----- If this is the same pmsg as we got back when we loaded ...
  1178.     if (fSameAsLoad)
  1179.     {
  1180.         TraceTag(tagForm,"fSameAsLoad true");
  1181.         pmsg = pMessage;
  1182.     }
  1183.  
  1184.     // ----- Put ourselves in no-scribble mode
  1185.     fNoScribble = TRUE;
  1186.  
  1187.     // ----- set our message class
  1188.     prop.ulPropTag  = PR_MESSAGE_CLASS;
  1189.     prop.Value.LPSZ = TEXT("IPM.Checkers");
  1190.     hr = pMessage->SetProps(1, &prop, NULL);
  1191.     if (FAILED(hr) )
  1192.         {
  1193.         TraceTag(tagForm,"failed setprops in initnew 0x%08lx",hr);
  1194.         return hr;
  1195.         }
  1196.  
  1197.     // ----- Write out our data
  1198.     AssertSz(turn,"nobody's turn? this is not a good sign...");
  1199.     prop.ulPropTag  = PR_BOARD;
  1200.     prop.Value.bin.lpb = (unsigned char *) b;
  1201.     prop.Value.bin.cb  = sizeof(b);
  1202.     hr = pmsg->SetProps(1, &prop, NULL);
  1203.     if (FAILED(hr) )
  1204.         {
  1205.         TraceTag(tagForm,"failed setprops in on pr_board here 0x%08lx",hr);
  1206.         return hr;
  1207.         }
  1208.     prop.ulPropTag  = PR_TURN;
  1209.     prop.Value.l = (long) turn;
  1210.     hr = pmsg->SetProps(1, &prop, NULL);
  1211.     if (FAILED(hr) )
  1212.         {
  1213.         TraceTag(tagForm,"failed setprops in on pr_turn here 0x%08lx",hr);
  1214.         return hr;
  1215.         }
  1216.     prop.ulPropTag  = PR_MOVENUMBER;
  1217.     prop.Value.l = (long) movenum;
  1218.     hr = pmsg->SetProps(1, &prop, NULL);
  1219.     if (FAILED(hr) )
  1220.         {
  1221.         TraceTag(tagForm,"failed setprops in on pr_turn here 0x%08lx",hr);
  1222.         return hr;
  1223.         }
  1224.     prop.ulPropTag  = PR_SCORINGFUNC;
  1225.     prop.Value.l = (long) score;
  1226.     hr = pmsg->SetProps(1, &prop, NULL);
  1227.     if (FAILED(hr) )
  1228.         {
  1229.         TraceTag(tagForm,"failed setprops in on pr_turn here 0x%08lx",hr);
  1230.         return hr;
  1231.         }
  1232.     prop.ulPropTag  = PR_BODY;
  1233.     prop.Value.lpszA = TextizeBoard(b);
  1234.     TraceTag(tagForm,"Here's the board I saved:\n%s",prop.Value.lpszA);
  1235.     hr = pmsg->SetProps(1, &prop, NULL);
  1236.     if (FAILED(hr) )
  1237.         {
  1238.         TraceTag(tagForm,"failed setprops in on pr_body here 0x%08lx",hr);
  1239.         return hr;
  1240.         }
  1241.  
  1242.  
  1243.     ADVISE(OnSaved)();
  1244.     return hr;
  1245. }
  1246.  
  1247.  
  1248. /*
  1249.  *  FRM::SaveCompleted
  1250.  *
  1251.  *  Purpose:
  1252.  *      Terminates no-scribble and hands-off modes, returning the object
  1253.  *      to its normal storage mode.
  1254.  *
  1255.  *  Arguments:
  1256.  *      LPMESSAGE           Our new home message, if we need to change.
  1257.  *
  1258.  *  Returns:
  1259.  *      HRESULT             S_OK, or error value.
  1260.  */
  1261. STDMETHODIMP
  1262. FRM::SaveCompleted(LPMESSAGE pmsg)
  1263. {
  1264.     TraceTag(tagFormFunc,"FRM::SaveCompleted");
  1265.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  1266.  
  1267.     // Reset modes
  1268.     fDirty = FALSE;
  1269.     fNoScribble = FALSE;
  1270.  
  1271.     return NOERROR;
  1272. }
  1273.  
  1274.  
  1275. /*
  1276.  *  FRM::HandsOffMessage
  1277.  *
  1278.  *  Purpose:
  1279.  *      Releases our reference on the message so that a Save As operation
  1280.  *      can occur.
  1281.  *
  1282.  *  Arguments:
  1283.  *
  1284.  *  Returns:
  1285.  *      HRESULT             S_OK, or error value.
  1286.  */
  1287. STDMETHODIMP
  1288. FRM::HandsOffMessage(void)
  1289. {
  1290.     TraceTag(tagFormFunc,"FRM::HandsOffMessage");
  1291.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  1292.     return NOERROR;
  1293. }
  1294.  
  1295.  
  1296. // IMAPIForm methods follow /////////////////////////////
  1297.  
  1298. /*
  1299.  *  FRM::DoVerb
  1300.  *
  1301.  *  Purpose:
  1302.  *      Performs the specified verb on the message.
  1303.  *
  1304.  *  Arguments:
  1305.  *      LONG                What to do.
  1306.  *      LPMAPIVIEWCONTEXT   Our view context.
  1307.  *      HWND                Our parent window.
  1308.  *      LPCRECT             Where we should display ourselves given a choice.
  1309.  *
  1310.  *  Returns:
  1311.  *      HRESULT             S_OK, or error value.
  1312.  */
  1313. STDMETHODIMP
  1314. FRM::DoVerb(LONG iVerb, LPMAPIVIEWCONTEXT pmvc, ULONG ulhwndParent,
  1315.                LPCRECT prcPosRect)
  1316. {
  1317.     TraceTag(tagFormFunc,"FRM::DoVerb iVerb=%d",iVerb);
  1318.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  1319.  
  1320.     switch (iVerb)
  1321.         {
  1322.         default:
  1323.         case OLEIVERB_HIDE:
  1324.         case OLEIVERB_DISCARDUNDOSTATE:
  1325.             TraceTag(tagForm,"DoVerb: not implemented iVerb");
  1326.             return ResultFromScode(E_NOTIMPL);
  1327.  
  1328.         case OLEIVERB_UIACTIVATE:
  1329.         case OLEIVERB_INPLACEACTIVATE:
  1330.             TraceTag(tagForm,"DoVerb: not implemented iVerb=%d",iVerb);
  1331.             if (iVerb < 0)
  1332.                 return ResultFromScode(E_NOTIMPL);
  1333.             return ResultFromScode(OLEOBJ_S_INVALIDVERB);
  1334.  
  1335.         case OLEIVERB_SHOW:
  1336.             ShowCurrentMessage(ulhwndParent);
  1337.             return NOERROR;
  1338.  
  1339.         case OLEIVERB_OPEN:
  1340.         case OLEIVERB_PRIMARY:
  1341.             TraceTag(tagForm,"fSentMessage = %d",(int) fSentMessage);
  1342.             if (fSentMessage)
  1343.                 LaunchReplyMessage(ulhwndParent);
  1344.             else
  1345.                 ShowCurrentMessage(ulhwndParent);
  1346.  
  1347.             return NOERROR;
  1348.         }
  1349. }
  1350.  
  1351.  
  1352. /*
  1353.  *  FRM::ShutdownForm
  1354.  *
  1355.  *  Purpose:
  1356.  *      Closes down any UI associated with the form.
  1357.  *
  1358.  *  Arguments:
  1359.  *      DWORD               One of OLECLOSE_SAVEIFDIRTY, OLECLOSE_NOSAVE,
  1360.  *                          or OLECLOSE_PROMPTSAVE.
  1361.  *
  1362.  *  Returns:
  1363.  *      HRESULT             S_OK, or error value.
  1364.  */
  1365. STDMETHODIMP
  1366. FRM::ShutdownForm(DWORD dwSaveOptions)
  1367. {
  1368.     HRESULT hr = NOERROR;
  1369.  
  1370.     TraceTag(tagFormFunc,"FRM::ShutdownForm dwSaveOptions=%d",dwSaveOptions);
  1371.     TraceTag(tagForm,"pMessageSite 0x%08x",(ULONG) pMessageSite);
  1372.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  1373.  
  1374.     // ----- no way I'm closeing if I'm in a modal dialog
  1375.     //       especially if the modal dialog occurs in a remoted interface
  1376.     if (!IsWindowEnabled(hMainWnd))
  1377.         return ResultFromScode(E_ABORT);
  1378.  
  1379.     // ----- be kind, and save ourself  
  1380.     switch (dwSaveOptions)
  1381.     {
  1382.         case OLECLOSE_NOSAVE:
  1383.             break;
  1384.  
  1385.         case OLECLOSE_SAVEIFDIRTY:
  1386.             if (fDirty)
  1387.                 hr = pMessageSite->SaveMessage();
  1388.             break;
  1389.  
  1390.         default:
  1391.         case OLECLOSE_PROMPTSAVE:
  1392.             if (fDirty)
  1393.                 if (IDYES == MessageBox(hMainWnd, "Save changes?", "Checkers", MB_YESNO))
  1394.                     hr = pMessageSite->SaveMessage();
  1395.             break;
  1396.  
  1397.     }
  1398.  
  1399.     Assert(hMainWnd && IsWindow(hMainWnd));
  1400.     if (NOERROR == hr)
  1401.     {
  1402.         // ----- let everyone know we're shutting down
  1403.         ADVISE(OnShutdown)();
  1404.  
  1405.         // ----- Release everything we have remembered thus far
  1406.         Forget();
  1407.  
  1408.         // ----- make sure everyone has Unadvised
  1409.         AssertSz(0==afAdvisee[0],"0 didn't Unadvise before ShutdownForm");
  1410.         AssertSz(0==afAdvisee[1],"1 didn't Unadvise before ShutdownForm");
  1411.         AssertSz(0==afAdvisee[2],"2 didn't Unadvise before ShutdownForm");
  1412.         AssertSz(0==afAdvisee[3],"3 didn't Unadvise before ShutdownForm");
  1413.  
  1414.         // ----- post a quit message to our UI
  1415.         SendMessage(hMainWnd,WM_CLOSE,0,0);
  1416.     }
  1417.  
  1418.     return hr;
  1419. }
  1420.  
  1421. STDMETHODIMP
  1422. FRM::SetViewContext(LPMAPIVIEWCONTEXT pViewContextNew)
  1423. {
  1424.     TraceTag(tagFormFunc,"FRM::SetViewContext");
  1425.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  1426.     AssertSz(pViewContextNew,"no view context to set");
  1427.  
  1428.     /* View context is used for next and previous behavior
  1429.        The checkers form does not do next and previous because
  1430.        there is not a standard read note.  It is always in 
  1431.        reply mode */
  1432.  
  1433.     return NOERROR;
  1434. }
  1435.  
  1436. STDMETHODIMP
  1437. FRM::GetViewContext(LPMAPIVIEWCONTEXT FAR * ppViewContext)
  1438. {
  1439.     TraceTag(tagFormFunc,"FRM::GetViewContext");
  1440.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  1441.     AssertSz(ppViewContext,"get view context to where?");
  1442.  
  1443.     if (ppViewContext) *ppViewContext = NULL; /* not supported */
  1444.  
  1445.     return NOERROR;
  1446. }
  1447.  
  1448. STDMETHODIMP
  1449. FRM::Advise(LPMAPIVIEWADVISESINK pAdvise, ULONG FAR * pdwStatus)
  1450. {
  1451.     LONG i;
  1452.  
  1453.     TraceTag(tagFormFunc,"FRM::Advise");
  1454.  
  1455.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  1456.     Assert(pdwStatus);
  1457.     Assert(pAdvise);
  1458.  
  1459.     // ----- remember who to advise    
  1460.     for (i=0; i<MAX_ADVISE; i++)
  1461.         if (!afAdvisee[i])
  1462.         {
  1463.             aAdvisePtrs[i] = pAdvise;
  1464.             afAdvisee[i] = 1;
  1465.             *pdwStatus = i + 1; /* ulConnection of zero is not valid */
  1466.             pAdvise->AddRef();
  1467.             return NOERROR;
  1468.         }
  1469.  
  1470.     // ----- bad news
  1471.     AssertSz(0,"out of aAdvisPtrs");
  1472.     return ResultFromScode(E_FAIL);
  1473.     return NOERROR;
  1474. }
  1475.  
  1476. STDMETHODIMP
  1477. FRM::Unadvise(ULONG ulConnection)
  1478. {
  1479.     TraceTag(tagFormFunc,"FRM::Unadvise");
  1480.  
  1481.     AssertSz(cRef > 0,"excuse me.  are you refering to me?");
  1482.     AssertSz(ulConnection < MAX_ADVISE && ulConnection >= 0,"testing, 123");
  1483.     AssertSz(ulConnection,"a non-zero ulConnection is not valid according to OLE");
  1484.  
  1485.     // ----- forget about advising this guy
  1486.     --ulConnection; // remember, we added one in advise
  1487.     AssertSz(afAdvisee[(int) ulConnection],"never wanted ::Advise in ::Unadvise?");
  1488.     afAdvisee[(int) ulConnection] = 0;
  1489.     aAdvisePtrs[(int) ulConnection]->Release();
  1490.     return NOERROR;
  1491. }
  1492.  
  1493.  
  1494. /*
  1495.  *  FRM::FRM
  1496.  *
  1497.  *  Purpose:
  1498.  *      Initialize or new form object
  1499.  *
  1500.  */
  1501. FRM::FRM(REFCLSID clsid)
  1502. {
  1503.     LONG i;
  1504.  
  1505.     TraceTag(tagFormFunc,"FRM::FRM .................");
  1506.  
  1507.     cRef = 1;
  1508.     this->clsid = clsid;
  1509.  
  1510.     pMessage = NULL;
  1511.     pMessageSite = NULL;
  1512.     pSession = NULL;
  1513.  
  1514.     pFormMgr = NULL;
  1515.     pFormInfo = NULL;
  1516.  
  1517.     fDirty = FALSE;
  1518.  
  1519.     for (i=0; i<MAX_ADVISE; i++)
  1520.         {
  1521.             aAdvisePtrs[i] = NULL;
  1522.             afAdvisee[i] = 0;
  1523.         }
  1524.  
  1525.     turn = 0;
  1526. }
  1527.  
  1528. /*
  1529.  *  FRM::~FRM
  1530.  *
  1531.  *  Purpose:
  1532.  *      Destroy our form object
  1533.  */
  1534. FRM::~FRM(void)
  1535. {
  1536.     TraceTag(tagFormFunc,"FRM::~FRM Bye now ...");
  1537.     AssertSz(0==cRef,"quit referring to this form please");
  1538.     AssertSz(NULL == pMessage,"still refing the message");
  1539.     AssertSz(NULL == pMessageSite,"still refing the messagesite");
  1540.     AssertSz(NULL == pSession,"still refing the session");
  1541. }
  1542.  
  1543. /*
  1544.  *  S a m p l e   F o r m   C l a s s   F a c t o r y
  1545.  *
  1546.  *  Because we are an exe server, we must implement a class factory
  1547.  *  so that other viewers (like Exchange) can learn of our clsid
  1548.  *
  1549.  */
  1550.  
  1551. /*
  1552.  *  FRMFAC::CreateInstance
  1553.  *
  1554.  *  Purpose:
  1555.  *      Creates a new form object of the IPM.Form class.
  1556.  *
  1557.  *  Arguments:
  1558.  *      LPUNKNOWN       Outer object to aggregate with (not supported).
  1559.  *      REFIID          Desired interface on new form object.
  1560.  *      LPVOID FAR *    Where to put new form object.
  1561.  *
  1562.  *  Returns:
  1563.  *      HRESULT         S_OK, or one of the following errors:
  1564.  *                      CLASS_E_NOAGGREGATION, E_OUTOFMEMORY,
  1565.  *                      E_NOINTERFACE, E_INVALIDARG.
  1566.  */
  1567. STDMETHODIMP
  1568. FRMFAC::CreateInstance(LPUNKNOWN punkOuter, REFIID riid, LPVOID FAR * ppvObject)
  1569. {
  1570.     FRM *   pfrm = NULL;
  1571.     HRESULT hr;
  1572.  
  1573.     TraceTag(tagFormFunc,"FRMFAC::CreateInstance");
  1574.  
  1575.     // ----- Initialize out parameter and check validity of parameters
  1576.     if (!ppvObject)
  1577.         {
  1578.         hr = ResultFromScode(E_INVALIDARG);
  1579.         goto Cleanup;
  1580.         }
  1581.     *ppvObject = NULL;
  1582.  
  1583.     if (punkOuter)
  1584.         {
  1585.         hr = ResultFromScode(CLASS_E_NOAGGREGATION);
  1586.         goto Cleanup;
  1587.         }
  1588.  
  1589.     // ----- Instantiate new form
  1590.     if (!(pfrm = new FRM(clsid)))
  1591.         {
  1592.         TraceTag(tagForm,"E_OUTOFMEMORY 0x%08lx",hr);
  1593.         hr = ResultFromScode(E_OUTOFMEMORY);
  1594.         goto Cleanup;
  1595.         }
  1596.  
  1597.     // ----- Get the desired interface
  1598.     hr = pfrm->QueryInterface(riid, ppvObject);
  1599.     AssertSz(0==hr,"QueryInterface failed");
  1600.  
  1601. Cleanup:
  1602.     ReleaseObj(pfrm);
  1603.     TraceTag(tagForm,"return 0x%08lx initial reference %d",hr,cRef);
  1604.     return hr;
  1605. }
  1606.  
  1607. /*
  1608.  *  FRMFAC::QueryInterface
  1609.  *
  1610.  *  Purpose:
  1611.  *      Returns a pointer to the specified interface.
  1612.  *
  1613.  *  Arguments:
  1614.  *      REFIID          Interface we want.
  1615.  *      LPUNKNOWN *     Interface we return.
  1616.  *
  1617.  *  Returns:
  1618.  *      HRESULT         Error status.
  1619.  */
  1620. STDMETHODIMP
  1621. FRMFAC::QueryInterface(REFIID riid, LPVOID FAR * ppvObj)
  1622. {
  1623.     TraceTag(tagFuncTriv,"FRMFAC::QueryInterface %s",DumpCLSID(riid));
  1624.     if (IsEqualIID(riid, IID_IUnknown) || 
  1625.         IsEqualIID(riid, IID_IClassFactory))
  1626.         {
  1627.         *ppvObj = this;
  1628.         AddRef();
  1629.         TraceTag(tagForm,"return ok");
  1630.         return NOERROR;
  1631.         }
  1632.  
  1633.     *ppvObj = NULL;
  1634.     TraceTag(tagForm,"return no interface");
  1635.     return ResultFromScode(E_NOINTERFACE);
  1636. }
  1637.  
  1638. /*
  1639.  *  FRMFAC::LockServer
  1640.  *
  1641.  *  Purpose:
  1642.  *      
  1643.  *
  1644.  *  Arguments:
  1645.  *      BOOL            Whether to increment or decrement DLL reference count.
  1646.  *
  1647.  *  Returns:
  1648.  *      HRESULT         S_OK always.
  1649.  */
  1650. STDMETHODIMP
  1651. FRMFAC::LockServer(BOOL fLock)
  1652. {
  1653.     TraceTag(tagFormFunc,"LockServer (not implemented)");
  1654.     return NOERROR;
  1655. }
  1656.  
  1657. /*
  1658.  *  FRMFAC::AddRef
  1659.  *
  1660.  *  Purpose:
  1661.  *      Increments reference count on the form class factory.
  1662.  *
  1663.  *  Arguments:
  1664.  *
  1665.  *  Returns:
  1666.  *      ULONG           New value of reference count.
  1667.  */
  1668. STDMETHODIMP_(ULONG)
  1669. FRMFAC::AddRef(void)
  1670. {
  1671.     TraceTag(tagFuncTriv,"FRMFAC::AddRef ret %d",cRef+1);
  1672.     return ++cRef;
  1673. }
  1674.  
  1675.  
  1676. /*
  1677.  *  FRMFAC::Release
  1678.  *
  1679.  *  Purpose:
  1680.  *      Decrements reference count on the form class factory.
  1681.  *      If count is decremented to zero, the object is freed.
  1682.  *
  1683.  *  Arguments:
  1684.  *
  1685.  *  Returns:
  1686.  *      ULONG           New value of reference count.
  1687.  */
  1688. STDMETHODIMP_(ULONG)
  1689. FRMFAC::Release(void)
  1690. {
  1691.     TraceTag(tagFuncTriv,"FRMFAC::Release cRef %d",cRef);
  1692.     if (!(--cRef))
  1693.         {
  1694.         TraceTag(tagForm,"return 0");
  1695.         delete this;
  1696.         return 0;
  1697.         }
  1698.     return cRef;
  1699. }
  1700.  
  1701.  
  1702. FRMFMR::FRMFMR()
  1703. {
  1704.     TraceTag(tagFuncTriv,"FRMFMR::FRMFMR");
  1705.     clsid = CLSID_MyFormsClsId;
  1706. }
  1707.  
  1708. FRMFAC::FRMFAC()
  1709. {
  1710.     TraceTag(tagFuncTriv,"FRMFAC::FRMFAC");
  1711.     cRef = 1;
  1712. }
  1713.  
  1714. FRMFAC::~FRMFAC(void)
  1715. {
  1716.     TraceTag(tagFuncTriv,"FRMFAC::~FRMFAC");
  1717.     AssertSz(!cRef,"0817236");
  1718. }
  1719.