본문 바로가기

Programming/Win32&MFC&COM

IAutoComplete를 이용한 자동 완성(Auto Complete) 기능 구현

반응형

웹 브라우저나 탐색기의 주소창에서 글자가 입력될 때마다 하단의 리스트가 갱신되는 것을 볼 수 있습니다.

이 기능은 자동 완성(Auto Complete)라는 기능입니다.

자동 완성은 SHAutoComplete()라는 API를 사용해서 간단하게 구현이 가능합니다.

이 함수를 사용하면 URL이나 폴더 경로를 간단하게 자동 완성이 가능하게 할 수 있습니다.

웹 브라우저나 탐색기의 주소창과 동일한 기능을 하는 텍스트 박스를 만들 수 있습니다.

SHAutoComplete() 함수는 에디트 컨트롤의 핸들(HWND)과 플래그를 전달 받습니다.

함수의 원형은 다음과 같습니다.

HRESULT SHAutoComplete(HWND hwndEdit, DWORD dwFlags);

이 방법으로 주소를 쉽게 적용하는 것이 가능합니다.

다만 특정 문자열에 대한 자동 완성 기능을 사용하려면 IAutoComplete를 사용해야 합니다.

IAutoCompleteInit()과 Enable()이라는 메소드를 가지고 있습니다.

기본적으로 Enable 상태이기 때문에 의도적으로 Disable을 시키지 않으면 Enable()를 사용할 필요가 없습니다.

Init()이 실제 자동 완성을 구현하는데 사용되는 메소드입니다.

함수의 원형은 다음과 같습니다.

HRESULT Init(HWND hwndEdit,
                   IUnknown *punkACL,
                   LPCWSTR pwszRegKeyPath,
                   LPCWSTR pwszQuickComplete);

SHAutoComplete()와 조금 다른 모습입니다.

첫 번째는 동일하게 에디드 컨트롤의 핸들을 전달합니다.

그리고 세 번째와 네 번째는 포맷 문자열(www.%s.com)과 같이 사용할 때 사용하는 인자입니다.

Ctrl + Enter를 누르면 입력한 글자가 해당 포맷에 맞게 변경됩니다.

IE에도 동일한 기능이 존재합니다.

일반적인 문자열의 자동 완성에서는 nullptr(NULL)을 전달하면 됩니다.

두 번째로 전달받는 것이 자동 완성에 사용될 실제 문자열들이 포함된 IEnumString 객체입니다.

IEnumString은 IEnumXXXX 형태의 인터페이스로 문자열을 열거합니다.

IEnumString은 Clone(), Next(), Reset(), Skip() 4개의 메소드가 존재합니다.

IEnumString을 상속받은 CPGEnumString의 소스 파일은 다음과 같습니다.

PGEnumString.cpp

PGEnumString.h

내부의 소스가 복잡하지 않기 때문에 분석이 어렵지 않습니다.

이제 IEnumString을 이용해서 자동 완성을 사용하는 방법은 다음과 같습니다.

먼저 PGEnumString.h를 사용할 코드의 헤더 파일에 추가합니다.

#include "PGEnumString.h"

그리고 사용할 곳의 헤더에 다음과 같이 에디트 컨트롤과 클래스 객체를 선언합니다.

CPGEnumString* m_pEnum;
CComPtr<IAutoComplete> m_pAC;
CEdit m_Edit;

물론 m_Edit는 정상적으로 DDX_Control 등으로 연결되어 있어야 합니다.

DDX_Control(pDX, IDC_EDIT1, m_Edit);

소스 코드는 다음과 같이 작성하면 됩니다.

HRESULT hr = m_pAC.CoCreateInstance(CLSID_AutoComplete);
if (SUCCEEDED(hr))
{
	CComQIPtr<IAutoComplete2> pAC2(m_pAC);
	hr = pAC2->SetOptions(ACO_UPDOWNKEYDROPSLIST | ACO_AUTOSUGGEST | ACO_AUTOAPPEND);
	pAC2.Release();

	std::vector<std::wstring> temp;
	temp.push_back(std::wstring(L"Text"));
	temp.push_back(std::wstring(L"Test"));
	temp.push_back(std::wstring(L"Word"));

	m_pEnum = new CPGEnumString(temp);
	hr = m_pAC->Init(m_Edit.GetSafeHwnd(), m_pEnum, nullptr, nullptr);
}

먼저 IAutoComplete2 객체로 변환한 이후에 SetOptions()를 사용해서 속성을 지정합니다.

그 이후에 파일 리스트를 CPGEnumString에 전달합니다.

CPGEnumString에는 SetList() 메소드가 존재하기 때문에 새로운 문자열 리스트로 변경할 수도 있습니다.

내부적으로는 wstring을 std::list에 저장해서 보관하고 있습니다.

그리고 마지막으로 InitInstance()에 CoInitializeEx() 함수를 추가합니다.

ExitInstance()에는 CoUninitialize()를 호출해주면 됩니다.

이제 실행하면 다음과 같이 표시가 되는 것을 확인할 수 있습니다.

참고로 Clone()을 사용해서 복사한 이후에 반드시 Release()를 호출해야 합니다.

Release()를 호출하지 않으면 메모리 누수가 발생할 소지가 있습니다.

마지막으로 주의할 점은 CPGEnumString* 타입인 m_pEnum는 delete를 사용하지 않는다는 점입니다.

new로 생성하지만 m_pAC->Init()이 호출되면서 참조 카운트가 1이 되었다가 소멸되면서 0이 됩니다.

그리고 내부적으로 delete가 호출되서 자동으로 객체가 사라집니다.



반응형