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