TN039: MFC/OLE 자동화 구현

참고 항목

다음 기술 노트는 온라인 설명서에 먼저 포함되어 있었으므로 업데이트되지 않았습니다. 따라서 일부 절차 및 항목은 만료되거나 올바르지 않을 수 있습니다. 최신 정보를 보려면 온라인 설명서 색인에서 관심 있는 항목을 검색하는 것이 좋습니다.

OLE IDispatch 인터페이스 개요

IDispatch 인터페이스는 애플리케이션이 Visual BASIC 또는 다른 언어와 같은 다른 애플리케이션에서 애플리케이션의 기능을 사용할 수 있도록 메서드 및 속성을 노출하는 방법입니다. 이 인터페이스의 가장 중요한 부분은 함수입니다 IDispatch::Invoke . MFC는 "디스패치 맵"을 사용하여 .를 구현 IDispatch::Invoke합니다. 디스패치 맵은 개체의 CCmdTarget속성을 직접 조작하거나 개체 내에서 멤버 함수를 호출하여 요청을 충족 IDispatch::Invoke 할 수 있도록 파생 클래스의 레이아웃 또는 "셰이프"에 대한 MFC 구현 정보를 제공합니다.

대부분의 경우 ClassWizard와 MFC는 협력하여 애플리케이션 프로그래머에서 OLE 자동화의 세부 정보를 대부분 숨깁니다. 프로그래머가 애플리케이션에 노출하는 실제 기능에 집중하며 기본 배관에 대해 걱정할 필요가 없습니다.

그러나 MFC가 백그라운드에서 수행하는 작업을 이해해야 하는 경우가 있습니다. 이 참고에서는 프레임워크가 멤버 함수 및 속성에 DISPID를 할당하는 방법을 설명합니다. 애플리케이션의 개체에 대한 "형식 라이브러리"를 만들 때와 같이 ID를 알아야 하는 경우에만 MFC에서 DISPID를 할당하는 데 사용하는 알고리즘에 대한 지식이 필요합니다.

MFC DISPID 할당

자동화의 최종 사용자(예: Visual Basic 사용자)는 코드에서 자동화 사용 속성 및 메서드의 실제 이름(예: obj)을 확인합니다. ShowWindow) 구현 IDispatch::Invoke 은 실제 이름을 받지 않습니다. 최적화를 위해 액세스할 메서드 또는 속성을 설명하는 32비트 "매직 쿠키"인 DISPID를 받습니다. 이러한 DISPID 값은 구현에서 호출IDispatch::GetIDsOfNamesIDispatch 다른 메서드를 통해 반환됩니다. 자동화 클라이언트 애플리케이션은 액세스하려는 각 멤버 또는 속성에 대해 한 번 호출 GetIDsOfNames 하고 나중에 호출할 수 있도록 캐시합니다 IDispatch::Invoke. 이렇게 하면 값비싼 문자열 조회는 호출당 한 번이 아니라 개체 사용당 IDispatch::Invoke 한 번만 수행됩니다.

MFC는 다음 두 가지 사항에 따라 각 메서드 및 속성에 대한 DISPID를 결정합니다.

  • 디스패치 맵의 맨 위에서의 거리(상대 1개)

  • 가장 많이 파생된 클래스에서 디스패치 맵의 거리(0 상대)

DISPID는 두 부분으로 나뉩니다. DISPIDLOWORD에는 디스패치 맵의 맨 위에서의 거리인 첫 번째 구성 요소가 포함됩니다. HIWORD에는 가장 파생된 클래스와의 거리가 포함됩니다. 예시:

class CDispPoint : public CCmdTarget
{
public:
    short m_x, m_y;
    // ...
    DECLARE_DISPATCH_MAP()
    // ...
};

class CDisp3DPoint : public CDispPoint
{
public:
    short m_z;
    // ...
    DECLARE_DISPATCH_MAP()
    // ...
};

BEGIN_DISPATCH_MAP(CDispPoint, CCmdTarget)
    DISP_PROPERTY(CDispPoint, "x", m_x, VT_I2)
    DISP_PROPERTY(CDispPoint, "y", m_y, VT_I2)
END_DISPATCH_MAP()

BEGIN_DISPATCH_MAP(CDisp3DPoint, CDispPoint)
    DISP_PROPERTY(CDisp3DPoint, "z", m_z, VT_I2)
END_DISPATCH_MAP()

볼 수 있듯이 OLE 자동화 인터페이스를 노출하는 두 가지 클래스가 있습니다. 이러한 클래스 중 하나는 다른 클래스에서 파생되므로 OLE 자동화 부분(이 경우 "x" 및 "y" 속성)을 포함하여 기본 클래스의 기능을 활용합니다.

MFC는 다음과 같이 CDispPoint 클래스에 대한 DISPID를 생성합니다.

property X    (DISPID)0x00000001
property Y    (DISPID)0x00000002

속성이 기본 클래스에 없으므로 DISPIDHIWORD는 항상 0입니다(CDispPoint에서 가장 많이 파생된 클래스와의 거리는 0임).

MFC는 다음과 같이 CDisp3DPoint 클래스에 대한 DISPID를 생성합니다.

property Z    (DISPID)0x00000001
property X    (DISPID)0x00010001
property Y    (DISPID)0x00010002

Z 속성은 속성 CDisp3DPoint를 노출하는 클래스에 정의되어 있으므로 HIWORD가 0인 DISPID가 제공됩니다. X 및 Y 속성은 기본 클래스에 정의되므로 DISPIDHIWORD는 1입니다. 이러한 속성이 정의된 클래스는 가장 파생된 클래스에서 파생된 하나의 거리에 있기 때문에.

참고 항목

LOWORD는 명시적 DISPID가 있는 맵에 항목이 있더라도 항상 맵의 DISP_FUNCTIONDISP_PROPERTY 위치에 따라 결정됩니다(_ID 버전의 매크로에 대한 자세한 내용은 다음 섹션 참조).

고급 MFC 디스패치 맵 기능

ClassWizard가 이 Visual C++릴리스에서 지원하지 않는 여러 가지 추가 기능이 있습니다. ClassWizard는 DISP_FUNCTION메서드, DISP_PROPERTY멤버 변수 속성 및 DISP_PROPERTY_EX get/set 멤버 함수 속성을 각각 정의하고 지원합니다. 이러한 기능은 일반적으로 대부분의 자동화 서버를 만드는 데 필요한 모든 기능입니다.

ClassWizard 지원 매크로가 적절DISP_PROPERTY_NOTIFYDISP_PROPERTY_PARAM하지 않은 경우 다음과 같은 추가 매크로를 사용할 수 있습니다.

DISP_PROPERTY_NOTIFY — 매크로 설명

DISP_PROPERTY_NOTIFY(
    theClass,
    pszName,
    memberName,
    pfnAfterSet,
    vtPropType)

매개 변수

theClass
클래스의 이름입니다.

pszName
속성의 외부 이름입니다.

Membername
속성이 저장되는 멤버 변수의 이름입니다.

pfnAfterSet
속성이 변경될 때 호출할 멤버 함수의 이름입니다.

vtPropType
속성의 형식을 지정하는 값입니다.

설명

이 매크로는 추가 인수를 허용한다는 점을 제외하고 DISP_PROPERTY 비슷합니다. 추가 인수인 pfnAfterSet 은 아무 것도 반환하지 않고 매개 변수인 'void OnPropertyNotify()'를 사용하지 않는 멤버 함수여야 합니다. 멤버 변수가 수정된 후에 호출됩니다.

DISP_PROPERTY_PARAM — 매크로 설명

DISP_PROPERTY_PARAM(
    theClass,
    pszName,
    pfnGet,
    pfnSet,
    vtPropType,
    vtsParams)

매개 변수

theClass
클래스의 이름입니다.

pszName
속성의 외부 이름입니다.

memberGet
속성을 가져오는 데 사용되는 멤버 함수의 이름입니다.

Memberset
속성을 설정하는 데 사용되는 멤버 함수의 이름입니다.

vtPropType
속성의 형식을 지정하는 값입니다.

vtsParams
각 매개 변수에 대한 공백으로 구분된 VTS_ 문자열입니다.

설명

DISP_PROPERTY_EX 매크로와 마찬가지로 이 매크로는 별도의 Get 및 Set 멤버 함수를 사용하여 액세스하는 속성을 정의합니다. 그러나 이 매크로를 사용하면 속성에 대한 매개 변수 목록을 지정할 수 있습니다. 다른 방법으로 인덱싱되거나 매개 변수화된 속성을 구현하는 데 유용합니다. 매개 변수는 항상 먼저 배치되고 속성에 대한 새 값이 잇습니다. 예시:

DISP_PROPERTY_PARAM(CMyObject, "item", GetItem, SetItem, VT_DISPATCH, VTS_I2 VTS_I2)

는 멤버 함수 가져오기 및 설정에 해당합니다.

LPDISPATCH CMyObject::GetItem(short row, short col)
void CMyObject::SetItem(short row, short col, LPDISPATCH newValue)

DISP_XXXX_ID — 매크로 설명

DISP_FUNCTION_ID(
    theClass,
    pszName,
    dispid,
    pfnMember,
    vtRetVal,
    vtsParams)
DISP_PROPERTY_ID(
    theClass,
    pszName,
    dispid,
    memberName,
    vtPropType)
DISP_PROPERTY_NOTIFY_ID(
    theClass,
    pszName,
    dispid,
    memberName,
    pfnAfterSet,
    vtPropType)
DISP_PROPERTY_EX_ID(
    theClass,
    pszName,
    dispid,
    pfnGet,
    pfnSet,
    vtPropType)
DISP_PROPERTY_PARAM_ID(
    theClass,
    pszName,
    dispid,
    pfnGet,
    pfnSet,
    vtPropType,
    vtsParams)

매개 변수

theClass
클래스의 이름입니다.

pszName
속성의 외부 이름입니다.

dispid
속성 또는 메서드에 대한 고정 DISPID입니다.

pfnGet
속성을 가져오는 데 사용되는 멤버 함수의 이름입니다.

pfnSet
속성을 설정하는 데 사용되는 멤버 함수의 이름입니다.

Membername
속성에 매핑할 멤버 변수의 이름입니다.

vtPropType
속성의 형식을 지정하는 값입니다.

vtsParams
각 매개 변수에 대한 공백으로 구분된 VTS_ 문자열입니다.

설명

이러한 매크로를 사용하면 MFC가 자동으로 할당하도록 하는 대신 DISPID 를 지정할 수 있습니다. 이러한 고급 매크로는 ID가 매크로 이름(예: DISP_PROPERTY_ID)에 추가되고 ID가 pszName 매개 변수 바로 다음에 지정된 매개 변수에 의해 결정된다는 점을 제외하고 이름이 동일합니다. AFXDISP를 참조하세요. 이러한 매크로에 대한 자세한 내용은 H입니다. _ID 항목은 디스패치 맵의 끝에 배치해야 합니다. 매크로의 _ID 버전과 동일한 방식으로 자동 DISPID 생성에 영향을 줍니다(DISPID는 위치에 따라 결정됨). 예시:

BEGIN_DISPATCH_MAP(CDisp3DPoint, CCmdTarget)
    DISP_PROPERTY(CDisp3DPoint, "y", m_y, VT_I2)
    DISP_PROPERTY(CDisp3DPoint, "z", m_z, VT_I2)
    DISP_PROPERTY_ID(CDisp3DPoint, "x", 0x00020003, m_x, VT_I2)
END_DISPATCH_MAP()

MFC는 다음과 같이 CDisp3DPoint 클래스에 대한 DISPID를 생성합니다.

property X    (DISPID)0x00020003
property Y    (DISPID)0x00000002
property Z    (DISPID)0x00000001

고정 DISPID를 지정하면 기본 기존 디스패치 인터페이스와 이전 버전과의 호환성을 확인하거나 특정 시스템 정의 메서드 또는 속성(일반적으로 DISPID_NEWENUM 컬렉션과 같은 부정 DISPID표시됨)을 구현하는 데 유용합니다.

COleClientItem에 대한 IDispatch 인터페이스 검색

대부분의 서버는 OLE 서버 기능과 함께 문서 개체 내에서 자동화를 지원합니다. 이 자동화 인터페이스에 액세스하려면 멤버 변수에 직접 액세스 COleClientItem::m_lpObject 해야 합니다. 아래 코드는 에서 파생된 COleClientItem개체에 대한 인터페이스를 검색 IDispatch 합니다. 이 기능이 필요한 경우 아래 코드를 애플리케이션에 포함할 수 있습니다.

LPDISPATCH CMyClientItem::GetIDispatch()
{
    ASSERT_VALID(this);
    ASSERT(m_lpObject != NULL);

    LPUNKNOWN lpUnk = m_lpObject;

    Run();      // must be running

    LPOLELINK lpOleLink = NULL;
    if (m_lpObject->QueryInterface(IID_IOleLink,
        (LPVOID FAR*)&lpOleLink) == NOERROR)
    {
        ASSERT(lpOleLink != NULL);
        lpUnk = NULL;
        if (lpOleLink->GetBoundSource(&lpUnk) != NOERROR)
        {
            TRACE0("Warning: Link is not connected!\n");
            lpOleLink->Release();
            return NULL;
        }
        ASSERT(lpUnk != NULL);
    }

    LPDISPATCH lpDispatch = NULL;
    if (lpUnk->QueryInterface(IID_IDispatch, &lpDispatch) != NOERROR)
    {
        TRACE0("Warning: does not support IDispatch!\n");
        return NULL;
    }

    ASSERT(lpDispatch != NULL);
    return lpDispatch;
}

그런 다음 이 함수에서 반환된 디스패치 인터페이스를 직접 사용하거나 형식 안전 액세스를 위해 연결 COleDispatchDriver 될 수 있습니다. 직접 사용하는 경우 포인터를 사용할 때 해당 멤버를 Release 호출해야 합니다( COleDispatchDriver 소멸자가 기본적으로 이 작업을 수행).

참고 항목

번호별 기술 참고 사항
범주별 기술 참고 사항