在搜索时接收 parent 对 IShellFolder/IShellFolder2 EnumObjects 实现的 window 句柄?
Receiving parent's window handle on IShellFolder/IShellFolder2 EnumObjects implementations while searching?
在我正在处理的名称空间扩展项目中收到 parent window 句柄时,我感到很抓狂。
使用案例如下:
- 用户通过 windows 资源管理器浏览到虚拟文件夹
- 用户执行搜索(上面的搜索框)
- 我需要在搜索开始前检索搜索框的文本。
我已经在带有 ISearchBoxInfo 界面的测试控制台应用程序上成功做到了这一点 (https://msdn.microsoft.com/en-us/library/windows/desktop/dd562062%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396)
我可以通过两种方式接收指向该接口的指针:
- 使用 IObjectWithSite::SetSite 调用 - 这不相关,因为搜索是在不同的线程中进行的,我无法在这些线程之间共享 COM object
- 识别 window 句柄并通过 IWebBrowser2 接口检索 ISearchBox。
这两种方法都不起作用,因为当我执行搜索时,EnumObjects 是通过不同的线程调用的,我无法找到一种方法来识别谁是 te parent探险家window.
搜索时,出现的hwnd总是null,如下:
这是 EnumObjects 代码:
// Allows a client to determine the contents of a folder by
// creating an item identifier enumeration object and returning
// its IEnumIDList interface. The methods supported by that
// interface can then be used to enumerate the folder's contents.
HRESULT CFolderViewImplFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList)
{
HRESULT hr;
_fd = hwnd;
if (hwnd != NULL) // NULL when performing a search
{
const int n = GetWindowTextLength(hwnd);
wstring text(n + 1, L'#');
if (n > 0)
{
GetWindowText(hwnd, &text[0], text.length());
}
}
if (m_nLevel >= g_nMaxLevel)
{
*ppenumIDList = NULL;
hr = S_FALSE; // S_FALSE is allowed with NULL out param to indicate no contents.
}
else
{
CFolderViewImplEnumIDList *penum = new (std::nothrow) CFolderViewImplEnumIDList(grfFlags, m_nLevel + 1, this);
hr = penum ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
hr = penum->Initialize();
if (SUCCEEDED(hr))
{
hr = penum->QueryInterface(IID_PPV_ARGS(ppenumIDList));
}
penum->Release();
}
}
return hr;
}
除了我的测试,我还实现了 IShellFolderViewCB.MessageSFVCB,它在 correct 线程上运行,其中我可以检索 IShellBrowser,从而检索句柄 - 当我进行搜索时,我遇到以下消息:
103, 103, 67, UnmergeMenu, WindowClosing, 106, ViewRelease, ViewRelease
之后,不再发布消息(无论我是否 re-search)- 第一个断点总是在 EnumObjects 方法处,我没有关于 parent window 的上下文。
任何光线都很好。
== 编辑 ==
我想出了一些解决方法 - 这并不适用于所有情况,但适用于大多数情况 - 其他选项仍然不错。
每当使用 hwnd = NULL 调用 EnumObjects 时,我都会执行以下操作:(它在 C# 中 - 但在 C++ 中也很容易实现)
static public string PrepareSearch(string currentFolderName, IntPtr hwnd)
{
SHDocVw.ShellWindows shellWindows = new ShellWindows();
SHDocVw.IWebBrowser2 foundBrowser = null;
bool wasFound = false;
string foundTxt = null;
foreach (SHDocVw.IWebBrowser2 eie in shellWindows)
{
// as the search is conducted in another thread, while the main window is "free" and in a search mode, it should be first busy.
string locName = eie.LocationName;
string exeName = eie.FullName;
if (!string.IsNullOrEmpty(exeName) && exeName.IndexOf("explorer.exe", StringComparison.OrdinalIgnoreCase) >= 0 &&
!string.IsNullOrEmpty(locName) &&
eie.Busy && eie.ReadyState == tagREADYSTATE.READYSTATE_LOADING)
{
// in here we're ok, we would also want to get the window title to make sure we're searching correctly.
string title = NSEFolder.WindowText((IntPtr)eie.HWND);
if (!string.IsNullOrEmpty(title) &&
title.IndexOf(currentFolderName) >= 0)
{
// one or more windows were found, ignore the quick search.
if (wasFound)
{
return null;
}
wasFound = true;
foundTxt = locName;
}
}
}
if (wasFound && !string.IsNullOrEmpty(foundTxt))
{
return foundTxt;
}
return null;
}
基本上我要遍历所有资源管理器 windows,试图找到一个确实是 "explorer.exe" + 非空搜索字符串 (LocationName) + 忙... + 标题包含的名称当前文件夹名称。
当 2 windows 忙碌并且标题中有相同的文件夹名称时,它将失败 - 但这可能已经足够了......这里不确定。
因此,在与微软交谈后 - 不可能这样做。
我添加的解决方法是有效的(尽管并不完美)。
在我正在处理的名称空间扩展项目中收到 parent window 句柄时,我感到很抓狂。
使用案例如下:
- 用户通过 windows 资源管理器浏览到虚拟文件夹
- 用户执行搜索(上面的搜索框)
- 我需要在搜索开始前检索搜索框的文本。
我已经在带有 ISearchBoxInfo 界面的测试控制台应用程序上成功做到了这一点 (https://msdn.microsoft.com/en-us/library/windows/desktop/dd562062%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396)
我可以通过两种方式接收指向该接口的指针:
- 使用 IObjectWithSite::SetSite 调用 - 这不相关,因为搜索是在不同的线程中进行的,我无法在这些线程之间共享 COM object
- 识别 window 句柄并通过 IWebBrowser2 接口检索 ISearchBox。
这两种方法都不起作用,因为当我执行搜索时,EnumObjects 是通过不同的线程调用的,我无法找到一种方法来识别谁是 te parent探险家window.
搜索时,出现的hwnd总是null,如下:
这是 EnumObjects 代码:
// Allows a client to determine the contents of a folder by
// creating an item identifier enumeration object and returning
// its IEnumIDList interface. The methods supported by that
// interface can then be used to enumerate the folder's contents.
HRESULT CFolderViewImplFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList)
{
HRESULT hr;
_fd = hwnd;
if (hwnd != NULL) // NULL when performing a search
{
const int n = GetWindowTextLength(hwnd);
wstring text(n + 1, L'#');
if (n > 0)
{
GetWindowText(hwnd, &text[0], text.length());
}
}
if (m_nLevel >= g_nMaxLevel)
{
*ppenumIDList = NULL;
hr = S_FALSE; // S_FALSE is allowed with NULL out param to indicate no contents.
}
else
{
CFolderViewImplEnumIDList *penum = new (std::nothrow) CFolderViewImplEnumIDList(grfFlags, m_nLevel + 1, this);
hr = penum ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
hr = penum->Initialize();
if (SUCCEEDED(hr))
{
hr = penum->QueryInterface(IID_PPV_ARGS(ppenumIDList));
}
penum->Release();
}
}
return hr;
}
除了我的测试,我还实现了 IShellFolderViewCB.MessageSFVCB,它在 correct 线程上运行,其中我可以检索 IShellBrowser,从而检索句柄 - 当我进行搜索时,我遇到以下消息:
103, 103, 67, UnmergeMenu, WindowClosing, 106, ViewRelease, ViewRelease
之后,不再发布消息(无论我是否 re-search)- 第一个断点总是在 EnumObjects 方法处,我没有关于 parent window 的上下文。
任何光线都很好。
== 编辑 ==
我想出了一些解决方法 - 这并不适用于所有情况,但适用于大多数情况 - 其他选项仍然不错。
每当使用 hwnd = NULL 调用 EnumObjects 时,我都会执行以下操作:(它在 C# 中 - 但在 C++ 中也很容易实现)
static public string PrepareSearch(string currentFolderName, IntPtr hwnd)
{
SHDocVw.ShellWindows shellWindows = new ShellWindows();
SHDocVw.IWebBrowser2 foundBrowser = null;
bool wasFound = false;
string foundTxt = null;
foreach (SHDocVw.IWebBrowser2 eie in shellWindows)
{
// as the search is conducted in another thread, while the main window is "free" and in a search mode, it should be first busy.
string locName = eie.LocationName;
string exeName = eie.FullName;
if (!string.IsNullOrEmpty(exeName) && exeName.IndexOf("explorer.exe", StringComparison.OrdinalIgnoreCase) >= 0 &&
!string.IsNullOrEmpty(locName) &&
eie.Busy && eie.ReadyState == tagREADYSTATE.READYSTATE_LOADING)
{
// in here we're ok, we would also want to get the window title to make sure we're searching correctly.
string title = NSEFolder.WindowText((IntPtr)eie.HWND);
if (!string.IsNullOrEmpty(title) &&
title.IndexOf(currentFolderName) >= 0)
{
// one or more windows were found, ignore the quick search.
if (wasFound)
{
return null;
}
wasFound = true;
foundTxt = locName;
}
}
}
if (wasFound && !string.IsNullOrEmpty(foundTxt))
{
return foundTxt;
}
return null;
}
基本上我要遍历所有资源管理器 windows,试图找到一个确实是 "explorer.exe" + 非空搜索字符串 (LocationName) + 忙... + 标题包含的名称当前文件夹名称。
当 2 windows 忙碌并且标题中有相同的文件夹名称时,它将失败 - 但这可能已经足够了......这里不确定。
因此,在与微软交谈后 - 不可能这样做。 我添加的解决方法是有效的(尽管并不完美)。