In Win32 app the folder selection dialog closes if the folder is not selected and the user clicks OK

0

In my C++ Win 32 application, I am making a dialog to select a folder using IFileOpenDialog for this. Please see the code below:

HRESULT DialogService::CreateDialogToPickFolder(HWND hWnd)
{
     IFileOpenDialog* pPickFolderDialog = NULL;
     IShellItem* pPickedFolder = NULL;
     HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pPickFolderDialog));

     if (SUCCEEDED(hr))
     {
         DWORD dialogOptions;
         hr = pPickFolderDialog->GetOptions(&dialogOptions);
         if (SUCCEEDED(hr))
         {
             hr = pPickFolderDialog->SetOptions(dialogOptions | FOS_PICKFOLDERS);
             if (SUCCEEDED(hr))
             {
                 hr = pPickFolderDialog->Show(hWnd);
                 if (SUCCEEDED(hr))
                 {
                     hr = pPickFolderDialog->GetResult(&pPickedFolder);
                     if (SUCCEEDED(hr))
                     {
                         PWSTR pszFolderPath = NULL;
                         hr = pPickedFolder->GetDisplayName(SIGDN_FILESYSPATH, &pszFolderPath);
                         if (SUCCEEDED(hr))
                         {
                             // Some code...
                         }
                     }
                     pPickedFolder->Release();
                 }
             }
         }
         pPickFolderDialog->Release();
    }
    return hr;
}

I can’t cope with the following problem. If I do not select a folder (the "Folder" field, in the dialog, empty) and click the "Select Folder" button (i.e. OK), the line of code

hr = pPickedFolder->GetDisplayName(SIGDN_FILESYSPATH, &pszFolderPath);

writes the path that was previously selected into pszFolderPath variable and the dialog closes. But I want to ensure that, in this case, the dialog remains on the screen and the value of pszFolderPath remains 0x00000000. Because the dialog should be closed only if the folder was really selected by the user, and the name of the selected folder is displayed in the "Folder" field of the dialog box. (Of course, if, to close the dialog, the user clicked the "Select Folder" button. It goes without saying.) What can I do to prevent the dialog from closing when I click the OK button when the folder is not selected? Help me please.

c++
winapi
dialog
asked on Stack Overflow Nov 24, 2019 by Nu pogodi

1 Answer

0

OnFileOk event called just before the dialog is about to return with a result. It gives a chance to keep the dialog remain open. If you consider the folder path is invalid return S_FALSE to prevent the dialog from closing.

    // Create an event handling object, and hook it up to the dialog.
    IFileDialogEvents *pfde = NULL;
    hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
    if (SUCCEEDED(hr))
    {
        // Hook up the event handler.
        DWORD dwCookie;
        hr = pPickFolderDialog->Advise(pfde, &dwCookie);
        if (SUCCEEDED(hr))
        {            
            // ...
            hr = pPickFolderDialog->GetOptions(&dialogOptions);
            // ...

            // Unhook the event handler.
            pfd->Unadvise(dwCookie);
        }
        pfde->Release();
    }


// IFileDialogEvents methods
IFACEMETHODIMP OnFileOk(IFileDialog * pfd) 
{ 
    // If the folder field is empty, return S_FALSE, the dialog should remain open.
    // Check folder is selected or not here.
    //return S_FALSE;

    return S_OK; 
};

It may require a parsing operation in OnFileOk method to confirm the "Folder:" field is empty or not.

Refer to "Common Item Dialog".

UPDATE: Add code lines for checking path in the "Folder:" is empty or not. The following is an example using UIAutomation you can refer to.

#include <UIAutomation.h>

IUIAutomation *pClientUIA;
IUIAutomationElement *pRootElement;

BOOL IsPathEmpty(HWND hwnd)
{
    HRESULT hr;

    hr = CoCreateInstance(CLSID_CUIAutomation, NULL, CLSCTX_INPROC_SERVER, IID_IUIAutomation, reinterpret_cast<void**>(&pClientUIA));
    if (S_OK != hr)
    {
        printf("CoCreateInstance error: %d\n", GetLastError());
        return FALSE;
    }

    hr = pClientUIA->ElementFromHandle(hwnd, &pRootElement);
    if (S_OK != hr)
    {
        printf("ElementFromHandle error: %d\n", GetLastError());
        return FALSE;
    }
    BSTR name;
    hr = pRootElement->get_CurrentClassName(&name);
    if (S_OK != hr)
    {
        printf("get_CurrentClassName error: %d\n", GetLastError());
        return FALSE;
    }
    wprintf(L"Class Name: %s\n", name);


    IUIAutomationCondition *pCondition;
    VARIANT varProp;
    varProp.vt = VT_I4;
    varProp.uintVal = UIA_EditControlTypeId;
    hr = pClientUIA->CreatePropertyCondition(UIA_ControlTypePropertyId, varProp, &pCondition);
    if (S_OK != hr)
    {
        printf("CreatePropertyCondition error: %d\n", GetLastError());
        return FALSE;
    }

    IUIAutomationElementArray *pElementFound;
    hr = pRootElement->FindAll(TreeScope_Descendants, pCondition, &pElementFound);
    if (S_OK != hr)
    {
        printf("CreatePropertyCondition error: %d\n", GetLastError());
        return FALSE;
    }

    int eleCount;
    pElementFound->get_Length(&eleCount);
    for (int i = 0; i < eleCount; i++)
    {
        IUIAutomationElement *pElement;
        hr = pElementFound->GetElement(i, &pElement);
        if (S_OK != hr)
        {
            printf("CreatePropertyCondition error: %d\n", GetLastError());
            return FALSE;
        }
        hr = pElement->get_CurrentName(&name);
        if (S_OK != hr)
        {
            printf("CreatePropertyCondition error: %d\n", GetLastError());
            return FALSE;
        }
        wprintf(L"Control Name: %s\n", name);
        OutputDebugString(name);

        if (0 == wcscmp(name, L"Folder:"))
        {
            VARIANT varPropText;
            hr = pElement->GetCurrentPropertyValue(UIA_ValueValuePropertyId, &varPropText);
            if (S_OK != hr)
            {
                printf("CreatePropertyCondition error: %d\n", GetLastError());
                return FALSE;
            }

            if (0 == wcscmp(varPropText.bstrVal, L""))
            {
                return TRUE;
            }
        }
    }

    return FALSE;
}

The OnFileOk method can all IsPathEmpty method like this:

    // IFileDialogEvents methods
    IFACEMETHODIMP OnFileOk(IFileDialog * pfd)
    { 
        // If the folder field is empty, return S_FALSE, the dialog should remain open.
        // Check folder is selected or not here.
        //return S_FALSE;

        IOleWindow *pWindow;
        HRESULT hr = pfd->QueryInterface(IID_PPV_ARGS(&pWindow));

        if (SUCCEEDED(hr))
        {
            HWND hwndDialog;
            hr = pWindow->GetWindow(&hwndDialog);

            if (SUCCEEDED(hr))
            {
                if (IsPathEmpty(hwndDialog))
                    return S_FALSE;
            }
            pWindow->Release();
        }

        return S_OK; 
    };
answered on Stack Overflow Nov 25, 2019 by Rita Han • edited Nov 26, 2019 by Rita Han

User contributions licensed under CC BY-SA 3.0