FORM.CPP
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:48k
源码类别:

Windows编程

开发平台:

Visual C++

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