I am writing a Windows application, which collects (possibly hundreds of) names of files, all of which live in subfolders of one particular folder (which the user can select). The list is just a subset of all the files in the folders.
I do not want to implement a user-interface that offers all kinds of sorting and selection possibilities because Windows Explorer is always much better in this regard.
Is it there an API that allows me to launch Windows Explorer from my application such that it displays my list of files as if it were the result of a search operation?
The Explorer saved search format (.search-ms) is documented on MSDN. The only downside is that it will perform the actual search when you open it, it does not contain a list of paths of files found.
If this is unacceptable then you will have to get your hands dirty deep in IShellFolder and friends.
If hosting your own window is acceptable then IExplorerBrowser will get you 99% of the way there. Call IExplorerBrowser::FillFromObject to fill the view with a custom list of files or manipulate the view directly. Example code here.
If you must display the list in Explorer then I think you will have to bite the bullet and implement a namespace extension.
You can use the SHOpenFolderAndSelectItems function to open one particular folder.
LPCWSTR pszPathToOpen = L"C:\\Users\\strives";
PIDLIST_ABSOLUTE pidl;
if (SUCCEEDED(SHParseDisplayName(pszPathToOpen, 0, &pidl, 0, 0)))
{
// we don't want to actually select anything in the folder, so we pass an empty
// PIDL in the array. if you want to select one or more items in the opened
// folder you'd need to build the PIDL array appropriately
ITEMIDLIST idNull = { 0 };
LPCITEMIDLIST pidlNull[1] = { &idNull };
SHOpenFolderAndSelectItems(pidl, 1, pidlNull, 0);
ILFree(pidl);
}
Alternatively, you can call ShellExecute on the folder directly to run its default action (which is normally to open in a browser window):
ShellExecute(NULL, NULL, "C:\\Users\\strives", NULL, NULL, SW_SHOWNORMAL);
Related
I am writing a Shell Namespace Extension.
How may I force explorer to access (and open) items that are not enumerated with IShellFolder::EnumObjects by typing their parsing name in the address bar?
I have seen SFGAO_NONENUMERATED flag which I believe is used to mark such items (so in theory it should be possible, otherwise why do one need this flag?). I don't want to use SFGAO_HIDDEN or SFGAO_SYSTEM because such items could be seen nevertheless.
I am expecting explorer to call IShellFolder::ParseDisplayName with absolute or at least relative path to the item requested. But instead it is calling IShellFolder::ParseDisplayName only for the first segment of the parsing name (basically the first folder's parsing name). After this explorer queries content of each sub folder via IShellFolder::EnumObjects and calls IShellFolder::GetDisplayNameOf to get both display and parsing name of each item enumerated.
Based on this comment from Raymond Chen I understood that it is normal for explorer to rely on both display and parsing names to retrieve the item requested in the address bar. But the biggest issue here is that my items (that I want to obtain) are not enumerated. So explorer does not know about their existence.
I am wondering if it's possible to somehow use ShellExecute to open multiple files at once using the default verb handler. For example, if I have multiple mp3 files in a folder, I can select all of them, then right click and select "Play". This will bring up one instance of WMP (which is my default mp3 player) and will add all of the files I selected to the current playlist.
1) Is this accomplished using some standardized ShellExecute behavior?
2) Or is this done by first determining what the default program is and then supplying the list of files as arguments to that executable?
My goal is to be able to take a list of files and open them using the default verb with the default program (ideally without having to dig through the registry first).
I.e. the equivalent of this, but for multiple files:
ShellExecute(NULL, NULL, the_file_to_open, NULL, NULL, SW_SHOWNORMAL);
No, ShellExecute can't do this. Instead, the way to do it is with IContextMenu.
Broadly speaking:
Bind to the parent folder with SHBindToObject
Query for a context menu for the files in question with IShellFolder::GetUIObjectOf
Initialise the context menu with IContextMenu::QueryContextMenu, passing the CMF_DEFAULTONLY flag
Invoke the default command with IContextMenu::InvokeCommand
If you look at Classic Shell Start menu, you see that it has a section for recent applications. Every item in the section can contain popup menu, usually used for displaying recent documents, opened earlier in the application. For instance, the 'Word' item contains all .doc and .docx files recently opened in Word. I know a way to build such list: get all links from the 'Recent' folder and filter out matched documents, by .exe name.
But Classic Shell Start menu also shows popup menu for programs like RDC. For RDC client, namely, it shows a list of recent connections. It seems there is a concept of pseudo-documents in Windows, but I have never heard of it. How to get such list for a random .exe file?
Regards,
The shell maintains the list of recent documents. SHAddToRecentDocs allows a program to add an item to the list. The item can be:
A path and filename
A PIDL representing the shell object
(Windows 7+) A SHARDAPPIDINFO or SHARDAPPIDINFOIDLIST structure that identifies a shell item, and associated application.
(Windows 7+) An IShellLink
The key idea from the list and documentation is that the item can be a PIDL, which can represent a fancy kind of 'file'. (What you called a pseudo-document. This is an oversimplification. Read the documentation.)
To obtain the recent files list, use SHGetFolderLocation specifying CSIDL_RECENT and use the returned PIDLIST_ABSOLUTE to iterate the shell items.
If the item is not a file the list can be retrieved through the COM IApplicationDocumentLists which requires an Application User Model ID. This excludes pinned items, for which there is no programmatic access for the same reason that there is no access to the start menu pin list.
There are a lot of caveats to this, which is best explained by the documentation:
SHAddToRecentDocs
Managing the File System
(edited to add information about the jump list, and missing IShellLink from the list.)
I need to programatically extract information from a solution which contains almost 150 projects in it. The solution file is not flat though, so some of the projects are organized into folders, the folder hierarchy can be more levels deep.
This fits a recursive solution: I could write a function, which enumerates a list, and if the element is a project it would examine it, if it is a folder it would go into the folder and recursively call itself to examine the folder's content. The gist of it:
$dte = [System.Runtime.InteropServices.Marshal]::GetActiveObject("visualstudio.dte.11.0")
function traverseproject {
param([object]$prjnode, [int]$level)
if ($prjnode.Kind -eq "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}")
{
Write $prjnode.Name
Write $level
}
if ($prjnode.Kind -eq "{66A26720-8FB5-11D2-AA7E-00C04F688DDE}")
{
foreach ($prjsubnode in $prjnode)
{
traverseproject($prjsubnode, $level + 1)
}
}
}
foreach($prjn in $dte.solution.projects)
{
traverseproject($prjn, 0)
}
The problem is that the $prjnode object what the recursive function gets is weird.
Write $prjnode.Name doesn't output anything. Probably for the same reason I cannot iterate through the nodes of the folder object. Right now in the code above it's foreach ($prjsubnode in $prjnode), that just doesn't do anything silently. I tried foreach ($prjsubnode in $prjnode.ProjectItems), that gives error. I tried any kind of combinations.
From the error messages it seems that the $prjnode is type of a DTE ProjectItem link, 8E2F1269-185E-43C7-8899-950AD2769CCF. I can print out the Count property and it seems valid, but I don't see any property on the interface where I could get a hold of the contained elements. So maybe that's why I cannot iterate through? There's no way? I see the Visual Basic example at the bottom of the MSDN page I linked, but I need a working PowerShell solution.
The first call of the function seems to work OK, for example it sees the $prjnode.Kind property, but after the first recursive call things are lost.
Since you're already loading the dte, Check out http://studioshell.codeplex.com/
The feature that helps you the most:
Manage your projects, references, breakpoints, stack frame locals, menus, toolbars, Visual Studio settings, IDE windows, and even your code from PowerShell scripts, all in a consistent and discoverable way.
Here is how you can get all loaded projects with StudioShell
$projects = ls -path "DTE:\solution\Projects" -recurse
| where {$_.FileName -match ".csproj"}
Note that it may take up to 15 minutes for big solutions.
When you select a number of files on OpenFileDialog you get the working directory path in a structure member of OPENFILENAME but when you select a single file you get the path to filename in that variable member.
Would there be a possible way to get the path first before looping and counting to check the number of files?
Not quite sure what you mean, OpenFileDialog is a .NET class. Assuming native: you can set the OPENFILENAME.lpfnHook member to a callback function. That gives you notifications like CDN_FILEOK and CDN_FOLDERCHANGE. Gives you a preview of what was selected before the dialog closes and a way to cancel it.