我试图从其中一个Windows虚拟文件夹(例如,相机或iPhone图片文件夹)加载文件的内容.下面是我正在使用的一些示例代码:
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;
Run Code Online (Sandbox Code Playgroud)
TAttachDialog只是TCustomFileOpenDialog的后代,它暴露了ShellItems属性.在我的实际应用程序中,我需要返回一个TStream对象.所以,在这个例子中,我使用TFileStream顶部复制源文件作为概念证明,我已经使用Delphi流成功访问了该文件.一切正常,直到我尝试FStrm.CopyFrom,此时我收到"未实现"错误.我做错了什么,或者有更好的办法完全做我想做的事情?
唯一一次TStream
本身引发"未实现"错误的是,如果Seek()
在后代类中没有覆盖32位或64位版本(或者其中一个错误地称为该inherited
方法).如果这是真的,EStreamError
则会引发异常,说"ClassName.Seek not implemented".
TOLEStream
确实覆盖Seek()
要调用的32位版本IStream.Seek()
.但是,它不会覆盖TStream.GetSize()
属性getter.所以当你OStrm.Size
在调用之前读取值时CopyFrom()
,它调用默认TStream.GetSize()
方法,Seek()
用于确定流大小 - Seek()
获取当前位置,然后Seek()
再次到流的末尾,保存结果,然后Seek()
再次返回到以前的位置.
所以,我的猜测是IStream
你获得的可能不支持随机搜索所以它的Seek()
方法返回E_NOTIMPL
,这TOLEStream.Seek()
将检测并引发一个EOleSysError
异常说"未实现".
尝试调用IStream.Stat()
以获取流大小(或从中派生类TOLEStream
并覆盖GetSize()
要调用的方法Stat()
),然后将返回的大小传递给CopyFrom()
if> 0(如果传递Count=0
给CopyFrom()
它,它将读取源流Position
和Size
属性,从而导致相同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;
Run Code Online (Sandbox Code Playgroud)
另一种方法是简单地避免TStream.CopyFrom()
和自己手动复制字节,方法是分配一个本地缓冲区然后调用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;
Run Code Online (Sandbox Code Playgroud)