(Safe) SHAutoComplete

For a long time, MSDN has stated that SHAutoComplete can only be called once on a HWND, but a recent blog post by Raymond Chen “documents” that this issue was fixed in Vista.

It sure would be nice to avoid the leak on older platforms as well, but MS is not going to help you (Implementing such a thing would reveal that their Subclassing API that supposedly is XP+ only actually goes all the way back to Win98 (Really IE5 on Win95 / NT4))

Without knowing the internals of browseui.dll (Where most of the autocomplete stuff seems to be implemented) we are left with a ugly hack: Searching for the browseui subclass procedure that IAutoComplete uses, and if we find it; hope that its associated data is a IUnknown interface!

#define _WIN32_WINNT 0x501
#include <windows.h>
#include <Shlwapi.h>
#include <Commctrl.h>
#include <Shldisp.h>

int GetOSVerMajor() {return LOBYTE(LOWORD(GetVersion()));}

HRESULT WINAPI SafeSHAutoComplete(HWND hwndEdit,DWORD dwFlags) 
{
	typedef struct {
		SUBCLASSPROC*Proc;
		UINT_PTR Id;
		DWORD_PTR Data;
	} CCSC_ITEM;
	typedef struct {
		UINT Count;
		UINT Alloc;
		UINT Unknown[2];
		UINT_PTR Unknown2;
		CCSC_ITEM Items[1];
	} CCSC_HDR;

	HMODULE hBrowseUI=GetModuleHandle("BROWSEUI");
	HMODULE hComctl32=GetModuleHandle("COMCTL32");

	BOOL (WINAPI*CCGetWindowSubclass)(HWND hWnd,SUBCLASSPROC pfnSc,UINT_PTR uId,DWORD_PTR*pData);
	*(FARPROC*)&CCGetWindowSubclass=GetProcAddress(hComctl32,"GetWindowSubclass");
	//*(FARPROC*)&CCGetWindowSubclass=GetProcAddress(hComctl32,(LPCSTR)411);
	
	IUnknown*pUnk=NULL;
	
	CCSC_HDR*pCCSCHdr=(CCSC_HDR*)GetProp(hwndEdit,"UxSubclassInfo");
	if (!pCCSCHdr)pCCSCHdr=(CCSC_HDR*)GetProp(hwndEdit,"CC32SubclassInfo");
	
	if (GetOSVerMajor()<6 && hBrowseUI && pCCSCHdr) 
	{
		const UINT_PTR PEOffset=0x300;
		const UINT_PTR StartSearch=(UINT_PTR)hBrowseUI+PEOffset;
		const UINT_PTR EndSearch=(UINT_PTR)hBrowseUI+(0x140000-PEOffset);
		UINT_PTR SubclassProc=StartSearch;
		DWORD_PTR Data;
		
		// For whatever reason, only the v6 comctl seems to work with GetWindowSubclass 
		// (Even searching the whole address space fails with the older (undocumented) version)
		if (CCGetWindowSubclass)for (;SubclassProc<EndSearch;++SubclassProc) 
		{
			if (CCGetWindowSubclass(hwndEdit,(SUBCLASSPROC)SubclassProc,0,&Data) && Data)
			{
				pUnk=(IUnknown*)Data;
				break;
			}
		}
		if (!pUnk) 
		{
			if (pCCSCHdr && pCCSCHdr->Count>1) 
			{
				for(int i=0;i<pCCSCHdr->Count;++i) 
				{
					if (/*pCCSCHdr->Items[i].Id==0 &&*/ 
						(UINT_PTR)pCCSCHdr->Items[i].Proc>=StartSearch && 
						(UINT_PTR)pCCSCHdr->Items[i].Proc<=EndSearch) 
					{
						pUnk=(IUnknown*)pCCSCHdr->Items[i].Data;
						break;
					}
				}
			}
		}
	}
	
	if (pUnk) 
	{
		IAutoComplete2*pAC2;
		if (SUCCEEDED( pUnk->QueryInterface(IID_IAutoComplete2,(void**)&pAC2) ))
		{
			HRESULT hr=pAC2->SetOptions(dwFlags);
			pAC2->Release();
			if (SUCCEEDED(hr))return hr;
		}
	}

	return SHAutoComplete(hwndEdit,dwFlags);
}

This code has been tested on Win98(IE5)/Win2000:SP0/WinXP:SP2,SP3 but since it uses undocumented stuff, it could break at any time!

Advertisements

Tags: , , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s