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 / simple.frm / formbase.cpp next >
C/C++ Source or Header  |  1996-04-11  |  66KB  |  2,705 lines

  1. //////////////////////////////////////////////////////////////////////////////
  2. //
  3. //  FILE:           FORMBASE.CPP
  4. //
  5. //  
  6. //
  7. //  Copyright (c) 1986-1996, Microsoft Corporation.
  8. //  All rights reserved.
  9. //
  10. //--
  11.  
  12. #include        "precomp.h"
  13. #include        <cindex.h>
  14.  
  15. static BOOL     g_FModalUp = FALSE;
  16. static BOOL     g_FMBoxUp = FALSE;
  17. static HWND     g_hwndUp = NULL;
  18.  
  19.  
  20. BOOL CALLBACK FormDlgProcSend(HWND, UINT, WPARAM, LPARAM);
  21. BOOL CALLBACK FormDlgProcRead(HWND, UINT, WPARAM, LPARAM);
  22.  
  23. SizedSPropTagArray(cpropMsg, tagaRead) = MESSAGE_TAGS;
  24.  
  25. //szRE_PREFIX and szFW_PREFIX have to have the same length
  26. char szRE_PREFIX[] = "RE: ";
  27. char szFW_PREFIX[] = "FW: ";
  28.  
  29.  
  30. #define EXCLUDED_PROPS_ON_REPLY     32
  31. SizedSPropTagArray (EXCLUDED_PROPS_ON_REPLY, sptExcludedProps) =
  32. {
  33.     EXCLUDED_PROPS_ON_REPLY, 
  34.     {
  35.         PR_SENDER_NAME,
  36.         PR_SENDER_ENTRYID,
  37.         PR_SENDER_SEARCH_KEY,
  38.         PR_SENDER_EMAIL_ADDRESS,
  39.         PR_SENDER_ADDRTYPE,
  40.  
  41.         PR_RECEIVED_BY_NAME,
  42.         PR_RECEIVED_BY_ENTRYID,
  43.         PR_RECEIVED_BY_SEARCH_KEY,
  44.  
  45.         PR_SENT_REPRESENTING_NAME,
  46.         PR_SENT_REPRESENTING_ENTRYID,
  47.         PR_SENT_REPRESENTING_SEARCH_KEY,
  48.         PR_SENT_REPRESENTING_EMAIL_ADDRESS,
  49.         PR_SENT_REPRESENTING_ADDRTYPE,
  50.  
  51.         PR_RCVD_REPRESENTING_NAME,
  52.         PR_RCVD_REPRESENTING_ENTRYID,
  53.         PR_RCVD_REPRESENTING_SEARCH_KEY,
  54.  
  55.         PR_MESSAGE_FLAGS,
  56.         PR_MESSAGE_RECIPIENTS,
  57.  
  58.         PR_READ_RECEIPT_ENTRYID,
  59.         PR_REPORT_ENTRYID,
  60.  
  61.         PR_REPLY_RECIPIENT_ENTRIES,
  62.         PR_REPLY_RECIPIENT_NAMES,
  63.  
  64.         PR_PARENT_KEY,
  65.  
  66.         PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED,
  67.  
  68.         PR_READ_RECEIPT_REQUESTED,
  69.  
  70.         PR_CLIENT_SUBMIT_TIME,
  71.         PR_MESSAGE_DELIVERY_TIME,
  72.         PR_MESSAGE_DOWNLOAD_TIME,
  73.  
  74.         PR_BODY,
  75.         PR_SUBJECT,
  76.         PR_SUBJECT_PREFIX,
  77.         PR_MESSAGE_ATTACHMENTS  
  78.  
  79.     } 
  80. };
  81.  
  82.  
  83. ////    CBaseForm::CBaseForm
  84. //
  85.  
  86. CBaseForm::CBaseForm(CClassFactory * pClassFactory) 
  87.                 : m_lsterr(g_szFormName)
  88. {
  89.     m_pClassFactory = pClassFactory;
  90.     m_pClassFactory->AddRef();
  91.  
  92.     m_ulViewStatus = 0;
  93.     m_ulSiteStatus = 0;
  94.     m_cRef = 1;
  95.  
  96.     m_pviewctxOverride = NULL;
  97.     m_pviewctx = NULL;
  98.     m_pmsgsite = NULL;
  99.     m_pmsg = NULL;
  100.  
  101.     m_hwnd = NULL;
  102.     m_hwndDialog = NULL;
  103.     
  104.     m_pses = NULL;
  105.     m_pab = NULL;
  106.     m_pval = NULL;
  107.  
  108.         
  109.     m_padrlist = NULL;
  110.     m_fRecipientsDirty = FALSE;
  111.     
  112.     m_fDirty = FALSE;
  113.  
  114.     m_fSameAsLoaded = FALSE;
  115.  
  116.     //
  117.     //  Add self to the head of the linked list of forms
  118.     //
  119.  
  120.     m_pfrmNext = g_PfrmFirst;
  121.     g_PfrmFirst = this;
  122.  
  123.     m_cbConvIdx = 0;
  124.     m_lpbConvIdx = NULL;
  125.     m_fConvTopicSet = FALSE;
  126.  
  127.     m_hChsFldDll = NULL;
  128.     m_lpfnHrPickFolder = NULL;
  129.     m_cbCFDState = 0;
  130.     m_pbCFDState = NULL;
  131.  
  132.  
  133.     m_state = stateUninit;
  134. }
  135.  
  136.  
  137. ////    CBaseForm::~CBaseForm
  138. //
  139. CBaseForm::~CBaseForm()
  140. {
  141.     CBaseForm *         pfrm;
  142.     //
  143.     //  Remove ourselves from the form link list
  144.     //
  145.  
  146.     if (g_PfrmFirst == this)
  147.     {
  148.         g_PfrmFirst = m_pfrmNext;
  149.     }
  150.     else
  151.     {
  152.         for (pfrm = g_PfrmFirst; pfrm != NULL; pfrm = pfrm->GetNext())
  153.         {
  154.             if (pfrm->m_pfrmNext == this)
  155.             {
  156.                 pfrm->m_pfrmNext = m_pfrmNext;
  157.                 break;
  158.             }
  159.         }
  160.         Assert(pfrm != NULL);
  161.     }
  162.  
  163.     MAPIFreeBuffer(m_pval);
  164.  
  165.     FreePadrlist(m_padrlist);
  166.  
  167.     MAPIFreeBuffer(m_lpbConvIdx);
  168.  
  169.     if(m_hChsFldDll)
  170.         FreeLibrary(m_hChsFldDll);
  171.  
  172.     MAPIFreeBuffer(m_pbCFDState);
  173.     
  174.     //
  175.     //  Let the class factory know we are gone
  176.     //
  177.  
  178.     UlRelease(m_pClassFactory);
  179.     
  180. }
  181.  
  182.  
  183. ////    CBaseForm::DeInitObjects
  184. //
  185.  
  186. void CBaseForm::DeInitObjects()
  187. {
  188.     if (m_hwnd != NULL)
  189.     {
  190.         DestroyWindow(m_hwnd);            // Destroy the frame window
  191.     }
  192.  
  193.     Assert(m_pmsg == NULL);
  194.     Assert(m_pmsgsite == NULL);
  195.     Assert(m_pviewctx == NULL);
  196.     Assert(m_pviewctxOverride == NULL);
  197.     Assert(m_pab == NULL);
  198.     Assert(m_pses == NULL);
  199.  
  200.     m_state = stateDead;
  201. }
  202.  
  203. ///////////////////////////////////////////////////////////////////////////////
  204. //
  205. //  IUnknown interface
  206. //
  207. ///////////////////////////////////////////////////////////////////////////////
  208.  
  209. ////    CBaseForm::QueryInterface
  210. //
  211. //
  212. //  DESCRIPTION:    The form supports IMAPIform,
  213. //                  IPersistMessage and IAMPIFormAdviseSink
  214.  
  215. STDMETHODIMP CBaseForm::QueryInterface(REFIID riid, LPVOID * ppvObj)
  216. {
  217.     if (riid == IID_IUnknown || riid == IID_IMAPIForm)
  218.     {
  219.         *ppvObj = (LPVOID) (IMAPIForm *) this;
  220.     }
  221.     else if (riid == IID_IPersistMessage)
  222.     {
  223.         *ppvObj = (LPVOID) (IPersistMessage *) this;
  224.     }
  225.     else if (riid == IID_IMAPIFormAdviseSink)
  226.     {
  227.         *ppvObj = (LPVOID) (IMAPIFormAdviseSink *) this;
  228.     }
  229.     else
  230.     {
  231.         *ppvObj = NULL;
  232.         return  m_lsterr.HrSetLastError(ResultFromScode(E_NOINTERFACE));
  233.     }
  234.  
  235.     AddRef();
  236.     return hrSuccess;
  237. }
  238.  
  239. ////    CBaseForm::AddRef
  240. //
  241. //  Description:
  242. //
  243.  
  244. STDMETHODIMP_(ULONG) CBaseForm::AddRef ()
  245. {
  246.     m_cRef += 1;
  247.     return m_cRef;
  248. }
  249.  
  250. ////    CBaseForm::Release
  251. //
  252.  
  253. STDMETHODIMP_(ULONG) CBaseForm::Release ()
  254. {
  255.     ULONG cRef = -- m_cRef;
  256.  
  257.     if (cRef == 0)
  258.     {
  259.         // Let the class factory now we are gone
  260.         m_pClassFactory->ObjDestroyedCallback();
  261.         delete this;
  262.     }
  263.  
  264.     return cRef;
  265. }
  266.  
  267.  
  268. ///////////////////////////////////////////////////////////////////////////////
  269. //
  270. //  IMAPIForm interface
  271. //
  272. ///////////////////////////////////////////////////////////////////////////////
  273.  
  274.  
  275. ////    IMAPIForm::SetViewContext
  276. //
  277. //
  278.  
  279. STDMETHODIMP CBaseForm::SetViewContext(IN IMAPIViewContext * pvc)
  280. {
  281.    
  282.     //
  283.     //  If we currently have a view context, then release it
  284.     //
  285.  
  286.     if (m_pviewctx != NULL)
  287.     {
  288.         m_pviewctx->SetAdviseSink(NULL);
  289.         m_pviewctx->Release();
  290.     }
  291.  
  292.     //
  293.     // Accept the new view context.
  294.     //
  295.  
  296.     m_pviewctx = pvc;
  297.  
  298.     //
  299.     //  If the new view context is non-null, then save it away, setup
  300.     //  the advise sink back to check for things and get the current set
  301.     //  of status flags
  302.     //
  303.  
  304.     m_ulViewStatus = 0;
  305.     if (pvc != NULL)
  306.     {
  307.         m_pviewctx->AddRef ();
  308.         m_pviewctx->SetAdviseSink (this);
  309.         m_pviewctx->GetViewStatus(&m_ulViewStatus);
  310.     }
  311.     
  312.     ConfigWinMenu();
  313.     
  314.     return hrSuccess;
  315. }
  316.  
  317.  
  318. ////    IMAPIForm::GetViewContext
  319. //
  320.  
  321. STDMETHODIMP CBaseForm::GetViewContext(OUT IMAPIViewContext * FAR * ppvc)
  322. {
  323.     Assert(ppvc);
  324.  
  325.     *ppvc = m_pviewctx;
  326.     
  327.     if(m_pviewctx != NULL)
  328.     {
  329.         m_pviewctx->AddRef();
  330.         return hrSuccess;
  331.     }
  332.     else
  333.         return ResultFromScode(S_FALSE);
  334. }
  335.  
  336.  
  337. ////    IMAPIForm::ShutdownForm
  338. //
  339. //  Description:
  340. //      This routine is called to shut down the form and if necessary
  341. //      to cause save changes to the form.
  342. //
  343.  
  344. STDMETHODIMP CBaseForm::ShutdownForm(DWORD dwSaveOptions)
  345. {
  346.   HRESULT             hr;
  347.     
  348.     //
  349.     //  Check for valid state to make the call
  350.     //
  351.  
  352.     switch( m_state )
  353.     {
  354.         default:
  355.         case stateDead:
  356.             m_viewnotify.OnShutdown ();
  357.             return m_lsterr.HrSetLastError(ResultFromScode(E_UNEXPECTED));
  358.  
  359.         case stateUninit:
  360.         case stateNormal:
  361.         case stateNoScribble:
  362.         case stateHandsOffFromSave:
  363.         case stateHandsOffFromNormal:
  364.             break;
  365.     }
  366.  
  367.  
  368.     hr = HrQuerySave(dwSaveOptions);
  369.     if(HR_FAILED(hr))
  370.         return hr;
  371.     //
  372.     //  Save us from ourselfs by add-refing the object
  373.     //
  374.  
  375.     AddRef();
  376.  
  377.     //
  378.     //  Release the view context
  379.     //
  380.  
  381.     if (m_pviewctx != NULL)
  382.     {
  383.         m_pviewctx->SetAdviseSink(NULL);
  384.         m_pviewctx->Release();
  385.         m_pviewctx = NULL;
  386.     }
  387.  
  388.     //
  389.     //  We need to notify anyone who has an advise on us that we are
  390.     //  shutting down.  We want to do this in such a manner as to
  391.     //  protect ourselves since we are referencing the data structure
  392.     //  internally.  Thus the AddRef/Release pair.
  393.     //
  394.  
  395.     m_viewnotify.OnShutdown ();
  396.  
  397.     //
  398.     //  Release message objects if we have them
  399.     //
  400.  
  401.     if(g_FModalUp)  
  402.         m_pviewctxOverride = NULL;
  403.  
  404.     UlRelease(m_pmsg);
  405.     m_pmsg = NULL;
  406.     
  407.     UlRelease(m_pmsgsite);
  408.     m_pmsgsite = NULL;
  409.  
  410.     UlRelease(m_pab);
  411.     m_pab = NULL;
  412.  
  413.     UlRelease(m_pses);
  414.     m_pses = NULL;
  415.  
  416.  
  417.     //
  418.     //  Tell all objects to be closed and de-initialized, only IUnknown
  419.     //          calls are legal after this.
  420.     //
  421.  
  422.     DeInitObjects();
  423.  
  424.     //
  425.     //  We are now all done -- release our internal reference
  426.     //
  427.  
  428.     Release();
  429.  
  430.     return hrSuccess;
  431. }
  432.                    
  433.  
  434. ////    IMAPIForm::DoVerb
  435. //
  436.  
  437. STDMETHODIMP CBaseForm::DoVerb(LONG iVerb, LPMAPIVIEWCONTEXT pviewctx,
  438.                                ULONG hwndParent, LPCRECT lprcPosRect)
  439. {
  440.     HRESULT             hr;
  441.  
  442.     //
  443.     //  If a view context was passed in, then we need to get the
  444.     //  status bits from this view context.  Also we are going to save
  445.     //  the current view context and use this view context for the
  446.     //  duration of the verb execution.
  447.     //
  448.  
  449.     if (pviewctx != NULL)
  450.     {
  451.         m_pviewctxOverride = pviewctx;
  452.         pviewctx->GetViewStatus(&m_ulViewStatus);
  453.     }
  454.  
  455.     //
  456.     //   Execute the requested verb.  If we do not understand the verb
  457.     //  or we do not support the verb then we return NO SUPPORT and let
  458.     //  the viewer deal with this.
  459.     //
  460.  
  461.     switch (iVerb)
  462.     {
  463.     
  464.     case EXCHIVERB_OPEN:
  465.         hr = HrOpenForm((HWND) hwndParent, lprcPosRect, m_ulViewStatus);
  466.         break;
  467.  
  468.     case EXCHIVERB_REPLYTOSENDER:
  469.         hr = HrReply(eREPLY, (HWND) hwndParent, lprcPosRect);
  470.         if(HR_SUCCEEDED(hr))
  471.         {
  472.             m_pviewctxOverride = NULL;
  473.             ShutdownForm(SAVEOPTS_NOSAVE);
  474.         }
  475.         break;
  476.  
  477.     case EXCHIVERB_FORWARD:
  478.         hr = HrReply(eFORWARD, (HWND) hwndParent, lprcPosRect);
  479.         if(HR_SUCCEEDED(hr))
  480.         {
  481.             m_pviewctxOverride = NULL;
  482.             ShutdownForm(SAVEOPTS_NOSAVE);
  483.         }
  484.         break;
  485.     
  486.     case EXCHIVERB_PRINT:
  487.     case EXCHIVERB_REPLYTOALL:
  488.     case EXCHIVERB_SAVEAS:
  489.     case EXCHIVERB_REPLYTOFOLDER:
  490.         //the viewer should not call us here 
  491.         //(see Value in extensions section of smpfrm.cfg)
  492.         Assert(FALSE);
  493.  
  494.     default:
  495.         hr = m_lsterr.HrSetLastError(ResultFromScode(MAPI_E_NO_SUPPORT));
  496.         break;
  497.     }
  498.  
  499.     //
  500.     //  If we moved to a different view context, then switch back to
  501.     //  the one we started with.
  502.     //
  503.  
  504.     m_pviewctxOverride = NULL;
  505.     
  506.     if(m_pviewctx != NULL)
  507.     {
  508.         m_ulViewStatus =0;
  509.         m_pviewctx->GetViewStatus(&m_ulViewStatus);
  510.         ConfigWinMenu();
  511.     }
  512.     
  513.     return hr;
  514. }
  515.  
  516.  
  517. ////    IMAPIForm::Advise
  518. //
  519.  
  520. STDMETHODIMP CBaseForm::Advise (IN IMAPIViewAdviseSink * pViewAdvise,
  521.                                 OUT ULONG FAR * pulConnection)
  522. {
  523.     HRESULT     hr;
  524.  
  525.     hr = m_viewnotify.Advise (pViewAdvise, pulConnection);
  526.     if (FAILED(hr))
  527.     {
  528.         hr = m_lsterr.HrSetLastError(hr);
  529.     }
  530.     return hr;
  531. }
  532.  
  533.  
  534. ////    IMAPIForm::Unadvise
  535. //
  536.  
  537. STDMETHODIMP CBaseForm::Unadvise(ULONG ulConnection)
  538. {
  539.     HRESULT     hr;
  540.     
  541.     hr = m_viewnotify.Unadvise(ulConnection);
  542.     if (FAILED(hr))
  543.     {
  544.         hr = m_lsterr.HrSetLastError(hr);
  545.     }
  546.  
  547.     return hr;
  548. }
  549.  
  550.  
  551. ///////////////////////////////////////////////////////////////////////////////
  552. //
  553. //  IPersistMessage interface
  554. //
  555. ///////////////////////////////////////////////////////////////////////////////
  556.  
  557. ////    IPersistMessage::GetClassID
  558.  
  559. STDMETHODIMP CBaseForm::GetClassID(LPCLSID lpClassID)
  560. {
  561.     *lpClassID = CLSID_IPM_NOTE_SAMPLE;
  562.     return hrSuccess;
  563. }
  564.  
  565.  
  566. ////  IPersistMessage::GetLastError
  567. //
  568. //  Description:  This routine is used to get back a string giving more
  569. //              information about the last error in the form. 
  570. //
  571.  
  572. STDMETHODIMP CBaseForm::GetLastError(HRESULT hr, ULONG ulFlags,
  573.                                      LPMAPIERROR FAR * lppMAPIError)
  574. {
  575.     return m_lsterr.HrGetLastError(hr, ulFlags, lppMAPIError);
  576. }
  577.  
  578.  
  579. ////    IPersistMessage::IsDirty
  580. //
  581.  
  582. STDMETHODIMP CBaseForm::IsDirty ()
  583. {
  584.  
  585.     if(m_fDirty)
  586.         return ResultFromScode(S_OK);
  587.  
  588.     if(NULL == m_hwndDialog)
  589.     {
  590.         m_fDirty = FALSE;
  591.     }
  592.     else if(m_eFormType == eformRead)
  593.     {
  594.         m_fDirty = (m_fRecipientsDirty ||
  595.                     Edit_GetModify(GetDlgItem(m_hwndDialog, ID_BODY)));
  596.     }
  597.     else
  598.     {
  599.         m_fDirty = (m_fRecipientsDirty ||
  600.                     Edit_GetModify(GetDlgItem(m_hwndDialog, ID_BODY)) ||
  601.                     Edit_GetModify(GetDlgItem(m_hwndDialog, ID_SUBJECT)) ||
  602.                     Edit_GetModify(GetDlgItem(m_hwndDialog, ID_TO)) ||
  603.                     Edit_GetModify(GetDlgItem(m_hwndDialog, ID_CC)));
  604.     }
  605.  
  606.     return ResultFromScode ((m_fDirty ? S_OK : S_FALSE));
  607. }
  608.  
  609.  
  610. ////  IPersistMessage::InitNew
  611. //
  612. //  Description: This function is called in the case of composing a new
  613. //      message.  There is a small set of properties which are set by
  614. //      the constructor of the message, however in general it can be
  615. //      assumed the message is clean.
  616. //
  617.  
  618. STDMETHODIMP CBaseForm::InitNew(LPMAPIMESSAGESITE pmsgsite, LPMESSAGE pmsg)
  619. {
  620.     //
  621.     //  Ensure we are in a state where we can accept this call
  622.     //
  623.  
  624.     switch(m_state)
  625.     {
  626.     case stateUninit:
  627.     case stateHandsOffFromSave:
  628.     case stateHandsOffFromNormal:
  629.         break;
  630.  
  631.     default:
  632.         return m_lsterr.HrSetLastError(ResultFromScode(E_UNEXPECTED));
  633.     }
  634.  
  635.     //
  636.     //  If we currently have a message site, then release it as we
  637.     //  will no longer be using it.
  638.     //
  639.         
  640.     UlRelease(m_pmsgsite);
  641.     m_pmsgsite = NULL;
  642.     
  643.     //
  644.     //  Save away the pointers to the message and message site
  645.     //
  646.  
  647.     m_pmsgsite = pmsgsite;
  648.     pmsgsite->AddRef();
  649.  
  650.     m_ulSiteStatus = 0;
  651.     m_pmsgsite->GetSiteStatus(&m_ulSiteStatus);
  652.  
  653.     m_pmsg = pmsg;
  654.     pmsg->AddRef();
  655.  
  656.     //
  657.     //  Make an assumption on the message flags and status
  658.     //
  659.  
  660.     m_ulMsgStatus = 0;
  661.     m_ulMsgFlags = MSGFLAG_UNSENT;
  662.     
  663.     if(m_hwnd)
  664.         DisplayMessage();
  665.  
  666.     //
  667.     //  We succeeded in doing the InitNew so move to the normal state
  668.     //
  669.  
  670.     m_state = stateNormal;
  671.  
  672.     //
  673.     //  Tell everybody who cares that we just loaded a new message
  674.     //
  675.  
  676.     m_viewnotify.OnNewMessage();
  677.     return hrSuccess;
  678.  
  679. }
  680.  
  681. //// IPersistMessage::Load
  682. //
  683. //  Description:  This routine is called as part of loading an existing
  684. //      message into the form.
  685. //
  686.  
  687. STDMETHODIMP CBaseForm::Load(LPMAPIMESSAGESITE pmsgsite, LPMESSAGE pmsg,
  688.                              ULONG ulMsgStatus, ULONG ulMsgFlags)
  689. {
  690.     //
  691.     //  Ensure we are in a state where we can accept this call
  692.     //
  693.  
  694.     switch(m_state)
  695.     {
  696.     case stateUninit:
  697.     case stateHandsOffFromSave:
  698.     case stateHandsOffFromNormal:
  699.         break;
  700.  
  701.     default:
  702.         return m_lsterr.HrSetLastError(ResultFromScode(E_UNEXPECTED));
  703.     }
  704.  
  705.     //
  706.     //  If we currently have a message site, then release it as we
  707.     //  will no longer be using it.
  708.     //
  709.         
  710.     UlRelease(m_pmsgsite);
  711.     m_pmsgsite = NULL;
  712.  
  713.     UlRelease(m_pmsg);
  714.     m_pmsg = NULL;
  715.     
  716.  
  717.     HRESULT hr = HrGetMsgDataFromMsg(pmsg, ulMsgFlags);
  718.     if(HR_FAILED(hr))
  719.         goto err;
  720.  
  721.     //
  722.     //  Save away the message and message site which are passed in.
  723.     //
  724.  
  725.     m_pmsg = pmsg;
  726.     pmsg->AddRef();
  727.  
  728.     m_pmsgsite = pmsgsite;
  729.     pmsgsite->AddRef();
  730.  
  731.     //
  732.     //  Get the site status flags for disabling buttons & menus
  733.     //
  734.     m_ulSiteStatus = 0;
  735.     m_pmsgsite->GetSiteStatus(&m_ulSiteStatus);
  736.  
  737.     //
  738.     //  Save away these properties
  739.     //
  740.  
  741.     m_ulMsgStatus = ulMsgStatus;
  742.     m_ulMsgFlags = ulMsgFlags;
  743.  
  744.     //
  745.     //  Put us into the normal state
  746.     //
  747.  
  748.     m_state = stateNormal;
  749.  
  750.     
  751.     //
  752.     //  if our form is up, display the message
  753.     //
  754.     if(m_hwnd)
  755.         DisplayMessage();
  756.     
  757.     //
  758.     //  Tell everybody who cares that we just loaded a new message
  759.     //
  760.  
  761.     m_viewnotify.OnNewMessage();
  762.     return hrSuccess;
  763.  
  764. err:
  765.     return hr;
  766. }
  767.  
  768. ////    IPersistMessage::Save
  769. //
  770. //  Description:  
  771. //      This function will be called whenever a save operation of the
  772. //      information into the form should be done.  We should only make 
  773. //      modifications to the message in this function.
  774. //
  775.  
  776. STDMETHODIMP CBaseForm::Save(IN LPMESSAGE pmsg, IN ULONG fSameAsLoad)
  777. {
  778.     HRESULT             hr;
  779.     
  780.     //
  781.     //  Check that we are in a state where we are willing to accept
  782.     //  this call.  Must have a message.
  783.     //
  784.  
  785.     switch( m_state )
  786.     {
  787.     default:
  788.         Assert(FALSE);
  789.     case stateDead:
  790.     case stateUninit:
  791.     case stateNoScribble:
  792.     case stateHandsOffFromSave:
  793.     case stateHandsOffFromNormal:
  794.         return m_lsterr.HrSetLastError(ResultFromScode(E_UNEXPECTED));
  795.  
  796.     case stateNormal:
  797.         break;
  798.     }
  799.     
  800.     if (fSameAsLoad)
  801.     {
  802.         //
  803.         //  Its the same message interface as was loaded into us.  We can
  804.         //      assume that the pmsg passed in is either NULL or an interface
  805.         //      on the same object as the message we already have loaded
  806.         //
  807.  
  808.         hr = HrSaveInto(m_pmsg);
  809.     }
  810.     else
  811.     {
  812.         //
  813.         //  We need to copy everything into the new message as we are going
  814.         //      to clone ourselves into it.
  815.         //
  816.  
  817.         hr = m_pmsg->CopyTo(0, NULL, NULL, 0, NULL, &IID_IMessage, pmsg, 0, NULL);
  818.         if (FAILED(hr))
  819.         {
  820.             m_lsterr.HrSetLastError(hr, m_pmsg);
  821.             return hr;
  822.         }
  823.                             
  824.         //
  825.         //  Now make all of the incremental changes
  826.         //
  827.  
  828.         hr = HrSaveInto(pmsg);
  829.     }
  830.     
  831.     if (FAILED(hr))
  832.     {
  833.         return hr;
  834.     }
  835.     
  836.     m_state = stateNoScribble;
  837.     m_fSameAsLoaded = fSameAsLoad;
  838.     
  839.     return S_OK;
  840. }
  841.  
  842.  
  843. ////    IPersistMessage::SaveCompleted
  844. //
  845. //
  846.  
  847.  
  848. STDMETHODIMP CBaseForm::SaveCompleted(IN LPMESSAGE pmsg)
  849. {
  850.  
  851.     switch( m_state )
  852.     {
  853.     case stateHandsOffFromNormal:
  854.     case stateHandsOffFromSave:
  855.     case stateNoScribble:
  856.         break;
  857.  
  858.     default:
  859.         return m_lsterr.HrSetLastError(ResultFromScode(E_UNEXPECTED));
  860.     }
  861.  
  862.     if((stateHandsOffFromNormal == m_state || 
  863.         stateHandsOffFromSave == m_state)  && NULL == pmsg)
  864.     {
  865.         DebugTrace("smpfrm: SaveCompleted called in handsOff state with pmsg==NULL\r\n");
  866.         return  m_lsterr.HrSetLastError(ResultFromScode(E_INVALIDARG));
  867.     }
  868.     
  869.     ULONG ulOldState = m_state;
  870.     m_state = stateNormal;                        
  871.        
  872.     //state == NoScribble , pmsg == NULL
  873.     if(NULL == pmsg)
  874.     {
  875.         if(m_fSameAsLoaded)
  876.         {
  877.             ClearDirty();
  878.             m_viewnotify.OnSaved();
  879.         }
  880.  
  881.         return hrSuccess;
  882.     }
  883.     
  884.  
  885.     //state == handsOffFromNormal, pmsg != NULL
  886.     if(stateHandsOffFromNormal == ulOldState)
  887.     {
  888.         UlRelease(m_pmsg);
  889.     
  890.         m_pmsg = pmsg;
  891.         pmsg->AddRef();
  892.  
  893.         return hrSuccess;
  894.     }    
  895.  
  896.     //state == handsOffFromSave || NoScribble, pmsg != NULL
  897.     if(stateNoScribble == ulOldState)
  898.     {
  899.         UlRelease(m_pmsg);
  900.         m_pmsg = pmsg;
  901.         pmsg->AddRef();
  902.     }
  903.     
  904.     m_viewnotify.OnSaved();
  905.     ClearDirty();
  906.    
  907.    return hrSuccess;
  908. }
  909.  
  910.  
  911. ////  IPersistMessage::HandsOffMessage
  912. //
  913. //  Description: store, folder and message objects has to be released
  914. //              in this method.
  915. // 
  916. //
  917.  
  918. STDMETHODIMP CBaseForm::HandsOffMessage ()
  919. {
  920.     
  921.     switch( m_state )
  922.     {
  923.     case stateNormal:
  924.     case stateNoScribble:
  925.         break;
  926.  
  927.     default:
  928.         return m_lsterr.HrSetLastError(ResultFromScode(E_UNEXPECTED));
  929.     }
  930.  
  931.     if(stateNormal == m_state)
  932.         m_state = stateHandsOffFromNormal;
  933.     else
  934.         m_state = stateHandsOffFromSave;
  935.  
  936.     //
  937.     //  We must have a message
  938.     //
  939.  
  940.     Assert(m_pmsg != NULL);
  941.     m_pmsg->Release();
  942.     m_pmsg = NULL;
  943.  
  944.  
  945.     return hrSuccess;
  946. }
  947.  
  948.  
  949. ///////////////////////////////////////////////////////////////////////////////
  950. //
  951. //  IMAPIFormAdviseSink interfaces
  952. //
  953. ///////////////////////////////////////////////////////////////////////////////
  954.  
  955.  
  956.  
  957. ////    IMAPIFormAdviseSink::OnChange
  958. //
  959. //  Description: called to notify about changes in viewctx status 
  960.  
  961. STDMETHODIMP CBaseForm::OnChange(ULONG ulflag)
  962. {
  963.     if(m_pviewctxOverride == NULL) 
  964.     {
  965.         m_ulViewStatus = ulflag;
  966.         ConfigWinMenu();
  967.     }
  968.  
  969.     return hrSuccess;
  970. }
  971.  
  972. ////    CBaseForm::OnActivateNext
  973. //
  974. //  Description:  We only say that we will handle the next message if
  975. //              it is the exact same message class as the current message.
  976. //              If the next message has the same "unsentness" will reuse the 
  977. //              current object, otherwise ask our ClassFactory for a new one.
  978. //
  979.  
  980. STDMETHODIMP CBaseForm::OnActivateNext(LPCSTR lpszMessageClass, ULONG ulMessageStatus,
  981.                                         ULONG ulMessageFlags,
  982.                                        LPPERSISTMESSAGE FAR * ppPersistMessage)
  983. {
  984.     HRESULT hr;
  985.  
  986.     *ppPersistMessage = NULL;
  987.  
  988.     Assert(m_pval);
  989.     
  990.     
  991.     if(PR_MESSAGE_CLASS == m_pval[irtClass].ulPropTag)
  992.     {
  993.         //the message class comparison has to be case insensitive
  994.         if((lstrcmpi(m_pval[irtClass].Value.LPSZ, lpszMessageClass) != 0) &&
  995.             lstrcmpi(FormClassName, lpszMessageClass) != 0)
  996.         {
  997.             return ResultFromScode(S_FALSE);
  998.         }
  999.     }
  1000.     else
  1001.     {
  1002.         if(lstrcmpi(FormClassName, lpszMessageClass) != 0)
  1003.         {
  1004.             return ResultFromScode(S_FALSE);
  1005.         }
  1006.     }
  1007.  
  1008.  
  1009.     if((m_ulMsgFlags & MSGFLAG_UNSENT) == (ulMessageFlags & MSGFLAG_UNSENT))
  1010.         //tell the viewer to reuse our object
  1011.         return ResultFromScode(S_OK);
  1012.  
  1013.     
  1014.     //Get a new object from our class factory
  1015.     hr = m_pClassFactory->CreateInstance(NULL, IID_IPersistMessage, (LPVOID FAR *)ppPersistMessage);
  1016.     if(hr)
  1017.         return ResultFromScode (S_FALSE);
  1018.     else
  1019.         return ResultFromScode(S_OK);
  1020. }
  1021.  
  1022.  
  1023. ///////////////////////////////////////////////////////////////////////////////
  1024. //
  1025. //  Non-IMAPIinterface functions
  1026. //
  1027. ///////////////////////////////////////////////////////////////////////////////
  1028.  
  1029.  
  1030. ///     CBaseForm::HrGetMsgDataFromMsg
  1031. //
  1032. //      fills in m_pval and m_padrlist (for unsent msgs only)
  1033. //      with the info from pmsg
  1034. HRESULT CBaseForm::HrGetMsgDataFromMsg(LPMESSAGE pmsg, ULONG ulMsgFlags)
  1035. {
  1036.     Assert(pmsg);
  1037.  
  1038.     FreePadrlist(m_padrlist);
  1039.     m_padrlist = NULL;
  1040.  
  1041.  
  1042.     ULONG   cValues = 0;
  1043.     MAPIFreeBuffer(m_pval);
  1044.     m_pval = NULL;
  1045.  
  1046.     MAPIFreeBuffer(m_lpbConvIdx);
  1047.     m_lpbConvIdx = NULL;
  1048.  
  1049.     HRESULT hr = pmsg->GetProps((LPSPropTagArray) &tagaRead, 0,
  1050.                                     &cValues, &m_pval);
  1051.     if (HR_FAILED(hr))
  1052.     {
  1053.         m_lsterr.HrSetLastError(hr, pmsg);
  1054.         goto err;
  1055.     }
  1056.  
  1057.     if(PROP_TYPE(m_pval[irtBody].ulPropTag) == PT_ERROR  &&
  1058.        GetScode(m_pval[irtBody].Value.l) == MAPI_E_NOT_ENOUGH_MEMORY)
  1059.     {
  1060.         hr = HrStreamInMsgBody(pmsg, m_pval, &m_pval[irtBody].Value.LPSZ, &m_lsterr);
  1061.         if(hr)
  1062.         {
  1063.             goto err;   
  1064.         }
  1065.         else
  1066.         {
  1067.             m_pval[irtBody].ulPropTag = PR_BODY;
  1068.         }
  1069.     }
  1070.     
  1071.     Assert(cValues ==  cpropMsg);
  1072.  
  1073.     if(PR_CONVERSATION_INDEX == m_pval[irtConvIdx].ulPropTag)
  1074.     {
  1075.         LPSPropValue pval = &m_pval[irtConvIdx];
  1076.  
  1077.         m_cbConvIdx = pval->Value.bin.cb;
  1078.         if(MAPIAllocateBuffer(m_cbConvIdx, (LPVOID *)&m_lpbConvIdx))
  1079.         {
  1080.             m_lpbConvIdx = NULL;
  1081.             m_cbConvIdx = 0;
  1082.         }
  1083.         else
  1084.         {
  1085.             CopyMemory(m_lpbConvIdx, pval->Value.bin.lpb, m_cbConvIdx);
  1086.         }
  1087.     }
  1088.     else
  1089.     {
  1090.         m_lpbConvIdx = NULL;
  1091.         m_cbConvIdx = 0;
  1092.     }
  1093.     
  1094.     m_fConvTopicSet = (PR_CONVERSATION_TOPIC == m_pval[irtConvTopic].ulPropTag);
  1095.         
  1096.     
  1097.     if(ulMsgFlags & MSGFLAG_UNSENT)
  1098.     {
  1099.         hr = GetMsgAdrlist (pmsg, &m_padrlist, &m_lsterr);
  1100.         if(HR_FAILED(hr))
  1101.         {
  1102.             goto err;
  1103.         }
  1104.     }
  1105.  
  1106.     return hrSuccess;
  1107.  
  1108. err:
  1109.     MAPIFreeBuffer(m_pval);
  1110.     m_pval = NULL;
  1111.  
  1112.     FreePadrlist(m_padrlist);
  1113.     m_padrlist = NULL;
  1114.  
  1115.     return hr;
  1116.  
  1117. }
  1118.  
  1119. ///         CBaseForm::ClearDirty
  1120. //
  1121. //      Clears dirty state
  1122. void CBaseForm::ClearDirty(void)
  1123. {
  1124.     m_fDirty = FALSE;
  1125.     m_fRecipientsDirty = FALSE;
  1126.  
  1127.     if(m_eFormType == eformSend)
  1128.     {
  1129.         Edit_SetModify(GetDlgItem(m_hwndDialog, ID_BODY), FALSE);
  1130.         Edit_SetModify(GetDlgItem(m_hwndDialog, ID_SUBJECT), FALSE);
  1131.         Edit_SetModify(GetDlgItem(m_hwndDialog, ID_TO), FALSE);
  1132.         Edit_SetModify(GetDlgItem(m_hwndDialog, ID_CC), FALSE);
  1133.     }
  1134.     else
  1135.     {
  1136.         Edit_SetModify(GetDlgItem(m_hwndDialog, ID_BODY), FALSE);
  1137.     }
  1138. }
  1139.  
  1140. ////    CBaseForm::Address
  1141. //
  1142. //  Description:
  1143. //      This function is used to address the form.
  1144. //      The parameter determines which button in the address
  1145. //      dialog has the focus.
  1146. //
  1147.  
  1148. void CBaseForm::Address(int id)
  1149. {
  1150.     Assert( ID_TO_BUTTON == id || ID_CC_BUTTON == id);
  1151.  
  1152.     HRESULT hr;
  1153.  
  1154.     if (m_pses == NULL)
  1155.     {
  1156.         hr = m_pmsgsite->GetSession(&m_pses);
  1157.         if(hr)
  1158.         {
  1159.             m_lsterr.HrSetLastError(hr, m_pmsgsite);
  1160.             ShowError();
  1161.             return;
  1162.         }
  1163.     }
  1164.     
  1165.     Assert(m_pses != NULL);
  1166.  
  1167.     if (m_pab == NULL)
  1168.     {
  1169.         hr = m_pses->OpenAddressBook((ULONG) m_hwnd, NULL, 0, &m_pab);
  1170.         if(hr)
  1171.         {
  1172.             m_lsterr.HrSetLastError(hr, m_pses);
  1173.             ShowError();
  1174.         }
  1175.         if(HR_FAILED(hr)) //if it's a real error (not a warning)
  1176.             return; 
  1177.     }
  1178.     
  1179.     Assert(m_pab != NULL);
  1180.  
  1181.     
  1182.     ADRPARM adrparm = { 0, NULL, AB_RESOLVE | DIALOG_MODAL, NULL, 0L,
  1183.                         NULL, NULL, NULL, NULL, "Address Book", NULL,
  1184.                         "Send Note To", 2, (id == ID_TO_BUTTON ? 0:1),
  1185.                         NULL, NULL, NULL, NULL };
  1186.     ULONG   ulHwndAddr = (ULONG) m_hwnd;
  1187.     
  1188.     hr = m_pab->Address(&ulHwndAddr, &adrparm, &m_padrlist);
  1189.     if (!hr)
  1190.     {
  1191.         m_fRecipientsDirty = TRUE;
  1192.         UpdateRecipientsDisplay();
  1193.     }
  1194.     else if(GetScode(hr) != MAPI_E_USER_CANCEL)
  1195.     {
  1196.         m_lsterr.HrSetLastError(hr, m_pab);
  1197.         ShowError();
  1198.     }
  1199.         
  1200. }
  1201.  
  1202. ////    CBaseForm::ClearWindow
  1203. //
  1204. //  Description:
  1205. //      This routine is called when the window for this form object is
  1206. //      destroyed.  We clear out pointer to the window and the windows
  1207. //      pointer to us.
  1208. //
  1209.  
  1210. void CBaseForm::ClearWindow()
  1211. {
  1212.     Assert(m_hwnd != NULL);
  1213.  
  1214.     //
  1215.     //  Clear the back pointer to us and remove the reference count for it.
  1216.     //
  1217.  
  1218.     SetWindowLong(m_hwnd, 0, 0);
  1219.     Release();
  1220.  
  1221.     //
  1222.     //  Clear out pointer to the window since it is now dead
  1223.     //
  1224.  
  1225.     m_hwnd = NULL;
  1226.     m_hwndDialog = NULL;
  1227. }
  1228.  
  1229. ////  CBaseForm::HrOpenForm
  1230. //
  1231. //  Description:  This is the internal routine which is called from the
  1232. //      open/display verb.  It will cause UI to appear if there is none
  1233. //      and force the window to the foreground if there is already UI.
  1234. //
  1235.  
  1236. HRESULT CBaseForm::HrOpenForm(HWND hwndParent, LPCRECT lprcPosRect,
  1237.                               ULONG ulViewFlags)
  1238. {
  1239.  
  1240.     if (lprcPosRect == NULL)
  1241.         return m_lsterr.HrSetLastError(ResultFromScode(E_INVALIDARG));
  1242.  
  1243.     //
  1244.     //  If any modal forms are visible then do not do anything 
  1245.     //
  1246.  
  1247.     Assert( g_FModalUp && g_hwndUp || !g_FModalUp && !g_hwndUp);
  1248.     if (g_FMBoxUp || (g_FModalUp && hwndParent != g_hwndUp))
  1249.         return m_lsterr.HrSetLastError(
  1250.                                ResultFromScode(OLEOBJ_S_CANNOT_DOVERB_NOW));
  1251.     
  1252.     if (!(ulViewFlags & VCSTATUS_MODAL))
  1253.     {
  1254.         //  If we are not modal then don't do anything relative to the parent
  1255.         hwndParent = NULL;
  1256.     }
  1257.  
  1258.     //
  1259.     //  Check to see if we have a window up
  1260.     //
  1261.  
  1262.     if (m_hwnd != 0)
  1263.     {
  1264.         MoveWindow(m_hwnd, lprcPosRect->left, lprcPosRect->top,
  1265.                    lprcPosRect->right - lprcPosRect->left,
  1266.                    lprcPosRect->bottom - lprcPosRect->top,
  1267.                    TRUE);
  1268.         SetForegroundWindow(m_hwnd);
  1269.         
  1270.         return S_OK;
  1271.     }
  1272.            
  1273.     m_hwnd = CreateWindow((m_ulMsgFlags & MSGFLAG_UNSENT) ?
  1274.                           g_szSendWinClass : g_szReadWinClass,
  1275.                           g_szWindowCaption, WS_OVERLAPPEDWINDOW,
  1276.                           0, 0, 10, 10, hwndParent, NULL, g_hinst, NULL);
  1277.  
  1278.     if (m_hwnd == NULL)
  1279.     {
  1280.         return m_lsterr.HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
  1281.     }
  1282.  
  1283.     //
  1284.     //  Put the pointer to this object into the window
  1285.     //
  1286.  
  1287.     SetWindowLong(m_hwnd, 0, (long) (void FAR *) this);
  1288.     //we just created another pointer to ourself, so addref
  1289.     AddRef();
  1290.  
  1291.     //
  1292.     //  Create the dialog as a child of this window
  1293.     //
  1294.  
  1295.     if (m_ulMsgFlags & MSGFLAG_UNSENT)
  1296.     {
  1297.         m_eFormType = eformSend;
  1298.         m_hwndDialog = CreateDialog(g_hinst, MAKEINTRESOURCE(IDR_SEND_FORM),
  1299.                                     m_hwnd, &FormDlgProcSend);
  1300.         m_HAccelTable = LoadAccelerators(g_hinst, MAKEINTRESOURCE(IDR_SEND_FORM));
  1301.     }
  1302.     else
  1303.     {
  1304.         m_eFormType = eformRead;
  1305.         m_hwndDialog = CreateDialog(g_hinst, MAKEINTRESOURCE(IDR_READ_FORM),
  1306.                                     m_hwnd, &FormDlgProcRead);
  1307.         m_HAccelTable = LoadAccelerators(g_hinst, MAKEINTRESOURCE(IDR_READ_FORM));
  1308.     }
  1309.         
  1310.  
  1311.     DisplayMessage();
  1312.  
  1313.     //
  1314.     //  Position the window where it is suppose to be
  1315.     //
  1316.                                    
  1317.     MoveWindow(m_hwnd, lprcPosRect->left, lprcPosRect->top,
  1318.         lprcPosRect->right - lprcPosRect->left,
  1319.         lprcPosRect->bottom - lprcPosRect->top,
  1320.         TRUE);
  1321.  
  1322.     ShowWindow(m_hwnd, SW_SHOW);
  1323.  
  1324.     SetForegroundWindow(m_hwnd);
  1325.                  
  1326.     //
  1327.     //  If we are modal, then we loop until the form is closed
  1328.     //
  1329.  
  1330.     if (ulViewFlags & VCSTATUS_MODAL)
  1331.     {
  1332.         MSG         msg;
  1333.  
  1334.         BOOL fOldModalUp = g_FModalUp;
  1335.         HWND hwndOldUp = g_hwndUp;
  1336.  
  1337.         g_FModalUp = TRUE;
  1338.         g_hwndUp = m_hwnd;
  1339.  
  1340.         while ((m_hwnd != NULL) && (GetMessage(&msg, m_hwnd, 0, 0)))
  1341.         {
  1342.             //first call our method and see if this message makes sense to us.
  1343.             //if not, let WIN API care about it.
  1344.             if (!TranslateMessage(msg))
  1345.             {
  1346.                 ::TranslateMessage(&msg);
  1347.                 ::DispatchMessage(&msg);
  1348.             }
  1349.         }
  1350.  
  1351.         g_FModalUp = fOldModalUp;
  1352.         g_hwndUp = hwndOldUp;
  1353.     }
  1354.  
  1355.     return hrSuccess;
  1356. }
  1357.  
  1358. ////    CBaseForm::HrSaveInto
  1359. //
  1360. //  Description:
  1361. //    This routine gives one central location which save all modified
  1362. //      properties into a message.
  1363. //
  1364.  
  1365. HRESULT CBaseForm::HrSaveInto(LPMESSAGE pmsg)
  1366. {
  1367.     ULONG               cval = 0;
  1368.     HRESULT             hr;                                    
  1369.  
  1370.     //
  1371.     //  First, write out the recipient table
  1372.     //
  1373.     
  1374.     if(m_padrlist && m_fRecipientsDirty)
  1375.     {
  1376.         hr = pmsg->ModifyRecipients(0, m_padrlist);
  1377.         if (FAILED(hr))
  1378.         {
  1379.             m_lsterr.HrSetLastError(hr, pmsg);
  1380.             return hr;
  1381.         }
  1382.     }
  1383.     //
  1384.     //  Next set up and save the rest of the info
  1385.     //
  1386.       
  1387.     HrGetMsgDataFromUI(m_hwndDialog);
  1388.  
  1389.     //PR_DISPLAY_TO AND PR_DISPLAY_CC can't be set
  1390.     ULONG ulToTag = m_pval[irtTo].ulPropTag;
  1391.     m_pval[irtTo].ulPropTag = PR_NULL;
  1392.     
  1393.     ULONG ulCcTag = m_pval[irtCc].ulPropTag;
  1394.     m_pval[irtCc].ulPropTag = PR_NULL;
  1395.     
  1396.     ULONG ulNormSubjectTag = m_pval[irtNormSubject].ulPropTag;
  1397.     m_pval[irtNormSubject].ulPropTag = PR_NULL;
  1398.  
  1399.     m_pval[irtClass].ulPropTag = PR_MESSAGE_CLASS;
  1400.     m_pval[irtClass].Value.lpszA = FormClassName;
  1401.  
  1402.     /*
  1403.      *If the message didn't have PR_CONVERSATION_TOPIC when we loaded it, we'll
  1404.      *  set it every time we save the message. Otherwise we don't touch it
  1405.      */
  1406.     if(!m_fConvTopicSet)
  1407.     {   
  1408.         m_pval[irtConvTopic].ulPropTag = PR_CONVERSATION_TOPIC;
  1409.         if(PR_SUBJECT == m_pval[irtSubject].ulPropTag)
  1410.         {
  1411.             m_pval[irtConvTopic].Value.LPSZ = m_pval[irtSubject].Value.LPSZ;
  1412.         }
  1413.         else
  1414.         {
  1415.             m_pval[irtConvTopic].Value.LPSZ = "";
  1416.         }
  1417.     }
  1418.     else
  1419.     {
  1420.         m_pval[irtConvTopic].ulPropTag = PR_NULL;
  1421.     }
  1422.  
  1423.     /*
  1424.      * if the message doesn't have a PR_CONVERSATION_INDEX, create and set it
  1425.      *
  1426.      */
  1427.     if(m_cbConvIdx == 0)
  1428.     {
  1429.         if(!ScAddConversationIndex(0, NULL, &m_cbConvIdx,   &m_lpbConvIdx))
  1430.         {
  1431.             m_pval[irtConvIdx].ulPropTag = PR_CONVERSATION_INDEX;
  1432.             m_pval[irtConvIdx].Value.bin.lpb = m_lpbConvIdx;
  1433.             m_pval[irtConvIdx].Value.bin.cb = m_cbConvIdx;
  1434.         }
  1435.         else
  1436.         {
  1437.             m_pval[irtConvIdx].ulPropTag = PR_NULL;
  1438.         }
  1439.     }
  1440.     else
  1441.     {
  1442.         m_pval[irtConvIdx].ulPropTag = PR_NULL;
  1443.     }
  1444.  
  1445.     LPSPropProblemArray pProblems = NULL;
  1446.  
  1447.     hr = pmsg->SetProps(cpropMsg, m_pval, &pProblems);
  1448.     if (hr)
  1449.     {
  1450.         m_lsterr.HrSetLastError(hr, pmsg);
  1451.         goto err;
  1452.     }
  1453.     
  1454.     if(pProblems)
  1455.     {
  1456.         for(UINT ind = 0; ind < pProblems->cProblem; ++ind)
  1457.         { 
  1458.             if(PR_BODY == pProblems->aProblem[ind].ulPropTag &&
  1459.                MAPI_E_NOT_ENOUGH_MEMORY == pProblems->aProblem[ind].scode)
  1460.             {
  1461.                 hr = HrStreamOutMsgBody(pmsg, m_pval[irtBody].Value.LPSZ, &m_lsterr);
  1462.                 if(hr)
  1463.                 {
  1464.                     MAPIFreeBuffer(pProblems);
  1465.                     pProblems = NULL;
  1466.                     goto err;
  1467.                 }
  1468.                 break;
  1469.             }
  1470.         }
  1471.  
  1472.         MAPIFreeBuffer(pProblems);
  1473.         pProblems = NULL;
  1474.     }
  1475.  
  1476. err:
  1477.     m_pval[irtTo].ulPropTag = ulToTag;
  1478.     m_pval[irtCc].ulPropTag = ulCcTag;
  1479.     m_pval[irtNormSubject].ulPropTag = ulNormSubjectTag;
  1480.     
  1481.     return hr;
  1482. }
  1483.  
  1484.  
  1485. BOOL CBaseForm::TranslateMessage(MSG& msg)
  1486. {
  1487.     //
  1488.     //  We translate accelerators before the dialog message so that we
  1489.     //  can get our accelerators to override the dialog's.
  1490.     //
  1491.  
  1492.     if(msg.hwnd == m_hwnd || IsChild(m_hwnd, msg.hwnd))
  1493.     {
  1494.         if (::TranslateAccelerator(m_hwnd, m_HAccelTable, &msg))
  1495.         {
  1496.             return TRUE;
  1497.         }
  1498.     }
  1499.     
  1500.     if ((m_hwndDialog != NULL) && ::IsDialogMessage(m_hwndDialog, &msg))
  1501.     {
  1502.         return TRUE;
  1503.     }
  1504.     
  1505.     return FALSE;
  1506. }
  1507.  
  1508. ///  CBaseForm::UpdateRecipientsDisplay
  1509. //  go through m_padrlist and display TO and CC recipients in the 
  1510. //  edit boxes.
  1511. // Called only for send form. The Read form can use PR_DISPLAY_*
  1512. void CBaseForm::UpdateRecipientsDisplay(void)
  1513. {
  1514.     Assert(m_hwndDialog);
  1515.     
  1516.     Assert(m_eFormType == eformSend);
  1517.  
  1518.     HWND hwTo = GetDlgItem(m_hwndDialog, ID_TO);
  1519.     HWND hwCC = GetDlgItem(m_hwndDialog, ID_CC);
  1520.     
  1521.     Edit_SetText(hwTo, "");
  1522.     Edit_SetText(hwCC, "");
  1523.     
  1524.     if(m_padrlist == NULL || m_padrlist->cEntries == 0) return;
  1525.  
  1526.     #define ADRTEXTLEN  512
  1527.     char szTo [ADRTEXTLEN] = "";
  1528.     char szCC [ADRTEXTLEN] = "";
  1529.     
  1530.     BOOL fToFull = FALSE;
  1531.     BOOL fCCFull = FALSE;
  1532.  
  1533.     
  1534.     LPADRENTRY pae;
  1535.     for(pae = m_padrlist->aEntries;
  1536.         pae < m_padrlist->aEntries + m_padrlist->cEntries;
  1537.         ++pae)
  1538.     {
  1539.         if( NULL == pae->rgPropVals) continue;
  1540.  
  1541.         LPSPropValue lpsprop;
  1542.         LPSTR szDisplay = NULL;
  1543.         LPSTR szName = NULL;
  1544.         BOOL * pfFull = NULL;
  1545.         for(lpsprop = pae->rgPropVals;
  1546.             lpsprop < pae->rgPropVals + pae->cValues;
  1547.             ++lpsprop)
  1548.         {
  1549.             if(lpsprop->ulPropTag == PR_RECIPIENT_TYPE)
  1550.             {
  1551.                 if(lpsprop->Value.l == MAPI_TO)
  1552.                 {
  1553.                     szDisplay = szTo;
  1554.                     pfFull = &fToFull;
  1555.                 }
  1556.                 else if(lpsprop->Value.l == MAPI_CC)
  1557.                 {
  1558.                     szDisplay = szCC;
  1559.                     pfFull = &fCCFull;
  1560.                 }
  1561.             }
  1562.             else if(lpsprop->ulPropTag == PR_DISPLAY_NAME)
  1563.             {
  1564.                 szName = lpsprop->Value.LPSZ;
  1565.             }
  1566.             
  1567.             if(NULL != szName && NULL != szDisplay)
  1568.             {
  1569.                 Assert(pfFull);
  1570.  
  1571.                 if(*pfFull) break;
  1572.  
  1573.                 if(lstrlen(szDisplay) + lstrlen(szName) + 7 < ADRTEXTLEN)
  1574.                 {
  1575.                     lstrcat(szDisplay, szName);
  1576.                     lstrcat(szDisplay, "; ");
  1577.                 }
  1578.                 else
  1579.                 {
  1580.                     lstrcat(szDisplay, "...");
  1581.                     *pfFull = TRUE;
  1582.                 }
  1583.  
  1584.                 break;
  1585.             }
  1586.         }
  1587.     }
  1588.  
  1589.     //get rid of the "; " after the last recipient
  1590.     if(lstrlen(szTo) > 0  &&  !fToFull) 
  1591.         szTo[lstrlen(szTo)-2] = 0;
  1592.     if(lstrlen(szCC) > 0  &&  !fCCFull) 
  1593.         szCC[lstrlen(szCC)-2] = 0;
  1594.  
  1595.     Edit_SetText(hwTo, szTo);
  1596.     Edit_SetText(hwCC, szCC);
  1597.  
  1598. }
  1599.  
  1600.  
  1601. ///  CBaseForm::DisplayMessage
  1602. //
  1603. //  display the info from m_pval in the dialog
  1604. void CBaseForm::DisplayMessage(void)
  1605. {
  1606.     char    sz[256];
  1607.  
  1608.     Assert(m_hwnd);
  1609.     Assert(m_hwndDialog);
  1610.  
  1611.     if(NULL == m_pval) return;
  1612.  
  1613.     if (m_pval[irtSubject].ulPropTag == PR_SUBJECT)
  1614.     {
  1615.         SetDlgItemText(m_hwndDialog, ID_SUBJECT, m_pval[irtSubject].Value.LPSZ);
  1616.         lstrcpyn(sz, m_pval[irtSubject].Value.LPSZ, 200);
  1617.         sz[200] = 0;
  1618.         lstrcat(sz, " - ");
  1619.         lstrcat(sz, g_szWindowCaption);
  1620.         SetWindowText(m_hwnd, sz);
  1621.     }
  1622.     else
  1623.     {
  1624.         SetWindowText(m_hwnd, g_szWindowCaption);
  1625.     }
  1626.  
  1627.     if (m_pval[irtBody].ulPropTag == PR_BODY)
  1628.         SetDlgItemText(m_hwndDialog, ID_BODY, m_pval[irtBody].Value.LPSZ);
  1629.  
  1630.     if(m_eFormType == eformRead)
  1631.     {
  1632.         if (m_pval[irtFrom].ulPropTag == PR_SENDER_NAME)
  1633.         SetDlgItemText(m_hwndDialog, ID_FROM, m_pval[irtFrom].Value.LPSZ);
  1634.  
  1635.         if (m_pval[irtTime].ulPropTag == PR_CLIENT_SUBMIT_TIME) {
  1636.             FormatTime(&m_pval[irtTime].Value.ft, sz);
  1637.             SetDlgItemText(m_hwndDialog, ID_SENT, sz);
  1638.         }
  1639.  
  1640.         if (m_pval[irtTo].ulPropTag == PR_DISPLAY_TO)
  1641.             SetDlgItemText(m_hwndDialog, ID_TO, m_pval[irtTo].Value.LPSZ);
  1642.  
  1643.         Edit_SetModify(GetDlgItem(m_hwndDialog, ID_BODY), FALSE);
  1644.     }
  1645.     else if (m_eFormType == eformSend)
  1646.     {
  1647.         UpdateRecipientsDisplay();
  1648.  
  1649.         Edit_SetModify(GetDlgItem(m_hwndDialog, ID_BODY), FALSE);
  1650.         Edit_SetModify(GetDlgItem(m_hwndDialog, ID_SUBJECT), FALSE);
  1651.         Edit_SetModify(GetDlgItem(m_hwndDialog, ID_TO), FALSE);
  1652.         Edit_SetModify(GetDlgItem(m_hwndDialog, ID_CC), FALSE);
  1653.     }
  1654.     else
  1655.     {
  1656.         Assert(FALSE);
  1657.     }
  1658.   
  1659. }
  1660.  
  1661.  
  1662. /// CBaseForm::HrGetMsgDataFromUI
  1663. //
  1664. // save the  message info from the dialog into the m_pval
  1665. HRESULT CBaseForm::HrGetMsgDataFromUI(HWND hDlg)
  1666. {
  1667.  
  1668.     LONG cb = 0;
  1669.     HRESULT hr = hrSuccess;
  1670.  
  1671.  
  1672.     //have to call IsDirty() to make sure  m_fDirty is current
  1673.     if(!m_fDirty)
  1674.         IsDirty();
  1675.     
  1676.     if(m_eFormType == eformRead)
  1677.     {
  1678.         Assert(m_pval);
  1679.  
  1680.         if(!m_fDirty)
  1681.             return hrSuccess;
  1682.  
  1683.         //everything is read-only except for the body
  1684.         cb = GetWindowTextLength(GetDlgItem(hDlg, ID_BODY));
  1685.         if(m_pval[irtBody].ulPropTag == PR_BODY &&
  1686.          cb <= lstrlen(m_pval[irtBody].Value.LPSZ))
  1687.         {
  1688.             GetWindowText(GetDlgItem(hDlg, ID_BODY), m_pval[irtBody].Value.LPSZ, cb+1);
  1689.         }
  1690.         else
  1691.         {
  1692.             if(hr = MAPIAllocateMore(cb+1, m_pval, (LPVOID FAR *)&m_pval[irtBody].Value.LPSZ))
  1693.             {
  1694.                 return m_lsterr.HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
  1695.             }
  1696.  
  1697.             GetWindowText(GetDlgItem(hDlg, ID_BODY), m_pval[irtBody].Value.LPSZ, cb+1);
  1698.         }
  1699.  
  1700.         m_pval[irtBody].ulPropTag = PR_BODY;
  1701.     }
  1702.     else if (m_eFormType == eformSend)
  1703.     {
  1704.         if(!m_fDirty && m_pval != NULL) 
  1705.             return hrSuccess;
  1706.  
  1707.         if(NULL != m_pval)
  1708.         {
  1709.             MAPIFreeBuffer(m_pval);
  1710.             m_pval = NULL;
  1711.         }
  1712.         if(MAPIAllocateBuffer(sizeof(SPropValue)* cpropMsg, (LPVOID FAR *) &m_pval))
  1713.         {
  1714.             return m_lsterr.HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
  1715.         }
  1716.         
  1717.         ZeroMemory(m_pval, sizeof(SPropValue)* cpropMsg);
  1718.         
  1719.         
  1720.         m_pval[irtTime].ulPropTag = PR_NULL;
  1721.         m_pval[irtFrom].ulPropTag = PR_NULL;                     
  1722.  
  1723.         cb = GetWindowTextLength(GetDlgItem(hDlg, ID_SUBJECT)) + 1;
  1724.         /*if(cb > 0)
  1725.         {*/
  1726.             if(MAPIAllocateMore(cb+1, m_pval, (LPVOID FAR *)&m_pval[irtSubject].Value.LPSZ))
  1727.             {
  1728.                 return m_lsterr.HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
  1729.             }
  1730.  
  1731.             cb = GetWindowText(GetDlgItem(hDlg, ID_SUBJECT), m_pval[irtSubject].Value.LPSZ, cb+1);
  1732.             m_pval[irtSubject].ulPropTag = PR_SUBJECT;
  1733.         /*}
  1734.         else
  1735.         { //no subject
  1736.     
  1737.             m_pval[irtSubject].ulPropTag = PR_NULL;
  1738.         } */
  1739.     
  1740.         cb = GetWindowTextLength(GetDlgItem(hDlg, ID_BODY)) +1 ;
  1741.         /*if(cb > 0)
  1742.         { */
  1743.             if(MAPIAllocateMore(cb+1, m_pval, (LPVOID FAR *)&m_pval[irtBody].Value.LPSZ))
  1744.             {
  1745.                 return m_lsterr.HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
  1746.             }
  1747.  
  1748.             cb = GetWindowText(GetDlgItem(hDlg, ID_BODY), m_pval[irtBody].Value.LPSZ, cb+1);
  1749.             m_pval[irtBody].ulPropTag = PR_BODY;
  1750.         /*}
  1751.         else
  1752.         { //no body
  1753.     
  1754.             m_pval[irtBody].ulPropTag = PR_NULL;
  1755.         } */
  1756.     
  1757.         cb = GetWindowTextLength(GetDlgItem(hDlg, ID_TO));
  1758.         if(cb > 0)
  1759.         {
  1760.             if(hr = MAPIAllocateMore(cb+1, m_pval, (LPVOID FAR *)&m_pval[irtTo].Value.LPSZ))
  1761.             {
  1762.                 return m_lsterr.HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
  1763.             }
  1764.  
  1765.             GetWindowText(GetDlgItem(hDlg, ID_TO), m_pval[irtTo].Value.LPSZ, cb+1);
  1766.             m_pval[irtTo].ulPropTag = PR_DISPLAY_TO;
  1767.         }
  1768.         else
  1769.         { //no to
  1770.     
  1771.             m_pval[irtTo].ulPropTag = PR_NULL;
  1772.         }
  1773.         
  1774.         cb = GetWindowTextLength(GetDlgItem(hDlg, ID_CC));
  1775.         if(cb > 0)
  1776.         {
  1777.             if(hr = MAPIAllocateMore(cb+1, m_pval, (LPVOID FAR *)&m_pval[irtCc].Value.LPSZ))
  1778.             {
  1779.                 return m_lsterr.HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
  1780.             }
  1781.  
  1782.             GetWindowText(GetDlgItem(hDlg, ID_CC), m_pval[irtCc].Value.LPSZ, cb+1);
  1783.             m_pval[irtTo].ulPropTag = PR_DISPLAY_CC;
  1784.         }
  1785.         else
  1786.         { //no cc
  1787.     
  1788.             m_pval[irtCc].ulPropTag = PR_NULL;
  1789.         }
  1790.     }   
  1791.     else
  1792.     {
  1793.         Assert(FALSE);
  1794.     }
  1795.     return hr;
  1796. }
  1797.  
  1798. /// CBaseForm::IsAddressed
  1799. //
  1800. //Does m_padrlist contain a recipient?
  1801. BOOL CBaseForm::IsAddressed(void)
  1802. {
  1803.     Assert(m_eFormType == eformSend) ;
  1804.         
  1805.     if(NULL == m_padrlist || m_padrlist->cEntries == 0)
  1806.         return FALSE;
  1807.     
  1808.     for(LPADRENTRY pae = m_padrlist->aEntries;
  1809.         pae < m_padrlist->aEntries + m_padrlist->cEntries; ++pae)
  1810.     {
  1811.         if(pae->rgPropVals)
  1812.             return TRUE;
  1813.     }
  1814.  
  1815.     return FALSE;
  1816. }
  1817.  
  1818. ///     CBaseForm::ConfigMenu
  1819. //Enable/disable menu commands based on the values of m_ulSiteStatus
  1820. // and m_ulViewStatus
  1821. void CBaseForm::ConfigMenu(HMENU hMenu)
  1822. {
  1823.     if(m_eFormType == eformRead)
  1824.     {
  1825.         EnableMenuItem(hMenu, IDC_MESSAGE_SAVE,
  1826.             MF_BYCOMMAND|((m_ulSiteStatus & VCSTATUS_SAVE)? MF_ENABLED:MF_GRAYED));
  1827.         EnableMenuItem(hMenu, IDC_MESSAGE_DELETE,
  1828.             MF_BYCOMMAND| (!(m_ulViewStatus & VCSTATUS_READONLY) &&
  1829.                             (m_ulSiteStatus & VCSTATUS_DELETE) ? MF_ENABLED:MF_GRAYED));
  1830.         EnableMenuItem(hMenu, IDC_MESSAGE_COPY,
  1831.             MF_BYCOMMAND| (!(m_ulViewStatus & VCSTATUS_READONLY) &&
  1832.                             (m_ulSiteStatus & VCSTATUS_COPY) ? MF_ENABLED:MF_GRAYED));
  1833.         EnableMenuItem(hMenu, IDC_MESSAGE_MOVE,
  1834.             MF_BYCOMMAND| (!(m_ulViewStatus & VCSTATUS_READONLY) &&
  1835.                             (m_ulSiteStatus & VCSTATUS_MOVE) ? MF_ENABLED:MF_GRAYED));
  1836.         EnableMenuItem(hMenu, IDC_VIEW_ITEMABOVE,
  1837.             MF_BYCOMMAND|(m_ulViewStatus & VCSTATUS_PREV ? MF_ENABLED:MF_GRAYED));
  1838.         EnableMenuItem(hMenu, IDC_VIEW_ITEMBELOW,
  1839.             MF_BYCOMMAND|(m_ulViewStatus & VCSTATUS_NEXT ? MF_ENABLED:MF_GRAYED));
  1840.     }
  1841.  
  1842.     else
  1843.     {
  1844.         EnableMenuItem(hMenu, IDC_MESSAGE_SUBMIT,
  1845.             MF_BYCOMMAND|((m_ulSiteStatus &VCSTATUS_SUBMIT) ? MF_ENABLED:MF_GRAYED));
  1846.         EnableMenuItem(hMenu, IDC_MESSAGE_SAVE,
  1847.             MF_BYCOMMAND|((m_ulSiteStatus & VCSTATUS_SAVE)? MF_ENABLED:MF_GRAYED));
  1848.         EnableMenuItem(hMenu, IDC_MESSAGE_DELETE,
  1849.             MF_BYCOMMAND| (!(m_ulViewStatus & VCSTATUS_READONLY) &&
  1850.                             (m_ulSiteStatus & VCSTATUS_DELETE) ? MF_ENABLED:MF_GRAYED));
  1851.         EnableMenuItem(hMenu, IDC_MESSAGE_COPY,
  1852.             MF_BYCOMMAND| (!(m_ulViewStatus & VCSTATUS_READONLY) &&
  1853.                             (m_ulSiteStatus & VCSTATUS_COPY) ? MF_ENABLED:MF_GRAYED));
  1854.         EnableMenuItem(hMenu, IDC_MESSAGE_MOVE,
  1855.             MF_BYCOMMAND| (!(m_ulViewStatus & VCSTATUS_READONLY) &&
  1856.                             (m_ulSiteStatus & VCSTATUS_MOVE) ? MF_ENABLED:MF_GRAYED));
  1857.                             
  1858.     }
  1859. }
  1860.         
  1861. ///CBaseForm::HrReply
  1862. //
  1863. //
  1864. HRESULT CBaseForm::HrReply(eREPLYTYPE eReplyType, HWND hwndParent, LPCRECT prect)
  1865. {
  1866.     
  1867.     //reply all is not implemented
  1868.     Assert(eREPLY == eReplyType || eFORWARD == eReplyType);
  1869.     
  1870.     HRESULT hr;
  1871.     LONG cb;
  1872.     
  1873.     char * szBody = NULL;
  1874.     char * szSubject = NULL;
  1875.     SPropValue val[3] = {0};
  1876.     
  1877.     enum { eName, eAddrType, eEID, eRecipType, eDim};
  1878.     SizedSPropTagArray(eDim, sptSender) = 
  1879.         {eDim, {PR_SENDER_NAME, PR_SENDER_ADDRTYPE,
  1880.                 PR_SENDER_ENTRYID, PR_NULL}};
  1881.  
  1882.     LPSPropProblemArray pProblems = NULL;
  1883.     LPMAPIFORM          pfrmReply = NULL;
  1884.     LPPERSISTMESSAGE    ppermsg = NULL;
  1885.     LPMAPIMESSAGESITE   pmsgsite = NULL;
  1886.     LPMAPIVIEWCONTEXT   pviewctx = NULL;
  1887.     LPMESSAGE           pmsg = NULL;
  1888.     ULONG cbNewConvIdx = 0;
  1889.     LPBYTE lpbNewConvIdx = NULL;
  1890.     
  1891.     Assert(m_pmsg);
  1892.     
  1893.  
  1894.     hr = m_pClassFactory->CreateInstance(NULL, IID_IMAPIForm, (LPVOID FAR *) &pfrmReply);
  1895.     if(hr)
  1896.     {
  1897.         m_lsterr.HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
  1898.         goto err;
  1899.     }
  1900.      
  1901.     hr = pfrmReply->QueryInterface(IID_IPersistMessage, (LPVOID *) &ppermsg);
  1902.     if(hr)
  1903.     {
  1904.         m_lsterr.HrSetLastError(hr, pfrmReply);
  1905.         goto err;
  1906.     }
  1907.                                     
  1908.     hr = m_pmsgsite->NewMessage(FALSE, NULL, ppermsg, &pmsg, &pmsgsite, &pviewctx);
  1909.     if(hr)
  1910.     {
  1911.         m_lsterr.HrSetLastError(hr, m_pmsgsite);
  1912.         goto err;
  1913.     }
  1914.     
  1915.     hr = m_pmsg->CopyTo(0, NULL, (LPSPropTagArray)&sptExcludedProps,
  1916.                         0, NULL, &IID_IMessage, pmsg, 0, &pProblems);
  1917.     if(hr)
  1918.     {
  1919.         m_lsterr.HrSetLastError(hr, m_pmsg);
  1920.         goto err;
  1921.     }
  1922.     
  1923.     if(pProblems)
  1924.     {
  1925.         DebugTraceProblems("SmplForm: CopyTo returned ...", pProblems);
  1926.         //  if any of the errors is other than MAPI_E_COMPUTED, fail
  1927.         for(UINT ind = 0; ind < pProblems->cProblem; ++ind)
  1928.         { 
  1929.             if(MAPI_E_COMPUTED != pProblems->aProblem[ind].scode)
  1930.             {
  1931.                 hr = m_lsterr.HrSetLastError(
  1932.                             ResultFromScode(pProblems->aProblem[ind].scode));
  1933.                 MAPIFreeBuffer(pProblems);
  1934.                 pProblems = NULL;
  1935.                 goto err;
  1936.                 
  1937.             }
  1938.         }
  1939.         MAPIFreeBuffer(pProblems);
  1940.         pProblems = NULL;
  1941.     }
  1942.  
  1943.     if(m_pval && m_pval[irtNormSubject].ulPropTag == PR_NORMALIZED_SUBJECT)
  1944.         cb = lstrlen(m_pval[irtNormSubject].Value.LPSZ);
  1945.     else
  1946.         cb = 0;
  1947.     
  1948.     if(hr = MAPIAllocateBuffer(cb+lstrlen(szRE_PREFIX)+1, (LPVOID FAR *) &szSubject))
  1949.     {
  1950.         m_lsterr.HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
  1951.         goto err;
  1952.     }
  1953.     
  1954.  
  1955.     *szSubject = '\0';
  1956.     
  1957.     if(eREPLY == eReplyType)
  1958.     {
  1959.         lstrcat(szSubject, szRE_PREFIX);
  1960.     }
  1961.     else
  1962.     {
  1963.         lstrcat(szSubject, szFW_PREFIX);
  1964.     } 
  1965.  
  1966.     if(cb > 0)
  1967.     {
  1968.         lstrcat(szSubject, m_pval[irtNormSubject].Value.LPSZ);
  1969.     }
  1970.  
  1971.     val[1].Value.lpszA = szSubject;
  1972.     val[1].ulPropTag = PR_SUBJECT;
  1973.     
  1974.     HrSaveToString(&szBody);
  1975.     if(szBody)
  1976.     {
  1977.         val[0].Value.LPSZ = szBody;
  1978.         val[0].ulPropTag = PR_BODY;
  1979.     }
  1980.     else
  1981.         val[0].ulPropTag = PR_NULL;
  1982.  
  1983.     
  1984.     /*
  1985.      * Create a conversation index for the reply msg based on that of ours
  1986.      *
  1987.      */
  1988.     if(!ScAddConversationIndex(m_cbConvIdx, m_lpbConvIdx,
  1989.                                 &cbNewConvIdx, &lpbNewConvIdx))
  1990.     {
  1991.         val[2].ulPropTag = PR_CONVERSATION_INDEX;
  1992.         val[2].Value.bin.cb = cbNewConvIdx;
  1993.         val[2].Value.bin.lpb = lpbNewConvIdx;
  1994.     }
  1995.     else
  1996.     {
  1997.         val[2].ulPropTag = PR_NULL;
  1998.     }
  1999.  
  2000.     hr = pmsg->SetProps(3, val, &pProblems);
  2001.     MAPIFreeBuffer(lpbNewConvIdx);
  2002.     lpbNewConvIdx = NULL;
  2003.  
  2004.     MAPIFreeBuffer(szSubject);
  2005.     szSubject = NULL;
  2006.     
  2007.     if(!hr)
  2008.     {
  2009.         if(pProblems)
  2010.         {
  2011.             for(UINT ind = 0; ind < pProblems->cProblem; ++ind)
  2012.             { 
  2013.                 if(PR_BODY == pProblems->aProblem[ind].ulPropTag &&
  2014.                    MAPI_E_NOT_ENOUGH_MEMORY == pProblems->aProblem[ind].scode)
  2015.                 {
  2016.                     hr = HrStreamOutMsgBody(pmsg, szBody, &m_lsterr);
  2017.                     if(hr)
  2018.                     {
  2019.                         MAPIFreeBuffer(pProblems);
  2020.                         pProblems = NULL;
  2021.                         goto err;
  2022.                     }
  2023.                     break;
  2024.                 }
  2025.             }
  2026.             MAPIFreeBuffer(pProblems);
  2027.             pProblems = NULL;
  2028.         }
  2029.     }
  2030.     else
  2031.     {
  2032.         m_lsterr.HrSetLastError(hr, m_pmsg);
  2033.         goto err;
  2034.     }
  2035.  
  2036.     // if it's a reply, set the addressee
  2037.     if(eREPLY == eReplyType)
  2038.     {               
  2039.         LPADRLIST pal = NULL;
  2040.         ULONG cVal = 0;
  2041.         LPSPropValue pval = NULL;
  2042.  
  2043.         hr = MAPIAllocateBuffer(CbNewADRLIST(1), (LPVOID FAR *)&pal);
  2044.         if(hr)
  2045.         {
  2046.             m_lsterr.HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
  2047.             goto err;
  2048.         }
  2049.         hr = m_pmsg->GetProps((LPSPropTagArray) &sptSender, 0, &cVal, &pval);
  2050.         if(hr) //treat warnings as an error, 'cause the props we ask for are required
  2051.         {
  2052.             m_lsterr.HrSetLastError(hr, m_pmsg);
  2053.             MAPIFreeBuffer(pal);
  2054.             goto err;
  2055.         }
  2056.         
  2057.         Assert(cVal == eDim);
  2058.  
  2059.         pval[eRecipType].ulPropTag = PR_RECIPIENT_TYPE;
  2060.         pval[eRecipType].Value.l = MAPI_TO;
  2061.  
  2062.         Assert(pval[eName].ulPropTag == PR_SENDER_NAME);
  2063.         pval[eName].ulPropTag = PR_DISPLAY_NAME;
  2064.  
  2065.         Assert(pval[eAddrType].ulPropTag == PR_SENDER_ADDRTYPE);
  2066.         pval[eAddrType].ulPropTag = PR_ADDRTYPE;
  2067.  
  2068.         Assert(pval[eEID].ulPropTag == PR_SENDER_ENTRYID);
  2069.         pval[eEID].ulPropTag = PR_ENTRYID;
  2070.         
  2071.         pal->aEntries[0].rgPropVals = pval;
  2072.         
  2073.         pal->cEntries = 1;
  2074.         pal->aEntries[0].cValues = eDim;
  2075.         
  2076.         hr = pmsg->ModifyRecipients(0, pal);
  2077.         FreePadrlist(pal); //this will also free pval
  2078.         pal = NULL;
  2079.         pval = NULL;
  2080.         if(hr)
  2081.         {
  2082.             m_lsterr.HrSetLastError(hr, pmsg);
  2083.             goto err;
  2084.         }
  2085.     }
  2086.  
  2087.     hr = ppermsg->Load(pmsgsite, pmsg, 0, MSGFLAG_UNSENT );
  2088.     if(hr)
  2089.     {
  2090.         m_lsterr.HrSetLastError(hr, ppermsg);
  2091.         goto err;
  2092.     }
  2093.  
  2094.     hr = pfrmReply->DoVerb(EXCHIVERB_OPEN, pviewctx, (ULONG)hwndParent, prect);
  2095.     if(hr)
  2096.     {
  2097.         m_lsterr.HrSetLastError(hr, pfrmReply);
  2098.         pfrmReply->ShutdownForm(SAVEOPTS_NOSAVE);
  2099.         goto err;
  2100.     }
  2101.  
  2102. err:
  2103.     
  2104.     UlRelease(pfrmReply);
  2105.     UlRelease(ppermsg);
  2106.     UlRelease(pmsgsite);
  2107.     UlRelease(pviewctx);
  2108.     UlRelease(pmsg);
  2109.     delete [] szBody;
  2110.     
  2111.     return hr;
  2112. }
  2113.  
  2114. ///     CBaseForm::HrSaveToString
  2115. //      
  2116. //  The returned string has to be freed using delete
  2117. HRESULT CBaseForm::HrSaveToString(LPSTR * pszMessage)
  2118. {
  2119.     Assert(m_pval);
  2120.     Assert(pszMessage);
  2121.  
  2122.     ostrstream  ostrBody;
  2123.     char *szMsg = NULL;
  2124.  
  2125.     ostrBody << "\r\n-----------------------------";
  2126.     ostrBody << "\r\nFrom:\t";
  2127.     if(m_pval[irtFrom].ulPropTag == PR_SENDER_NAME)
  2128.     {
  2129.         ostrBody << m_pval[irtFrom].Value.LPSZ;
  2130.     }
  2131.     else
  2132.     {
  2133.         ostrBody << "Unknown";
  2134.     }
  2135.  
  2136.     if(m_pval[irtTime].ulPropTag == PR_CLIENT_SUBMIT_TIME)
  2137.     {
  2138.         char sz[30];
  2139.         FormatTime(&m_pval[irtTime].Value.ft, sz);
  2140.         ostrBody << "\r\nSent:\t";
  2141.         ostrBody << sz;
  2142.     }
  2143.     
  2144.     if(m_pval[irtTo].ulPropTag == PR_DISPLAY_TO)
  2145.     {
  2146.         ostrBody << "\r\nTo:\t";
  2147.         ostrBody <<  m_pval[irtTo].Value.LPSZ;
  2148.     }
  2149.  
  2150.     if(m_pval[irtSubject].ulPropTag == PR_SUBJECT)
  2151.     {
  2152.         ostrBody << "\r\nSubject:\t";
  2153.         ostrBody << m_pval[irtSubject].Value.LPSZ;
  2154.     }
  2155.  
  2156.     ostrBody << "\r\n";
  2157.     if(m_pval && m_pval[irtBody].ulPropTag == PR_BODY)
  2158.         ostrBody << m_pval[irtBody].Value.LPSZ;
  2159.         
  2160.     ostrBody << ends;
  2161.     
  2162.     szMsg = ostrBody.str();
  2163.  
  2164.     if(szMsg != NULL)
  2165.     {
  2166.         *pszMessage = szMsg;
  2167.         return hrSuccess;
  2168.     }
  2169.     else
  2170.     {
  2171.         *pszMessage = NULL;
  2172.         return m_lsterr.HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
  2173.     }
  2174. }
  2175.  
  2176. ///     CBaseForm::HrQuerySave 
  2177. //
  2178. //
  2179. HRESULT CBaseForm::HrQuerySave (DWORD ulSaveOptions)
  2180. {
  2181.     HRESULT             hr = hrSuccess;
  2182.     UINT                ui;
  2183.  
  2184.     //
  2185.     // Check to see if we are marked as dirty.  If we are clean then
  2186.     //  we can just return without doing any work
  2187.     //
  2188.  
  2189.     if(GetScode(IsDirty()) != S_OK)
  2190.         return hrSuccess;
  2191.  
  2192.     switch( ulSaveOptions )
  2193.     {
  2194.     default:
  2195.         Assert(FALSE);
  2196.  
  2197.     case SAVEOPTS_SAVEIFDIRTY:
  2198.         break;
  2199.  
  2200.     case SAVEOPTS_NOSAVE:
  2201.         return hrSuccess;
  2202.  
  2203.     case SAVEOPTS_PROMPTSAVE:
  2204.         ui = ShowMessageBox (m_hwnd, "Would you like to save changes?",
  2205.                            g_szFormName, MB_ICONEXCLAMATION | MB_YESNOCANCEL);
  2206.         switch (ui)
  2207.         {
  2208.         case IDYES:
  2209.             break;
  2210.  
  2211.         default:
  2212.             Assert(FALSE);
  2213.         case IDNO:
  2214.             return hrSuccess;
  2215.         
  2216.         case IDCANCEL:
  2217.             return MAPI_E_USER_CANCEL;
  2218.  
  2219.         }
  2220.     }
  2221.  
  2222.     hr = m_pmsgsite->SaveMessage();
  2223.     if(hr)
  2224.         m_lsterr.HrSetLastError(hr, m_pmsgsite);
  2225.  
  2226.     return hr;
  2227. }
  2228.  
  2229. ///  CBaseForm::DoDelete
  2230. //
  2231. //  called only from our UI
  2232. void CBaseForm::DoDelete(void)
  2233. {
  2234.     HRESULT hr;
  2235.     RECT rect;
  2236.  
  2237.     GetWindowRect(m_hwnd, &rect);
  2238.     
  2239.     hr = m_pmsgsite->DeleteMessage(m_pviewctx, &rect);
  2240.     if(HR_FAILED(hr))
  2241.     {
  2242.         m_lsterr.HrSetLastError(hr, m_pmsgsite);
  2243.         ShowError();
  2244.     }
  2245.     if(NULL == m_pmsg)
  2246.     {
  2247.         ShutdownForm(SAVEOPTS_NOSAVE);
  2248.     }
  2249. }
  2250.  
  2251. ///  CBaseForm::DoSave
  2252. //
  2253. //  called only from our UI
  2254. void CBaseForm::DoSave(void)
  2255. {
  2256.     HRESULT hr;
  2257.  
  2258.     hr = m_pmsgsite->SaveMessage();
  2259.     if (FAILED(hr))
  2260.     {
  2261.         m_lsterr.HrSetLastError(hr, m_pmsgsite);
  2262.         ShowError();
  2263.     }
  2264. }
  2265.             
  2266. ///  CBaseForm::DoSubmit
  2267. //
  2268. //  called only from our UI
  2269. void CBaseForm::DoSubmit(void)
  2270. {
  2271.     HRESULT hr;
  2272.  
  2273.     if(!IsAddressed())
  2274.     {
  2275.         ShowMessageBox(m_hwndDialog, "No recipients", g_szFormName, MB_OK);
  2276.         return;
  2277.     } 
  2278.     
  2279.     hr = m_pmsgsite->SubmitMessage(0);
  2280.     if (FAILED(hr))
  2281.     {
  2282.         m_lsterr.HrSetLastError(hr, m_pmsgsite);
  2283.         ShowError();
  2284.     }
  2285.     else
  2286.     {
  2287.         m_viewnotify.OnSubmitted();
  2288.     }   
  2289.     
  2290.     if (m_pmsg == NULL)
  2291.         ShutdownForm(SAVEOPTS_NOSAVE);
  2292. }
  2293.  
  2294. ///  CBaseForm::DoNext
  2295. //
  2296. //  called only from our UI
  2297. void CBaseForm::DoNext(ULONG ulDir)
  2298. {   
  2299.     Assert(VCDIR_NEXT == ulDir || VCDIR_PREV == ulDir);
  2300.  
  2301.     HRESULT hr;
  2302.     
  2303.     hr = HrQuerySave(SAVEOPTS_PROMPTSAVE);
  2304.     if(hr)
  2305.     {
  2306.         if(hr != MAPI_E_USER_CANCEL)
  2307.             ShowError();
  2308.         return;
  2309.     }
  2310.  
  2311.     RECT rect;
  2312.     GetWindowRect(m_hwnd, &rect);
  2313.         
  2314.     hr = ViewCtx()->ActivateNext(ulDir, &rect);
  2315.     if(NULL == m_pmsg)
  2316.     {
  2317.         ShutdownForm(SAVEOPTS_NOSAVE);
  2318.     }
  2319. }
  2320.  
  2321. ///  CBaseForm::DoReply
  2322. //
  2323. //  called only from our UI
  2324. void CBaseForm::DoReply(eREPLYTYPE eType)
  2325. {
  2326.     HRESULT hr;
  2327.  
  2328.     hr = HrQuerySave(SAVEOPTS_PROMPTSAVE);
  2329.     if(hr)
  2330.     {
  2331.         if(hr != MAPI_E_USER_CANCEL)
  2332.             ShowError();
  2333.         return;
  2334.     }
  2335.  
  2336.     RECT rect;
  2337.     GetWindowRect(m_hwnd, &rect);
  2338.  
  2339.     int iOffset = GetSystemMetrics(SM_CYCAPTION);
  2340.     OffsetRect(&rect, iOffset, iOffset);
  2341.     
  2342.     hr = HrReply(eType, m_hwnd, &rect);
  2343.     if(!hr)
  2344.     {
  2345.         ShutdownForm(SAVEOPTS_NOSAVE);
  2346.     }
  2347.     else
  2348.     {
  2349.         ShowError();
  2350.     }
  2351. }
  2352.  
  2353. ///  CBaseForm::DoCopy
  2354. //
  2355. //  called only from our UI
  2356. void CBaseForm::DoCopy(void)
  2357. {
  2358.     HRESULT         hr;
  2359.     LPMAPIFOLDER    pfld = NULL;
  2360.     LPMDB           pmdb = NULL;
  2361.     
  2362.     if(!FGetFoldChooser())
  2363.     {
  2364.         ShowMessageBox(m_hwnd, "Can't copy", g_szFormName, MB_OK | MB_ICONSTOP);
  2365.         return;
  2366.     }
  2367.  
  2368.     Assert(m_lpfnHrPickFolder && m_hChsFldDll);
  2369.     
  2370.     if (m_pses == NULL)
  2371.     {
  2372.         hr = m_pmsgsite->GetSession(&m_pses);
  2373.         if(hr)
  2374.         {
  2375.             m_lsterr.HrSetLastError(hr, m_pmsgsite);
  2376.             ShowError();
  2377.             return;
  2378.         }
  2379.     }
  2380.  
  2381.     BOOL fOldModalUp = g_FModalUp;
  2382.  
  2383.     g_FModalUp = TRUE;
  2384.  
  2385.     hr = (*m_lpfnHrPickFolder)(g_hinst, m_hwnd, m_pses, &pfld, &pmdb,
  2386.                                 &m_cbCFDState, &m_pbCFDState);
  2387.  
  2388.     
  2389.     g_FModalUp = fOldModalUp;
  2390.  
  2391.     if(hr)
  2392.     {
  2393.         if(GetScode(hr) != MAPI_E_USER_CANCEL)
  2394.             ShowMessageBox(m_hwnd, "Can't copy", g_szFormName, MB_OK | MB_ICONSTOP);
  2395.  
  2396.         return;
  2397.     }
  2398.  
  2399.     Assert(m_pmsgsite);
  2400.     Assert(pfld);
  2401.     Assert(pmdb);
  2402.  
  2403.     hr = m_pmsgsite->CopyMessage(pfld);
  2404.     pfld->Release();
  2405.     pmdb->Release();
  2406.     if(hr)
  2407.     {
  2408.         m_lsterr.HrSetLastError(hr, m_pmsgsite);
  2409.         ShowError();
  2410.         return;
  2411.     }
  2412.  
  2413. }
  2414.  
  2415.  
  2416. ///  CBaseForm::DoMove
  2417. //
  2418. //  called only from our UI
  2419. void CBaseForm::DoMove(void)
  2420. {
  2421.     HRESULT         hr;
  2422.     LPMAPIFOLDER    pfld = NULL;
  2423.     LPMDB           pmdb = NULL;
  2424.     
  2425.     if(!FGetFoldChooser())
  2426.     {
  2427.         ShowMessageBox(m_hwnd, "Can't move", g_szFormName, MB_OK | MB_ICONSTOP);
  2428.         return;
  2429.     }
  2430.  
  2431.     Assert(m_lpfnHrPickFolder && m_hChsFldDll);
  2432.     
  2433.     if (m_pses == NULL)
  2434.     {
  2435.         hr = m_pmsgsite->GetSession(&m_pses);
  2436.         if(hr)
  2437.         {
  2438.             m_lsterr.HrSetLastError(hr, m_pmsgsite);
  2439.             ShowError();
  2440.             return;
  2441.         }
  2442.     }
  2443.  
  2444.     
  2445.     BOOL fOldModalUp = g_FModalUp;
  2446.  
  2447.     g_FModalUp = TRUE;
  2448.  
  2449.     hr = (*m_lpfnHrPickFolder)(g_hinst, m_hwnd, m_pses, &pfld, &pmdb,
  2450.                                 //&m_cbCFDState, &m_pbCFDState);
  2451.                                 NULL, NULL);
  2452.  
  2453.     g_FModalUp = fOldModalUp;
  2454.  
  2455.     if(hr)
  2456.     {
  2457.         if(GetScode(hr) != MAPI_E_USER_CANCEL)
  2458.             ShowMessageBox(m_hwnd, "Can't move", g_szFormName, MB_OK | MB_ICONSTOP);
  2459.  
  2460.         return;
  2461.     }
  2462.  
  2463.     Assert(m_pmsgsite);
  2464.     Assert(pfld);
  2465.     Assert(pmdb);
  2466.  
  2467.     RECT rect;
  2468.     GetWindowRect(m_hwnd, &rect);
  2469.  
  2470.     hr = m_pmsgsite->MoveMessage(pfld, ViewCtx(), &rect);
  2471.     pfld->Release();
  2472.     pmdb->Release();
  2473.     if(HR_FAILED(hr))
  2474.     {
  2475.         m_lsterr.HrSetLastError(hr, m_pmsgsite);
  2476.         ShowError();
  2477.         return;
  2478.     }
  2479.  
  2480.     if(NULL == m_pmsg)
  2481.     {
  2482.         ShutdownForm(SAVEOPTS_NOSAVE);
  2483.     }
  2484.  
  2485.  
  2486. }
  2487.  
  2488. //wraper for MessageBox()
  2489. int CBaseForm::ShowMessageBox(HWND hwnd, LPCTSTR lpszText, LPCTSTR lpszTitle, UINT uiStyle)
  2490. {
  2491.     int iret;
  2492.     BOOL fOldModalUp = g_FMBoxUp;
  2493.     
  2494.     g_FMBoxUp = TRUE;
  2495.  
  2496.     iret = MessageBox(hwnd, lpszText, lpszTitle, uiStyle);
  2497.  
  2498.     g_FMBoxUp = fOldModalUp;
  2499.  
  2500.     return iret;
  2501. }
  2502.         
  2503. //wraper for m_lsterr.ShowError()
  2504. void CBaseForm::ShowError(void)
  2505. {
  2506.     int iret;
  2507.     BOOL fOldModalUp = g_FMBoxUp;
  2508.     
  2509.     g_FMBoxUp = TRUE;
  2510.  
  2511.     iret = m_lsterr.ShowError(m_hwnd);
  2512.  
  2513.     g_FMBoxUp = fOldModalUp;
  2514.  
  2515. }
  2516.  
  2517. BOOL CBaseForm::FGetFoldChooser(void)
  2518. {
  2519.     if(m_lpfnHrPickFolder)
  2520.         return TRUE;
  2521.  
  2522.     Assert(!m_hChsFldDll);
  2523.  
  2524.     UINT uiErrMode = SetErrorMode(SEM_NOOPENFILEERRORBOX);
  2525.  
  2526.     m_hChsFldDll = LoadLibrary(szChsFldDllName);
  2527.  
  2528.     SetErrorMode(uiErrMode);
  2529.  
  2530.     if(m_hChsFldDll)
  2531.     {
  2532.         if((m_lpfnHrPickFolder = (HRPICKFOLDER)GetProcAddress(m_hChsFldDll,
  2533.                                         szChsFldFnName)))
  2534.         {
  2535.             return TRUE;
  2536.         }
  2537.  
  2538.         DebugTrace("smpfrm: GetProcAddress for %s failed", szChsFldFnName);
  2539.         
  2540.         FreeLibrary(m_hChsFldDll);
  2541.         m_hChsFldDll = NULL;
  2542.     }
  2543.     else
  2544.     {
  2545.         DebugTrace("smpfrm: failed to load choose folder dll\n");
  2546.     }
  2547.  
  2548.     return FALSE;
  2549. }
  2550.  
  2551. //
  2552. //if the body is to large for GetProps, have to use IStream
  2553. //
  2554. HRESULT HrStreamInMsgBody(LPMESSAGE pmsg, LPVOID pbase,
  2555.                             LPSTR *  pszBody, CLastError * plasterror)
  2556. {
  2557.     Assert(pmsg);
  2558.     Assert(pszBody);
  2559.     Assert(plasterror);
  2560.  
  2561.     HRESULT hr;
  2562.     LPSTREAM lpstreamBody = NULL;
  2563.     STATSTG statstg;
  2564.     LPSTR szRet = NULL;
  2565.     ULONG cb = 0;
  2566.  
  2567.     Assert(pszBody);
  2568.     *pszBody = NULL;
  2569.  
  2570.     hr = pmsg->OpenProperty(PR_BODY, &IID_IStream,
  2571.                             STGM_READ, 0, (LPUNKNOWN FAR *) &lpstreamBody);
  2572.     if(S_OK != GetScode(hr))
  2573.     {
  2574.         plasterror->HrSetLastError(hr, pmsg);
  2575.         goto err;
  2576.     }
  2577.     
  2578.     hr = lpstreamBody->Stat(&statstg, STATFLAG_NONAME);
  2579.     if(S_OK != GetScode(hr))
  2580.     {
  2581.         plasterror->HrSetLastError(hr, lpstreamBody);
  2582.         goto err;
  2583.     }
  2584.     Assert(statstg.cbSize.HighPart == 0);
  2585.     
  2586.     //if p base is not null, link the new buffer to it
  2587.     if(pbase)
  2588.     {
  2589.         if(MAPIAllocateMore(statstg.cbSize.LowPart + 1, pbase, (LPVOID FAR *) &szRet))
  2590.         {
  2591.             plasterror->HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
  2592.             goto err;
  2593.         }
  2594.     }
  2595.     else
  2596.     {
  2597.         if(MAPIAllocateBuffer(statstg.cbSize.LowPart + 1, (LPVOID FAR *) &szRet))
  2598.         {
  2599.             plasterror->HrSetLastError(ResultFromScode(E_OUTOFMEMORY));
  2600.             goto err;
  2601.         }
  2602.     }
  2603.     
  2604.     hr = lpstreamBody->Read(szRet, statstg.cbSize.LowPart, &cb);
  2605.     if(S_OK != GetScode(hr))
  2606.     {
  2607.         plasterror->HrSetLastError(hr);
  2608.         goto err;
  2609.     }
  2610.     szRet[statstg.cbSize.LowPart] = '\0';
  2611.     
  2612. err:
  2613.     UlRelease(lpstreamBody);
  2614.     lpstreamBody = NULL;
  2615.     if(hr)
  2616.     {
  2617.         if(!pbase)
  2618.             MAPIFreeBuffer(szRet);
  2619.         szRet = NULL;
  2620.  
  2621.     }
  2622.  
  2623.     *pszBody = szRet;
  2624.  
  2625.     return hr;
  2626. }
  2627. //
  2628. //if the body is to large for SetProps, have to use IStream
  2629. //
  2630. HRESULT HrStreamOutMsgBody(LPMESSAGE pmsg, LPSTR szBody, CLastError * plasterror)
  2631. {
  2632.     Assert(pmsg);
  2633.     Assert(szBody);
  2634.     Assert(plasterror);
  2635.  
  2636.     HRESULT hr;
  2637.     LPSTREAM lpstreamBody = NULL;
  2638.     ULONG cb = 0;
  2639.  
  2640.     Assert(szBody);
  2641.     
  2642.     hr = pmsg->OpenProperty(PR_BODY, &IID_IStream,
  2643.                             STGM_READWRITE, 0, (LPUNKNOWN FAR *) &lpstreamBody);
  2644.     if(S_OK != GetScode(hr))
  2645.     {
  2646.         plasterror->HrSetLastError(hr, pmsg);
  2647.         goto err;
  2648.     }
  2649.     
  2650.     hr = lpstreamBody->Write(szBody, lstrlen(szBody)+1, NULL);
  2651.     if(hr)
  2652.         plasterror->HrSetLastError(hr);
  2653.     
  2654. err:
  2655.     UlRelease(lpstreamBody);
  2656.     lpstreamBody = NULL;
  2657.  
  2658.     return hr;
  2659. }
  2660.  
  2661. /*
  2662.  *  Formats a Win32 file time as a MAPI date/time string.
  2663.  *  NOTE: converts from GMT to local time.
  2664.  */
  2665. void FormatTime(FILETIME *pft, LPSTR szTime)
  2666. {
  2667.     FILETIME        ft;
  2668.     SYSTEMTIME      systime;
  2669.  
  2670.     FileTimeToLocalFileTime(pft, &ft);
  2671.     FileTimeToSystemTime(&ft, &systime);
  2672.     wsprintf(szTime,
  2673.         "%04.4d/%02.2d/%02.2d %02.2d:%02.2d",
  2674.         systime.wYear, systime.wMonth, systime.wDay,
  2675.         systime.wHour, systime.wMinute);
  2676. }
  2677.  
  2678. ///     GetMsgAdrlist 
  2679. //    retrieves recipients adrlist of a message
  2680. HRESULT GetMsgAdrlist (LPMESSAGE pmsg, LPADRLIST *  ppAdrList, CLastError * plasterror)
  2681. {
  2682.     *ppAdrList = NULL;
  2683.  
  2684.     LPMAPITABLE pTable = NULL;
  2685.     HRESULT hr;
  2686.     
  2687.     hr = pmsg->GetRecipientTable (0, &pTable);
  2688.     if(!hr)
  2689.     {
  2690.         hr = HrQueryAllRows(pTable, NULL, NULL, NULL, 0, (LPSRowSet *)ppAdrList);
  2691.         if(hr)
  2692.         {
  2693.             plasterror->HrSetLastError(hr, pTable);
  2694.         }
  2695.     }
  2696.     else
  2697.     {
  2698.         plasterror->HrSetLastError(hr, pmsg);
  2699.     }
  2700.     
  2701.     pTable->Release();
  2702.     
  2703.     return hr;
  2704. }
  2705.