C++ Winapi - Get Directory (path) identifier - c++

I'm using Winapi's function SHBrowseForFolder and I need to set a default folder for browsing. To achieve that, I need to get the desired default folder's "PIDL". I can get the "PIDL" using this function: SHGetFolderLocation(hwnd, nCSIDL, NULL, NULL, &pidlRoot); but as you can see, the second parameter is another identifier "nCSIDL" (type int).
How can I get a path's indentifier by path (LPCSTR)?
This is my code:
TCHAR szDir[MAX_PATH];
BROWSEINFO bInfo;
HWND hwnd = ObPanelAPI::GetPanelHandleByName("*Current");
bInfo.hwndOwner = hwnd;
bInfo.pszDisplayName = szDir;
bInfo.lpszTitle = "Seleccionar directorio"; //Dialog title
bInfo.ulFlags = 0 ;
bInfo.lpfn = NULL;
bInfo.lParam = 0;
bInfo.iImage = -1;
//Default folder set
LPITEMIDLIST pidlRoot = NULL;
//*******I NEED TO GET nCSIDL HERE********
SHGetFolderLocation(hwnd, nCSIDL, NULL, NULL, &pidlRoot);
bInfo.pidlRoot = pidlRoot;
LPITEMIDLIST lpItem = SHBrowseForFolder(&bInfo);

I'm using Winapi's function SHBrowseForFolder and I need to set a default folder for browsing. To achieve that, I need to get the desired default folder's "PIDL".
First, you need to differentiate something here. BROWSEINFO has a pidlRoot field, which specifies the top-most folder that the browse dialogs displays. A user cannot select items above that folder. This is NOT the same thing as a "default folder", which is just the folder that is initially selected within the chosen root when the dialog appears.
To set a "default folder", you actually do not need a PIDL, you can use a path string instead (the dialog accepts both). Assign a pointer to the path string to the BROWSEINFO.lParam field and provide a callback function in the BROWSEINFO.lpfn field. Inside the callback, when it receives the BFFM_INITIALIZED notification, it can send the dialog a BFFM_SETSELECTION message, setting the wParam parameter to TRUE and setting the lParam parameter to the path string pointer. This is documented on MSDN:
BFFCALLBACK function pointer
BFFM_SETSELECTION
Specifies the path of a folder to select. The path can be specified as a string or a PIDL.
Now, with that said, the BROWSEINFO.pidlRoot field requires a PIDL, and the BFFM_SETSELECTION message does accept a PIDL as input, so...
I can get the "PIDL" using this function: SHGetFolderLocation(hwnd, nCSIDL, NULL, NULL, &pidlRoot); but as you can see, the second parameter is another identifier "nCSIDL" (type int).
That function is for retrieving PIDLs for special folders that are pre-defined by Microsoft, like Documents, AppData, User Profiles, etc. These are identified by constant CSIDL values (and KNOWNFOLDERID values on Vista+) which are consistent on every Windows version, though the particular paths may differ from one system to another based on user configuration.
How can I get a path's indentifier by path (LPCSTR)?
There are a few different ways:
IShellFolder::ParseDisplayName(). Call SHGetDesktopFolder() to get the IShellFolder for the Shell namespace root, and then call its ParseDisplayName() to parse the path.
SHParseDisplayName(). Similar to above, but with some additional options available. Do be aware of the following note in the documentation, though:
You should call this function from a background thread. Failure to do so could cause the UI to stop responding.
ILCreateFromPath(). However, it has been observed that ILCreateFromPath() does not always return the same PIDLs that IShellFolder::ParseDisplayName()/SHParseDisplayName() return, that sometimes that difference can cause things like SHBrowseForFolder() to behave incorrectly. I have not personally experienced this, but other people claim they have. You will just have to try it for yourself.

Related

Issues with CFileDialog for multiple file selection

I'm using the following code to retrieve multiple file selection via UI:
CFileDialog fd(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_EXPLORER,
NULL, hParentWnd ? CWnd::FromHandle(hParentWnd) : NULL);
fd.m_pOFN->Flags |= OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_NODEREFERENCELINKS;
int nLnBuff = 32767;
TCHAR* pBuffFileSelect = new TCHAR[nLnBuff];
memset(pBuffFileSelect, 0, nLnBuff * sizeof(TCHAR));
fd.m_ofn.lpstrFile = pBuffFileSelect;
fd.m_ofn.nMaxFile = nLnBuff;
if(fd.DoModal() == IDOK)
{
POSITION fileNamesPosition = fd.GetStartPosition();
while(fileNamesPosition != NULL)
{
CString strSelPath = fd.GetNextPathName(fileNamesPosition);
TRACE("path: %s\n", CStringA(strSelPath));
}
}
delete[] pBuffFileSelect;
So when I try it on my PC, I run the method above and when the "Open File" dialog open up, just as a test, I navigated to my desktop and selected all files using Ctrl+A shortcut and then clicked Open. As a result I started getting the following paths:
The first path is a link, which is correct (it exists on my Public desktop):
"C:\Users\Public\Desktop\avp.lnk"
But then the second path is wrong. It gives me:
"C:\Users\Public\Desktop\1.txt"
when it's supposed to be (for the desktop that I picked):
"C:\Users\UserName\Desktop\1.txt"
and then every consecutive path has "Public" instead of "UserName".
I should point out that I have several user accounts set up on this PC and the one that I'm testing this method from is a Standard user account. The app that I'm running this method from is not running elevated (or with regular user privileges) so it should not have access to other user accounts anyway.
So what am I doing wrong here?
Checked the sources, and GetOpenFileName assumes that all the items are in fact in the same file path. This isn't true for the Desktop (there are items in different paths merged into one shell view), and so you'll see the bad behavior.
The solution is to use the Common Item dialogs, which use the shell namespace rather than file system paths. All the desktop items are in a common shell path, and then you can use IShellItem::GetDisplayName to convert to a file system path.
Unfortunately, MFC doesn't have a wrapper for the common item dialog, so you'll have to manage that yourself.

How can I make the command prompt window open in the top left corner?

I'm fairly new (about 10 weeks into an level 1 high school course) and I'm trying to see how I can format the command prompt window. I've learned how to set the size of the window, but not the position. I'm using code::blocks on a windows XP
First, Read This
Then, try these... (in a batch file)
Set mycmdHeight=40
Set mycmdWidth=80
Set mycmdxPos=0
Set mycmdyPos=120
Or, programmatically, look here or here
You can use windows function to move the console windows in your desire location.
First look the function to return the handle of current windows.
HWND WINAPI GetConsoleWindowNT(void)
{
// declare function pointer type
typedef HWND WINAPI (*GetConsoleWindowT)(void);
// declare one such function pointer
GetConsoleWindowT GetConsoleWindow;
// get a handle on kernel32.dll
HMODULE hK32Lib = GetModuleHandle(TEXT("KERNEL32.DLL"));
// assign procedure address to function pointer
GetConsoleWindow = (GetConsoleWindowT)GetProcAddress(hK32Lib,TEXT("GetConsoleWindow"));
// check if the function pointer is valid
// since the function is undocumented
if ( GetConsoleWindow == NULL ) {
return NULL;
}
// call the undocumented function
return GetConsoleWindow();
}
Use above function to get the handle of current window.
HWND hwnd = GetConsoleWindowNT();
Now, you can move your windows at your desire location using MoveWindow function like below:
MoveWindow(hWnd,1230,750,200,100,TRUE);
You can get the complete sample program here.

force SHBrowseForFolder() to show desired directory

I've been searching online and fighting this thing for over an hour and still can't seem to get it to work. Most people seem to be satisfied when they get it this far on forums etc. but mine still doesn't work.
I'm trying to force the SHBrowseForFolder() function to start in a folder of my choosing.
char current[MAX_PATH];
strcpy(current,"C:\\Users");
char outbuf[MAX_PATH];
BROWSEINFO bis;
bis.hwndOwner = NULL;
bis.pidlRoot = NULL;
bis.pszDisplayName = outbuf;
bis.lpszTitle = (LPCSTR)"HERE";
bis.ulFlags = BIF_NEWDIALOGSTYLE|BIF_RETURNONLYFSDIRS;
bis.lpfn = NULL;
bis.lParam = (LPARAM)current;
SHBrowseForFolder(
&bis
);
It seems like this should be a relatively simple task. :/
At the moment, the above code is still showing the default: the Desktop folder.
Beyond starting in a specific folder, if possible, I'd also like it to ONLY show that folder and below, with no access to parent directories.
What am I missing here?
You can also send a BFFM_SETSELECTION message from your BrowseCallbackProc, like:
int FAR PASCAL BrowseNotify(HWND hWnd, UINT iMessage, long wParam, LPARAM lParam)
{ if (iMessage == BFFM_INITIALIZED)
{ SendMessage(hWnd, BFFM_SETSELECTION, 1, (LPARAM) szInitialPathName); // Set initial folder
return 1;
}
return 0;
}
Set the BFFCALLBACK (lpfn) to a BrowseCallbackProc. From there you can call SendMessage with BFFM_SETEXPANDED to specify the path of a folder to expand in the Browse dialog box.
See:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773205(v=vs.85).aspx and
http://msdn.microsoft.com/en-us/library/windows/desktop/bb762598(v=vs.85).aspx
From my experience, that folder dialog is a bit flaky - it often scrolls the desired directory out of view and looks suboptimal. Just one of the joys of Windows...
Also, there is no way I have discovered to get it to show only that directory and its subs. The parent directories always seem to be there.
Set BIF.PidlRoot to the PIDL you don't want the user to browse below, select and expand the folder you want initially focused and selected - do as above - and it should work.
Jens. :)

Allowing IFileOpenDialog to pick a folder that doesn't exist yet

I'm trying to create a dialog to select a new folder to save files into. The IFileOpenDialog class works great except that it won't allow a new folder to be picked.
I.e. "Folder: C:\existings\new-folder" in the bottom of the dialog pops up the following message:
new-folder
Path does not exist.
Check the path and try again.
Here's the code I've got:
IFileDialog* fileDialog
CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&fileDialog));
DWORD options;
fileDialog->GetOptions(&options);
options &= ~FOS_FILEMUSTEXIST;
options &= ~FOS_PATHMUSTEXIST;
fileDialog->SetOptions(options | FOS_PICKFOLDERS);
fileDialog->Show(parentWindow);
Any pointers or hacks would be appreciated! Thanks!
To quote Michael from this other question:
[To head off some comments, the SHBrowseForFolder API still exists, but is still not an acceptable solution for our UI deciders.]
The same applies here...
I think you want to use CLSID_FileSaveDialog instead of CLSID_FileOpenDialog. And possibly make use of IFileSaveDialog in addition to the base class IFileDialog.
As you're selecting a folder, you could use the folder picker dialog.
This, with the right flags, has a "create" button at the bottom and a text entry allowing you to specifiy a non existant path.

CPRintDialog does not allow print range selection

Im trying to add printing functionality to my app. I display the CPrintDialog to get the printer options. How do I get the printing range option enabled ? Currently this option is disabled when I doModal() the dialog.
What arguments are you passing to the CPrintDialog constructor? The constructor is declared as
CPrintDialog(BOOL bPrintSetupOnly,
DWORD dwFlags = PD_ALLPAGES|PD_USEDEVMODECOPIES|PD_NOPAGENUMS|
PD_HIDEPRINTTOFILE|PD_NOSELECTION,
CWnd* pParentWnd = NULL);
so if you don't supply a value for the "dwFlags" argument, you'll get those default flags. The default flags includes PD_NOPAGENUMS, so the page selection controls will be disabled. You've also got to set the maximum page number, too, for the page selection controls to work. Try changing your code to something like
CPrintDialog dlg(FALSE,
PD_ALLPAGES|PD_USEDEVMODECOPIES|PD_HIDEPRINTTOFILE|PD_NOSELECTION);
dlg.m_pd.nMaxPage = 100;
dlg.DoModal();
Does your printing code set the amount of pages that are going to be printed? I don't have any code at hand but I think it's in OnPreparePrintDC() or something like that, where you set m_MaxPage or so member of the object you get as an argument.