2014-09-02 96 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