2010-11-29 8 views
2

사용자가 내 양식에 끌어 놓기 동작을 적용해야합니다. 탐색기에서 파일을 수락하는 것이 어렵지는 않았지만 OLE 개체 (Outlook 전자 메일)를 삭제하면 나 자신을 처리하기가 어렵습니다.IDropTarget.Drop 메서드에서 얻은 IDataObject를 Outlook 메시지로 변환하고 디스크에 저장하십시오.

지금까지 IDropTarget 인터페이스가 구현 된 델파이 폼이 있습니다.

function TForm1.Drop(const dataObj: IDataObject; grfKeyState: Integer; 
    pt: TPoint; var dwEffect: Integer): HResult; 
var 
    aFmtEtc: TFORMATETC; 
    aStgMed: TSTGMEDIUM; 
    pData: PChar; 
begin 


    if (dataObj = nil) then 
    raise Exception.Create('IDataObject-Pointer is not valid!'); 
    with aFmtEtc do 
    begin 
    cfFormat := CF_UNICODETEXT; 
    ptd := nil; 
    dwAspect := DVASPECT_CONTENT; 
    lindex := 0; 
    tymed := TYMED_ISTREAM Or TYMED_ISTORAGE; 
    end; 

    {Get the data} 
    OleCheck(dataObj.GetData(aFmtEtc, aStgMed)); 
    try 

    {Lock the global memory handle to get a pointer to the data} 
    pData := GlobalLock(aStgMed.hGlobal); 
    { Replace Text } 
    Memo1.Text := pData; 
    finally 
    {Finished with the pointer} 
    GlobalUnlock(aStgMed.hGlobal); 
    {Free the memory} 
    ReleaseStgMedium(aStgMed); 
    end; 
    Result := S_OK; 
end; 

이 구현은 메모에 메시지의 미리보기를 추가 : 여기

IDropTarget = interface(IUnknown) 
    ['{00000122-0000-0000-C000-000000000046}'] 
    function DragEnter(const dataObj: IDataObject; grfKeyState: Longint; 
     pt: TPoint; var dwEffect: Longint): HResult; stdcall; 
    function DragOver(grfKeyState: Longint; pt: TPoint; 
     var dwEffect: Longint): HResult; stdcall; 
    function DragLeave: HResult; stdcall; 
    function Drop(const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint; 
     var dwEffect: Longint): HResult; stdcall; 
    end; 

드롭 방식의 구현입니다.

Outlook에서받은 메시지를 하드 드라이브에 저장하는 방법은 무엇입니까? 나는 의사 코드 에서조차 어떤 언어로도 샘플을 비난 할 것이다.

+0

메모에 지정한 정확한 줄을 저장하고 싶습니까? – jachguate

답변

9

전 Outlook IDataObject을 처리했습니다.

Outlook은 Explorer에서 일반 셸 개체로 CFSTR_FILEDESCRIPTOR/CFSTR_FILECONTENTS을 노출합니다. VirtualStringTree 위에 떨어지는 경우

0: cfFormat: TClipFormat = "RenPrivateSourceFolder" (49972) 
    tymed: Longint = 4 (TYMED_ISTREAM) 

1: cfFormat: TClipFormat = "RenPrivateMessages" (49587) 
    tymed: Longint = 4 (TYMED_ISTREAM) 

2: cfFormat: TClipFormat = "RenPrivateItem" (49812) 
    tymed: Longint = 1 (TYMED_HGLOBAL) 

3: cfFormat: TClipFormat = CFSTR_FILEDESCRIPTOR ("FileGroupDescriptor", 49281) 
    tymed: Longint = 1 (TYMED_HGLOBAL) 

4: cfFormat: TClipFormat = CFSTR_FILEDESCRIPTORW ("FileGroupDescriptorW", 49280) 
    tymed: Longint = 1 (TYMED_HGLOBAL) 

5: cfFormat: TClipFormat = "FileNameW" (49159) 
    tymed: Longint = 2 (TYMED_FILE) 

6: cfFormat: TClipFormat = CFSTR_FILECONTENTS ("FileContents", 49282) 
    tymed: Longint = 12 (TYMED_ISTREAM, TYMED_ISTORAGE) 

7: cfFormat: TClipFormat = "Object Descriptor" (49166) 
    tymed: Longint = 1 (TYMED_HGLOBAL) 

8: cfFormat: TClipFormat = CF_TEXT (1) 
    tymed: Longint = 1 (TYMED_HGLOBAL) 

9: cfFormat: TClipFormat = CF_UNICODETEXT (13) 
    tymed: Longint = 1 (TYMED_HGLOBAL) 

10: cfFormat: TClipFormat = "CSV" (49993) 
    tymed: Longint = 1 (TYMED_HGLOBAL) 

내가 드래그 앤 드롭을 사용하지만, 쉘 개념은 동일합니다 : 내가 IDataObject의 형식을 열거하면

내가 그들을 참조하십시오. 나는 트릭 위에서 우리는 IDataObject에 포함 된 형식을 열거입니다

function HandleOleDrop(DataObject: IDataObject; var DropEffect: TDropEffect; 
    FileContentsCallback: TFileContentsCallback; 
    FilenameCallback: TFilenameCallback; 
    BitmapCallback: TBitmapCallback; 
    StreamCallback: TStreamCallback): HResult; 
var 
    enum: IEnumFORMATETC; 
    OLEFormat: TFormatEtc; 
    Fetched: Integer; 
begin 
    //Go through the formats list in order, looking for the formats we understand. 
    //The order they are given to us is the order the original application's idea 
    //of better to worse 
    Result := DataObject.EnumFormatEtc(DATADIR_GET, enum); 
    if Failed(Result) then 
     Exit; 

    Result := enum.Reset; 
    if Failed(Result) then 
     Exit; 

    while enum.Next(1, OLEFormat, @Fetched) = S_OK do 
    begin 
     OutputDebugString(PChar('[HelpDeskShellOperations.HandleOleDrop] ClipboardFormat = ' + ClipFormatToStr(OleFormat.cfFormat))); 
     if OLEFormat.cfFormat = CF_FILEDESCRIPTOR then 
    begin 
      //Transfer data as if it were a file, regardless of how it is actually stored 
      //If CF_FILEDESCRIPTOR is present, then a CF_FILECONTENTS must also be 
      //present later on in the formats list 
      Result := GetFileContents(DataObject, DropEffect, FileContentsCallback); 
      Break; 
     end 
     else if OleFormat.cfFormat = CF_HDROP then 
     begin 
      Result := GetHDropContents(DataObject, DropEffect, FilenameCallback); 
      Break; 
     end 
     else if OleFormat.cfFormat = CF_BITMAP then 
     begin 
      Result := GetBitmapContents(DataObject, DropEffect, BitmapCallback); 
      Break; 
     end; 
{  else if OleFormat.cfFormat = CF_PNGStream then 
     begin 
      Result := GetStreamContents(OleFormat.cfFormat, DataObject, 'untitled.png', DropEffect, StreamCallback); 
      Break; 
     end 
     else if OleFormat.cfFormat = CF_GIFStream then 
     begin 
      Result := GetStreamContents(OleFormat.cfFormat, DataObject, 'untitled.gif', DropEffect, StreamCallback); 
      Break; 
     end 
     else if OleFormat.cfFormat = CF_JFIFStream then 
     begin 
      Result := GetStreamContents(OleFormat.cfFormat, DataObject, 'untitled.jpeg', DropEffect, StreamCallback); 
      Break; 
     end;} 

     //  else if Formats[i] = ... then 
     //  begin 
     //Handle another format 
     //   Break; 
     //  end 
    end; 
end; 

(결국 데이터베이스에 Outlook 전자 메일을 저장) 내 코드의 조각을 재 게시 할 수 있습니다. 우리는 CF_FILEDESCRIPTOR을 좋아하고 특별히 그것을 처리 :

function GetFileContents(DataObject: IDataObject; var Effect: TDropEffect; FileContentsCallback: TFileContentsCallback): HResult; 
var 
    hr: HResult; 
    format: TFormatEtc; 
    FileDescriptorMedium: TStgMedium; 
    fgdGlobal: hGlobal; //global memory object where the descriptors are 
    fgd: PFileGroupDescriptor; //descriptors 
    Descriptors: PFileDescriptorArray; //helper to access descriptors dynamic array 
    nItem: Integer; 
    FileContentsMedium: TStgMedium; 
    // stm: IStream; 
begin 
    //1. Extract the CFSTR_FILEDESCRIPTOR format as a TYMED_HGLOBAL value. 
    ZeroMemory(@format, SizeOf(TFormatEtc)); 
    format.cfFormat := CF_FILEDESCRIPTOR; 
    format.dwAspect := DVASPECT_CONTENT; 
    format.tymed := TYMED_HGLOBAL; 
    ZeroMemory(@FileDescriptorMedium, SizeOf(FileDescriptorMedium)); 

    Result := DataObject.GetData(format, FileDescriptorMedium); //Free the medium at the end 
    if Failed(Result) then 
     Exit; 
    try 
     //2. The hGlobal member of the returned STGMEDIUM structure points to a 
      // global memory object. Lock that object by passing the hGlobal value 
      // to GlobalLock. 
     fgdGlobal := FileDescriptorMedium.hGlobal; 
     fgd := GlobalLock(fgdGlobal); 
     try 
     //3. Cast the pointer returned by GlobalLock to a FILEGROUPDESCRIPTOR pointer. 
     // It will point to a FILEGROUPDESCRIPTOR structure followed by one or more 
     // FILEDESCRIPTOR structures. Each FILEDESCRIPTOR structure contains a 
     // description of a file that is contained by one of the accompanying 
     // CFSTR_FILECONTENTS formats. 
     Descriptors := Addr(fgd.fgd[0]); 

     //4. Examine the FILEDESCRIPTOR structures to determine which one corresponds 
     // to the file you want to extract. The zero-based index of that 
     // FILEDESCRIPTOR structure is used to identify the file's 
     // CFSTR_FILECONTENTS format. Because the size of a global memory block is 
     // not byte-precise, use the structure's nFileSizeLow and nFileSizeHigh 
     // members to determine how many bytes represent the file in the global 
     // memory object. 
     for nItem := 0 to fgd.cItems - 1 do 
     begin 
      //5. Call IDataObject::GetData with the cfFormat member of the FORMATETC 
      // structure set to the CFSTR_FILECONTENTS value and the lIndex member 
      // set to the index that you determined in the previous step. The tymed 
      // member is typically set to TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE. 
      // The data object can then choose its preferred data transfer mechanism. 
      ZeroMemory(@format, SizeOf(TFormatEtc)); 
      format.cfFormat := CF_FILECONTENTS; 
      format.dwAspect := DVASPECT_CONTENT; 
      format.lindex := nItem; 
      format.tymed := TYMED_HGLOBAL or TYMED_ISTREAM or TYMED_ISTORAGE; 
      //NOTE: IStorage based files are a bitch to deal with 
      ZeroMemory(@FileContentsMedium, SizeOf(FileContentsMedium)); 

      //Now get the file's contents 
      hr := DataObject.GetData(format, FileContentsMedium); 
      if Failed(hr) then 
       Continue; 
      try 
       { 
       Progress: nItem+1 of fgd.cItems 
       } 

         //6. The STGMEDIUM structure that IDataObject::GetData returns will 
         // contain a pointer to the file's data. Examine the tymed member of 
         // the structure to determine the data transfer mechanism. 

       { i now have afile's name/size/date/etc in 
       Descriptors[nItem] 
       and the guts of file in 
       FileContentsMedium 
       (which could actually be in an hGlobal, IStream or IStorage. 
       Use ConvertStorageMediumToIStream to convert whatever it is into an IStream) 
       } 
      // stm := ConvertStorageMediumToIStream(FileContentsMedium); 

       if Assigned(FileContentsCallback) then 
        FileContentsCallback(Descriptors[nItem], FileContentsMedium, nItem + 1, fgd.cItems); 
      finally 
       //Call ReleaseStgMedium to release the global memory object. 
       ReleaseStgMedium(FileContentsMedium); 
      end; 
     end; 
     finally 
     GlobalUnlock(fgdGlobal); 
     end; 
    finally 
     ReleaseStgMedium(FileDescriptorMedium); 
    end; 
end; 

을 그리고 그것을 원하는 파일을 넣을 수 있도록 내 응용 프로그램은 콜백을 가지고있다. 필자의 경우 파일을 개체에 저장하여 나중에 데이터베이스에 저장할 수 있습니다.

function TfrmTicketDetail.FileContentsCallback(const Descriptor: TFileDescriptor; const medium: TStgMedium; Progress: Integer; MaxProgress: Integer): Boolean; 
var 
    szFilename: string; 
    Attachment: TAttachment; 
    ListItem: TVirtualListItem; 
    stm: IStream; 
    vclStream: TStream; 
begin 
    try 
     szFilename := descriptor.cFilename; 

     Attachment := TAttachment.Create(dmodGlobal.ADOConnection); 

     Attachment.RecordID := ToolKit.CreateGUID; 
     Attachment.AutoGenerateRecordID := false; 
     Attachment.setStateNew; 
     Attachment.PrevState := Attachment.State; 
     Attachment.IsLink := False; 

     {Helpdesk attachments don't let you save the "date" of the attachment} 
     if (descriptor.dwFlags and FD_CREATETIME) = FD_CREATETIME then 
      Attachment.CreationDate := FileTimeToDateTime(descriptor.ftCreationTime) 
     else if (descriptor.dwFlags and FD_WRITESTIME) = FD_WRITESTIME then 
      Attachment.CreationDate := FileTimetoDateTime(descriptor.ftLastWriteTime) 
     else 
      Attachment.CreationDate := Now; 

     Attachment.FileName := ExtractFileName(szFilename); 
     Attachment.FullPath := ExtractFilePath(szFilename); 
     Attachment.Extension := ExtractFileExt(szFilename); 
     Attachment.Version := ''; 
     Attachment.Comment := ''; 
     Attachment.TicketGUID := StringToGUID(GUID); 
     Attachment.UserGUID := StringToGUID(dmodGlobal.UserGUID); 

     stm := ConvertStorageMediumToIStream(medium); 
     vclStream := TOleStream.Create(stm); 
     Attachment.FileStream := vclStream; 

     {Most often they don't fill in the file size structure member, 
     so we have to go to the contents to find the size.} 
     if (descriptor.dwFlags and FD_FILESIZE) = FD_FILESIZE then 
     begin 
      Attachment.Size := (Descriptor.nFileSizeLow and $7FFFFFFF); 
      //Yes, i know, ignoring the upper 32-bits, and throwing away the "sign" bit. 
      //If you wanna change the database to handle an unsigned 64-bit, you BE MY GUEST - jerk wad 
      //descriptor.nFileSizeLow or (descriptor.nFileSizeHigh shl 32) 
     end 
     else 
     begin 
      Attachment.Size := Integer(GetIStreamSize(stm) and $7FFFFFFF); 
     end; 

     //Add it to the listivew 
     ListItem := lvAttachments.Items.Add; 
     ListItem.Data := Attachment; 
     olAttachments.Add(Attachment); 
     TAttachment.RefreshAttachmentListItem(ListItem); 
     FFlags.SetFlags(FLAG_MODIFIED); 
     SetStatus(STATUS_MODIFIED); 

     Result := True; 
    except 
     on e: Exception do 
     begin 
      Result := False; 
      MessageDlg('New Attachment Failed: ' + ToolKit.CRLF + e.Message, mtError, [mbOK], 0); 
     end; 
    end; 
end; 
+0

와우, 이건 그냥, 와우 야. 정말 고맙습니다. – v00d00

+0

분명히 이것은 오래 전 이었지만 CF_FILEDESCRIPTOR와 같은 모든 상수에 대한 정의는 어디서 얻었습니까? – Tony

+0

@ 토니'CF_FILEDESCRIPTOR : = RegisterClipboardFormat (CFSTR_FILEDESCRIPTOR);'[쉘 클립 보드 형식] 참조 (http://msdn.microsoft.com/en-us/library/windows/desktop/bb776902.aspx) –

관련 문제