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?

+3


source to share


1 answer


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;

      

+5


source







All Articles