2014-09-02 94 views
3

我試圖從Windows虛擬文件夾(例如,相機或iPhone圖片文件夾)中加載文件的內容。下面是我使用玩弄這一些示例代碼:使用Delphi 2007加載虛擬文件夾中的文件

procedure TfrmForm.ButtonClick(Sender: TObject); 
Var 
    Dialog: TAttachDialog; 
    Enum: IEnumShellItems; 
    Name: LPWSTR; 
    Item: IShellItem; 
    Strm: IStream; 
    OStrm: TOLEStream; 
    FStrm: TFileStream; 
    Result: HRESULT; 
    Buf: Array[0..99] Of Char; 
    Read: LongInt; 
begin 
    Result := CoInitializeEx(Nil, COINIT_APARTMENTTHREADED Or 
           COINIT_DISABLE_OLE1DDE); 
    If Succeeded(Result) Then 
    Begin 
    Dialog := TAttachDialog.Create(Self); 
    Try 
     Dialog.Options := [fdoAllowMultiSelect, fdoPathMustExist, 
         fdoFileMustExist]; 
     Dialog.Title := 'Select Attachments'; 

     If Dialog.Execute(Self.Handle) Then 
     Begin 
     If FAILED(Dialog.ShellItems.EnumItems(Enum)) Then 
      Raise Exception.Create('Could not get the list of files selected.'); 

     While Enum.Next(1, Item, Nil) = S_OK Do 
     Begin 
      If (Item.GetDisplayName(SIGDN_NORMALDISPLAY, Name) = S_OK) Then 
      Begin 
      mResults.Lines.Add(Name); 
      CoTaskMemFree(Name); 
      End; 

      If Item.BindToHandler(Nil, BHID_Stream, IID_IStream, Strm) = S_OK Then 
      Begin 
      OStrm := TOLEStream.Create(Strm); 
      FStrm := TFileStream.Create('C:\Temp\Test.jpg', fmCreate); 
      FStrm.CopyFrom(OStrm, OStrm.Size); 
      FreeAndNil(OStrm); 
      FreeAndNil(FStrm); 
      Strm := Nil; 
      End; 

      Item := Nil; 
     End; 
     End; 
    Finally 
     FreeAndNil(Dialog); 
    End; 
    CoUninitialize; 
    End; 
end; 

TAttachDialog只是TCustomFileOpenDialog的後裔暴露出ShellItems財產。在我的實際應用程序中,我需要返回一個TStream對象。所以,在這個例子中,我使用TFileStream頂部複製源文件作爲概念證明,我已經使用Delphi流成功訪問了該文件。一切工作正常,直到我嘗試FStrm.CopyFrom在哪一點我得到一個「未實施」的錯誤。我在做什麼不對,或者有什麼更好的方法來完成我想要的?

回答

5

唯一一次TStream本身提出了一個「沒有實現」錯誤是,如果既不Seek() 32位或64位版本在子類中被覆蓋(或其中一人錯誤地叫inherited方法)。如果這是真的,則會引發一個EStreamError異常,說「ClassName.Seek not implemented」。

TOLEStream確實覆蓋了Seek()的32位版本,致電IStream.Seek()。但是,它不會覆蓋TStream.GetSize()屬性獲取器。因此,在調用CopyFrom()之前,如果您正在讀取OStrm.Size值,它會調用默認的TStream.GetSize()方法,該方法使用Seek()來確定流大小 - Seek()以獲取當前位置,然後Seek()再次到達流的末尾,然後保存結果,然後Seek()再次回到之前的位置。

所以,我的猜測是,該IStream你已經獲得可能不支持隨機求所以其Seek()方法返回E_NOTIMPL,這TOLEStream.Seek()將檢測併發出EOleSysError異常說「沒有實現」。

嘗試調用IStream.Stat()獲得視頻流的大小(或TOLEStream派生類和重寫GetSize()方法來調用Stat()),然後將返回的大小CopyFrom()如果> 0(如果傳遞Count=0CopyFrom(),它會讀出的源數據流的PositionSize性能,因此引起同一誤差Seek()),例如:

var 
    ... 
    Stat: STATSTG; 
begin 
    ... 
    if Item.BindToHandler(Nil, BHID_Stream, IID_IStream, Strm) = S_OK Then 
    try 
    OStrm := TOLEStream.Create(Strm); 
    try 
     FStrm := TFileStream.Create('C:\Temp\Test.jpg', fmCreate); 
     try 
     OleCheck(Strm.Stat(Stat, STATFLAG_NONAME)); 
     if Stat.cbSize.QuadPart > 0 then 
      FStrm.CopyFrom(OStrm, Stat.cbSize.QuadPart); 
     finally 
     FreeAndNil(FStrm); 
     end; 
    finally 
     FreeAndNil(OStrm); 
    end; 
    finally 
    Strm := Nil; 
    end; 
    ... 
end; 

另一種方法是簡單地避免TStream.CopyFrom()和手動複製字節自己,通過分配一個本地緩衝器中的ND然後調用在一個循環OStrm.Read(),寫各讀緩衝器向FStrm,直到OStrm.Read()報告沒有更多的字節閱讀:

var 
    ... 
    Buf: array[0..1023] of Byte; 
    NumRead: Integer; 
begin 
    ... 
    if Item.BindToHandler(Nil, BHID_Stream, IID_IStream, Strm) = S_OK Then 
    try 
    OStrm := TOLEStream.Create(Strm); 
    try 
     FStrm := TFileStream.Create('C:\Temp\Test.jpg', fmCreate); 
     try 
     repeat 
      NumRead := OStrm.Read(Buf[0], SizeOf(Buf)); 
      if NumRead <= 0 then Break; 
      FStrm.WriteBuffer(Buf[0], NumRead); 
     until False; 
     finally 
     FreeAndNil(FStrm); 
     end; 
    finally 
     FreeAndNil(OStrm); 
    end; 
    finally 
    Strm := Nil; 
    end; 
    ... 
end; 
+0

謝謝!這很好。 – Caynadian 2014-09-03 13:16:34