UI 요소의 이름이 올바르게 지정되었는지 확인

이 항목에서는 Microsoft Active Accessibility가 IAccessible Name 속성을 통해 클라이언트 애플리케이션에 이름을 정확하게 노출할 수 있도록 Microsoft Win32 애플리케이션에서 UI 요소의 이름을 지정하는 올바른 방법을 설명합니다.

이 섹션의 정보는 Microsoft Active Accessibility에만 적용됩니다. Microsoft UI 자동화 사용하는 애플리케이션이나 HTML, DHTML(동적 HTML) 또는 XML과 같은 태그 언어를 기반으로 하는 애플리케이션에는 적용되지 않습니다.

개요

Microsoft Active Accessibility에서 애플리케이션의 각 UI 요소는 IAccessible 인터페이스를 노출하는 개체로 표시됩니다. 클라이언트 애플리케이션은 IAccessible 인터페이스의 속성과 메서드를 사용하여 UI 요소와 상호 작용하고 해당 요소에 대한 정보를 검색합니다. IAccessible 인터페이스에서 노출하는 가장 중요한 속성 중 하나는 Name 속성입니다. 클라이언트 애플리케이션은 Name 속성을 사용하여 사용자에게 UI 요소를 찾거나 식별하거나 알릴 수 있습니다. Microsoft Active Accessibility에서 특정 UI 요소의 Name 속성을 제대로 노출할 수 없는 경우 클라이언트 애플리케이션은 해당 UI 요소를 사용자에게 표시할 수 없으며 UI 요소는 장애가 있는 사용자에게 액세스할 수 없습니다.

잘못된 이름 지정으로 인해 문제가 발생하는 방법

UI 요소의 잘못된 이름 지정으로 인한 문제를 설명하기 위해 다음 그림에 표시된 이름 입력 양식을 고려합니다.

이름과 성을 입력하기 위한 간단한 양식 그림

양식의 UI 요소는 괜찮아 보이지만 프로그래밍 방식 구현이 잘못되었습니다. 화면 읽기 프로그램과 같은 Microsoft Active Accessibility 클라이언트의 경우 맨 위 편집 컨트롤의 Name 속성 은 "성:"이고 아래쪽 편집 컨트롤의 Name 속성은 빈 문자열("")입니다. 사용자가 이름을 입력해야 하지만 화면 읽기 프로그램에서는 위쪽 편집 컨트롤을 "성"으로 읽습니다. 화면 읽기 프로그램은 두 번째 편집 컨트롤을 "이름 없음"으로 읽으므로 사용자는 두 번째 편집 컨트롤에 무엇을 입력할지 전혀 알 수 없습니다. 화면 읽기 프로그램은 사용자가 이 간단한 형식으로 데이터를 입력하는 데 도움을 줄 수 없습니다.

폼의 또 다른 문제는 편집 컨트롤 중 하나에 바로 가기 키가 할당되지 않는다는 것입니다. 사용자는 탭에서 컨트롤을 사용하거나 마우스를 사용해야 합니다.

다음 섹션에서는 이러한 문제의 원인을 설명하고 문제를 해결하기 위한 지침을 제공합니다.

MSAA에서 Name 속성을 가져오는 방법

Microsoft Active Accessibility는 UI 요소의 형식에 따라 다른 위치에서 Name 속성 문자열을 가져옵니다. 연결된 창 텍스트가 있는 대부분의 UI 요소에서 Microsoft Active Accessibility는 창 텍스트를 Name 속성 문자열로 사용합니다. 이러한 유형의 UI 요소의 예로는 단추, 메뉴 항목 및 도구 설명과 같은 컨트롤이 있습니다.

다음 컨트롤의 경우 Microsoft Active Accessibility는 창 텍스트를 무시하고 대신 탭 순서로 컨트롤 바로 앞에 정적 텍스트 레이블(또는 그룹 상자 레이블)을 찾습니다.

  • 콤보 상자
  • 날짜 및 시간 선택기
  • 편집 및 서식 있는 편집 컨트롤
  • IP 주소 컨트롤
  • 목록 상자
  • 목록 보기
  • 진행률 표시줄
  • 스크롤 막대
  • SS_ICON 또는 SS_BITMAP 스타일이 있는 정적 컨트롤
  • 트랙바
  • 트리 뷰

앞의 컨트롤에 정적 텍스트 레이블이 포함되지 않거나 레이블이 올바르게 구현되지 않은 경우 Microsoft Active Accessibility는 클라이언트 애플리케이션에 올바른 Name 속성을 제공할 수 없습니다.

대부분의 이전 컨트롤에는 실제로 연결된 창 텍스트가 있습니다. 리소스 편집기에서는 "edit1" 또는 "listbox3"과 같은 제네릭 문자열로 구성된 창 텍스트를 자동으로 생성합니다. 개발자는 생성된 창 텍스트를 더 의미 있는 텍스트로 바꿀 수 있지만 대부분은 그렇지 않습니다. 생성된 창 텍스트는 사용자에게 의미가 없으므로 Microsoft Active Accessibility는 이를 무시하고 함께 제공되는 정적 텍스트 레이블을 대신 사용합니다.

명명 문제를 찾고 수정하는 방법

잘못된 이름 지정으로 인해 문제가 발생하는 방법에 표시된 이름 입력 양식에서 문제의 원인은 컨트롤의 탭 순서가 올바르지 않은 것입니다. 검사와 같은 테스트 도구를 사용하여 UI를 검사하면 개체 계층 구조에 문제가 표시됩니다. 다음 스크린샷은 검사에 표시되는 이름 입력 양식의 끊어진 개체 계층 구조를 보여 줍니다.

이름 입력 양식의 잘못된 개체 계층 구조를 보여 주는 검사 도구의 스크린샷

이전 스크린샷에서 개체 계층 구조는 이름 입력 양식의 사용자 인터페이스에 표시되므로 컨트롤의 구조와 일치하지 않습니다. 또한 검사에서 마지막 항목에 잘못된 이름을 할당했습니다(이름을 입력하기 위한 편집 컨트롤이며 이름이 "이름:"이어야 함). 마지막으로, 검사에서 마지막 항목의 이름을 찾을 수 없습니다(성을 입력하기 위한 편집 컨트롤이며 이름이 "성:"이어야 함).

다음 예제에서는 이름 입력 양식에 대한 리소스 파일의 내용을 보여 줍니다. 탭 순서는 사용자 인터페이스에 표시되는 컨트롤의 논리적 구조와 일치하지 않습니다. 또한 두 편집 컨트롤에 대한 바로 가기 키가 지정되지 않습니다.

IDD_INPUTNAME DIALOGEX 22, 17, 312, 118
STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
CAPTION "Enter your name"
FONT 8, "System", 0, 0, 0x0
BEGIN
    DEFPUSHBUTTON   "OK",IDOK,179,35,30,11,WS_GROUP
    LTEXT           "First Name:",IDC_STATIC,8,16,43,8
    LTEXT           "Last Name:",IDC_STATIC,8,33,43,8
    EDITTEXT        IDC_EDIT1,53,15,120,12,ES_AUTOHSCROLL
    EDITTEXT        IDC_EDIT2,53,34,120,12,ES_AUTOHSCROLL
END

이름 입력 양식의 문제를 해결하려면 리소스(.rc) 파일을 편집하여 바로 가기 키를 지정하고 컨트롤을 다음 순서로 배치해야 합니다.

  1. "&First Name:" 정적 텍스트 레이블입니다.
  2. 이름(IDC_EDIT1)을 입력하기 위한 편집 컨트롤입니다.
  3. "&Last Name:" 정적 텍스트 레이블입니다.
  4. 성을 입력하기 위한 편집 컨트롤(IDC_EDIT2)입니다.
  5. "확인" 기본 푸시 단추입니다.

다음 예제에서는 이름 입력 양식에 대해 수정된 리소스 파일을 보여 냅니다.

IDD_INPUTNAME DIALOGEX 22, 17, 312, 118
STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
CAPTION "Enter your name"
FONT 8, "System", 0, 0, 0x0
BEGIN
    LTEXT           "&First Name:",IDC_STATIC,8,16,43,8
    EDITTEXT        IDC_EDIT1,53,15,120,12,ES_AUTOHSCROLL
    LTEXT           "&Last Name:",IDC_STATIC,8,33,43,8
    EDITTEXT        IDC_EDIT2,53,34,120,12,ES_AUTOHSCROLL
    DEFPUSHBUTTON   "OK",IDOK,179,35,30,11,WS_GROUP
END

리소스 파일을 수정하려면 파일을 직접 편집하거나 Microsoft Visual Studio에서 탭 순서 도구를 사용할 수 있습니다. Ctrl+D를 누르거나 서식 메뉴에서 탭 순서를 선택하여 Visual Studio의 탭 순서 도구에 액세스할 수 있습니다.

애플리케이션을 수정하고 다시 빌드한 후 이름 입력 양식의 UI는 이전과 동일하게 표시됩니다. 그러나 이제 Microsoft Active Accessibility는 클라이언트 애플리케이션에 올바른 이름 속성을 제공하고 사용자가 ALT+F 또는 ALT+L 바로 가기 키를 누를 때 포커스를 올바르게 설정합니다. 또한 다음 스크린샷과 같이 검사 시 올바른 개체 계층 구조가 표시됩니다.

이름 입력 양식의 올바른 개체 계층 구조를 보여 주는 액세스 가능한 탐색기 도구의 스크린샷

트랙바 이름을 올바르게 지정하는 방법

트랙바(또는 슬라이더)를 정의할 때 트랙바의 기본 정적 텍스트 레이블이 트랙바 앞에 나타나고 최소 및 최대 범위에 대한 정적 텍스트 레이블이 트랙바 뒤로 표시되는지 확인합니다. Microsoft Active Accessibility는 컨트롤 바로 앞에 있는 정적 텍스트 레이블을 컨트롤의 Name 속성 으로 사용합니다. 기본 정적 텍스트 레이블을 트랙바 바로 앞에 배치하고 그 다음에 다른 레이블을 배치하면 Microsoft Active Accessibility에서 클라이언트에 올바른 Name 속성을 제공합니다.

다음 그림에서는 "Speed"라는 기본 정적 텍스트 레이블이 있는 일반적인 트랙바와 최소("min") 및 최대("max") 범위에 대한 정적 텍스트 레이블을 보여 줍니다.

최소 및 최대 범위에 대한 기본 레이블과 레이블이 있는 트랙바 컨트롤의 그림

다음 예제에서는 리소스 파일에서 트랙바 및 해당 정적 텍스트 레이블을 정의하는 올바른 방법을 보여 줍니다.

BEGIN
    ...

    LTEXT           "&Speed",IDC_STATIC,47,20,43,8
    CONTROL         "",IDC_SLIDER1,"msctls_trackbar32",
                    TBS_AUTOTICKS | TBS_BOTH | WS_TABSTOP,
                    32,32,62,23
    LTEXT           "min",IDC_STATIC,16,37,15,8
    LTEXT           "max",IDC_STATIC,94,38,43,8

    ...
END

보이지 않는 레이블을 사용하여 컨트롤 이름을 지정하는 방법

모든 컨트롤에 대해 표시되는 레이블이 항상 가능하거나 바람직하지는 않습니다. 예를 들어 레이블을 추가하면 UI 모양이 바람직하지 않은 변경이 발생할 수 있습니다. 이 경우 보이지 않는 레이블을 사용할 수 있습니다. Microsoft Active Accessibility는 보이지 않는 레이블과 연결된 텍스트를 계속 선택하지만 레이블이 시각적 UI에 표시되거나 방해되지 않습니다.

표시되는 레이블과 마찬가지로 보이지 않는 레이블은 탭 순서에서 컨트롤 바로 앞에 와야 합니다. 리소스 파일(.rc)에서 레이블을 보이지 않게 하려면 정적 텍스트 컨트롤의 스타일 부분을 추가하거나 |~WS_VISIBLE 스타일 부분에 추가 NOT WS_VISIBLE 합니다. Visual Studio에서 리소스 편집기를 사용하는 경우 Visible 속성을 False로 설정할 수 있습니다.

직접 주석을 사용하여 Name 속성을 지정하는 방법

Microsoft Active Accessibility 런타임 구성 요소에 포함된 기본 프록시인 Oleacc.dll 모든 표준 Windows 컨트롤에 대해 IAccessible 개체를 자동으로 제공합니다. 표준 Windows 컨트롤을 사용자 지정하는 경우 기본 프록시는 사용자 지정된 컨트롤에 대한 모든 IAccessible 속성을 정확하게 제공하기 위해 최선을 다합니다. 사용자 지정된 컨트롤을 철저히 테스트하여 기본 프록시가 정확하고 완전한 속성 값을 제공하는지 확인해야 합니다. 테스트 결과 정확하지 않거나 불완전한 속성 값이 표시되면 직접 주석이라는 동적 주석 기술을 사용하여 올바른 속성 값을 제공하고 누락된 값을 추가할 수 있습니다.

동적 주석은 Microsoft Active Accessibility 프록시에서 지원하는 컨트롤에만 적용되는 것이 아닙니다. 자체 IAccessible 구현을 제공하는 모든 컨트롤에 대한 속성을 수정하거나 제공하는 데 사용할 수도 있습니다 .

이 섹션에서는 직접 주석을 사용하여 컨트롤에 대한 IAccessible 개체의 Name 속성에 올바른 값을 제공하는 방법을 중시합니다. 직접 주석을 사용하여 다른 속성 값도 제공할 수 있습니다. 또한 직접 주석 외에 다른 동적 주석 기술을 사용할 수 있으며 동적 주석 API의 기능과 기능은 이 섹션에 설명된 기능보다 훨씬 확장됩니다. 동적 주석에 대한 자세한 내용은 동적 주석 API를 참조하세요.

Name 속성에 주석을 추가하기 위한 단계

직접 주석을 사용하여 컨트롤의 Name 속성을 변경하려면 다음 단계를 수행합니다.

  1. 다음 헤더 파일을 포함합니다.

    • Initguid.h
    • Oleacc.h

    참고 항목

    GUID를 정의하려면 Oleacc.h 앞에 Initguid.h를 동일한 파일에 포함해야 합니다.

     

  2. 일반적으로 애플리케이션 초기화 프로세스 중에 CoInitializeEx 함수를 호출하여 COM(구성 요소 개체 모델) 라이브러리를 초기화합니다.

  3. 대상 컨트롤을 만든 직후(일반적으로 WM_INITDIALOG 메시지 중에) 주석 관리자의 인스턴스를 만들고 IAccPropServices 포인터에 대한 포인터를 가져옵니다.

  4. IAccPropServices::SetHwndPropStr 메서드를 사용하여 대상 컨트롤의 Name 속성에 주석을 추가합니다.

  5. IAccPropServices 포인터를 해제합니다.

  6. 대상 컨트롤이 제거되기 전에(일반적으로 WM_DESTROY 메시지를 처리할 때) 주석 관리자의 인스턴스를 만들고 해당 IAccPropServices 인터페이스에 대한 포인터를 가져옵니다.

  7. IAccPropServices::ClearHwndProps 메서드를 사용하여 대상 컨트롤에서 Name 속성 주석을 지웁니다.

  8. IAccPropServices 포인터를 해제합니다.

  9. 애플리케이션이 종료되기 전에(일반적으로 WM_DESTROY 메시지를 처리하는 동안) CoUninitialize 함수를 호출하여 COM 라이브러리를 해제합니다.

IAccPropServices::SetHwndPropStr 함수는 5개의 매개 변수를 사용합니다. 처음 세 개(hwnd, idObjectidChild)가 결합하여 컨트롤을 식별합니다. 네 번째 매개 변수 idProp은 변경할 속성의 식별자를 지정합니다. Name 속성을 변경하려면 idPropPROPID_ACC_NAME 설정합니다. (직접 주석을 통해 설정할 수 있는 다른 속성 목록은 다음을 참조 하세요.직접 주석을 사용합니다.) SetHwndPropStr str마지막 매개 변수는 Name 속성으로 사용할 새 문자열입니다.

Name 속성에 주석을 추가한 예제

다음 예제 코드에서는 직접 주석을 사용하여 컨트롤에 대한 IAccessible 개체의 Name 속성을 변경하는 방법을 보여 줍니다. 작업을 단순하게 유지하기 위해 이 예제에서는 하드 코딩된 문자열("새 컨트롤 이름")을 사용하여 Name 속성을 설정합니다. 하드 코딩된 문자열은 지역화할 수 없으므로 애플리케이션의 최종 버전에서 사용하면 안 됩니다. 대신 항상 리소스 파일에서 문자열을 로드합니다. 또한 이 예제에서는 CoInitializeExCoUninitialize 함수에 대한 호출을 표시하지 않습니다.

#include <initguid.h>
#include <oleacc.h>

// AnnotateControlName - Uses direct annotation to change the Name property 
// of the IAccessible object for a control.
//
// hDlg - Handle of the dialog box that contains the control.
// hwndCtl - Handle of the control whose Name property is to be changed.
HRESULT AnnotateControlName(HWND hDlg, HWND hwndCtl)
{
    HRESULT hr;        

    IAccPropServices *pAccPropSvc = NULL;  

    // Create an instance of the annotation manager and retrieve the 
    // IAccPropServices pointer.
    hr = CoCreateInstance(CLSID_AccPropServices, NULL, CLSCTX_SERVER, 
        IID_IAccPropServices, (void **) &pAccPropSvc);

    if (hr != S_OK || pAccPropSvc == NULL)
        return hr;

    // Set the Name property for the control.
    // Note: A hard-coded string is used here to keep the example simple.
    // Always use localizable string resources in your applications. 
    hr = pAccPropSvc->SetHwndPropStr(hwndCtl, OBJID_CLIENT, CHILDID_SELF, 
        PROPID_ACC_NAME, L"New Control Name");

    pAccPropSvc->Release();
    
    return hr;
}

// RemoveAnnotatedNameFromControl - Removes the annotated name from the 
// Name property of the IAccessible object for a control.
//
// hDlg - Handle of the dialog box that contains the control.
// hwndCtl - Handle of the control whose annotated name is to be removed.
HRESULT RemoveAnnotatedNameFromControl(HWND hDlg, HWND hwndCtl)
{
    HRESULT hr;

    IAccPropServices *pAccPropSvc = NULL;

    // Create an instance of the annotation manager and retrieve the 
    // IAccPropServices pointer.
    hr = CoCreateInstance(CLSID_AccPropServices, NULL, CLSCTX_SERVER, 
        IID_IAccPropServices, (void **) &pAccPropSvc);

    if (hr != S_OK || pAccPropSvc == NULL)
        return hr;

    // Remove the annotated name from the Name property for the control.
    MSAAPROPID propid = PROPID_ACC_NAME;
    hr = pAccPropSvc->ClearHwndProps(hwndCtl, OBJID_CLIENT, CHILDID_SELF, 
        &propid, 1);

    // Release the annotation manager.
    pAccPropSvc->Release();

    return hr;
}