Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get a file's size through `IShellItem`

I've implemented a directory walking algorithm for the Windows Shell using IShellItem, IShellFolder, IStorage, IStream, etc. All is well and good. I can even walk inside shell namespace extensions (e.g. .zip) files.

However, I have problems extracting (regular) file sizes when files are being used by some other program with exclusive access.

AFAIK, there is nothing but the STATSTG structure that gives more information than the file's name. There are essentially 3 ways to get a hold of a STATSTG for a IShellItem:

  1. Iterate using IEnumSTATSTG instead of IEnumIDList. Instead of invoking IShellFolder::EnumObjects(), get the IStorage for the folder and invoke IStorage::EnumElements(). You now get STATSTG structures directly.
  2. Get the IStorage for the IShellItem and invoke IStorage::Stat().
  3. Get the IStream for the IShellItem and invoke IStream::Stat().

I would really like to use #1 because it would give me all the information I need. However, I cannot get it to enumerate folder contents. I successfully extract the IStorage for the folder: it's own Stat() gives me the proper folder name. I successfully extract the IEnumSTATSTG, but the first call to Next(1, &item, NULL) returns S_FALSE and terminates the enumeration.

I would fallback to use #2 as it is still not so bad, but extracting the IStorage for regular disk files produces an error using both of IShellItem::BindToHandler(0, BHID_Storage, ...) and IShellFolder::BindToStorage(child, ...).

I finally tried #3 although it just plains seems wrong and it succeeds as long as files are not being used with exclusive access by another program.

I've googled around a bit and found several code snippets that use approach #3.

Question: Can anyone explain how I'm supposed to get the file's STATSTG without using approach #3?

Should approach #1 work, or does the IStorage implementation for regular folders simply not produce listings? Should approach #2 work or is the IStorage implementation simply not implemented for regular files?

Environment: Windows Vista Ultimate 32-bit, Visual Studio 2008 Express. Using C++, no ATL, all custom COM wrappers (in-house, may be suitably modified assuming somwthing is wrong there).

like image 555
André Caron Avatar asked Nov 15 '10 06:11

André Caron


2 Answers

Have you tried getting hold of the IShellItem2 interface, and then querying for the value of the PKEY_Size property?

like image 182
Roger Lipscombe Avatar answered Oct 21 '22 00:10

Roger Lipscombe


Even with the accepted answer, it took some doing.

The first thing you need is the Windows Properties reference. From there you have to know that you want to go into System.Size. From there you get the two important pieces of information:

System.Size

The system-provided file system size of the item, in bytes.

shellPKey = PKEY_Size
typeInfo
       type = UInt64

Knowing that it's a UInt64, you can then get ahold of the IShellItem2 interface, in order to use one of the many property-getting methods:

//Get the IShellItem2 interface out of the IShellItem object
IShellItem2 si2 = shellItem as IShellItem2;

//Get the file fize (in bytes)
UInt64 fileSize;
si2.GetUInt64(PKEY_Size, ref fileSize);
like image 31
Ian Boyd Avatar answered Oct 20 '22 23:10

Ian Boyd