virtual folder is invisible in the CFileDialog in windows 7

440 Views Asked by At

-------------------------update 5th-------------------------------

            if (fIsFolder)
            {
                dwAttribs |= SFGAO_FOLDER;
            }
            else
            {
                dwAttribs |= SFGAO_SYSTEM;

                dwAttribs |= SFGAO_FILESYSTEM;
            }

            if (nLevel < g_nMaxLevel)
            {
                dwAttribs |= SFGAO_HASSUBFOLDER;

                dwAttribs |= SFGAO_FILESYSANCESTOR;
            }

Now this one works normally in application A, stackoverflow image upload file dialog and in regular folder explorer.

-----------------------------------------------------update 4th----------------------------------------------------------- After add dwAttribs |= SFGAO_SYSTEM; , now at least for application A, it works as expected in the file dialog. But the folder is still not clickable in the regular folder dialog or for CWFileDialog.

            if (fIsFolder)
            {
                dwAttribs |= SFGAO_FOLDER;

                dwAttribs |= SFGAO_FILESYSTEM;

                dwAttribs |= SFGAO_FILESYSANCESTOR;
            }
            else
            {
                dwAttribs |= SFGAO_SYSTEM;// this line of code works better

                dwAttribs |= SFGAO_FILESYSTEM;
            }

-----------------------------------update third----------------------------------------- This is the code in the virtual folder's ISHellFolder:

After I edit the following code to add dwAttribs |= SFGAO_FILESYSTEM;, the folder cannot be browsed when I double click it in the folder view. But it can be opened by left clicking it in the tree view.

 HRESULT CFolderViewImplFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, ULONG *rgfInOut)
{
// If SFGAO_FILESYSTEM is returned, GetDisplayNameOf(SHGDN_FORPARSING) on that item MUST
// return a filesystem path.
HRESULT hr = E_INVALIDARG;

DWORD dwAttribs = 0;

dwAttribs |= SFGAO_FILESYSTEM;

if (1 == cidl)
{
    int nLevel = 0;
    hr = _GetLevel(apidl[0], &nLevel);
    if (SUCCEEDED(hr))
    {
        BOOL fIsFolder = FALSE;
        hr = _GetFolderness(apidl[0], &fIsFolder);
        if (SUCCEEDED(hr))
        {
            
            if (fIsFolder)
            {
                dwAttribs |= SFGAO_FOLDER;
            }
            if (nLevel < g_nMaxLevel)
            {
                dwAttribs |= SFGAO_HASSUBFOLDER;
            }
  
        }
    }
}

*rgfInOut &= dwAttribs;

return hr;
}` 

Or

DWORD dwAttribs = 0;

if (1 == cidl)
{
    int nLevel = 0;
    hr = _GetLevel(apidl[0], &nLevel);
    if (SUCCEEDED(hr))
    {
        BOOL fIsFolder = FALSE;
        hr = _GetFolderness(apidl[0], &fIsFolder);
        if (SUCCEEDED(hr))
        {
            
            if (fIsFolder)
            {
                dwAttribs |= SFGAO_FOLDER;

                dwAttribs |= SFGAO_FILESYSTEM;

                dwAttribs |= SFGAO_FILESYSANCESTOR;
            }
            else
            {
                dwAttribs |= SFGAO_SYSTEM;
            }

            if (nLevel < g_nMaxLevel)
            {
                dwAttribs |= SFGAO_HASSUBFOLDER;
            }
        }
    }
 }

*rgfInOut &= dwAttribs;

Other code are the same as in update 2nd. The weird stuff is, after I edit these code, application A which uses CFileDialog normally( not wrapping it, not set m_bPickNonFileSysFoldersMode to true) can display all virtual folder normally as I expected. But in all other applications( including the one uses CWFileDilao which set m_bPickNonFileSysFoldersMode to true is still cannot view the virtual folder.

---------------------------------------update 2nd-------------------------

I code a simple class which derives from CFileDialog:

class CWFileDlg : public CFileDialog
{
public:
CWFileDlg(BOOL bOpenFileDialog, // TRUE for FileOpen, FALSE for FileSaveAs
    LPCTSTR lpszDefExt = NULL,
    LPCTSTR lpszFileName = NULL,
    DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
    LPCTSTR lpszFilter = NULL,
    CWnd* pParentWnd = NULL,
    DWORD dwSize = 0,
    BOOL bVistaStyle = TRUE);

~CWFileDlg();
};

CWFileDlg::CWFileDlg(BOOL bOpenFileDialog, // TRUE for FileOpen, FALSE for FileSaveAs
                    LPCTSTR lpszDefExt,
                    LPCTSTR lpszFileName,
                    DWORD dwFlags,
                    LPCTSTR lpszFilter,
                    CWnd* pParentWnd,
                    DWORD dwSize,
                    BOOL bVistaStyle) : CFileDialog(bOpenFileDialog, lpszDefExt, lpszFileName, dwFlags, lpszFilter, pParentWnd, dwSize, bVistaStyle)
{
    m_bPickNonFileSysFoldersMode = TRUE;
}

CWFileDlg::~CWFileDlg()
{

}

Then I call it like:

CWFileDlg dlg(TRUE, NULL, 0, OFN_SHAREAWARE | OFN_ENABLESIZING | OFN_ALLOWMULTISELECT, L"all(*.*)|*.*||", this);

BOOL IsPickNonFileSysFoldersMode = dlg.IsPickNonFileSysFoldersMode();
//the value it gets is 1, which is TRUE

INT_PTR result = dlg.DoModal();

But in the pop up dialog, virtual folder is stil invisible. Tree view for it is also invisible. ------------------------------------------updates----------------------------

protected:

BOOL m_bVistaStyle;
BOOL m_bPickFoldersMode;
BOOL m_bPickNonFileSysFoldersMode;
DWORD m_dwCookie;
void* m_pIFileDialog;
void* m_pIFileDialogCustomize;

The m_bPickNonFileSysFoldersMode is not a public type. How could I set it to TRUE ?

Error   1   error C2248: 'CFileDialog::m_bPickNonFileSysFoldersMode' : cannot access protected member declared in class 'CFileDialog'   c:\users\liyuan.liu\documents\dp-dll\testvirtualfolder\browser2\browser2\browser2dlg.cpp    166 1   browser2

By using Microsoft windows sample code, I installed a virtual folder on windows 7 by using shell namespace extension. The following screenshot is using regular folder browser to open it: enter image description here

Then I wrote a project which can create a file dialog:

CFileDialog dlg(TRUE, NULL, 0, OFN_SHAREAWARE | OFN_ENABLESIZING | OFN_ALLOWMULTISELECT, L"all(*.*)|*.*||", this);
INT_PTR result = dlg.DoModal();

However, in the file browser, the virtual folder is invisible:

enter image description here

But! When I was trying to upload the screenshot to StackOverflow ( the web browser I am using is chrome), the file dialog which is used to select file can display the tree view ( only the tree view) of the virtual folder: enter image description here

By googling, it seems that the CFileDialog cannot support to display the virtual folder since it doesn't actually exist in the system. Any solutions to solve that? I also tried with

bi.pidlRoot = pidlVirtual;
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_RETURNFSANCESTORS | BIF_BROWSEINCLUDEFILES | BIF_BROWSEFILEJUNCTIONS;
LPITEMIDLIST pidl = SHBrowseForFolder(&bi);

Also doesn't work.

1

There are 1 best solutions below

6
On

Unfortunately, what the file dialog shows is function of the app that opens it. Starting with Vista, it's the IFileDialog interface which is used, and it defines a set of options through the IFileDialog::SetOptions method.

The FOS_FORCEFILESYSTEM flag ensures that returned items are file system items (SFGAO_FILESYSTEM), and that depends on how you wrote your extension. SFGAO_FILESYSTEM should ring a bell, it's one of the flags the IShellFolder::GetAttributesOf method can return. If what you do is really virtual (ie: if you don't return this flag), they won't be shown when IFileDialog is configured like this.

If you test your extension with different apps (notepad, word, excel, browsers, etc.), you'll see that you see it sometimes, and sometimes you don't.

Spelunking into MFC's code (dlgfile.cpp), you will find this:

    // We only expect and handle file system paths (for compatibility with GetOpenFileName functionality), so set the
    // "force file system" flag which enables GetOpenFileName-like download behavior for non file system paths, unless
    // the m_bPickNonFileSysFoldersMode is set to allow picking non-file system folders (like libraries in Windows 7).
    dwFlags |= FOS_FORCEFILESYSTEM;
    if (m_bPickNonFileSysFoldersMode)
    {
        dwFlags &= ~FOS_FORCEFILESYSTEM;
    }

So, you need to set m_bPickNonFileSysFoldersMode to TRUE for CFileDialog to show your extension. in MFC you must derive from CFileDialog since this member is protected (BTW this is a stupid MFC design decision), for example::

class MyFileDialog : public CFileDialog
{
public:
    MyFileDialog(LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter = NULL, CWnd* pParentWnd = NULL);
};

MyFileDialog::MyFileDialog(LPCTSTR lpszDefExt, LPCTSTR lpszFileName, DWORD dwFlags, LPCTSTR lpszFilter, CWnd* pParentWnd) : CFileDialog(TRUE, lpszDefExt, lpszFileName, dwFlags, lpszFilter, pParentWnd)
{
    m_bPickNonFileSysFoldersMode = TRUE;
}

Last thing to remember: make sure your virtual folder (namespace extension, etc.) is registered with the same bitness (x86 vs x64) as the app using the CFileDialog.

But it won't fix other apps you don't own that use this flag...