Project source
Form source (Pascal)
Form source (DFM)
Project source: EMailTest.dpr |
program EMailTest; {Test application for UnitOOPS OLE Drag and Drop Components} uses Forms, fmEmailTest in 'fmEMailTest.pas' {Form1}; {$R *.RES} begin Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end.Back to top |
Form source: fmEMailTest.pas |
unit fmEMailTest; {Test form UnitOOPS OLE Drag and Drop Components. Demonstrates accepting drops of e-mail messages from Microsoft Outlook Express, Netscape (4.x, 6), Outlook (message and attachments), NeoPlanet or Eudora. Last modified: 11/28/2000} interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, uoole, StdCtrls, ComCtrls, ExtCtrls; type TForm1 = class(TForm) UOTextTarget1: TUOTextTarget; Panel1: TPanel; Label1: TLabel; Panel2: TPanel; Panel3: TPanel; ListView1: TListView; Panel4: TPanel; Memo1: TMemo; Panel5: TPanel; ckbSaveOutlookAsMsg: TCheckBox; SaveDialog1: TSaveDialog; procedure FormCreate(Sender: TObject); procedure UOTextTarget1Drop(Sender: TObject; Acceptor: TWinControl; const dropText: String; X, Y: Integer); procedure ListView1Change(Sender: TObject; Item: TListItem; Change: TItemChange); procedure FormDestroy(Sender: TObject); procedure UOTextTarget1DragEnter(Sender: TObject; effect: TDropEffect; X, Y: Integer); private { Private declarations } FSavedMsgNumber: integer; procedure UpdateMemo; function StripTagsFrom(const aString: string): string; procedure ProcessMailMessage(aSL: TStringList); public { Public declarations } end; var Form1: TForm1; implementation uses ActiveX, ComObj, ShlObj, uoNetscape, uoUtil; resourceString // clipboard format names for internet messages sOutlookExpressMessage = 'Athena Mail Messages'; sOutlookExpressMessage5 = 'Outlook Express Messages'; sNetscapeMessage = 'Netscape Message'; sNetscape6Message = 'text/nsmessage'; sEudoraMessage = 'EudoraTransferClipboardFormat'; sFileGroupDescriptor = 'FileGroupDescriptor'; sFileContents = 'FileContents'; {$R *.DFM} procedure TForm1.FormCreate(Sender: TObject); // Set up the TUOTextTarget to accept the appropriate custom formats begin // Initialize FSavedMsgNumber := 1; with UOTextTarget1, CustomFormats do begin // Don't allow moves OverrideDropEffects[deMove] := deCopy; // Advertise that we accept these custom types // Outlook Express // AddObject(sOutlookExpressMessage, TObject(TYMED_HGLOBAL+TYMED_ISTREAM)); Add(sOutlookExpressMessage); Add(sOutlookExpressMessage5); // Netscape Add(sNetscapeMessage); Add(sNetscape6Message); // Eudora Add(sEudoraMessage); // For Outlook, need both of the following. // See UOTextTarget1DragEnter for how to enforce that both formats are // present simultaneously. Add(sFileGroupDescriptor); AddObject(sFileContents, TObject(TYMED_ISTREAM)); // For NeoPlanet, need CF_HDROP alone Add(IntToStr(CF_HDROP)); end; // with end; procedure TForm1.UpdateMemo; // Update the memo to synchronize with the list view var aLi: TListItem; begin // Empty the memo Memo1.Clear; // First look for a selection aLi := ListView1.Selected; // If there's no selection, look for a focused item if not assigned(aLi) then aLi := ListView1.ItemFocused; // If we found one, put its message body in the memo if assigned(aLi) then Memo1.Lines.Text := PChar(aLi.Data); end; function TForm1.StripTagsFrom(const aString: string): string; // Strip HTML tags from the string passed function StripEnclosedBy(const bString: string; const sTag: string): string; // Strip all material enclosed by (and including) <tag>...</tag> var openPos, closePos: integer; tag: string; begin result := bString; tag := UpperCase(sTag); // First, remove <TAG></TAG> tags and everything between openPos := AnsiPos('<'+tag, UpperCase(result)); while (openPos <> 0) do begin closePos := AnsiPos('</'+tag, UpperCase(result)); if (closePos <> 0) then System.Delete(result, openPos, closePos-openPos+length(tag)+3) // Delete the </TAG> else System.Delete(result, openPos, length(result)-openPos+1); // Malformed (no </TAG>) openPos := AnsiPos('<'+tag+'>', UpperCase(result)); end; // while end; var j, k: Integer; inTag, onlySpaces: boolean; s: string; begin result := ''; s := aString; // Kill everything enclosed in either <script></script> or <head></head> s := StripEnclosedBy(s, 'script'); s := StripEnclosedBy(s, 'head'); // Then, remove all other tags, LEAVING enclosed material inTag := false; for j := 1 to length(s) do // Iterate begin // Figure out if we're inside a HTML tag if (s[j] = '<') then inTag := true; if inTag and (s[j] = '>') then begin inTag := false; // Tag is ended. Skip to next iteration so we don't pick up '>' continue; end; // If not in a tag, we do a straight copy if not inTag then result := result + s[j]; end; // for // Finally, remove all multiple cr/lf combinations, i.e. // all lines that either a) are empty or b) contain only spaces with TStringList.Create do try Text := result; // Shove our string into the list for j := count downto 1 do // Iterate begin // Delete any empty ones if (Strings[j-1] = '') then Delete(j-1) else begin // Look for lines that have only spaces onlySpaces := true; for k := 1 to length(Strings[j-1]) do // Iterate begin onlySpaces := onlySpaces and (Strings[j-1][k] = ' '); // Don't finish the loop if we found a non-space! if not onlySpaces then break; end; // for if onlySpaces then Delete(j-1); end; end; // for // Put the list back onto the string. result := Text; finally Free; // the string list end; // with end; procedure TForm1.ProcessMailMessage(aSL: TStringList); // We have a modified RFC822-compliant e-mail message in the string list aSL // in the form 'To=some address', (rather than 'To: some address') etc, etc . // Parse it and put the resulting message into our user interface. var aLi: TListItem; j: integer; spacerIndex: integer; aPos: integer; s: string; body: string; begin // The headers end with 2 cr/lf's. That'll give a single blank string // in the string list at that index. Find it. spacerIndex := aSL.IndexOf(''); // Everything beyond that is the message body. body := aSL[spacerIndex+1]; for j := spacerIndex+2 to aSL.Count-1 do // Iterate begin body := body + #13#10 + aSL[j]; end; // for // Now erase the body and the spacer from the string list // Go backwards to avoid messing up the indices. for j := aSL.Count-1 downto spacerIndex do // Iterate begin aSL.Delete(j); end; // for // Everything that's left in the string list is header material. for j := 0 to spacerIndex-1 do // Iterate begin // All headers are in the form "Header: value" // Change all ': ' to = so we can use the names/values // functionality of the string list aPos := pos(': ', aSL[j]); if (aPos <> 0) then begin s := aSL[j]; // Delete the ':' System.Delete(s, aPos, 1); // Replace the ' ' with '=' s[aPos] := '='; // And put it back in the list in the same position aSL[j] := s; end; end; // for // If the body is HTML, do a simple-minded "remove tags" process // We could alternatively use a HTML viewer such as the Internet // Explorer control. // Only look at the first part of the 'COntent-Type' string (neglecting the // charset section) if AnsiCompareText(Copy(aSL.Values['Content-Type'], 1, 10), 'text/html;') = 0 then body := StripTagsFrom(body); // See function above // Add the new item to the list view aLi := ListView1.Items.Add; // Populate the list item with aLi do begin // The first column Caption := aSL.Values['From']; // The remaining columns with SubItems do begin // This one may be empty. If so, first check if there's a 'Newsgroups' // entry. s := aSL.Values['To']; if (s = '') then begin s := aSL.Values['Newsgroups']; // If this isn't here either, then the e-mail is to the owner of the // mailbox if (s = '') then s := 'Me' end; Add(s); Add(aSL.Values['Subject']); Add(aSL.Values['Date']); // This one may be empty. Put something in, if it is. s := aSL.Values['X-Mailer']; if (s = '') then s := '<unknown>'; Add(s); end; // with // Now, put the body of the message in the TListItem's Data property // We allocate a new PChar, which We'll free when deleting the TListItem, // in the form's OnDestroy handler. Data := StrNew(PChar(body)); end; // with // Make sure the memo is displayed ListView1.Selected := aLi; end; function FindEudoraFolder(eudora: variant; const folderName: string): variant; // Find the first Eudora folder in the Eudora application object "eudora" // with the name contained in "folderName". There may be more than one folder // with the same name, so this is only a 99% solution. var theFolder: variant; procedure WalkFoldersIn(ParentFolder: variant); var j: integer; ChildFolder: variant; S: string; begin if (not varIsEmpty(theFolder)) then exit; // found already - bug out for j := 1 to ParentFolder.Folders.Count do // Iterate begin ChildFolder := ParentFolder.Folders[j]; s := ChildFolder.Name; if (AnsiCompareText(s, folderName) = 0) then theFolder := ChildFolder; if varIsEmpty(theFolder) and (ChildFolder.Folders.Count > 0) then WalkFoldersIn(ChildFolder); end; end; begin // No folder yet found theFolder := unassigned; // Walk the folders recursively WalkFoldersIn(eudora.rootfolder); // Anything found is now in theFolder result := theFolder; end; procedure TForm1.UOTextTarget1Drop(Sender: TObject; Acceptor: TWinControl; const dropText: String; X, Y: Integer); // Handle a drop of one or more e-mail messages. // In its initial form, this method was a handler for Outlook Express messages // only. It subsequently grew to incorporate other e-mail clients. It would // probably benefit from being broken into several routines. However, since then // main task is to demonstrate getting content from several different e-mail // clients, this code rearrangement is not a high priority. const nSelectionsMax = 16383; // Win95 listbox limit on number of items type TIntegerArray = array[0..nSelectionsMax-1] of integer; PIntegerArray = ^TIntegerArray; var hasOutlookFormats: boolean; aSL: TStringList; s, t, sURL: string; aMS: TMemoryStream; aHWnd: HWnd; aIntArray: PIntegerArray; nSelections: integer; j: integer; eudora: variant; eudoraFolder: variant; eudoraMessage: variant; nFiles: integer; aFd: TFileDescriptor; anIStorage: IStorage; bSL: TStringList; OutlookHeaders: TStringList; formatCount: integer; messageFound: boolean; W: WideString; anIStream: IStream; aHRes: HRESULT; tempFileName: string; anIndex: integer; curAttachIndex: integer; attachmentFileName: string; curAttachStorageName: WideString; aSubStorage: IStorage; aString, tmpString: string; begin messageFound := false; // Clear the memo Memo1.Clear; // Instantiate a string list for message processing aSL := TStringList.Create; try // *****Outlook Express***** // Is there an Outlook Express message here? if UOTextTarget1.DataObjectHasFormat(sOutlookExpressMessage) or UOTextTarget1.DataObjectHasFormat(sOutlookExpressMessage5) then begin messageFound := true; // Get the file group descriptor as there may be multiple messages. s := UOTextTarget1.DataObjectGetFormat(sFileGroupDescriptor); // How many fgd's in the returned data? That's a DWORD at the head // of the string-encoded data nFiles := uoDecodeDWORDFromString(s); // Make a temporary file tempFileName := uoGetTempFileName('uox'); // Process all of the files being dragged for j := 1 to nFiles do // Iterate begin // Get the j'th file descriptor. It starts after the initial DWORD, // and has the length of a TFileDescriptor. System.Move(s[SizeOf(DWORD)+1+(j-1)*SizeOf(TFileDescriptor)], aFd, SizeOf(TFileDescriptor)); // Its content is on an IStream, at lindex j-1 // Global overrides for lindex and tymed DataObjectLindex := j-1; DataObjectTymed := TYMED_ISTREAM; t := UOTextTarget1.DataObjectGetFormat(sFileContents); // Watch out for nothing at that index... if (t <> '') then begin // Get the IStream DWORD(anIStream) := uoDecodeDWORDFromString(t); // Put it on the temporary file uoSaveIStreamToFile(anIStream, tempFileName); // Reload into the string list // If you want to process any attachments they can be decoded here // since the message will be listed as multi-part MIME, etc. aSL.LoadFromFile(tempFileName); // Process the message to get it into the list view ProcessMailMessage(aSL); end; {if} end; {for j := 1...} aSL.Clear; // Delete the temporary file DeleteFile(tempFilename); end; // *****Netscape***** // Netscape only gives us a mailbox:/ url - we then have to go to the // Netscape OLE interface to get the actual message. // We look for Netcape's message clipboard format and text together. // Navigator 3.x supplies the former, but not the latter. if not messageFound and (( (UOTextTarget1.DataObjectHasFormat(sNetscapeMessage) and UOTextTarget1.DataObjectHasFormat(IntToStr(CF_TEXT)) ) or UOTextTarget1.DataObjectHasFormat(sNetscape6Message) )) then begin // Grab the (local) url for the message. This is supplied by Netscape in // several formats. It's easiest to grab in text format. if UOTextTarget1.DataObjectHasFormat(IntToStr(CF_TEXT)) then sURL := UOTextTarget1.DataObjectGetFormat(IntToStr(CF_TEXT)) else begin tmpString := UOTextTarget1.DataObjectGetFormat(sNetscape6Message); // The string that was returned is a wide string encoded byte-by-byte // on an AnsiString, so we pull only the odd ones out. // Accumulate the odd chars into a string sURL := ''; for j := 1 to length(tmpString) do // Iterate begin if odd(j) then sURL := sURL + tmpString[j]; end; // for end; messageFound := true; // Now, use the Netscape OLE interface to grab the message's text aMS := TMemoryStream.Create; SetCursor(Screen.Cursors[crHourglass]); try // Put the text on the newly-created memory stream. // Ignore the returned value, most likely 'message/rfc822' getNetscapeURLOnStream(sURL, aMS); // Then copy that stream to the string s SetLength(s, aMS.size); Move(aMS.Memory^, s[1], aMS.size); // Finally, assign the string to our string list. aSL.Text := s; finally aMS.Free; SetCursor(Screen.Cursors[crDefault]); end; end; // *****Eudora Pro 4.x***** // ****WARNING****HERE BE DRAGONS****WARNING**** // // Tunnel in and get the listbox that Eudora uses for e-mail // listing display, then get the number of selections and their indices (since // Eudora's OLE interface doesn't support the concept of selected message). // THEN, use the OLE interface to grab the messages. (Phew) // // Look for both the Eudora clipboard format AND text. Text is presented also // only when dragging from a Eudora listbox, which is precisely what we're looking // for. if (not messageFound) and UOTextTarget1.DataObjectHasFormat(sEudoraMessage) then begin if UOTextTarget1.DataObjectHasFormat(IntToStr(CF_TEXT)) then begin messageFound := true; aHWnd := FindWindow('EudoraMainWindow', nil); // Main window aHWnd := FindWindowEx(aHWnd, 0, 'MDIClient', nil); // MDI Client aHWnd := SendMessage(aHWnd, WM_MDIGETACTIVE, 0, 0); // Active MDI child // The title of the active MDI child window is the open mailbox window. Get it. SetLength(s, 255); SetLength(s, GetWindowText(aHWnd, PChar(s), 255)); // Title of active mailbox // Now burrow further in until we get to the main listbox. aHWnd := FindWindowEx(aHWnd, 0, 'AfxMDIFrame42', nil); aHWnd := FindWindowEx(aHWnd, 0, '#32770', nil); aHWnd := FindWindowEx(aHWnd, 0, 'ListBox', nil); // Handle to message list // Now, get the selected item indices in the listbox. // Ridiculously large (128k) allocation in case the user tries to drag // 16383 messages. It's not a big deal these days, I guess... GetMem(aIntArray, nSelectionsMax*sizeOf(integer)); // Going with run-time automation, so put up an hourglass in case it takes // a while to locate the mailbox. By importing the Eudora type library and // doing vtable automation (interfaces) this can be made much faster, probably. SetCursor(Screen.Cursors[crHourglass]); try // Get the number of selections, and their (zero-based) indices, using the standard // LB_GETSELITEMS message for multi-select list boxes. nSelections := SendMessage(aHWnd, LB_GETSELITEMS, nSelectionsMax, integer(aIntArray)); // We now know we have nSelections selections, with indices in aIntArray^, in the // mailbox whose name is "s". We can now use OLE automation to grab the messages. try // At any point in the following, an EOleSysError could occur. try // Construct the Eudora application object eudora := CreateOleObject('Eudora.EuApplication.1'); // Use a helper function (see above) to find the first mailbox that's named "s" eudoraFolder := FindEudoraFolder(eudora, s); // Oops - we could have done it using (untested, but according to the manual) // eudoraFolder := eudora.Folder(s, -1); // We only get the first mailbox named "s". If it's the wrong one, then // the indices may be wrong. Check first to avoid an exception. Passing // here doesn't mean we've got the right one, by the way... if ((aIntArray^[nSelections-1]) < eudoraFolder.Messages.Count) then begin // Iterate over the selected messages for j := 1 to nSelections do begin // listbox selections are zero-based, but Eudora uses 1-based message indexing. eudoraMessage := eudoraFolder.Messages[(aIntArray^[j-1])+1]; // Take the raw RFC822 message, and give it to the string list. // It will get processed in ProcessMailMessage() below aSL.Text := eudoraMessage.RawMessage; // The raw Eudora message doesn't, however, include the date // Include it explicitly aSL.Insert(0, 'Date: '+eudoraMessage.Date); // And process it ProcessMailMessage(aSL); // And clear out the string list for the next time around, or below. aSL.Clear; end; // for end else begin MessageBox(Self.Handle, PChar('The drop cannot proceed.' + #13 + #10 + #13 + #10 + 'You ' + 'probably have more than one folder named "'+s+'". This demo found only the first ' + 'one, which is obviously not the correct one, as it doesn''t have enough messages in it.'), PChar('UnitOOPS EMailTest Demo'), MB_OK + MB_ICONERROR + MB_SETFOREGROUND); end; finally // Throw away the automation object eudoraFolder := unAssigned; eudora := unAssigned; end; except on EOleSysError do begin MessageBox(Self.Handle, PChar('The Eudora automation server could not be started.' + #13 + #10 + #13 + #10 + 'Please ' + 'ensure that you have enabled automation in Eudora (Tools | Options | Automation ' + 'and check "Automation enabled from this machine". Alternatively, stop Eudora, ' + 'and start it once with the command line "Eudora /EnableAutomation"'), 'UnitOOPS EMailTest Demo', MB_OK + MB_ICONERROR + MB_SETFOREGROUND); end; end; finally FreeMem(aIntArray, nSelectionsMax*sizeOf(integer)); SetCursor(Screen.Cursors[crDefault]); end; end else begin // Eudora format, but no CF_TEXT. MessageBox(Self.Handle, 'E-mail messages can only be dragged here from a Eudora list view.', 'UnitOOPS FileTest Demo', MB_OK + MB_ICONWARNING + MB_SETFOREGROUND); end; end; // *****Outlook***** // Do we have fgd? hasOutlookFormats := (not messageFound) and UOTextTarget1.DataObjectHasFormat(sFileGroupDescriptor); // Global override for tymed - reset automatically by calls to // DataObjectHasFormat and DataObjectGetFormat DataObjectTymed := TYMED_ISTREAM; // Do we have fc? Do the call first, so we're guaranteed that it won't // be short-circuited, so that the global tymed and lindex get reset properly. hasOutlookFormats := UOTextTarget1.DataObjectHasFormat(sFileContents) and hasOutlookFormats; // We have the formats we're looking for. if hasOutlookFormats then begin messageFound := true; // Get the file group descriptors s := UOTextTarget1.DataObjectGetFormat(sFileGroupDescriptor); // How many fgd's in the returned data? That's a DWORD at the head // of the string-encoded data nFiles := uoDecodeDWORDFromString(s); // Get the headers, too bSL := TStringList.Create; OutlookHeaders := nil; try if UOTextTarget1.DataObjectHasFormat(IntToStr(CF_TEXT)) then begin bSL.Text := UOTextTarget1.DataObjectGetFormat(IntToStr(CF_TEXT)); end; // Figure out the current headers displayed in Outlook. Outlook only // supplies, in a drop, the headers that are actually displayed. // Also, the user may have dragged them into unusual orders. // Here we'll get the header names and orders as currently displayed, and // use them below to decide which header is in which position. OutLookHeaders := uoTokenizeRaw(bSL[0], [#9]); for j := 1 to OutLookHeaders.Count do // Iterate begin // Append = so we can use names/values string list functions OutlookHeaders[j-1] := OutlookHeaders[j-1]+'='; end; // for // Make a temporary file tempFileName := uoGetTempFileName('uox'); // Process all of the files being dragged for j := 1 to nFiles do // Iterate begin // Get the j'th file descriptor. It starts after the initial DWORD, // and has the length of a TFileDescriptor. System.Move(s[SizeOf(DWORD)+1+(j-1)*SizeOf(TFileDescriptor)], aFd, SizeOf(TFileDescriptor)); // Its content is on an IStream, at lindex j-1 // Global overrides for lindex and tymed DataObjectLindex := j-1; DataObjectTymed := TYMED_ISTORAGE; t := UOTextTarget1.DataObjectGetFormat(sFileContents); // Watch out for nothing at that index... if (t <> '') then begin // Get the IStorage DWORD(anIStorage) := uoDecodeDWORDFromString(t); // Are we saving Outlook as MSG files? (A saved .MSG file can be double- // clicked to start up Outlook editing that message). // This is just an exercise in putting the IStorage on a file. // How? Make another storage that IS a file, and copy to there! // Because the default is direct (as opposed to transacted) mode, // the change gets saved immediately. if ckbSaveOutlookAsMsg.Checked then begin // Make sure the user sees the dialog. SetForegroundWindow(Handle); with SaveDialog1 do begin // Preset filename in save dialog FileName := Format('Dropped Outlook Message #%d.msg', [FSavedMsgNumber]); // Execute the save dialog if Execute then begin // uoSaveIStorageToFile() introduced in V1.31 uoSaveIStorageToFile(anIStorage, SaveDialog1.FileName); inc(FSavedMsgNumber); end; end; // with end; // **** W := '__substg1.0_1000001E'; // Seems to be the stream name for message text aHRes := anIStorage.OpenStream(PWideChar(w), nil, STGM_READ or STGM_SHARE_EXCLUSIVE, 0, anIStream); if SUCCEEDED(aHRes) then begin // Put the IStream contents on the file uoSaveIStreamToFile(anIstream, tempFileName); // Load it into a string list aSL.LoadFromFile(tempFilename); // Add the headers back in RFC822 format. Check for existence in the // current Outlook view, and whether they've been rearranged. if (bSL.Count > j) then begin with uoTokenizeRaw(bSL[j], [#9]) do try aSL.Insert(0, ''); anIndex := OutlookHeaders.IndexOfName('To'); if (anIndex >= 0) then aSL.Insert(0, 'To: '+strings[anIndex]); anIndex := OutlookHeaders.IndexOfName('Subject'); if (anIndex >= 0) then aSL.Insert(0, 'Subject: '+strings[anIndex]); anIndex := OutlookHeaders.IndexOfName('Received'); if (anIndex >= 0) then aSL.Insert(0, 'Date: '+strings[anIndex]); anIndex := OutlookHeaders.IndexOfName('From'); if (anIndex >= 0) then aSL.Insert(0, 'From: '+strings[anIndex]); finally Free; end; // with end; // Now, look for attachments. // Initialize counter and string curAttachIndex := 0; aString := ''; repeat // Form the storage name for attachment with index curAttachIndex curAttachStorageName := Format('__attach_version1.0_#%8.8d', [curAttachIndex]); // Now attempt to open that storage aHRes := anIStorage.OpenStorage(PWideChar(curAttachStorageName), nil, STGM_READ or STGM_SHARE_EXCLUSIVE, nil, 0, aSubStorage); // If we succeed, we have a storage of that name, i.e. an attachment if SUCCEEDED(aHRes) then begin // Find the file name for the attachment W := '__substg1.0_3001001E'; // Name // Open the stream aSubStorage.OpenStream(PWideChar(w), nil, STGM_READ or STGM_SHARE_EXCLUSIVE, 0, anIStream); uoSaveIStreamToFile(anIStream, tempFileName); with TStringList.Create do try LoadFromFile(tempFileName); attachmentFileName := strings[0]; // Prepend the temp path to the file name to get a fully- // qualified file path for saving the attachment attachmentFileName := uoGetTempPath+attachmentFileName; // Add attachment to list for reporting via messagebox aString := aString + attachmentFileName + #13#10; finally // wrap up Free; end; // try/finally // Read the contents of the attachment W := '__substg1.0_37010102'; // Contents // Open the stream aSubStorage.OpenStream(PWideChar(w), nil, STGM_READ or STGM_SHARE_EXCLUSIVE, 0, anIStream); // Save it to the file name obtained above uoSaveIStreamToFile(anIStream, attachmentFileName); end; inc(curAttachIndex); until FAILED(aHRes); // Pop up a message if there were attachments (now saved) in the // Outlook message currently being dropped (this messagebox could // fire once for every message dropped). if (curAttachIndex > 1) then begin aString := '%d attachment(s) were found in this Outlook message. They have ' + 'been copied to the following locations:'#13#10#13#10 + aString; aString := Format(aString, [curAttachIndex-1]); Application.MessageBox(PChar(aString), 'UnitOOPS EMailTest Demo', MB_OK + MB_ICONINFORMATION + MB_SETFOREGROUND); end; // Delete the temporary file DeleteFile(tempFilename); // And process the message ProcessMailMessage(aSL); end; end; // Clear the string list for the next go-around aSL.Clear; end; finally // Clear up temporary string lists bSL.Free; OutlookHeaders.Free; end; end; // *****NeoPlanet***** if (not messageFound) and UOTextTarget1.DataObjectHasFormat(IntToStr(CF_HDROP)) then begin bSL := UOTextTarget1.DataObjectFormatList; try formatCount := bSL.Count; finally bSL.Free; end; if (formatCount = 1) then begin // Get the HDROP format s := UOTextTarget1.DataObjectGetFormat(IntToStr(CF_HDROP)); // Get the list of files (one per e-mail) bSL := uoFileListFromHDROP(s); try // Now iterate over these files, and pull in the messages. Each of these // files contains a RFC822 message. for j := 1 to bSL.Count do // Iterate begin aSL.LoadFromFile(bSL[j-1]); ProcessMailMessage(aSL); aSL.Clear; end; // for finally bSL.Free; end; end; end; // Now, if aSL has any content, it has a message. Both Netscape and OE use // RFC 822 format, so we process them in the same manner. This has already // been called above for Outlook and Eudora, which can transfer multiple // messages at once. if (aSL.Count > 0) then ProcessMailMessage(aSL); finally aSL.Free; // Make sure the bottom-most item in the list view is fully visible. ListView1.Items[ListView1.Items.Count-1].MakeVisible(false); end; end; procedure TForm1.ListView1Change(Sender: TObject; Item: TListItem; Change: TItemChange); // The list view has changed. Make sure the memo is synchronized properly. begin UpdateMemo; end; procedure TForm1.FormDestroy(Sender: TObject); var j: Integer; begin // Clean up - delete the data element of each list item where we // stored a message body with ListView1 do begin for j := 1 to Items.Count do // Iterate begin if assigned(Items[j-1].Data) then StrDispose(PChar(Items[j-1].Data)); end; // for end; // with end; procedure TForm1.UOTextTarget1DragEnter(Sender: TObject; effect: TDropEffect; X, Y: Integer); // Ensure that, if we have FileContents, we also have FileGroupDescriptor // (Both are needed for Outlook). This is the standard way of enforcing that // multiple formats occur together. We only need this in OnDragEnter. // Ensure that we don't accidentally block Netscape messages. begin // If we have FileGroupDescriptor, we MUST have FileContents (Outlook) with (Sender as TUOTextTarget) do begin // Reset all of the drop effects to "Copy" (i.e., disallow move, link) OverrideDropEffects[deMove] := deCopy; OverrideDropEffects[deCopy] := deCopy; OverrideDropEffects[deLink] := deCopy; // Do we have fgd? if DataObjectHasFormat(sFileGroupDescriptor) and not DataObjectHasFormat(sNetscapeMessage) then begin // Global override for tymed - reset automatically by calls to // DataObjectHasFormat and DataObjectGetFormat DataObjectTymed := TYMED_ISTREAM; // Do we have fc? Do the call first, so we're guaranteed that it won't // be short-circuited, so that the global tymed and lindex get reset properly. if not DataObjectHasFormat(sFileContents) then begin // We don't have both formats. All drop effects will be interpreted as // "nothing", i.e., ignore the drop. Since we're in OnDragEnter, this // means that a no-drop cursor is all the user will see. OverrideDropEffects[deMove] := deNone; OverrideDropEffects[deCopy] := deNone; OverrideDropEffects[deLink] := deNone; end; end; end; end; end.Back to top |
Form source: fmEMailTest.dfm |
object Form1: TForm1 Left = 210 Top = 148 Width = 625 Height = 309 Caption = 'Test of dropping News and E-mail items' Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] OnCreate = FormCreate OnDestroy = FormDestroy PixelsPerInch = 96 TextHeight = 13 object Panel1: TPanel Left = 0 Top = 0 Width = 617 Height = 33 Align = alTop BevelOuter = bvNone BorderWidth = 3 Caption = 'Panel1' TabOrder = 0 object Label1: TLabel Left = 3 Top = 3 Width = 611 Height = 27 Align = alClient Caption = 'Drag an e-mail or news message from Outlook Express, Netscape, C' + 'ollabra, Outlook or Eudora 4.x onto the window below.' WordWrap = True end end object Panel2: TPanel Left = 0 Top = 33 Width = 617 Height = 249 Align = alClient BevelOuter = bvNone BorderWidth = 3 TabOrder = 1 object Panel3: TPanel Left = 3 Top = 3 Width = 611 Height = 118 Align = alTop BevelOuter = bvNone BorderWidth = 3 Caption = 'Panel3' TabOrder = 0 object ListView1: TListView Left = 3 Top = 3 Width = 605 Height = 112 Align = alClient ColumnClick = False Columns = < item Caption = 'From' Width = 100 end item Caption = 'To' Width = 100 end item Caption = 'Subject' Width = 150 end item Caption = 'Date' Width = 150 end item Caption = 'Mailer' Width = 100 end> ReadOnly = True RowSelect = True OnChange = ListView1Change TabOrder = 0 ViewStyle = vsReport end end object Panel4: TPanel Left = 3 Top = 121 Width = 611 Height = 125 Align = alClient BevelOuter = bvNone BorderWidth = 3 Caption = 'Panel4' TabOrder = 1 object Memo1: TMemo Left = 3 Top = 3 Width = 605 Height = 119 Align = alClient ScrollBars = ssVertical TabOrder = 0 end end end object UOTextTarget1: TUOTextTarget AcceptorControl = Panel2 AcceptTextFormats = [dtfCustom] OnDragEnter = UOTextTarget1DragEnter OnDrop = UOTextTarget1Drop Left = 496 Top = 88 end endBack to top |