-1

What is the IFileOperation equivalent of

SHFILEOPSTRUCTW op = {hDlg, FO_COPY, directoryFrom, directoryTo, 0, FALSE, NULL, NULL};
int status = SHFileOperationW(&op);

where directoryFrom is a directory path relative to my working directory and directoryTo is absolute?

  1. May I directly pass my absolute path to SHCreateItemFromParsingName to get an IShellItem? What are parsing names and display names, anyway?
  2. If SHCreateItemFromParsingName is appropriate for absolute paths, should I use it in conjunction with GetFullPathNameW to resolve the relative path or is there a simpler alternative?

I am finding the documentation on those topics could be much better.

purefanatic
  • 933
  • 2
  • 8
  • 23
  • 3
    `IFileOperation` works on `IShellItem` interfaces. You can't create an `IShellItem` for a relative path by itself, only for an absolute path. It is not that hard to create an absolute path by using `GetCurrentDirectory()` or equivalent and then appending your relative path to it. Otherwise, you can get an `IShellItem` for your working directory first, and then use `SHCreateItemFromRelativeName()` to create an `IShellItem` for a path relative to the working directory. But really, you shouldn't be relying on the working directory to begin with. Always use absolute paths. – Remy Lebeau Jun 25 '20 at 21:47
  • 1
    A Shell Item does not always correspond to a physical file. Hence the need for IFileDialog which supports virtual items. A Shell Item's parsing name is supposed to be unique in a parent Shell Folder, and is used to get IShellItem. A display name is used for display only and can be ambiguous (two items can have the same display name in a given Shell Folder). For physical file or folders, parsing and display names are the same. SHCreateItemFromParsingName works fine with absolute path for physical files and folders. – Simon Mourier Jun 25 '20 at 22:02
  • @RemyLebeau I thought about using `SHCreateItemFromRelativeName`, too, thank you for confirming that it should work. While I could maybe do away with working directories to solve my current problem I would still argue that working directories are invaluable and simplify a lot of things - although they should probably not be changeable in my opinion. – purefanatic Jun 26 '20 at 11:05
  • @SimonMourier That would explain why `SHCreateItemFromParsingName` might _work_ with display names but the official documentation makes it needlessly confusing by specifying it to work _only_ with display names without any explanation. Still, I don't quite understand your distinction between physical and non-physical files and folders and how they interact with parsing and display names. Am I correct in assuming that a physical file or folder would be one that actually exists in the file system? Does `SHCreateItemFromParsingName` not work then for nonexistent paths? – purefanatic Jun 26 '20 at 11:14
  • If you could write up a detailed explanation on those paths and names and their relationship to shelll items and the `SHCreateItem...` functions I would accept that as an answer as this is the point I am struggling most with. – purefanatic Jun 26 '20 at 11:14

1 Answers1

1

The Windows Shell is organized into a hierarchy called the Shell namespace: Introduction to the Shell Namespace:

The Shell namespace organizes the file system and other objects managed by the Shell into a single tree-structured hierarchy. Conceptually, it is a larger and more inclusive version of the file system.

What it means is, in the Shell Namespace, there are file system (or what I call "physical", considering possible kernel file systems as physical too) items and virtual items. Example of virtual items are:

  • The "This PC" folder
  • The Recycle Bin (where item have links into the file system)
  • The Control Panel (fully virtual)
  • Search folders (where items are links into other items)
  • Custom namespace extensions (you can extend the Shell namespace with a custom Shell Namespace Extension to connect to a database, a cloud storage, etc.)
  • etc.

All these items are identified using the same system: PIDLs. A PIDL is to a Shell Item what a full file path is to a (physical) file or folder.

Items can also be identified using their Parsing Name which is indirectly defined by IShellFolder::GetDisplayNameOf method and by _SHGDNF enumeration, if the containing folder supports it. Note a PIDL is strictly mandatory (each item has one) while a parsing name is not.

Since Windows Vista, it's recommended to use IShellItem-based API. And IFileDialog is also recommended because it supports PIDLs (=> virtual items), not only file system paths.

The pseudo code for SHCreateItemFromParsingName is more or less:

  1. call SHParseDisplayName(name) => a PIDL (old PIDL-based API)
  2. call SHCreateItemFromIDList(pidl) => an IShellItem

Which indeed proves that SHCreateItemFromParsingName could have been called SHCreateItemFromDisplayName...

Internally, SHParseDisplayName's code is more or less:

  1. get desktop's IShellFolder (desktop is the root of the Shell's namespace)
  2. call IShellFolder::ParseDisplayName(name) => a PIDL (relative to desktop which is the same as absolute since desktop is the namespace root)

IShellFolder is the interface implemented by all namespace folders: standard Windows-provided ones and custom ones, serving file system items or virtual items.

Now, IShellFolder::ParseDisplayName implementation vary. A custom Shell Folder can choose to implement it the way it likes (which can cause issues).

However desktop's IShellFolder implementation is quite complex and understands file system paths in lieu of display name. The doc says this:

A null-terminated Unicode string with the display name. Because each Shell folder defines its own parsing syntax, the form this string can take may vary. The desktop folder, for instance, accepts paths such as "C:\My Docs\My File.txt". It also will accept references to items in the namespace that have a GUID associated with them using the "::{GUID}" syntax. For example, to retrieve a fully qualified identifier list for the control panel from the desktop folder, you can use the following:

::{CLSID for Control Panel}\::{CLSID for printers folder}

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • Thank you for this detailed writeup. However, I am still uncertain where display names and parsing names are distinct from each other. For example, I should be able to pass ::{CLSID for Control Panel}\::{CLSID for printers folder} to `SHCreateItemFromParsingName` or `SHParseDisplayName`, right? But then, parsing name and display name are the same thing again? Could you provide an example where those two actually differ? – purefanatic Jun 26 '20 at 18:12
  • @purefanatic - if you read carefully including the links, you should be able to understand. You won't find much more explanation I'm afraid. – Simon Mourier Jun 26 '20 at 18:27
  • Sorry for the delay. I read everything and am a little smarter now. I think most of my confusion came from the fact that display names and parsing names are not considered disjoint: parsing names are more like a subset of display names, i.e. those display names that can be successfully parsed by `IShellFolder::ParseDisplayName` etc.. Only then it makes sense in my opinion. I feel like this could have been clearer in your answer text. The `_SHGDNF` documentation has helped me most as it comes up with actual examples. The shell namespace extension read was rather unrelated. – purefanatic Jul 07 '20 at 12:44
  • @purefanatic - You can only understand how IShellFolder really works when you write a namespace extension. All IShellFolder implementations are somewhat "extensions". Even the built-in ones. – Simon Mourier Jul 07 '20 at 12:58