Loading file from virtual folder using Delphi 2007
I am trying to load the contents of a file from one of the Windows virtual folders (for example, the picture folder or iPhone). Below is an example of the code I am using for this:
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 is only a descendant of TCustomFileOpenDialog that provides the ShellItems property. In my actual application, I need a TStream object. So, in this example, I am using TFileStream to copy the original file as a proof of concept that I have successfully accessed the file using a Delphi stream. Everything works fine until I try FStrm.CopyFrom, at which point I get a "Not implemented" error. What am I doing wrong with this, or is there a better way to completely do what I want?
source to share
The only time TStream
itself raises an "not implemented" error if none of the 32-bit or 64-bit versions Seek()
are overridden in the descendant class (or one of them is mistakenly called a method inherited
). If true, an exception is thrown EStreamError
saying "ClassName.Seek is not implemented".
TOLEStream
overrides the 32-bit version Seek()
to call IStream.Seek()
. However, it does not override the getter property TStream.GetSize()
. So when you read the value OStrm.Size
before the call CopyFrom()
, it calls the default method TStream.GetSize()
it uses Seek()
to determine the size of the stream - Seek()
to get the current position, then Seek()
again to the end of the stream, storing the result, then Seek()
again to return to the previous position.
So my guess is that the IStream
one you got probably doesn't support random search, so its method will Seek()
return E_NOTIMPL
that TOLEStream.Seek()
will catch and express an exception EOleSysError
that says "Not implemented".
Try calling IStream.Stat()
to get the size of the stream (or get the class from TOLEStream
and override the method GetSize()
to call Stat()
)) and then pass the returned size CopyFrom()
if> 0 (if you pass Count=0
before CopyFrom()
it will read the properties of the original stream Position
and Size
thus throwing the same error Seek()
). eg:
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;
An alternative would be to simply avoid TStream.CopyFrom()
and manually copy the bytes yourself, allocating the local buffer and then calling OStrm.Read()
in a loop, writing each read buffer to FStrm
until OStrm.Read()
it says there are no more bytes to 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;
source to share