Question

I hope this falls within the realm of this forum:

I want to use the windows shell(?) to allow users to select a number of files before allowing my programme to do a few things to them. For this I found the MSDN sample "CommonFileDialogModes" - http://msdn.microsoft.com/en-us/library/windows/desktop/dd940350%28v=vs.85%29.aspx

In the sample under this class: class CFileOpenBasketPickerCallback : public IFileDialogEvents, public IFileDialogControlEvents

they have this function:

// IFileDialogEvents
IFACEMETHODIMP OnFileOk(IFileDialog *pfd)
{
    // if this button is in the "Add" mode then do this, otherwise return S_OK
    IFileOpenDialog *pfod;
    HRESULT hr = pfd->QueryInterface(IID_PPV_ARGS(&pfod));
    if (SUCCEEDED(hr))
    {
        IShellItemArray *psia;
        hr = pfod->GetSelectedItems(&psia);
        if (SUCCEEDED(hr))
        {
            ReportSelectedItems(pfd, psia);
            psia->Release();
        }
        pfod->Release();
    }
    return S_FALSE; // S_FALSE keeps the dialog up; return S_OK to allow it to dismiss.
}

which calls:

void ReportSelectedItems(IUnknown *punkSite, IShellItemArray *psia)
{
    DWORD cItems;
    HRESULT hr = psia->GetCount(&cItems);
    for (DWORD i = 0; SUCCEEDED(hr) && (i < cItems); i++)
    {
        IShellItem *psi;
        hr = psia->GetItemAt(i, &psi);
        if (SUCCEEDED(hr))
        {
            PWSTR pszName;
            hr = GetIDListName(psi, &pszName);

        // .. I've cut some of this out for the example

                CoTaskMemFree(pszName);
            }
            psi->Release();
        }
    }
}

Now I know pszName contains the names of the files selected. So I can add some extra code in to write this to disk. That works fine. But I dont want to write it to disk. I want to pass it back to the original functions that called this. The arguments for ReportSelectedItems can be altered, but IFACEMETHODIMP OnFileOk(IFileDialog *pfd) cannot as it is inherited. Adding a vector& file_names to the argument will stop it compiling.

So how should I deal with this? I could use a global variable for file_names, but everything I am learning about programming is telling me not to. It would be a quick fix, but I worry that would encourage me to be lazy in the future. I find it difficult to read the windows code and I don't really want to delve too much into the details of it. I can't even find what is calling the OnFileOk function, even though I know it is from one of the two base classes.

Do I really need to work at understanding all the library code just to get this one function doing what I'd like? Is there an faster way of going about this?

So to summarize, how would I get information from this inherited function without using a global variable or writing to disk? As I mentined before, I don't have much of a grasp of the code I am working with. And for future reference, how should I deal with this type of situation? I use c++ and would like to avoid c# and c as much as possible.

Thanks as always.

Was it helpful?

Solution 2

A simple solution was to add member data inside the inherited class and link it from the constructor:

class CFileOpenBasketPickerCallback : public IFileDialogEvents, public IFileDialogControlEvents
{
public:
    CFileOpenBasketPickerCallback(vector<wstring>& files) : files_(files)
    {
    }
// functions

private:
vector<wstring>& files_;
};

When constructing the object

vector<std::wstring> files
CFileOpenBasketPickerCallback foacb(files);

And in IFACEMETHODIMP OnFileOk(IFileDialog *pfd)

ReportSelectedItems(pfd, psia, files_);

ReportSelectedItems is not a member so you can alter the arguments.

OTHER TIPS

It seems a fairly big omission for Microsoft to have left out any sort of user data associated with the IFileDialog callbacks, but that does seem to be the case.

I'm assuming that simply calling GetSelectedItems() once the dialog returns is something you don't want to do for some reason - because that would obviously be the simplest solution.

From a quick look at the docs one way you may be able to pass data back from the event callback is using the owner window that you pass to IFileDialog::Show() (which is actually IModalWindow::Show()).

In the event handler, you get given the IFileDialog* pointer. From this, you can QI the address of the IOleWindow interface which will give you the dialog's window:

IFACEMETHODIMP OnFileOk(IFileDialog *pfd)
{
    CComPtr<IOleWindow> pWindow;
    if (SUCCEEDED(pfd->QueryInterface(IID_IOleWindow, reinterpret_cast<void**>(&pWindow))))
    {
        HWND hwndDlg;
        if (SUCCEEDED(pWindow->GetWindow(&hwndDlg)))
        {
            HWND hwndOwner;
            if (hwndOwner = GetWindow(hwndDlg, GW_OWNER))
            {
                // hwndOwner is the owner window of the dialog
            }
        }
    }
    // more code
}

Now assuming that hwndOwner is your own window, you can associate any data you like with it using SetProp()/GetProp() - so you could use this as a mechanism to pass data back from within the callback.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top