windows-nt/Source/XPSP1/NT/shell/browseui/acthread.cpp
2020-09-26 16:20:57 +08:00

1025 lines
32 KiB
C++

// Copyright 1998 Microsoft
#include "priv.h"
#include "autocomp.h"
#define AC_GIVEUP_COUNT 1000
#define AC_TIMEOUT (60 * 1000)
//
// Thread messages
//
enum
{
ACM_FIRST = WM_USER,
ACM_STARTSEARCH,
ACM_STOPSEARCH,
ACM_SETFOCUS,
ACM_KILLFOCUS,
ACM_QUIT,
ACM_LAST,
};
// Special prefixes that we optionally filter out
const struct{
int cch;
LPCWSTR psz;
}
g_rgSpecialPrefix[] =
{
{4, L"www."},
{11, L"http://www."}, // This must be before "http://"
{7, L"http://"},
{8, L"https://"},
};
//+-------------------------------------------------------------------------
// CACString functions - Hold autocomplete strings
//--------------------------------------------------------------------------
ULONG CACString::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
ULONG CACString::Release()
{
ASSERT(m_cRef > 0);
if (InterlockedDecrement(&m_cRef))
{
return m_cRef;
}
delete this;
return 0;
}
CACString* CreateACString(LPCWSTR pszStr, int iIgnore, ULONG ulSortIndex)
{
ASSERT(pszStr);
int cChars = lstrlen(pszStr);
// Allocate the CACString class with enough room for the new string
CACString* pStr = (CACString*)LocalAlloc(LPTR, cChars * sizeof(WCHAR) + sizeof(CACString));
if (pStr)
{
StrCpy(pStr->m_sz, pszStr);
pStr->m_ulSortIndex = ulSortIndex;
pStr->m_cRef = 1;
pStr->m_cChars = cChars;
pStr->m_iIgnore = iIgnore;
}
return pStr;
}
int CACString::CompareSortingIndex(CACString& r)
{
int iRet;
// If the sorting indices are equal, just do a string compare
if (m_ulSortIndex == r.m_ulSortIndex)
{
iRet = StrCmpI(r);
}
else
{
iRet = (m_ulSortIndex > r.m_ulSortIndex) ? 1 : -1;
}
return iRet;
}
HRESULT CACThread::QueryInterface(REFIID riid, void **ppvObj)
{
static const QITAB qit[] = { { 0 }, };
return QISearch(this, qit, riid, ppvObj);
}
ULONG CACThread::AddRef(void)
{
return InterlockedIncrement(&m_cRef);
}
ULONG CACThread::Release(void)
{
ASSERT(m_cRef > 0);
if (InterlockedDecrement(&m_cRef))
{
return m_cRef;
}
delete this;
return 0;
}
CACThread::CACThread(CAutoComplete& rAutoComp) : m_pAutoComp(&rAutoComp), m_cRef(1)
{
ASSERT(!m_fWorkItemQueued);
ASSERT(!m_idThread);
ASSERT(!m_hCreateEvent);
ASSERT(!m_fDisabled);
ASSERT(!m_pszSearch);
ASSERT(!m_hdpa_list);
ASSERT(!m_pes);
ASSERT(!m_pacl);
DllAddRef();
}
CACThread::~CACThread()
{
SyncShutDownBGThread(); // In case somehow
// These should have been freed.
ASSERT(!m_idThread);
ASSERT(!m_hdpa_list);
SAFERELEASE(m_pes);
SAFERELEASE(m_peac);
SAFERELEASE(m_pacl);
DllRelease();
}
BOOL CACThread::Init(IEnumString* pes, // source of the autocomplete strings
IACList* pacl) // optional interface to call Expand
{
// REARCHITECT: We need to marshal these interfaces to this thread!
ASSERT(pes);
m_pes = pes;
m_pes->AddRef();
m_peac = NULL;
pes->QueryInterface(IID_PPV_ARG(IEnumACString, &m_peac));
if (pacl)
{
m_pacl = pacl;
m_pacl->AddRef();
}
return TRUE;
}
//+-------------------------------------------------------------------------
// Called when the edit box recieves focus. We use this event to create
// a background thread or to keep the backgroung thread from shutting down
//--------------------------------------------------------------------------
void CACThread::GotFocus()
{
TraceMsg(AC_GENERAL, "CACThread::GotFocus()");
// Should not be NULL if the foreground thread is calling us!
ASSERT(m_pAutoComp);
//
// Check to see if autocomplete is supposed to be enabled.
//
if (m_pAutoComp && m_pAutoComp->IsEnabled())
{
m_fDisabled = FALSE;
if (m_fWorkItemQueued)
{
// If the thread hasn't started yet, wait for a thread creation event
if (0 == m_idThread && m_hCreateEvent)
{
WaitForSingleObject(m_hCreateEvent, 1000);
}
if (m_idThread)
{
//
// Tell the thread to cancel its timeout and stay alive.
//
// REARCHITECT: We have a race condition here. The thread can be
// in the process of shutting down!
PostThreadMessage(m_idThread, ACM_SETFOCUS, 0, 0);
}
}
else
{
//
// The background thread signals an event when it starts up.
// We wait on this event before trying a synchronous shutdown
// because any posted messages would be lost.
//
if (NULL == m_hCreateEvent)
{
m_hCreateEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
}
else
{
ResetEvent(m_hCreateEvent);
}
//
// Make sure we have a background search thread.
//
// If we start it any later, we run the risk of not
// having its message queue available by the time
// we post a message to it.
//
// AddRef ourselves now, to prevent us getting freed
// before the thread proc starts running.
//
AddRef();
// Call to Shlwapi thread pool
if (SHQueueUserWorkItem(_ThreadProc,
this,
0,
(DWORD_PTR)NULL,
NULL,
"browseui.dll",
TPS_LONGEXECTIME | TPS_DEMANDTHREAD
))
{
InterlockedExchange(&m_fWorkItemQueued, TRUE);
}
else
{
// Couldn't get thread
Release();
}
}
}
else
{
m_fDisabled = TRUE;
_SendAsyncShutDownMsg(FALSE);
}
}
//+-------------------------------------------------------------------------
// Called when the edit box loses focus.
//--------------------------------------------------------------------------
void CACThread::LostFocus()
{
TraceMsg(AC_GENERAL, "CACThread::LostFocus()");
//
// If there is a thread around, tell it to stop searching.
//
if (m_idThread)
{
StopSearch();
PostThreadMessage(m_idThread, ACM_KILLFOCUS, 0, 0);
}
}
//+-------------------------------------------------------------------------
// Sends the search request to the background thread.
//--------------------------------------------------------------------------
BOOL CACThread::StartSearch
(
LPCWSTR pszSearch, // String to search
DWORD dwOptions // ACO_* flags
)
{
BOOL fRet = FALSE;
// If the thread hasn't started yet, wait for a thread creation event
if (0 == m_idThread && m_fWorkItemQueued && m_hCreateEvent)
{
WaitForSingleObject(m_hCreateEvent, 1000);
}
if (m_idThread)
{
LPWSTR pszSrch = StrDup(pszSearch);
if (pszSrch)
{
//
// This is being sent to another thread, remove it from this thread's
// memlist.
//
//
// If the background thread is already searching, abort that search
//
StopSearch();
//
// Send request off to the background search thread.
//
if (PostThreadMessage(m_idThread, ACM_STARTSEARCH, dwOptions, (LPARAM)pszSrch))
{
fRet = TRUE;
}
else
{
TraceMsg(AC_GENERAL, "CACThread::_StartSearch could not send message to thread!");
LocalFree(pszSrch);
}
}
}
return fRet;
}
//+-------------------------------------------------------------------------
// Tells the background thread to stop and pending search
//--------------------------------------------------------------------------
void CACThread::StopSearch()
{
TraceMsg(AC_GENERAL, "CACThread::_StopSearch()");
//
// Tell the thread to stop.
//
if (m_idThread)
{
PostThreadMessage(m_idThread, ACM_STOPSEARCH, 0, 0);
}
}
//+-------------------------------------------------------------------------
// Posts a quit message to the background thread
//--------------------------------------------------------------------------
void CACThread::_SendAsyncShutDownMsg(BOOL fFinalShutDown)
{
if (0 == m_idThread && m_fWorkItemQueued && m_hCreateEvent)
{
//
// Make sure that the thread has started up before posting a quit
// message or the quit message will be lost!
//
WaitForSingleObject(m_hCreateEvent, 3000);
}
if (m_idThread)
{
// Stop the search because it can hold up the thread for quite a
// while by waiting for disk data.
StopSearch();
// Tell the thread to go away, we won't be needing it anymore. Note that we pass
// the dropdown window because during the final shutdown we need to asynchronously
// destroy the dropdown to avoid a crash. The background thread will keep browseui
// mapped in memory until the dropdown is destroyed.
HWND hwndDropDown = (fFinalShutDown ? m_pAutoComp->m_hwndDropDown : NULL);
PostThreadMessage(m_idThread, ACM_QUIT, 0, (LPARAM)hwndDropDown);
}
}
//+-------------------------------------------------------------------------
// Synchroniously shutdown the background thread
//
// Note: this is no longer synchronous because we now orphan this object
// when the associated autocomplet shuts down.
//
//--------------------------------------------------------------------------
void CACThread::SyncShutDownBGThread()
{
_SendAsyncShutDownMsg(TRUE);
// Block shutdown if background thread is about to use this variable
ENTERCRITICAL;
m_pAutoComp = NULL;
LEAVECRITICAL;
if (m_hCreateEvent)
{
CloseHandle(m_hCreateEvent);
m_hCreateEvent = NULL;
}
}
void CACThread::_FreeThreadData()
{
if (m_hdpa_list)
{
CAutoComplete::_FreeDPAPtrs(m_hdpa_list);
m_hdpa_list = NULL;
}
if (m_pszSearch)
{
LocalFree(m_pszSearch);
m_pszSearch = NULL;
}
InterlockedExchange(&m_idThread, 0);
InterlockedExchange(&m_fWorkItemQueued, 0);
}
DWORD WINAPI CACThread::_ThreadProc(void *pv)
{
CACThread *pThis = (CACThread *)pv;
HRESULT hrInit = SHCoInitialize();
if (SUCCEEDED(hrInit))
{
pThis->_ThreadLoop();
}
pThis->Release();
SHCoUninitialize(hrInit);
return 0;
}
HRESULT CACThread::_ProcessMessage(MSG * pMsg, DWORD * pdwTimeout, BOOL * pfStayAlive)
{
TraceMsg(AC_GENERAL, "AutoCompleteThread: Message %x received.", pMsg->message);
switch (pMsg->message)
{
case ACM_STARTSEARCH:
TraceMsg(AC_GENERAL, "AutoCompleteThread: Search started.");
*pdwTimeout = INFINITE;
_Search((LPWSTR)pMsg->lParam, (DWORD)pMsg->wParam);
TraceMsg(AC_GENERAL, "AutoCompleteThread: Search completed.");
break;
case ACM_STOPSEARCH:
while (PeekMessage(pMsg, pMsg->hwnd, ACM_STOPSEARCH, ACM_STOPSEARCH, PM_REMOVE))
{
NULL;
}
TraceMsg(AC_GENERAL, "AutoCompleteThread: Search stopped.");
break;
case ACM_SETFOCUS:
TraceMsg(AC_GENERAL, "AutoCompleteThread: Got Focus.");
*pdwTimeout = INFINITE;
break;
case ACM_KILLFOCUS:
TraceMsg(AC_GENERAL, "AutoCompleteThread: Lost Focus.");
*pdwTimeout = AC_TIMEOUT;
break;
case ACM_QUIT:
{
TraceMsg(AC_GENERAL, "AutoCompleteThread: ACM_QUIT received.");
*pfStayAlive = FALSE;
//
// If a hwnd was passed in then we are shutting down and we need to
// wait until the dropdown window is destroyed before exiting this
// thread. That way browseui will stay mapped in memory.
//
HWND hwndDropDown = (HWND)pMsg->lParam;
if (hwndDropDown)
{
// We wait 5 seconds for the window to go away, checking every 100ms
int cSleep = 50;
while (IsWindow(hwndDropDown) && (--cSleep > 0))
{
MsgWaitForMultipleObjects(0, NULL, FALSE, 100, QS_TIMER);
}
}
}
break;
default:
// pump any ole-based window message that might also be on this thread
TranslateMessage(pMsg);
DispatchMessage(pMsg);
break;
}
return S_OK;
}
//+-------------------------------------------------------------------------
// Message pump for the background thread
//--------------------------------------------------------------------------
HRESULT CACThread::_ThreadLoop()
{
MSG Msg;
DWORD dwTimeout = INFINITE;
BOOL fStayAlive = TRUE;
TraceMsg(AC_WARNING, "AutoComplete service thread started.");
//
// We need to call a window's api for a message queue to be created
// so we call peekmessage. Then we get the thread id and thread handle
// and we signal an event to tell the forground thread that we are listening.
//
while (PeekMessage(&Msg, NULL, ACM_FIRST, ACM_LAST, PM_REMOVE))
{
// purge any messages we care about from previous owners of this thread.
}
// The forground thread needs this is so that it can post us messages
InterlockedExchange(&m_idThread, GetCurrentThreadId());
if (m_hCreateEvent)
{
SetEvent(m_hCreateEvent);
}
HANDLE hThread = GetCurrentThread();
int nOldPriority = GetThreadPriority(hThread);
SetThreadPriority(hThread, THREAD_PRIORITY_BELOW_NORMAL);
while (fStayAlive)
{
while (fStayAlive && PeekMessage(&Msg, NULL, 0, (UINT)-1, PM_NOREMOVE))
{
if (-1 != GetMessage(&Msg, NULL, 0, 0))
{
if (!Msg.hwnd)
{
// No hwnd means it's a thread message, so it's ours.
_ProcessMessage(&Msg, &dwTimeout, &fStayAlive);
}
else
{
// It has an hwnd then it's not ours. We will not allow windows on our thread.
// If anyone creates their windows on their thread, file a bug against them
// to remove it.
}
}
}
if (fStayAlive)
{
TraceMsg(AC_GENERAL, "AutoCompleteThread: Sleeping for%s.", dwTimeout == INFINITE ? "ever" : " one minute");
DWORD dwWait = MsgWaitForMultipleObjects(0, NULL, FALSE, dwTimeout, QS_ALLINPUT);
#ifdef DEBUG
switch (dwWait)
{
case 0xFFFFFFFF:
ASSERT(dwWait != 0xFFFFFFFF);
break;
case WAIT_TIMEOUT:
TraceMsg(AC_GENERAL, "AutoCompleteThread: Timeout expired.");
break;
}
#endif
fStayAlive = (dwWait == WAIT_OBJECT_0);
}
}
TraceMsg(AC_GENERAL, "AutoCompleteThread: Thread dying.");
_FreeThreadData();
SetThreadPriority(hThread, nOldPriority);
// Purge any remaining messages before returning this thread to the pool.
while (PeekMessage(&Msg, NULL, ACM_FIRST, ACM_LAST, PM_REMOVE))
{}
TraceMsg(AC_WARNING, "AutoCompleteThread: Thread dead.");
return S_OK;
}
//+-------------------------------------------------------------------------
// Returns true if the search string matches one or more characters of a
// prefix that we filter out matches to
//--------------------------------------------------------------------------
BOOL CACThread::MatchesSpecialPrefix(LPCWSTR pszSearch)
{
BOOL fRet = FALSE;
int cchSearch = lstrlen(pszSearch);
for (int i = 0; i < ARRAYSIZE(g_rgSpecialPrefix); ++i)
{
// See if the search string matches one or more characters of the prefix
if (cchSearch <= g_rgSpecialPrefix[i].cch &&
StrCmpNI(g_rgSpecialPrefix[i].psz, pszSearch, cchSearch) == 0)
{
fRet = TRUE;
break;
}
}
return fRet;
}
//+-------------------------------------------------------------------------
// Returns the length of the prefix it the string starts with a special
// prefix that we filter out matches to. Otherwise returns zero.
//--------------------------------------------------------------------------
int CACThread::GetSpecialPrefixLen(LPCWSTR psz)
{
int nRet = 0;
int cch = lstrlen(psz);
for (int i = 0; i < ARRAYSIZE(g_rgSpecialPrefix); ++i)
{
if (cch >= g_rgSpecialPrefix[i].cch &&
StrCmpNI(g_rgSpecialPrefix[i].psz, psz, g_rgSpecialPrefix[i].cch) == 0)
{
nRet = g_rgSpecialPrefix[i].cch;
break;
}
}
return nRet;
}
//+-------------------------------------------------------------------------
// Returns the next autocomplete string
//--------------------------------------------------------------------------
HRESULT CACThread::_Next(LPWSTR pszUrl, ULONG cchMax, ULONG* pulSortIndex)
{
ASSERT(pulSortIndex);
HRESULT hr;
// Use the new interface if we have it
if (m_peac)
{
hr = m_peac->NextItem(pszUrl, cchMax, pulSortIndex);
}
// Fall back to the old IEnumString interface
else
{
LPWSTR pszNext;
ULONG ulFetched;
hr = m_pes->Next(1, &pszNext, &ulFetched);
if (S_OK == hr)
{
StrCpyN(pszUrl, pszNext, cchMax);
if (pulSortIndex)
{
*pulSortIndex = 0;
}
CoTaskMemFree(pszNext);
}
}
return hr;
}
//+-------------------------------------------------------------------------
// Searches for items that match pszSearch.
//--------------------------------------------------------------------------
void CACThread::_Search
(
LPWSTR pszSearch, // String to search for (we must free this)
DWORD dwOptions // ACO_* flags
)
{
if (pszSearch)
{
TraceMsg(AC_GENERAL, "CACThread(BGThread)::_Search(pszSearch=0x%x)", pszSearch);
// Save the search string in our thread data so it is still freed if this thread is killed
m_pszSearch = pszSearch;
// If we were passed a wildcard string, then everything matches
BOOL fWildCard = ((pszSearch[0] == CH_WILDCARD) && (pszSearch[1] == L'\0'));
// To avoid huge number of useless matches, avoid matches
// to common prefixes
BOOL fFilter = (dwOptions & ACO_FILTERPREFIXES) && MatchesSpecialPrefix(pszSearch);
BOOL fAppendOnly = IsFlagSet(dwOptions, ACO_AUTOAPPEND) && IsFlagClear(dwOptions, ACO_AUTOSUGGEST);
if (m_pes) // paranoia
{
// If this fails, the m_pes->Next() will likely do something
// bad, so we will avoid it altogether.
if (SUCCEEDED(m_pes->Reset()))
{
BOOL fStopped = FALSE;
m_dwSearchStatus = 0;
_DoExpand(pszSearch);
int cchSearch = lstrlen(pszSearch);
WCHAR szUrl[MAX_URL_STRING];
ULONG ulSortIndex;
while (!fStopped && IsFlagClear(m_dwSearchStatus, SRCH_LIMITREACHED) &&
(_Next(szUrl, ARRAYSIZE(szUrl), &ulSortIndex) == S_OK))
{
//
// First check for a simple match
//
if (fWildCard ||
(StrCmpNI(szUrl, pszSearch, cchSearch) == 0) &&
// Filter out matches to common prefixes
(!fFilter || GetSpecialPrefixLen(szUrl) == 0))
{
_AddToList(szUrl, 0, ulSortIndex);
}
// If the dropdown is enabled, check for matches after common prefixes.
if (!fAppendOnly)
{
//
// Also check for a match if we skip the protocol. We
// assume that szUrl has been cononicalized (protocol
// in lower case).
//
LPCWSTR psz = szUrl;
if (StrCmpN(szUrl, L"http://", 7) == 0)
{
psz += 7;
}
if (StrCmpN(szUrl, L"https://", 8) == 0 ||
StrCmpN(szUrl, L"file:///", 8) == 0)
{
psz += 8;
}
if (psz != szUrl &&
StrCmpNI(psz, pszSearch, cchSearch) == 0 &&
// Filter out "www." prefixes
(!fFilter || GetSpecialPrefixLen(psz) == 0))
{
_AddToList(szUrl, (int)(psz - szUrl), ulSortIndex);
}
//
// Finally check for a match if we skip "www." after
// the optional protocol
//
if (StrCmpN(psz, L"www.", 4) == 0 &&
StrCmpNI(psz + 4, pszSearch, cchSearch) == 0)
{
_AddToList(szUrl, (int)(psz + 4 - szUrl), ulSortIndex);
}
}
// Check to see if the search was canceled
MSG msg;
fStopped = PeekMessage(&msg, NULL, ACM_STOPSEARCH, ACM_STOPSEARCH, PM_NOREMOVE);
#ifdef DEBUG
fStopped = FALSE;
if (fStopped)
TraceMsg(AC_GENERAL, "AutoCompleteThread: Search TERMINATED");
#endif
}
if (fStopped)
{
// Search aborted so free the results
if (m_hdpa_list)
{
// clear the list
CAutoComplete::_FreeDPAPtrs(m_hdpa_list);
m_hdpa_list = NULL;
}
}
else
{
//
// Sort the results and remove duplicates
//
if (m_hdpa_list)
{
DPA_Sort(m_hdpa_list, _DpaCompare, 0);
//
// Perge duplicates.
//
for (int i = DPA_GetPtrCount(m_hdpa_list) - 1; i > 0; --i)
{
CACString& rStr1 = *(CACString*)DPA_GetPtr(m_hdpa_list, i-1);
CACString& rStr2 = *(CACString*)DPA_GetPtr(m_hdpa_list, i);
// Since URLs are case sensitive, we can't ignore case.
if (rStr1.StrCmpI(rStr2) == 0)
{
// We have a match, so keep the longest string.
if (rStr1.GetLength() > rStr2.GetLength())
{
// Use the smallest sort index
if (rStr2.GetSortIndex() < rStr1.GetSortIndex())
{
rStr1.SetSortIndex(rStr2.GetSortIndex());
}
DPA_DeletePtr(m_hdpa_list, i);
rStr2.Release();
}
else
{
// Use the smallest sort index
if (rStr1.GetSortIndex() < rStr2.GetSortIndex())
{
rStr2.SetSortIndex(rStr1.GetSortIndex());
}
DPA_DeletePtr(m_hdpa_list, i-1);
rStr1.Release();
}
}
else
{
//
// Special case: If this is a web site and the entries
// are identical except one has an extra slash on the end
// from a redirect, remove the redirected one.
//
int cch1 = rStr1.GetLengthToCompare();
int cch2 = rStr2.GetLengthToCompare();
int cchDiff = cch1 - cch2;
if (
// Length must differ by one
(cchDiff == 1 || cchDiff == -1) &&
// One string must have a terminating slash
((cch1 > 0 && rStr1[rStr1.GetLength() - 1] == L'/') ||
(cch2 > 0 && rStr2[rStr2.GetLength() - 1] == L'/')) &&
// Must be a web site
((StrCmpN(rStr1, L"http://", 7) == 0 || StrCmpN(rStr1, L"https://", 8) == 0) ||
(StrCmpN(rStr2, L"http://", 7) == 0 || StrCmpN(rStr2, L"https://", 8) == 0)) &&
// Must be identical up to the slash (ignoring prefix)
StrCmpNI(rStr1.GetStrToCompare(), rStr2.GetStrToCompare(), (cchDiff > 0) ? cch2 : cch1) == 0)
{
// Remove the longer string with the extra slash
if (cchDiff > 0)
{
// Use the smallest sort index
if (rStr1.GetSortIndex() < rStr2.GetSortIndex())
{
rStr2.SetSortIndex(rStr1.GetSortIndex());
}
DPA_DeletePtr(m_hdpa_list, i-1);
rStr1.Release();
}
else
{
// Use the smallest sort index
if (rStr2.GetSortIndex() < rStr1.GetSortIndex())
{
rStr1.SetSortIndex(rStr2.GetSortIndex());
}
DPA_DeletePtr(m_hdpa_list, i);
rStr2.Release();
}
}
}
}
}
// Pass the results to the foreground thread
ENTERCRITICAL;
if (m_pAutoComp)
{
HWND hwndEdit = m_pAutoComp->m_hwndEdit;
UINT uMsgSearchComplete = m_pAutoComp->m_uMsgSearchComplete;
LEAVECRITICAL;
// Unix loses keys if we post the message, so we send the message
// outside our critical section
SendMessage(hwndEdit, uMsgSearchComplete, m_dwSearchStatus, (LPARAM)m_hdpa_list);
}
else
{
LEAVECRITICAL;
// We've been orphaned, so free the list and bail
CAutoComplete::_FreeDPAPtrs(m_hdpa_list);
}
// The foreground thread owns the list now
m_hdpa_list = NULL;
}
}
else
{
ASSERT(0); // m_pes->Reset Failed!!
}
}
// We must free the search string
m_pszSearch = NULL;
// Note if the thread is killed here, we leak the string
// but at least we will not try to free it twice (which is worse)
// because we nulled m_pszSearch first.
LocalFree(pszSearch);
}
}
//+-------------------------------------------------------------------------
// Used to sort items alphabetically
//--------------------------------------------------------------------------
int CALLBACK CACThread::_DpaCompare(void *p1, void *p2, LPARAM lParam)
{
CACString* ps1 = (CACString*)p1;
CACString* ps2 = (CACString*)p2;
return ps1->StrCmpI(*ps2);
}
//+-------------------------------------------------------------------------
// Adds a string to our HDPA. Returns TRUE is successful.
//--------------------------------------------------------------------------
BOOL CACThread::_AddToList
(
LPTSTR pszUrl, // string to add
int cchMatch, // offset into string where the match occurred
ULONG ulSortIndex // controls order of items displayed
)
{
TraceMsg(AC_GENERAL, "CACThread(BGThread)::_AddToList(pszUrl = %s)",
(pszUrl ? pszUrl : TEXT("(null)")));
BOOL fRet = TRUE;
//
// Create a new list if necessary.
//
if (!m_hdpa_list)
{
m_hdpa_list = DPA_Create(AC_LIST_GROWTH_CONST);
}
if (m_hdpa_list && DPA_GetPtrCount(m_hdpa_list) < AC_GIVEUP_COUNT)
{
CACString* pStr = CreateACString(pszUrl, cchMatch, ulSortIndex);
if (pStr)
{
if (DPA_AppendPtr(m_hdpa_list, pStr) == -1)
{
pStr->Release();
m_dwSearchStatus |= SRCH_LIMITREACHED;
fRet = FALSE;
}
// If we have a nonzero sort index, the forground thread will need
// to use it to order the results
else if (ulSortIndex)
{
m_dwSearchStatus |= SRCH_USESORTINDEX;
}
}
}
else
{
m_dwSearchStatus |= SRCH_LIMITREACHED;
fRet = FALSE;
}
return fRet;
}
//+-------------------------------------------------------------------------
// This function will attempt to use the autocomplete list to bind to a
// location in the Shell Name Space. If that succeeds, the AutoComplete List
// will then contain entries which are the display names in that ISF.
//--------------------------------------------------------------------------
void CACThread::_DoExpand(LPCWSTR pszSearch)
{
LPCWSTR psz;
if (!m_pacl)
{
//
// Doesn't support IAutoComplete, doesn't have Expand method.
//
return;
}
if (*pszSearch == 0)
{
//
// No string means no expansion necessary.
//
return;
}
//
// psz points to last character.
//
psz = pszSearch + lstrlen(pszSearch);
psz = CharPrev(pszSearch, psz);
//
// Search backwards for an expand break character.
//
while (psz != pszSearch && *psz != TEXT('/') && *psz != TEXT('\\'))
{
psz = CharPrev(pszSearch, psz);
}
if (*psz == TEXT('/') || *psz == TEXT('\\'))
{
SHSTR ss;
psz++;
if (SUCCEEDED(ss.SetStr(pszSearch)))
{
//
// Trim ss so that it contains everything up to the last
// expand break character.
//
LPTSTR pszTemp = ss.GetInplaceStr();
pszTemp[psz - pszSearch] = TEXT('\0');
//
// Call expand on the string.
//
m_pacl->Expand(ss);
}
}
}