Platform SDK: Exchange 2000 Server

IDataSource Interface

[This is preliminary documentation and subject to change.]

The IDataSource interface defines methods and properties used to provide access to content in other objects or in the Microsoft Exchange 2000 Web Store.

IID
CD000029-8B95-11D1-82DB-00C04FB1625D
Extends
IDispatch

Member Summary

Properties

Name Type Description
ActiveConnection

[Visual Basic,VBScript] ADODB.Connection

[C++,IDL] _Connection*

Returns the active ADO Connection object if applicable.
IsDirty

[Visual Basic,VBScript] Boolean

[C++,IDL] VARIANT_BOOL

Specifies whether the local data has been changed since the last data source binding or call to Save.
Source

[Visual Basic,VBScript] Object

[C++,IDL] IUnknown*

Returns the interface on the currently bound object.
SourceClass

[Visual Basic,VBScript] String

[C++,IDL] BSTR

Returns the string identifier of the interface used to open the object.
SourceURL

[Visual Basic,VBScript] String

[C++,IDL] BSTR

Returns the URL of the currently bound object if applicable.

Methods

Name Description
Open Binds to and opens the existing item specified by URL.
OpenObject Binds to and opens the specified object.
Save Saves the current data into the currently bound data source
SaveTo Binds to and saves data into the item specified by URL.
SaveToContainer Binds to and saves data into a new item in the folder specified by URL. The item name is a generated globally unique identifier (GUID).
SaveToObject Binds to and saves data into the specified object.

Remarks

Objects expose implementations of the IDataSource interface to facilitate easy access to content (data) in other CDO, ADO and Active Directory objects, and items in the Microsoft Exchange 2000 Web Store. For example, the Message object exposes an implementation of the IDataSource interface.

There are two aspects associated with using the IDataSource interface on objects:

Opening other objects and resources is completely analogous to using Microsoft® Word to open its .doc files: when you "Open" a .doc file, the file is "bound" and the contents copied into the opening application. Subsequent "Save" commands overwrite the file contents with the local copy of data. When another file is opened or the user chooses Save As, the application rebinds to the new file and the appropriate data transfer occurs, either to or from the acting application. Save commands now act on the newly bound file. When a user closes the application, local data changes may not have been saved into the currently bound file. A dialog appears, "Do you wish to save the changes you have made to file?" Changes would of course be lost if the changes are not saved.

This reference only lists the loose semantic definition and COM interface binary signature required for an implementation of the IDataSource interface. For specific information about a particular implementation, see the corresponding COM class reference page.

Example

[Visual Basic]
Sub SaveEmbeddedPartsToFolder(iMsg As CDO.Message, URL As String)

    Dim iMsgB As New Message
    Dim iDsrcB As CDO.IDataSource
    Dim iFldr As New Folder
    Dim iDsrcFldr As CDO.IDataSource
    Dim Conn As ADODB.Connection
    Dim iBp As IBodyPart
    Dim iBps As IBodyParts
    Dim ContType As String
    
    Set iDsrcFldr = iFldr
    iDsrcFldr.Open URL
    Set Conn = iDsrcFldr.ActiveConnection
    
    Set iDsrcB = iMsgB
    
    Set iBp = iMsg
    Set iBps = iBp.BodyParts
    
    For Each iBp In iBps
        ContType = iBp.ContentMediaType
        If ContType = "message/rfc822" Then
        
            iDsrcB.OpenObject iBp, "IBodyPart"
            iDsrcB.SaveToContainer FolderURL, Conn, adModeReadWrite, adCreateOverwrite
            Debug.Print "Saved message has URL " & iDsrcB.SourceURL
        
        ElseIf ContType = "application/octet-stream" Then
            
            Dim Rec As New Record
            Dim Flds As ADODB.Fields
            Dim Fld As ADODB.Field
            Dim Stm1 As Stream
            Dim Stm2 As Stream
            
            'Get some unique name based on message id, etc
            Rec.Open FolderURL & "uniquename.x", Conn, adModeReadWrite, adCreateOverwrite
            Set Flds = Rec.Fields
            ' Get Stream for resource
            Set Fld = Flds.Item(-1)
            Set Stm1 = Fld.Value
            
            ' Get Decoded BodyPart stream
            Set Stm2 = iBp.GetDecodedContentStream
            ' Copy bodypart stream to resource stream
            Stm2.CopyTo Stm1
            
            ' Commit
            Stm1.Flush
            Stm1.Close
            Rec.Close
            Debug.Print "Saved app/oct stream a URL " & FolderURL & "uniquename.x"
            
        End If
    Next iBp
End Sub
[C++,IDL]
HRESULT SaveEmbeddedPartsToFolder( IMessage* pMsgIn, BSTR FolderURL ) {

   /* Assume these headers:
   *
   * #import <msado15.dll> no_namespace raw_interfaces_only
   * #import <cdoex.dll>   no_namespace raw_interfaces_only
   * #include <iostream.h>
   *
   */

   if(pMsgIn == NULL)
      return E_INVALIDARG;

   IDataSourcePtr   iDsrc;
   IBodyPart*      pBp      =   NULL;
   IBodyPart*      pBp2   =   NULL;
   IBodyParts*      pBps   =   NULL;
   _Connection*   pConn   =   NULL;
   _Stream*      pStm   =   NULL;
   _Stream*      pStm2   =   NULL;
   _Record*      pRec   =   NULL;
   Fields*         pFlds   =   NULL;
   Field*         pFld   =   NULL;
   long         i      =   0;
   long         count   =   0;

   // for optional params
   _variant_t      varOpt(DISP_E_PARAMNOTFOUND,VT_ERROR);
   HRESULT hr = S_OK;
   /* 
   * use this object to extract embedded messags
   */

   IMessagePtr Msg(__uuidof(Message));
   iDsrc = Msg;


   /*
   *  Use Folder to open specified/passed URL
   */
   IFolder*      pFldr      =   NULL;
   IDataSource*   pDsrcFldr   =   NULL;
   _bstr_t bstrEmpty("");
   hr = CoCreateInstance(__uuidof(Folder),
                     NULL,
                     CLSCTX_INPROC_SERVER,
                     __uuidof(IDataSource),
                     reinterpret_cast<void**>(&pDsrcFldr));

   hr = pDsrcFldr->Open(
                  _bstr_t(FolderURL),
                  NULL,
                  (ConnectModeEnum)NULL,
                  adFailIfNotExists,
                  adOpenSource,
                  bstrEmpty,
                  bstrEmpty
                  );
   if(FAILED(hr)) {
      cerr << "Error opening folder:" << _bstr_t(FolderURL) << endl;
      goto exit;
   }

   hr = pDsrcFldr->get_ActiveConnection(&pConn);

   /*
   ** Loop through passed Message, looking for embedded messages
   *  and attachments
   ** (error checking removed for clarity)
   */

   hr = pMsgIn->QueryInterface(__uuidof(IBodyPart),reinterpret_cast<void**>(&pBp));

   /*
   * fetch all bodyparts below message to scan for application/octet-stream
   *  and embedded messages message/rfc822
   *  (won't find "related" parts in MHTML formmatted messages)
   */

   hr = pBp->get_BodyParts(&pBps);

   hr = pBp->Release();
   pBp = NULL;

   pBps->get_Count(&count);

   for(i=1;i<=count;i++) {

      pBps->get_Item(i,&pBp);
      BSTR MediaType;

      pBp->get_ContentMediaType(&MediaType);
      if(_bstr_t(MediaType) == _bstr_t("message/rfc822")){
      
         cout << "Found an embedded message...saving" << endl;
         /* 
         *  Extract Embedded Message 
         *   (clears previous message out)
         */
         hr = iDsrc->raw_OpenObject(pBp,_bstr_t("IBodyPart"));
         if(FAILED(hr)) {
            cerr << "Failed to extract message!" << endl;
            goto exit;
         }

         /* 
         *  Save to some folder in a private Web Store
         *  such as file://backofficestorage/domain.microsoft.com/store/scanattachments/"
         *  (the file name will be like {GUID}.eml
         */

         /* 
         * Use available ActiveConnection in pConn
         * to save the Message to the container (folder)
         */

         hr  = iDsrc->raw_SaveToContainer(
                  _bstr_t(FolderURL),
                  pConn,
                  adModeReadWrite,
                  adCreateOverwrite,
                  (RecordOpenOptionsEnum) NULL,
                  NULL,
                  NULL
                  );
         if(FAILED(hr)) {
            cerr << "Error saving message to folder:" << _bstr_t(FolderURL) << endl;
            goto exit;
         }

         pBp->Release(); pBp = NULL;
         BSTR srcURL;
         hr = iDsrc->get_SourceURL(&srcURL);
         if(FAILED(hr)) {
            cerr << "Failed getting source URL " << endl;
            goto exit;
         }
         cout << "Saved embedded message at " << _bstr_t(srcURL) << endl;
         SysFreeString(srcURL);
      }
      else if(_bstr_t(MediaType) == _bstr_t("application/msword")) {
         cout << "Found an application/msword body part" << endl;

         hr = CoCreateInstance(
                     __uuidof(Record),
                     NULL,
                     CLSCTX_INPROC_SERVER,
                     __uuidof(_Record),
                     reinterpret_cast<void**>(&pRec));

         _bstr_t newURL = _bstr_t(FolderURL) + _bstr_t("savedwordfile.doc");
         hr = pRec->raw_Open(
                  _variant_t(newURL),
                  varOpt,
                  adModeReadWrite,
                  adCreateOverwrite,
                  (RecordOpenOptionsEnum) adOpenSource,
                  NULL,
                  NULL
                  );

         if(FAILED(hr))  {
            cerr << "Failed to create Record for new item" << endl;
            goto exit;
         }

         hr = pRec->get_Fields(&pFlds);
         hr = pFlds->get_Item(_variant_t((long)adDefaultStream),&pFld);
         
         if(FAILED(hr)) {
            cerr << "failed to get Default Stream Field for Record" << endl;
            goto exit;
         }
         pFlds->Release();

         VARIANT varVal;
         VariantInit(&varVal);
         hr = pFld->get_Value(&varVal);
         pFld->Release();

         IDispatch* pDisp = NULL;
         if( varVal.vt == VT_DISPATCH) {
            pDisp = varVal.pdispVal;
            hr = pDisp->QueryInterface(__uuidof(_Stream),reinterpret_cast<void**>(&pStm));
            pDisp->Release();
         }
         else {
            cerr << "Value is not a stream object" << endl;
            goto exit;
         }

         hr = pBp->raw_GetDecodedContentStream(&pStm2);
         if(FAILED(hr)) {
            cerr << "failed to get stream for bodypart" << endl;
            goto exit;
         }
         
         hr = pStm->put_Type(adTypeBinary);
         hr = pStm2->CopyTo(pStm,-1);
         if(FAILED(hr)) {
            cerr << "failed to copy stream" << endl;
            cout << hex << hr << endl;
            goto exit;
         }
         pStm->Flush();
         pStm->Close();

         pRec->Close();
         pRec->Release();

         pStm2->Release(); pStm2 = NULL;
         pStm->Release();  pStm  = NULL;
      }
   }

exit:
   if(pDsrcFldr)
      pDsrcFldr->Release();
   if(pBp)
      pBp->Release();
   if(pBps)
      pBps->Release();

   return hr;

}

[VBScript]
Sub SaveEmbeddedPartsToFolder(iMsg, URL)

    Const adModeRead        = 1
    Const adModeReadWrite   = 3
    Const adCreateOverwrite = 67108864

    Dim iMsgB
    Set iMsgB = CreateObject("CDO.Message")
    Dim iDsrcB
    Dim iFldr
    Dim iDsrcFldr
    Dim Conn
    Dim iBp
    Dim iBps
    Dim ContType
    
    Set iDsrcFldr = iFldr
    iDsrcFldr.Open URL
    Set Conn = iDsrcFldr.ActiveConnection
    
    Set iDsrcB = iMsgB
    
    Set iBp = iMsg
    Set iBps = iBp.BodyParts
    
    For Each iBp In iBps
        ContType = iBp.ContentMediaType
        If ContType = "message/rfc822" Then
        
            iDsrcB.OpenObject iBp, "IBodyPart"
            iDsrcB.SaveToContainer FolderURL, Conn, adModeReadWrite, adCreateOverwrite
        
        ElseIf ContType = "application/octet-stream" Then
            
            Dim Rec
            Set Rec = CreateObject("ADODB.Record")
            Dim Flds
            Dim Fld
            Dim Stm1
            Dim Stm2
            
            'Get some unique name based on message id, etc
            Rec.Open FolderURL & "uniquename.x", Conn, adModeReadWrite, adCreateOverwrite
            Set Flds = Rec.Fields
            ' Get Stream for resource which is accessed using -1 in Fields collection
            Set Fld = Flds.Item(-1)
            Set Stm1 = Fld.Value
            
            ' Get Decoded BodyPart stream
            Set Stm2 = iBp.GetDecodedContentStream
            ' Copy bodypart stream to resource stream
            Stm2.CopyTo Stm1
            
            ' Commit
            Stm1.Flush
            Stm1.Close
            Rec.Close            

        End If
    Next
End Sub