0

我希望这属于这个论坛的范围:

我想使用 windows shell(?) 来允许用户在允许我的程序对他们做一些事情之前选择一些文件。为此,我找到了 MSDN 示例“CommonFileDialogModes” - http://msdn.microsoft.com/en-us/library/windows/desktop/dd940350%28v=vs.85%29.aspx

在此类下的示例中: class CFileOpenBasketPickerCallback : public IFileDialogEvents, public IFileDialogControlEvents

他们有这个功能:

// 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.
}

调用:

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();
        }
    }
}

现在我知道 pszName 包含所选文件的名称。所以我可以添加一些额外的代码来将它写入磁盘。这很好用。但我不想将其写入磁盘。我想将它传递回调用它的原始函数。ReportSelectedItems 的参数可以更改,但 IFACEMETHODIMP OnFileOk(IFileDialog *pfd) 不能更改,因为它是继承的。向参数添加 vector& file_names 将停止编译。

那么我应该如何处理呢?我可以为 file_names 使用一个全局变量,但我正在学习的所有关于编程的知识都告诉我不要这样做。这将是一个快速的解决方案,但我担心这会鼓励我在未来变得懒惰。我发现阅读 Windows 代码很困难,我真的不想深入研究它的细节。我什至找不到调用 OnFileOk 函数的内容,即使我知道它来自两个基类之一。

我是否真的需要努力理解所有库代码才能让这个函数做我想做的事情?有没有更快的方法来解决这个问题?

总而言之,我如何在不使用全局变量或写入磁盘的情况下从这个继承的函数中获取信息?正如我之前提到的,我不太了解我正在使用的代码。为了将来参考,我应该如何处理这种情况?我使用 c++ 并希望尽可能避免使用 c# 和 c。

一如既往地感谢。

4

2 回答 2

1

微软似乎遗漏了与IFileDialog回调相关的任何类型的用户数据,但情况似乎确实如此。

我假设GetSelectedItems()在对话框返回后简单地调用是您出于某种原因不想做的事情 - 因为这显然是最简单的解决方案。

从快速查看文档中,您可能能够从事件回调中传回数据的一种方法是使用您传递给的所有者窗口IFileDialog::Show()(实际上是IModalWindow::Show())。

在事件处理程序中,您将获得IFileDialog*指针。从中,您可以 QIIOleWindow接口的地址,它将为您提供对话框的窗口:

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
}

现在假设这是您自己的窗口,您可以使用/hwndOwner将您喜欢的任何数据与它关联- 因此您可以使用它作为从回调中传回数据的机制。SetProp()GetProp()

于 2013-10-24T07:58:36.173 回答
0

一个简单的解决方案是在继承的类中添加成员数据并从构造函数链接它:

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

private:
vector<wstring>& files_;
};

构造对象时

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

在 IFACEMETHODIMP OnFileOk(IFileDialog *pfd)

ReportSelectedItems(pfd, psia, files_);

ReportSelectedItems 不是成员,因此您可以更改参数。

于 2013-10-25T04:02:45.373 回答