Shell BrowseForFolder preselected path - winapi

I have this call:
oShell.BrowseForFolder(Me.hwnd, "Select path:", 0, "C:\dir\")
This opens a standard file browser dialog with "C:\dir\" as root.
My problem is that you can not browse above the root folder. (as specified in doc http://msdn.microsoft.com/en-us/library/bb774065(v=vs.85).aspx)
Any suggestions on oppening this dialog with a selected path and full browsing posibility?
Thanks

The way to do this involves calling the underlying API, SHBrowseForFolder().
Since you want the entire shell namespace to be available you need to pass NULL as pidlRoot. In order to select your desired folder you will need to provide a callback in lpfn. Make this callback respond to BFFM_INITIALIZED by setting the selected folder. This selection is performed by sending the BFFM_SETSELECTION message to the dialog's window handle (passed to the callback function).
No code because I don't have VB6, but hopefully this outline of the method is enough to get you on your way.

Karl E Peterson's excellent website contains a sample which demonstrates the API call SHBrowseForFolder with a callback, as in David Heffernan's answer.
The KeyStuff project
Look at MFolderBrowse.bas, routine BrowseForFolderByPIDL which passes a callback function BrowseCallbackProc.

Try the old CCRP project. It has a nicely done implementation of the Browse dialog. I used it in several of my projects and it has properties to address the issue you are having.

Here a code ready for copy and paste in a C++ class:
// static
int CALLBACK Func::FolderBrowserCallback(HWND h_Dlg, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
if (uMsg == BFFM_INITIALIZED)
{
// Requires Windows XP or higher
SendMessageW(h_Dlg, BFFM_SETEXPANDED, TRUE, lpData);
}
return 0;
}
// returns an empty string u16_PathOut if an error occurrs or if the user cancels the dialog
void Func::GetOpenFolder(HWND h_Owner,
const WCHAR* u16_Title, // IN: Title at the top of dialog
int s32_CsidlRoot, // IN: Root folder for treeview (CSIDL_DRIVES -> My Computer)
const WCHAR* u16_Preselect, // IN: NULL or the folder to be preselected and expanded
WCHAR* u16_PathOut) // OUT: selected path
{
u16_PathOut[0] = 0;
// CoInitialize(NULL);
// InitCommonControls();
ITEMIDLIST* pk_RootPIDL = NULL; // NULL -> Root = Desktop
SHGetSpecialFolderLocation(h_Owner, s32_CsidlRoot, &pk_RootPIDL);
BROWSEINFOW k_Info = {0};
k_Info.hwndOwner = h_Owner;
k_Info.pidlRoot = pk_RootPIDL;
k_Info.lpszTitle = u16_Title;
k_Info.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI;
if (u16_Preselect)
{
k_Info.lpfn = FolderBrowserCallback;
k_Info.lParam = (LPARAM)u16_Preselect;
}
// DO NOT DISABLE Wow64FsRedirection HERE !!
LPITEMIDLIST pk_IDlist = SHBrowseForFolderW(&k_Info);
if (pk_IDlist)
{
SHGetPathFromIDListW(pk_IDlist, u16_PathOut);
CoTaskMemFree(pk_IDlist);
}
CoTaskMemFree(pk_RootPIDL);
}

Related

Which Windows API in C++ will help me in identifying which Dialog Class the Current ComboBox is using?

How can I know that for a particular ComboBox which Dialog Style is being used? Is there any Win32 API which can give me that information?
I am using CDialog for a few ComboBox, CDialogEx for some, and an in-house Dialog class, let's say Ctl3dDialogEx, for others. GetClassName() will return the Class name of the ComboBox (if I am passing a ComboBox Handler) which can be "CComboBox".
Is there any Win32 API where I will pass the ComboBox Handler and it will return back to me the Dialog class name, for eg : "CDialog", "CDialogEx", "Ctl3dDialogEx", etc?
Below code will help to understand maybe:
void ComboBox_DoSomeManipulation( HWND hldg , int n )
{
/*"hldg" is the handler of the Current ComBobox */
LPTSTR lpClassName;
int nMaxCount = 256;
/*This will return "CComboBox" as value in lpClassName */
GetClassName(hldg , lpClassName , _count_of(nMaxCount));
/*Is there any WIN API like above which can give */
/* Dialog class information like CDialog, CDialogEx */
/* which the ComboBox is using ? */
}
If your combo-box can somehow get hold of a genuine pointer to its parent window, then you can use dynamic_cast<CDialogEx*>(pParent) to see if it's CDialogEx (returns nullptr if not). You will need several separate checks, starting from the most derived class! So, if your Ctl3dDialogEx is derived from CDialogEx, then:
. . .
CWnd *pParent = pCombo->GetParent(); // This, as is, is not valid code!
if (dynamic_cast<Ctl3dDialogEx*>(pParent) != nullptr) {
// It's a Ctl3dDialogEx parent
}
else if (dynamic_cast<CDialogEx*>(pParent) != nullptr) {
// It's a CDialogEx
}
else { // Assuming no other options …
// It's a CDialog
}
I would recommend making an accessible (static?) copy of the parent window's this pointer during initialisation, if you can. But there are other ways …
For example, assuming you have control over the definition of ComboBox_DoSomeManipulation and when it's called, change the first argument from an HWND to a CWnd* and, when you call it, use this rather than this->m_hwnd. (But this depends on the structure of your code!)
There is no Windows API help since all those dialogs will be subclassing the Windows DIALOG class. If this is all in process, and you are using the same MFC instance, you might be able to do this:
CWnd* pWnd = CWnd::FromHandlePermanent(hdlg);
if (pWnd != NULL)
{
if (pWnd->GetRuntimeClass() == RUNTIME_CLASS(CDialog))
{
}
else if (pWnd->GetRuntimeClass() == RUNTIME_CLASS(CDialogEx))
{
}
else if (pWnd->GetRuntimeClass() == RUNTIME_CLASS(CDialogxyz))
{
}
}
Back in the old days, MS compilers used with MFC didn't play well with dynamic_cast<>, so generally, when using MFC, I don't use it. I probably should have more trust in it, but I was stuck using Visual C++ 6 until 2008, so I am probably a little jaded. The more "standard" "MFC way" is to use the MFC macros...
Another possible ways is something like:
if (CDialogxyz* pDlgxyz = DYNAMIC_DOWNCAST(CDialogxyz, pWnd))
{
}
else if (CDialogEx* pDlgEx = DYNAMIC_DOWNCAST(CDialogEx, pWnd))
{
}
else if (CDialog* pDlg = DYNAMIC_DOWNCAST(CDialog, pWnd))
{
}

Only show command in context menu on specific filename (+extension)

So basically I only want to display the Command when I right click on a file named "example.cs". Since I am using Visual Studio 2019 I can't go with the old BeforeQueryStatus way. Instead using the ProvideUIContextRule Attribute on my Package class. Which currently looks something like this:
[ProvideUIContextRule(_uiContextSupportedFiles,
name: "Supported Files",
expression: "CSharp",
termNames: new[] { "CSharp" },
termValues: new[] { "HierSingleSelectionName:.cs$" })]
Which totally looks fine for the extension of the file itself. So is there any way to restrict it to example.cs?
By the way I am using this Guide.
So for everyone else having the same issue as I had. The solution is fairly simple, regarding to the MSDN:
(...) The term evaluates to true whenever the current selection in the active hierarchy has a name that matches the regular expression pattern(...)
So basically changing
{ "HierSingleSelectionName:.cs$" } to { "HierSingleSelectionName:Program.cs$" } will only show files that end with Program.cs.
This leads to, that everything after the semicolon contains a Regular Expression.
To determine your command's visibility, you can implement QueryStatus method.
Implement Microsoft.VisualStudio.OLE.Interop.IOleCommandTarget like CommandsFilter. And add it as service to package.
var serviceContainer = (IServiceContainer)this; // this - is your Package/AsyncPakage
var commandTargetType = typeof(IOleCommandTarget);
var commandsFilter = new CommandsFilter();
serviceContainer.RemoveService(commandTargetType);
serviceContainer.AddService(commandTargetType, commandsFilter);
On every commands update will be called method QueryStatus in CommandsFilter.
Wait for your command id and change it status
class CommandsFilter : IOleCommandTarget {
// ...
public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) {
var cmdId = prgCmds[0].cmdID;
// check cmdId and set status depends on your conditions
// like fileName == "example.cs"
prgCmds[0].cmdf = (uint)GetVsStatus(status);
//....
}
private OLECMDF GetVsStatus(CommandStatus commandStatus) {
OLECMDF ret = 0;
if (commandStatus.HasFlag(CommandStatus.Supported))
ret |= OLECMDF.OLECMDF_SUPPORTED;
if (commandStatus.HasFlag(CommandStatus.Enabled))
ret |= OLECMDF.OLECMDF_ENABLED;
if (commandStatus.HasFlag(CommandStatus.Invisible))
ret |= OLECMDF.OLECMDF_INVISIBLE;
return ret;
}
Check sample with QueryStatus and others MS samples

What is the correct way to detect that an IShellItem refers to a Library?

I'm using the common file dialog with FOS_PICKFOLDERS to let the user pick a location to save files. If the user selects a library, e.g. Library\Documents then my current code fails at the point where I call IShellItem::GetDisplayName to extract a file system name. If the item were a file then this would succeed and the library's default save location would be used.
What I would like to do is to detect that the shell item is a library, then obtain an IShellLibrary interface, and then query it to find the default save location. Then I would save my files there.
What is the correct way to detect that an IShellItem refers to a Library?
Use SHLoadLibraryFromItem() to get an IShellLibrary from an IShellItem, eg:
IShellItem *pItem, *pSave;
IShellLibrary *pLibrary;
...
if (SUCCEEDED(SHLoadLibraryFromItem(pItem, STGM_READWRITE, IID_IShellLibrary, (void**)&pLibrary)))
{
pLibrary->GetDefaultSaveFolder(DSFT_DETECT, IID_IShellItem, (void**)&pSave);
pLibrary->Release();
}
else
{
pSave = pItem;
pSave->AddRef();
}
...
pSave->GetDisplayName(...);
pSave->Release();
The only way I found was to use IShellLibrary::LoadLibraryFromItem (MSDN here), to which you pass an IShellItem interface.
If it fails (i.e. HRESULT != S_OK), then the IShellItem is not a library.
So something like this:
bool IsLibrary(IShellItem *pItem)
{
bool bIsLibrary = false;
IShellLibrary *plib = NULL;
HRESULT hr = CoCreateInstance(CLSID_ShellLibrary, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&plib));
if (SUCCEEDED(hr))
{
hr = plib->LoadLibraryFromItem(pItem, STGM_READ);
if (SUCCEEDED(hr)) bIsLibrary = true;
plib->Release();
}
return bIsLibrary;
}
I have no idea if it's the "correct" way, but it may be useful anyway.

Hide an MFC dialog window

I have written an MFC dialog based application which is launched by some another application. For now, I have not added any code. It is just the default files that I got. The other application can successfully launch my application.
I am trying to hide the window of my application when the other application launches it.
BOOL CMyApp::InitInstance()
{
CMyAppDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
}
else if (nResponse == IDCANCEL)
{
}
return FALSE;
}
I tried to use:
dlg.ShowWindow(SW_HIDE)
but it still does not hide the window.
How can I accomplish this task?
I'd suggest you have another problem someplace.
If you create a totally new, blank MFC app (Visual Studio 2010) then in App::InitInstance, setting SW_HIDE rather than SW_SHOW does cause the resultant window to be hidden.
BOOL CProj1App::InitInstance()
{
// boilerplate code
. . .
// The one and only window has been initialized, so show and update it
m_pMainWnd->ShowWindow(SW_HIDE); // WORKS!
m_pMainWnd->UpdateWindow();
return TRUE;
}
As soon as you call DoModal your dialog is doomed to be shown. There is only one workaround that successfully avoids focus/flicker problems. See my answer here: Hiding an MFC dialog box
Hence, your code should look like this:
BOOL CMyApp::InitInstance()
{
CMyAppDlg dlg;
dlg.SetVisible(FALSE); // Sets m_visible flag to FALSE.
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
}
else if (nResponse == IDCANCEL)
{
}
return FALSE;
}
Solution to the above issue. The InitInstance code should be as follows:
BOOL CMyApp::InitInstance()
{
CWinApp::InitInstance();
AfxEnableControlContainer();
CMyAppDlg dlg;
dlg.Create(IDD_MyAppUI_DIALOG,NULL);
dlg.ShowWindow(SW_HIDE);
dlg.UpdateWindow();
m_pMainWnd = &dlg;
return TRUE;
}
First of all let me address some issues with previous solutions.
chintan s:
Indeed dialog will be killed when function goes out of scope. It would be a valid solution if dialog was declared as a member variable of the app class.
Vikky:
No need to call Windows API, since dialog is derived from CWnd and it inherits ShowWindow member that take only one parameter: show command.
ixe013:
This solution will work, however, before dialog hides, it will flash, since ShowWindow is called before OnInitDialog is called.
Pete:
This won’t work, since, modal dialog starts before m_pMainWnd has any value assigned to it.
The solution is pointed by ixe013.
This is so far the only solution that works but you will have to declare member variable in you dialog class, as described in the article.
You must hide the dialog from the inside.
Overload OnInitDialog
Call CDialogEx::OnInitDialog()
Hide your window and return
Here is the code
BOOL CMyAppDlg::OnInitDialog()
{
BOOL result = CDialogEx::OnInitDialog();
this->ShowWindow(SW_HIDE);
return result; // return TRUE unless you set the focus to a control
}
There is another method with a sentinel value, YMMV.
The showWindow method has 2 variable.
handle of window
nCmdShow(Controls how the window is to be shown)
BOOL WINAPI ShowWindow(
In HWND hWnd,
In int nCmdShow
);
HWND hWnd = GetSafeHwnd();
ShowWindow(hWnd,SW_HIDE);
See HERE

timed MessageBox in console application

I use MessageBox function in Win32 console application.
Application does not not use MFC, not even event loop.
I need to make a wrapper, MessageBoxTimed(), that exits
(and dialog box disappears) after N seconds, if user did not press any button.
Is there more or less simple way to do this ?
This will not be trivial. Since the MessageBox() function itself is modal, you will likely need to start another thread that waits for the predefined number of seconds, and is interrupt-able if the message box is dismissed manually.
If the timer expires, use the FindWindow() API to find the handle of the message box and then simulate a click of the OK button, or perhaps more appropriately a keypress of the ESC button.
EDIT: Actually, not too bad. This isn't fully tested, may need some additional cleanup, but is enough to get you started.
#include <Windows.h>
class TimedMB
{
public:
TimedMB() : timeout_(0), caption_(0)
{
interrupt_ = CreateEvent(NULL, FALSE, FALSE, NULL);
}
~TimedMB()
{
CloseHandle(interrupt_);
}
static DWORD WINAPI timer(LPVOID param)
{
TimedMB* mb = reinterpret_cast<TimedMB*>(param);
if(WAIT_TIMEOUT == WaitForSingleObject(mb->interrupt_, mb->timeout_))
{
HWND message_box = FindWindow(NULL, mb->caption_);
if(::IsWindow(message_box))
{
PostMessage(message_box, WM_COMMAND, IDCANCEL, 0);
}
}
return 0;
}
void DisplayMessageBox(const char* msg, const char* caption, DWORD timeout)
{
timeout_ = timeout;
caption_ = caption;
CreateThread(NULL, 0, &TimedMB::timer, this, 0, NULL);
::MessageBox(NULL, msg, caption, MB_OKCANCEL);
::SetEvent(interrupt_);
}
private:
HANDLE interrupt_;
DWORD timeout_;
const char* caption_;
};
int main()
{
TimedMB mb;
mb.DisplayMessageBox("Hello There!", "My Message Box", 5000);
}
If you need to dismiss it automatically, I'd avoid using MessageBox at all. Instead, I'd just put together a dialog that closes itself after the specified period of time. If memory serves, you can do this pretty easily by setting a time when you display the pseudo-message box dialog. When the time goes off or the user clicks "ok" (or "close", etc.) you close the window and cancel the timer.
Don't do this. Modal dialogs should be closed by user intervention. Deviating from this pattern is just confusing and non-standard. If you want a message windows that closes itself, then use a balloon window.

Resources