속성(공통 요소)

TSF(Text Services Framework)는 메타데이터를 텍스트 범위와 연결하는 속성을 제공합니다. 이러한 속성은 굵은 텍스트, 텍스트의 언어 식별자 및 음성 텍스트 서비스의 텍스트와 연결된 오디오 데이터와 같은 텍스트 서비스에서 제공하는 원시 데이터와 같은 특성을 표시하지만 이에 국한되지 않습니다.

다음 예제에서는 가능한 값이 빨강(R), 녹색(G) 또는 파랑(B)인 가상 텍스트 색 속성을 보는 방법을 보여 줍니다.

COLOR:      RR      GGGGGGGG
TEXT:  this is some colored text

다른 형식의 속성은 겹칠 수 있습니다. 예를 들어 이전 예제를 사용하여 굵게(B) 또는 기울임꼴(I)일 수 있는 텍스트 특성을 추가합니다.

ATTRIB:BBBBBBB      IIIIIIIIIIII
COLOR:      RR      GGGGGGGG
TEXT:  this is some colored text

텍스트 "this"는 굵게, "is"는 굵게와 빨간색이고, "some"는 정상적으로 표시되고, "colored"는 녹색이고 기울임꼴로 표시되고 "text"는 기울임꼴로 표시됩니다.

동일한 형식의 속성은 겹칠 수 없습니다. 예를 들어 "is" 및 "colored"에는 동일한 형식의 값이 겹치므로 다음 상황이 허용되지 않습니다.

COLOR: GGG GGGG RRR BBBBGGG     
COLOR:      RR      GGGGGGGG
TEXT:  this is some colored text

속성 형식

TSF는 세 가지 유형의 속성을 정의합니다.

속성 유형 설명
정적 정적 속성 개체는 속성 데이터를 텍스트와 함께 저장합니다. 또한 속성이 적용되는 각 범위에 대한 텍스트 정보 범위도 저장합니다. ITfReadOnlyProperty::GetType은 GUID_TFCAT_PROPSTYLE_STATIC 범주를 반환합니다.
Static-Compact 정적 압축 속성 개체는 정적 압축 속성이 범위 데이터를 저장하지 않는다는 점을 제외하고 정적 속성 개체와 동일합니다. 정적 압축 속성이 적용되는 범위를 요청하면 인접 속성의 각 그룹에 대해 범위가 만들어집니다. 정적 압축 속성은 문자별로 속성을 저장하는 가장 효율적인 방법입니다. ITfReadOnlyProperty::GetType은 GUID_TFCAT_PROPSTYLE_STATICCOMPACT 범주를 반환합니다.
사용자 지정 사용자 지정 속성 개체는 속성이 적용되는 각 범위에 대한 범위 정보를 저장합니다. 그러나 속성에 대한 실제 데이터는 저장하지 않습니다. 대신 사용자 지정 속성은 ITfPropertyStore 개체를 저장합니다. TSF 관리자는 이 개체를 사용하여 속성 데이터에 액세스하고 유지 관리합니다. ITfReadOnlyProperty::GetType은 GUID_TFCAT_PROPSTYLE_CUSTOM 범주를 반환합니다.

 

속성 작업

속성 값 및 특성은 ITfReadOnlyProperty 인터페이스를 사용하여 가져오고 ITfProperty 인터페이스를 사용하여 수정됩니다.

특정 속성 형식이 필요한 경우 ITfContext::GetProperty 가 사용됩니다. ITfContext::GetProperty에는 가져올 속성을 식별하는 GUID가 필요합니다. TSF는 사용되는 미리 정의된 속성 식별자 집합을 정의하거나 텍스트 서비스에서 자체 속성 식별자를 정의할 수 있습니다. 사용자 지정 속성을 사용하는 경우 속성 공급자는 속성 GUID 및 가져온 데이터의 형식을 게시해야 합니다.

예를 들어 텍스트 범위의 소유자에 대한 CLSID를 가져오려면 ITfContext::GetProperty를 호출하여 속성 개체를 가져오고, ITfProperty::FindRange를 호출하여 속성을 완전히 포함하는 범위를 가져온 다음, ITfReadOnlyProperty::GetValue를 호출하여 텍스트를 소유하는 텍스트 서비스의 CLSID를 나타내는 TfGuidAtom 가져옵니다. 다음 예제에서는 컨텍스트, 범위 및 편집 쿠키가 지정된 경우 텍스트를 소유하는 텍스트 서비스의 CLSID를 가져오는 함수를 보여 줍니다.

HRESULT GetTextOwner(   ITfContext *pContext, 
                        ITfRange *pRange, 
                        TfEditCookie ec, 
                        CLSID *pclsidOwner)
{
    HRESULT     hr;
    ITfProperty *pProp;

    *pclsidOwner = GUID_NULL;

    hr = pContext->GetProperty(GUID_PROP_TEXTOWNER, &pProp);
    if(S_OK == hr)
    {
        ITfRange    *pPropRange;

        hr = pProp->FindRange(ec, pRange, &pPropRange, TF_ANCHOR_START);
        if(S_OK == hr)
        {
            VARIANT var;

            VariantInit(&var);
            hr = pProp->GetValue(ec, pPropRange, &var);
            if(S_OK == hr)
            {
                if(VT_I4 == var.vt)
                {
                    /*
                    var.lVal is a TfGuidAtom that represents the CLSID of the 
                    text owner. Use ITfCategoryMgr to obtain the CLSID from 
                    the TfGuidAtom.
                    */
                    ITfCategoryMgr  *pCatMgr;

                    hr = CoCreateInstance(  CLSID_TF_CategoryMgr,
                                            NULL, 
                                            CLSCTX_INPROC_SERVER, 
                                            IID_ITfCategoryMgr, 
                                            (LPVOID*)&pCatMgr);
                    if(SUCCEEDED(hr))
                    {
                        hr = pCatMgr->GetGUID((TfGuidAtom)var.lVal, pclsidOwner);
                        if(SUCCEEDED(hr))
                        {
                            /*
                            *pclsidOwner now contains the CLSID of the text 
                            service that owns the text at the selection.
                            */
                        }

                        pCatMgr->Release();
                    }
                }
                else
                {
                    //Unrecognized VARIANT type 
                    hr = E_FAIL;
                }
                
                VariantClear(&var);
            }
            
            pPropRange->Release();
        }
        
        pProp->Release();
    }

    return hr;
}

ITfContext::EnumProperties에서 IEnumTfProperties 인터페이스를 가져와 속성을 열거할 수도 있습니다.

속성의 영구 스토리지

속성은 애플리케이션에 투명하며 하나 이상의 텍스트 서비스에서 사용되는 경우가 많습니다. 파일에 저장할 때와 같은 속성 데이터를 유지하려면 애플리케이션이 저장될 때 속성 데이터를 직렬화하고 데이터가 복원될 때 속성 데이터를 비직렬화해야 합니다. 이 경우 애플리케이션은 개별 속성에 관심이 없어야 하지만 컨텍스트의 모든 속성을 열거하고 저장해야 합니다.

속성 데이터를 저장할 때 애플리케이션은 다음 단계를 수행해야 합니다.

  1. ITfContext::EnumProperties를 호출하여 속성 열거자를 가져옵니다.
  2. IEnumTfProperties::Next를 호출하여 각 속성을 열거합니다.
  3. 각 속성에 대해 ITfReadOnlyProperty::EnumRanges를 호출하여 범위 열거자를 가져옵니다.
  4. IEnumTfRanges::Next를 호출하여 속성의 각 범위를 열거합니다.
  5. 속성의 각 범위에 대해 속성, 범위, TF_PERSISTENT_PROPERTY_HEADER_ACP 구조 및 애플리케이션에서 구현한 스트림 개체를 사용하여 ITextStoreACPServices::Serialize를 호출합니다.
  6. TF_PERSISTENT_PROPERTY_HEADER_ACP 구조체의 내용을 영구 메모리에 씁니다.
  7. 스트림 개체의 내용을 영구 메모리에 씁니다.
  8. 모든 속성의 모든 범위에 대해 이전 단계를 계속합니다.
  9. 애플리케이션은 데이터가 복원될 때 중지점을 식별할 수 있도록 스트림에 일부 유형의 종결자를 작성해야 합니다.
HRESULT SaveProperties( ITfContext *pContext, 
                        ITextStoreACPServices *pServices, 
                        TfEditCookie ec, 
                        IStream *pStream)
{
    HRESULT             hr;
    IEnumTfProperties   *pEnumProps;
    TF_PERSISTENT_PROPERTY_HEADER_ACP PropHeader;
    ULONG uWritten;
    
    //Enumerate the properties in the context. 
    hr = pContext->EnumProperties(&pEnumProps);
    if(SUCCEEDED(hr))
    {
        ITfProperty *pProp;
        ULONG       uFetched;

        while(SUCCEEDED(pEnumProps->Next(1, &pProp, &uFetched)) && uFetched)
        {
            //Enumerate all the ranges that contain the property. 
            IEnumTfRanges   *pEnumRanges;
            hr = pProp->EnumRanges(ec, &pEnumRanges, NULL);
            if(SUCCEEDED(hr))
            {
                IStream *pTempStream;

                //Create a temporary stream to write the property data to. 
                hr = CreateStreamOnHGlobal(NULL, TRUE, &pTempStream);
                if(SUCCEEDED(hr))
                {
                    ITfRange    *pRange;

                    while(SUCCEEDED(pEnumRanges->Next(1, &pRange, &uFetched)) && uFetched)
                    {
                        LARGE_INTEGER li;

                        //Reset the temporary stream pointer. 
                        li.QuadPart = 0;
                        pTempStream->Seek(li, STREAM_SEEK_SET, NULL);
                        
                        //Get the property header and data for the range. 
                        hr = pServices->Serialize(pProp, pRange, &PropHeader, pTempStream);

                        /*
                        Write the property header into the primary stream. 
                        The header also contains the size of the property 
                        data.
                        */
                        hr = pStream->Write(&PropHeader, sizeof(PropHeader), &uWritten);

                        //Reset the temporary stream pointer. 
                        li.QuadPart = 0;
                        pTempStream->Seek(li, STREAM_SEEK_SET, NULL);

                        //Copy the property data from the temporary stream into the primary stream. 
                        ULARGE_INTEGER  uli;
                        uli.QuadPart = PropHeader.cb;

                        hr = pTempStream->CopyTo(pStream, uli, NULL, NULL);

                        pRange->Release();
                    }
                    
                    pTempStream->Release();
                }
                
                pEnumRanges->Release();
            }
            
            pProp->Release();
        }
        
        pEnumProps->Release();
    }

    //Write a property header with zero size and guid into the stream as a terminator 
    ZeroMemory(&PropHeader, sizeof(PropHeader));
    hr = pStream->Write(&PropHeader, sizeof(PropHeader), &uWritten);

    return hr;
}

ITextStoreACPServices::SerializeITfPropertyStore::Serialize

속성 데이터를 복원할 때 애플리케이션은 다음 단계를 수행해야 합니다.

  1. 스트림 포인터를 첫 번째 TF_PERSISTENT_PROPERTY_HEADER_ACP 구조체의 시작 부분에 설정합니다.

  2. TF_PERSISTENT_PROPERTY_HEADER_ACP 구조를 읽습니다.

  3. TF_PERSISTENT_PROPERTY_HEADER_ACP 구조체의 guidType 멤버를 사용하여 ITfContext::GetProperty호출합니다.

  4. 애플리케이션은 이 시점에서 두 가지 중 하나를 수행할 수 있습니다.

    1. 애플리케이션에서 구현해야 하는 ITfPersistentPropertyLoaderACP 개체의 인스턴스를 만듭니다. 그런 다음, pStream 및 ITfPersistentPropertyLoaderACP 포인터에 대해 NULL을 사용하여 ITextStoreACPServices::Unserialize 를 호출합니다.
    2. 입력 스트림을 ITextStoreACPServices::UnserializenULL for pLoader전달합니다.

    첫 번째 메서드는 가장 효율적이므로 선호됩니다. 두 번째 메서드를 구현하면 ITextStoreACPServices::Unserialize 호출 중에 스트림에서 모든 속성 데이터를 읽을 수 있습니다. 첫 번째 메서드는 나중에 요청 시 속성 데이터를 읽게 합니다.

  5. 모든 속성 블록이 비직렬화될 때까지 이전 단계를 반복합니다.

HRESULT LoadProperties( ITfContext *pContext, 
                        ITextStoreACPServices *pServices, 
                        IStream *pStream)
{
    HRESULT hr;
    ULONG   uRead;
    TF_PERSISTENT_PROPERTY_HEADER_ACP PropHeader;

    /*
    Read each property header and property data from the stream. The 
    list of properties is terminated by a TF_PERSISTENT_PROPERTY_HEADER_ACP 
    structure with a cb member of zero.
    */
    hr = pStream->Read(&PropHeader, sizeof(PropHeader), &uRead);
    while(  SUCCEEDED(hr) && 
            (sizeof(PropHeader) == uRead) && 
            (0 != PropHeader.cb))
    {
        ITfProperty *pProp;

        hr = pContext->GetProperty(PropHeader.guidType, &pProp);
        if(SUCCEEDED(hr))
        {
            /*
            Have TSF read the property data from the stream. This call 
            requests a read-only lock, so be sure it can be granted 
            or else this method will fail.
            */
            CTSFPersistentPropertyLoader *pLoader = new CTSFPersistentPropertyLoader(&PropHeader, pStream);
            hr = pServices->Unserialize(pProp, &PropHeader, NULL, pLoader);

            pProp->Release();
        }

        //Read the next header. 
        hr = pStream->Read(&PropHeader, sizeof(PropHeader), &uRead);
    }

    return hr;
}