Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Copy Outlook Attachments From Clipboard

Status
Not open for further replies.

Robertio

Programmer
Oct 9, 2000
87
GB
Bit of a strange requirement, but a customer has asked for this so, any ideas on how to:

User copies attachment(s) from Outlook
They then right click in our app and paste. At which point the application will create a file for each attachment plus creating records in the database.

I have this working for when they copy files to the clipboard, the problem is with Outlook attachments. For normal files I can use
Clipboard.GetAsHandle(CF_HDROP)
if this <> 0 then
loop through the files using DragQueryFile to get the filename and copy the files

My problem is trying to get this to work with copied attachments. I'm fairly sure the code will be similar, but requires checking the FILE GROUP DESCRIPTOR and then processing the Handle in a slightly different manner. Alas, I seem to be spending a lot of time not getting anywhere fast with it.

Any help/ideas appreciated. I cannot access Outlook direct as it is run on their local machine and the destination app is within a Remote Desktop window.

Robertio
Software Developer
 
this works for me:

Code:
function TForm1.Drop(const dataObj: IDataObject;
  grfKeyState: Longint;
  pt: TPoint;
  var dwEffect: Longint): HResult;

var NoOfAttachments, Index : Integer;
begin
 NoOfAttachments := GetMessageCount(dataObj);
 for Index := 1 to NoOfAttachments do
  SaveMessage(dataObj, 'c:\temp\', Index);
end;

function TForm1.GetMessageCount(const dataObj: IDataObject) : integer;

var tFMT  : TFORMATETC;
 tSTGM : TSTGMEDIUM;
 tFDA : FILEDESCRIPTORA;
 tFDW : FILEDESCRIPTORW;
 tFGD : FILEGROUPDESCRIPTORW;
 lIdx : LongInt;
 lPtr : pointer;


begin
 tFMT.cfFormat := CF_FileGroupDescriptorW;
 tFMT.dwAspect := DVASPECT_CONTENT;
 tFMT.lindex := -1;
 tFMT.ptd := nil;
 tFMT.TYMED := TYMED_HGLOBAL;
// If the Unicode version is not available
// try to get the Ansi one
 if dataObj.QueryGetData(tFMT) <> S_OK then
  tFMT.cfFormat := CF_FileGroupDescriptor;
// Get the data
 if dataObj.GetData(tFMT, tSTGM) = S_OK then
  begin
  // Get a pointer to the data
  lPtr := GlobalLock(tSTGM.hGlobal);
  // Copy the structure
  MoveMemory(@tFGD, lPtr, SizeOf(tFGD));
  // Release the pointer
  GlobalUnlock(tSTGM.hGlobal);
  // Return the number of messages
  Result := tFGD.cItems;
  // Release the data
  ReleaseStgMedium(tSTGM);
 end;
end;

procedure TForm1.SaveMessage(const dataObj: IDataObject; Path: string; Index : Integer);

var tFMT  : TFORMATETC;
 tSTGM : TSTGMEDIUM;
 tFDA : FILEDESCRIPTORA;
 tFDW : FILEDESCRIPTORW;
 tFGD : FILEGROUPDESCRIPTORW;
 lIdx : LongInt;
 lPtr : pointer;
 Filename : Widestring;
 Ifile, iStg : IStorage;
 tIID : PGuid;


begin
 tFMT.cfFormat := CF_FileContents;
 tFMT.dwAspect := DVASPECT_CONTENT;
 tFMT.lindex := Index-1;
 tFMT.ptd := nil;
 tFMT.TYMED := TYMED_ISTREAM Or TYMED_ISTORAGE;
 if dataObj.GetData(tFMT, tSTGM) = S_OK then
  begin
   if tSTGM.TYMED = TYMED_ISTORAGE then
    begin
     Filename := Format('%smail%d.msg',[Path, Index]);
     iStg := IStorage(tSTGM.stg);
     StgCreateDocfile(PWideChar(Filename), STGM_CREATE Or STGM_READWRITE Or STGM_SHARE_EXCLUSIVE, 0, iFile);
     iStg.CopyTo(0, tIID, nil, iFile);
     iFile.Commit(0);
     iFile := nil;
     iStg := nil;
    end
   else
    begin
     //
    end; 
 end;

don't use this code in production, it's lacking in many areas, but it will get you started.

Cheers,
Daddy

-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
forgot an important part:

Code:
TForm1 = class(TForm)
...
  public
    { Public declarations }
    CF_FileContents, CF_FileGroupDescriptor, CF_FileGroupDescriptorW: UINT;
  end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  OleInitialize(nil);
  {Allow window to accept drop events}
  OleCheck(RegisterDragDrop(Handle, Self));
 CF_FileContents := $8000 Or RegisterClipboardFormat(CFSTR_FILECONTENTS) And $7FFF;
 CF_FileGroupDescriptor := $8000 Or RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA) And $7FFF;
 CF_FileGroupDescriptorW := $8000 Or RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW) And $7FFF;
 end;

// OnDestroy does the exact opposite. It calls RevokeDropTarget to indicate that
// drop events are no longer accepted.
// It then calls OleUninitialize, since the application is finished using all OLE functions.

procedure TForm1.FormDestroy(Sender: TObject);
begin
  {Finished accepting drops}
  RevokeDragDrop(Handle);
  OleUninitialize;
end;

/Daddy

-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
Thanks :)

Any chance you can tell me where the following are declared? I get undeclared identifier in Delphi 6, so don't know if it is a missing uses, or something added in a later version (or just that I'm suffering brain fade - switching back and forth between Delphi and C# is causing me no end of grief).

CFSTR_FILECONTENTS
CFSTR_FILEDESCRIPTORA
CFSTR_FILEDESCRIPTORW

FILEDESCRIPTORA
FILEDESCRIPTORW
FILEGROUPDESCRIPTORW



Robertio
Software Developer
 
In D7, they are all declared in unit ShlObj.
(Win32 API Shell objects Interface Unit)

Roo
Delphi Rules!
 
yeah forgot uses clause:

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, ActiveX, ShlObj, ComObj, StdCtrls;


the CFSTR_ are variables (declared in public section of your form, see code above)

/Daddy

-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
Robertio,

did you get it working?

/Daddy

-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
It compiles ;-) thanks to the uses, thanks.

The problem running it locally is tSTGM.TYMED <> TYMED_ISTORAGE as it is TYMED_ISTREAM which throws it into the non-existent else statement.

I've been too busy on other stuff to work out how to save an ISTREAM.

That was with a single attachment, with multiple attachments it ended up in something that looked like a never ending loop. Will try to get back to it later (customer willing).

Robertio
Software Developer
 
I'll check on the IStream part, it's a bit more complicated than IStorage.

/Daddy

-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
Thanks :) it always seems the complicated option is the only one.

Looks like it will be a couple of weeks before I get back to this due to other priorities. Will update this thread when I get back to it...

Robertio
Software Developer
 
Robertio,

try to change line
tFMT.TYMED := TYMED_ISTREAM Or TYMED_ISTORAGE;
to:
tFMT.TYMED := TYMED_ISTORAGE;

FYI, the code runs fine on my machine (W7 Enterprise with Outlook 2007)

/Daddy



-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top