서브클래싱 컨트롤

컨트롤이 원하는 거의 모든 작업을 수행하지만 몇 가지 기능이 더 필요한 경우 원래 컨트롤을 서브클래싱하여 기능을 변경하거나 추가할 수 있습니다. 서브클래스에는 기존 클래스의 모든 기능과 제공하려는 추가 기능이 모두 있을 수 있습니다.

이 문서에서는 서브클래스를 만드는 방법에 대해 설명하고 다음과 같은 항목을 다룹니다.

ComCtl32.dll 버전 6 이전의 서브클래싱 컨트롤

컨트롤을 서브클래스에 배치하고 컨트롤 내에 사용자 데이터를 저장할 수 있습니다. 버전 6 이전의 ComCtl32.dll을 사용하는 경우 이 작업을 수행합니다. 이전 버전의 ComCtl32.dll을 사용하여 서브클래스를 만드는 경우 몇 가지 단점이 있습니다.

새 컨트롤을 만들려면 Windows 공용 컨트롤 중 하나로 시작하여 특정 요구 사항에 맞게 확장하는 것이 가장 좋습니다. 컨트롤을 확장하려면 컨트롤을 만들고 기존 창 프로시저를 새 프로시저로 대체합니다. 새 프로시저는 컨트롤의 메시지를 가로채서 해당 메시지에서 작업을 수행하거나 기본 처리를 위해 원래 프로시저에 전달합니다. SetWindowLong 또는 SetWindowLongPtr 함수를 사용하여 컨트롤의 WNDPROC를 대체합니다. 다음 코드 샘플에서는 WNDPROC를 대체하는 방법을 보여 줍니다.

OldWndProc = (WNDPROC)SetWindowLongPtr (hButton,
GWLP_WNDPROC, (LONG_PTR)NewWndProc);

사용자 데이터 저장

사용자 데이터를 개별 창에 저장할 수 있습니다. 이 데이터는 새 창 프로시저에서 컨트롤을 그리는 방법이나 특정 메시지를 보낼 위치를 결정하는 데 사용할 수 있습니다. 예를 들어 데이터를 사용하여 컨트롤을 나타내는 클래스에 대한 C++ 클래스 포인터를 저장할 수 있습니다. 다음 코드 샘플에서는 SetProp을 사용하여 데이터를 창에 저장하는 방법을 보여 줍니다.

SetProp (hwnd, TEXT("MyData"), (HANDLE)pMyData);

이전 서브클래싱 방법의 단점

다음 목록에서는 앞에서 설명한 접근 방식을 사용하여 컨트롤을 서브클래싱할 때의 몇 가지 단점을 설명합니다.

  • 창 프로시저를 한 번만 대체할 수 있습니다.
  • 서브클래스를 만든 후에 제거하기가 어렵습니다.
  • 프라이빗 데이터를 창에 연결하면 비효율적입니다.
  • 서브클래스 체인에서 다음 프로시저를 호출하려면 이전 창 프로시저를 캐스팅하고 호출할 수 없으며 CallWindowProc 함수를 사용하여 호출해야 합니다.

ComCtl32.dll 버전 6을 사용하는 서브클래싱 컨트롤

참고

ComCtl32.dll 버전 6은 유니코드 전용입니다. ComCtl32.dll 버전 6에서 지원하는 일반적인 컨트롤은 ANSI 창 프로시저를 사용하여 서브클래싱하거나 슈퍼클래싱해서는 안 됩니다.

 

ComCtl32.dll 버전 6에는 서브클래스를 더 쉽게 만들고 앞에서 설명한 단점을 제거하는 네 가지 함수가 포함되어 있습니다. 새 함수는 여러 참조 데이터 세트와 관련된 관리를 캡슐화하므로 개발자는 서브클래스 관리가 아닌 프로그래밍 기능에 집중할 수 있습니다. 서브클래싱 함수는 다음과 같습니다.

SetWindowSubclass

이 함수는 처음에 창을 서브클래싱하는 데 사용됩니다. 각 서브클래스는 pfnSubclass 및 해당 uIdSubclass의 주소로 고유하게 식별됩니다. 둘 다 SetWindowSubclass 함수의 매개 변수입니다. 여러 서브클래스에서 동일한 서브클래스 프로시저를 공유할 수 있으며 ID는 각 호출을 식별할 수 있습니다. 참조 데이터를 변경하려면 SetWindowSubclass를 후속 호출할 수 있습니다. 중요한 장점은 각 서브클래스 인스턴스에 고유한 참조 데이터가 있다는 점입니다.

서브클래스 프로시저 선언에는 서브클래스 ID와 참조 데이터라는 두 개의 추가 데이터가 포함되므로 일반 창 프로시저와 약간 다릅니다. 다음 함수 선언의 마지막 두 매개 변수가 이를 보여 줍니다.

LRESULT CALLBACK MyWndProc (HWND hWnd, UINT msg,
WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass,
DWORD_PTR dwRefData);

새 창 프로시저에서 메시지를 받을 때마다 서브클래스 ID 및 참조 데이터가 포함됩니다.

참고

유니코드가 전처리기 정의로 지정되지 않은 경우에도 프로시저에 전달된 모든 문자열은 유니코드 문자열입니다.

 

다음 예제에서는 서브클래싱된 컨트롤에 대한 창 프로시저의 기본 구현을 보여 줍니다.

LRESULT CALLBACK OwnerDrawButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam,
                               LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    switch (uMsg)
    {
    case WM_PAINT:
        .
        .
        .
        return TRUE;
    // Other cases...
    } 
    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

창 프로시저는 다음 예제와 같이 대화 상자 프로시저의 WM_INITDIALOG 처리기에서 컨트롤에 연결할 수 있습니다.

case WM_INITDIALOG:
{
    HWND button = GetDlgItem(hDlg, IDC_OWNERDRAWBUTTON);
    SetWindowSubclass(button, OwnerDrawButtonProc, 0, 0);
    return TRUE;
}

GetWindowSubclass

이 함수는 서브클래스에 대한 정보를 검색합니다. 예를 들어 GetWindowSubclass를 사용하여 참조 데이터에 액세스할 수 있습니다.

RemoveWindowSubclass

이 함수는 서브클래스를 제거합니다. RemoveWindowSubclassSetWindowSubclass와 함께 사용하면 서브클래스를 동적으로 추가하고 제거할 수 있습니다.

DefSubclassProc

DefSubclassProc 함수는 서브클래스 체인에서 다음 처리기를 호출합니다. 또한 이 함수는 적절한 ID와 참조 데이터를 검색하고 다음 창 프로시저에 정보를 전달합니다.