如果在CodeProject,我将其改编为C++Builder。当它工作时,如果我调用了Bind()
函数,它将在表单关闭时崩溃。如果我不释放对象并让它泄漏资源,也不会崩溃。
这是一个类(注意:还没有测试注册表内容,也没有计划使用它):
//--------------------------------------------------------------------------------------------
// Name: CCustomAutoComplete (CCUSTOMAUTOCOMPLETE.H)
// Type: Wrapper class
// Description: Matches IAutoComplete, IEnumString and the registry (optional) to provide
// custom auto-complete functionality for EDIT controls - including those in
// combo boxes - in WTL projects.
//
// Author: Klaus H. Probst [kprobst@vbbox.com]
// URL: http://www.vbbox.com/
// Copyright: This work is copyright © 2002, Klaus H. Probst
// Usage: You may use this code as you see fit, provided that you assume all
// responsibilities for doing so.
// Distribution: Distribute freely as long as you maintain this notice as part of the
// file header.
//
//
// Updates:
//
//
// Notes:
//
//
// Dependencies:
//
// The usual ATL/WTL headers for a normal EXE, plus <atlmisc.h>
//
//--------------------------------------------------------------------------------------------
#if !defined(CCustomAutoComplete_INCLUDED)
#define CCustomAutoComplete_INCLUDED
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
#include <System.Classes.hpp>
#include <Registry.hpp>
#include <string.h>
// Bring in the GUID hack and the shell stuff
#include <initguid.h>
#include <shldisp.h>
#include <shlguid.h>
class CCustomAutoComplete : public IEnumString
{
private:
TStringList *m_pasList;
IAutoComplete *m_pac;
TRegistry* m_reg;
ULONG m_nCurrentElement;
ULONG m_nRefCount;
BOOL m_fBound;
public:
// Constructors/destructors
CCustomAutoComplete()
{
InternalInit();
}
CCustomAutoComplete(const HKEY p_hRootKey, const String& p_sSubKey)
{
InternalInit();
SetStorageSubkey(p_hRootKey, p_sSubKey);
}
CCustomAutoComplete(TStringList* p_sItemList)
{
InternalInit();
SetList(p_sItemList);
}
~CCustomAutoComplete()
{
if (m_reg)
delete m_reg;
if (m_pac)
m_pac->Release();
if (m_pasList)
delete m_pasList;
}
BOOL ListExists(bool create=false)
{
if (m_pasList)
return TRUE;
if (!create)
return FALSE;
return SetList(NULL);
}
public:
// Implementation
BOOL SetList(TStringList* p_sItemList)
{
Clear();
if (m_pasList)
delete m_pasList;
try {
m_pasList=new TStringList;
if (p_sItemList) {
m_pasList->Assign(p_sItemList);
}
}
catch(...) {
return FALSE;
}
return TRUE;
}
BOOL SetStorageSubkey(LPCTSTR p_lpszSubKey, HKEY p_hRootKey = HKEY_CURRENT_USER)
{
return SetStorageSubkey(p_hRootKey, String(p_lpszSubKey));
}
BOOL SetStorageSubkey(HKEY p_hRootKey, const String& p_sSubKey)
{
assert(p_hRootKey);
assert(p_sSubKey.Length());
if (InitStorage(p_hRootKey, p_sSubKey))
return LoadFromStorage();
return FALSE;
}
BOOL Bind(HWND p_hWndEdit, DWORD p_dwOptions = 0, LPCTSTR p_lpszFormatString = NULL)
{
assert(::IsWindow(p_hWndEdit));
if ((m_fBound) || (m_pac))
return FALSE;
HRESULT hr = S_OK;
if (SUCCEEDED(CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER, IID_IAutoComplete, (void**)&m_pac))) {
if (p_dwOptions) {
IAutoComplete2 *pac2;
if (SUCCEEDED(m_pac->QueryInterface(IID_IAutoComplete2, (void**)&pac2))) {
hr = pac2->SetOptions(p_dwOptions);
pac2->Release();
}
}
hr = m_pac->Init(p_hWndEdit, this, NULL, p_lpszFormatString);
if (SUCCEEDED(hr)) {
m_fBound = TRUE;
return TRUE;
}
m_pac->Release();
m_pac=NULL;
}
return FALSE;
}
VOID Unbind()
{
if (!m_fBound)
return;
assert(m_pac);
if (m_pac) {
m_pac->Release();
m_pac=NULL;
m_fBound = FALSE;
}
}
BOOL AddItem(String& p_sItem)
{
if (ListExists(true)) {
if (p_sItem.Length() != 0) {
if (m_pasList->IndexOf(p_sItem) == -1) {
m_pasList->Add(p_sItem);
if (m_reg)
return AddToStorage(p_sItem);
return TRUE;
}
}
}
return FALSE;
}
BOOL AddItem(LPCTSTR p_lpszItem)
{
String s=p_lpszItem;
return AddItem(s);
}
INT GetItemCount()
{
if (m_pasList) {
return m_pasList->Count;
}
return 0;
}
BOOL SetRecentItem(const String& p_sItem)
{
// Only supported if registry persistance is enabled.
// Call this function after setting the registry root
if ((!m_reg) || (!m_fBound))
return FALSE;
return WriteRecentItem(p_sItem);
}
BOOL GetRecentItem(String& p_sItem)
{
// Only supported if registry persistance is enabled.
// Call this function after setting the registry root
if ((!m_reg) || (!m_fBound))
return FALSE;
return ReadRecentItem(p_sItem);
}
BOOL RemoveItem(String& p_sItem)
{
if (m_pasList && p_sItem.Length() != 0) {
int i;
if ((i=m_pasList->IndexOf(p_sItem)) != -1) {
m_pasList->Delete(i);
if (m_reg)
return RemoveFromStorage(p_sItem);
}
}
return FALSE;
}
BOOL RemoveItem(LPCTSTR p_lpszItem)
{
String s=p_lpszItem;
return RemoveItem(s);
}
BOOL Clear()
{
if (m_pasList && m_pasList->Count != 0) {
if (m_reg) {
if (!ClearStorage())
return FALSE;
}
m_pasList->Clear();
return TRUE;
}
return FALSE;
}
BOOL Disable()
{
if ((!m_pac) || (!m_fBound))
return FALSE;
return SUCCEEDED(EnDisable(FALSE));
}
BOOL Enable(VOID)
{
if ((!m_pac) || (m_fBound))
return FALSE;
return SUCCEEDED(EnDisable(TRUE));
}
public:
//
// IUnknown implementation
//
STDMETHODIMP_(ULONG) AddRef()
{
return ::InterlockedIncrement(reinterpret_cast<LONG*>(&m_nRefCount));
}
STDMETHODIMP_(ULONG) Release()
{
ULONG nCount = 0;
nCount = (ULONG) ::InterlockedDecrement(reinterpret_cast<LONG*>(&m_nRefCount));
if (nCount == 0)
delete this;
return nCount;
}
STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject)
{
HRESULT hr = E_NOINTERFACE;
if (ppvObject != NULL)
{
*ppvObject = NULL;
if (IID_IUnknown == riid)
*ppvObject = static_cast<IUnknown*>(this);
if (IID_IEnumString == riid)
*ppvObject = static_cast<IEnumString*>(this);
if (*ppvObject != NULL)
{
hr = S_OK;
((LPUNKNOWN)*ppvObject)->AddRef();
}
}
else
{
hr = E_POINTER;
}
return hr;
}
public:
//
// IEnumString implementation
//
STDMETHODIMP Next(ULONG celt, LPOLESTR* rgelt, ULONG* pceltFetched)
{
HRESULT hr = S_FALSE;
if (!celt)
celt = 1;
ULONG i;
for (i = 0; i < celt; i++)
{
if (m_nCurrentElement >= GetItemCount())
break;
rgelt[i] = (LPWSTR)::CoTaskMemAlloc((ULONG) sizeof(WCHAR) * (m_pasList->Strings[m_nCurrentElement].Length() + 1));
wcscpy(rgelt[i], m_pasList->Strings[m_nCurrentElement].c_str());
if (pceltFetched)
(*pceltFetched)++;
m_nCurrentElement++;
}
if (i == celt)
hr = S_OK;
return hr;
}
STDMETHODIMP Skip(ULONG celt)
{
m_nCurrentElement += celt;
if (m_nCurrentElement > GetItemCount())
m_nCurrentElement = 0;
return S_OK;
}
STDMETHODIMP Reset(void)
{
m_nCurrentElement = 0;
return S_OK;
}
STDMETHODIMP Clone(IEnumString** ppenum)
{
if (!ppenum)
return E_POINTER;
try {
CCustomAutoComplete* pnew = new CCustomAutoComplete();
if (m_pasList) {
pnew->SetList(m_pasList);
}
pnew->AddRef();
*ppenum = pnew;
return S_OK;
}
catch(...) { };
return S_FALSE;
}
private:
// Internal implementation
void InternalInit()
{
m_pac=NULL;
m_nCurrentElement = 0;
m_nRefCount = 0;
m_fBound = FALSE;
m_reg = NULL;
m_pasList = NULL;
}
HRESULT EnDisable(BOOL p_fEnable)
{
HRESULT hr = S_OK;
assert(m_pac);
hr = m_pac->Enable(p_fEnable);
if (SUCCEEDED(hr))
m_fBound = p_fEnable;
return hr;
}
BOOL InitStorage(HKEY p_hRootKey, const String& p_sSubKey)
{
if (m_reg)
{
delete m_reg;
m_reg=NULL;
}
m_reg = new TRegistry(KEY_READ|KEY_WRITE);
if (m_reg) {
if (m_reg->OpenKey(p_sSubKey, true)) {
return TRUE;
}
delete m_reg;
m_reg=NULL;
}
return FALSE;
}
BOOL RemoveFromStorage(const String& p_sItem)
{
assert(m_reg);
BOOL result=FALSE;
try {
result=m_reg->DeleteValue(p_sItem);
}
catch(...) { };
return result;
}
BOOL LoadFromStorage()
{
assert(m_reg);
if (GetItemCount() != 0)
m_pasList->Clear();
BOOL result=TRUE;
try {
TStringList *values=new TStringList;
m_reg->GetValueNames(values);
for (UINT i=0;i<values->Count;i++) {
try {
String s=m_reg->ReadString(values->Strings[i]);
if (!s.IsEmpty()) {
if (!m_pasList->Add(s)) {
result=FALSE;
}
}
}
catch(...) {
result=FALSE;
};
}
delete values;
}
catch (...) {
result=FALSE;
}
return result;
}
BOOL ClearStorage()
{
// Since we do not hold the parent key to our
// own HKEY, we need to iterate through the
// array and delete each one.
if (m_pasList) {
for (int i = 0; i < m_pasList->Count; i++)
{
if (!RemoveFromStorage(m_pasList->Strings[i]))
return FALSE;
}
}
return TRUE;
}
BOOL AddToStorage(const String& p_sName)
{
assert(m_reg);
try {
m_reg->WriteString(p_sName, p_sName);
return TRUE;
}
catch(...) {};
return FALSE;
}
BOOL WriteRecentItem(const String& p_sItem)
{
assert(m_reg);
try {
m_reg->WriteString(_D("_Recent"), p_sItem);
return TRUE;
}
catch(...) {};
return FALSE;
}
BOOL ReadRecentItem(String& p_sItem)
{
assert(m_reg);
try {
p_sItem=m_reg->ReadString(_D("_Recent"));
return TRUE;
}
catch (...) {};
return FALSE;
}
};
#endif // !defined(CCustomAutoComplete_INCLUDED)
我是这样使用它的:
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
try {
m_pAutoComplete=new CCustomAutoComplete();
m_pAutoComplete->Bind(ButtonedEdit1->Handle, ACO_AUTOSUGGEST);
TStringList* sl=new TStringList;
sl->Add(_D("Test@xyz.com"));
sl->Add(_D("Tester@yahoo.com"));
sl->Add(_D("darpa@hotmail.com"));
m_pAutoComplete->SetList(sl);
delete sl;
}
catch(...) { };
}
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
// Note: I tried putting this in the ~TForm1 destructor as well.
if (m_pAutoComplete) {
if (m_pAutoComplete->Release()) {
m_pAutoComplete=NULL;
}
}
}
怎么了?