私が取り組んでいる名前空間拡張プロジェクトで親ウィンドウ ハンドルを受け取ることに頭を悩ませています。
ユースケースは次のとおりです。
- ユーザーが Windows エクスプローラーを使用して仮想フォルダーを参照する
- ユーザーが検索を実行します (上の検索ボックス)
- 検索を開始する前に、検索ボックスのテキストを取得する必要があります。
ISearchBoxInfo インターフェイス ( https://msdn.microsoft.com/en-us/library/windows/desktop/dd562062%28v=vs.85%29.aspx?f=を使用して、テスト コンソール アプリでそれを行うことができました。 255&MSPPError=-2147217396 )
このインターフェイスへのポインタを受け取る方法は 2 つあります。
- IObjectWithSite::SetSite 呼び出しの使用 - 検索は別のスレッドで実行され、それらのスレッド間で COM オブジェクトを共有できないため、これは関係ありません
- ウィンドウ ハンドルを識別し、IWebBrowser2 インターフェイスを介して ISearchBox を取得します。
検索を実行すると、EnumObjectsが別のスレッドを介して呼び出され、親エクスプローラー ウィンドウが誰であるかを特定する方法が見つからないため、両方の方法が機能しません。
検索を行うとき、次のように来る 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の実装も持っているので、これは IShellBrowser とハンドルを取得できる正しいスレッドで実行されます - 検索を行うと、次のメッセージが表示されます:
103, 103, 67, UnmergeMenu, WindowClosing, 106, ViewRelease, ViewRelease
その後、メッセージは投稿されません (再検索しても)。最初のブレークポイントは常に、親ウィンドウに関するコンテキストがない EnumObjects メソッドにあります。
どんな光の脱落もいいでしょう。
==編集==
私はいくつかの回避策を考え出しました - これはすべての場合に完璧ではありませんが、ほとんどの場合に機能します - 他のオプションはまだ良いでしょう.
EnumObjects が hwnd = NULL で呼び出されるたびに、私は次のことを行っています: (これは 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;
}
基本的に、私はすべてのエクスプローラーウィンドウを調べて、実際に「explorer.exe」+空ではない検索文字列(LocationName)+ビジー... +タイトルに現在のフォルダー名の名前が含まれているものを見つけようとしています。
2 つのウィンドウがビジー状態で、タイトルに同じフォルダー名が含まれていると失敗しますが、これで十分かもしれません... ここではわかりません。