void CMarkTextService::_ClearCompositionDisplayAttributes(TfEditCookie ec)
{
    ITfRange *pRangeComposition;
    ITfContext *pContext;
    ITfProperty *pDisplayAttributeProperty;

    // we need a range and the context it lives in
    if (_pComposition->GetRange(&pRangeComposition) != S_OK)
        return;

    if (pRangeComposition->GetContext(&pContext) != S_OK)
    {
        pContext = NULL;
        goto Exit;
    }

    // get our the display attribute property
    if (pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pDisplayAttributeProperty) != S_OK)
        goto Exit;

    // clear the value over the range
    pDisplayAttributeProperty->Clear(ec, pRangeComposition);

    pDisplayAttributeProperty->Release();

Exit:
    pRangeComposition->Release();
    SafeRelease(pContext);
}
BOOL CTextService::_SetCompositionDisplayAttributes(TfEditCookie ec, ITfContext *pContext, TfGuidAtom gaDisplayAttribute)
{
    ITfRange *pRangeComposition;
    ITfProperty *pDisplayAttributeProperty;
    HRESULT hr;

    // we need a range and the context it lives in
    if (_pComposition->GetRange(&pRangeComposition) != S_OK)
        return FALSE;

    hr = E_FAIL;

    // get our the display attribute property
    if (pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pDisplayAttributeProperty) == S_OK)
    {
        VARIANT var;
        // set the value over the range
        // the application will use this guid atom to lookup the acutal rendering information
        var.vt = VT_I4; // we're going to set a TfGuidAtom
        var.lVal = gaDisplayAttribute; 

        hr = pDisplayAttributeProperty->SetValue(ec, pRangeComposition, &var);

        pDisplayAttributeProperty->Release();
    }

    pRangeComposition->Release();
    return (hr == S_OK);
}
BOOL CSampleIME::_FindComposingRange(TfEditCookie ec, _In_ ITfContext *pContext, _In_ ITfRange *pSelection, _Outptr_result_maybenull_ ITfRange **ppRange)
{
    if (ppRange == nullptr)
    {
        return FALSE;
    }

    *ppRange = nullptr;

    // find GUID_PROP_COMPOSING
    ITfProperty* pPropComp = nullptr;
    IEnumTfRanges* enumComp = nullptr;

	/* GetProperty(GUID_PROP_COMPOSING, ...) -- nonzero if text is part of a composition */
    HRESULT hr = pContext->GetProperty(GUID_PROP_COMPOSING, &pPropComp);
    if (FAILED(hr) || pPropComp == nullptr)
    {
        return FALSE;
    }

	/* EnumRanges:
		Obtains an enumeration of ranges that contain unique values of the property GUID_PROP_COMPOSING within the given range */
    hr = pPropComp->EnumRanges(ec, &enumComp, pSelection);
    if (FAILED(hr) || enumComp == nullptr)
    {
        pPropComp->Release();
        return FALSE;
    }

    BOOL isCompExist = FALSE;
    VARIANT var;
    ULONG  fetched = 0;

    while (enumComp->Next(1, ppRange, &fetched) == S_OK && fetched == 1)
    {
        hr = pPropComp->GetValue(ec, *ppRange, &var);
        if (hr == S_OK)
        {
            if (var.vt == VT_I4 && var.lVal != 0)
            {
                isCompExist = TRUE;
                break;
            }
        }
        (*ppRange)->Release();
        *ppRange = nullptr;
    }

    pPropComp->Release();
    enumComp->Release();

    return isCompExist;
}
Beispiel #4
0
void CTextService::_ClearCompositionDisplayAttributes(TfEditCookie ec, ITfContext *pContext)
{
	ITfRange *pRange;
	if(_IsComposing() && _pComposition->GetRange(&pRange) == S_OK)
	{
		ITfProperty *pProperty;
		if(pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pProperty) == S_OK)
		{
			pProperty->Clear(ec, pRange);
			SafeRelease(&pProperty);
		}
		SafeRelease(&pRange);
	}
}
Beispiel #5
0
BOOL CTextService::_SetCompositionDisplayAttributes(TfEditCookie ec, ITfContext *pContext, ITfRange *pRange, TfGuidAtom gaDisplayAttribute)
{
	HRESULT hr = E_FAIL;

	ITfProperty *pProperty;
	if(pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pProperty) == S_OK)
	{
		VARIANT var;
		var.vt = VT_I4;
		var.lVal = gaDisplayAttribute;
		hr = pProperty->SetValue(ec, pRange, &var);
		SafeRelease(&pProperty);
	}

	return (hr == S_OK);
}
void CTextService::_ClearCompositionDisplayAttributes(TfEditCookie ec, ITfContext *pContext)
{
	if(_IsComposing())
	{
		ITfRange *pRange = nullptr;
		if(SUCCEEDED(_pComposition->GetRange(&pRange)) && (pRange != nullptr))
		{
			ITfProperty *pProperty = nullptr;
			if(SUCCEEDED(pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pProperty)) && (pProperty != nullptr))
			{
				pProperty->Clear(ec, pRange);
				SafeRelease(&pProperty);
			}
			SafeRelease(&pRange);
		}
	}
}
BOOL CSampleIME::_SetCompositionLanguage(TfEditCookie ec, _In_ ITfContext *pContext)
{
    HRESULT hr = S_OK;
    BOOL ret = TRUE;

    LANGID langidProfile = 0;
    GetLanguageProfile(&langidProfile);

    ITfRange* pRangeComposition = nullptr;
    ITfProperty* pLanguageProperty = nullptr;

    // we need a range and the context it lives in
    hr = _pComposition->GetRange(&pRangeComposition);
    if (FAILED(hr))
    {
        ret = FALSE;
        goto Exit;
    }

    // get our the language property
    hr = pContext->GetProperty(GUID_PROP_LANGID, &pLanguageProperty);
    if (FAILED(hr))
    {
        ret = FALSE;
        goto Exit;
    }

    VARIANT var;
    var.vt = VT_I4;   // we're going to set DWORD
    var.lVal = langidProfile; 

    hr = pLanguageProperty->SetValue(ec, pRangeComposition, &var);
    if (FAILED(hr))
    {
        ret = FALSE;
        goto Exit;
    }

    pLanguageProperty->Release();
    pRangeComposition->Release();

Exit:
    return ret;
}
void CTextService::_ClearCompositionDisplayAttributes(TfEditCookie ec, ITfContext *pContext)
{
    ITfRange *pRangeComposition;
    ITfProperty *pDisplayAttributeProperty;

    // get the compositon range.
    if (_pComposition->GetRange(&pRangeComposition) != S_OK)
        return;

    // get our the display attribute property
    if (pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pDisplayAttributeProperty) == S_OK)
    {
        // clear the value over the range
        pDisplayAttributeProperty->Clear(ec, pRangeComposition);

        pDisplayAttributeProperty->Release();
    }

    pRangeComposition->Release();
}
BOOL CTextService::_SetCompositionDisplayAttributes(TfEditCookie ec, ITfContext *pContext, ITfRange *pRange, TfGuidAtom gaDisplayAttribute)
{
	HRESULT hr = E_FAIL;

	ITfProperty *pProperty = nullptr;
	if(SUCCEEDED(pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pProperty)) && (pProperty != nullptr))
	{
		VARIANT var;
		VariantInit(&var);
		V_VT(&var) = VT_I4;
		V_I4(&var) = gaDisplayAttribute;

		hr = pProperty->SetValue(ec, pRange, &var);

		VariantClear(&var);
		SafeRelease(&pProperty);
	}

	return SUCCEEDED(hr);
}
STDAPI CStaticPropertyEditSession::DoEditSession(TfEditCookie ec)
{
    ITfProperty *pProperty;
    TF_SELECTION tfSelection;
    ULONG cFetched;

    if (_pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &cFetched) != S_OK || cFetched != 1)
        return S_FALSE;

    if (_pContext->GetProperty(c_guidPropStatic, &pProperty) == S_OK)
    {
        VARIANT var;
        var.vt = VT_BSTR; 
        var.bstrVal = SysAllocString(_sz);
        pProperty->SetValue(ec, tfSelection.range, &var);
        pProperty->Release();
    }

    tfSelection.range->Release();
    return S_OK;
}
BOOL CMarkTextService::_SetCompositionDisplayAttributes(TfEditCookie ec)
{
    ITfRange *pRangeComposition;
    ITfContext *pContext;
    ITfProperty *pDisplayAttributeProperty;
    VARIANT var;
    HRESULT hr;

    // we need a range and the context it lives in
    if (_pComposition->GetRange(&pRangeComposition) != S_OK)
        return FALSE;

    hr = E_FAIL;

    if (pRangeComposition->GetContext(&pContext) != S_OK)
    {
        pContext = NULL;
        goto Exit;
    }

    // get our the display attribute property
    if (pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pDisplayAttributeProperty) != S_OK)
        goto Exit;

    // set the value over the range
    // the application will use this guid atom to lookup the acutal rendering information
    var.vt = VT_I4; // we're going to set a TfGuidAtom
    var.lVal = _gaDisplayAttribute; // our cached guid atom for c_guidMarkDisplayAttribute

    hr = pDisplayAttributeProperty->SetValue(ec, pRangeComposition, &var);

    pDisplayAttributeProperty->Release();

Exit:
    pRangeComposition->Release();
    SafeRelease(pContext);
    return (hr == S_OK);
}
Beispiel #12
0
BOOL CDIME::_SetCompositionLanguage(TfEditCookie ec, _In_ ITfContext *pContext)
{
	debugPrint(L"CDIME::_SetCompositionLanguage()");
    HRESULT hr = S_OK;
    BOOL ret = TRUE;

	if (pContext == nullptr || _pComposition == nullptr)
    {
		debugPrint(L"CDIME::_SetCompositionLanguage() failed with null pContext or _pComposition");
		ret = FALSE;
        goto Exit;
    }

    ITfRange* pRangeComposition = nullptr;
    ITfProperty* pLanguageProperty = nullptr;

    // we need a range and the context it lives in
    hr = _pComposition->GetRange(&pRangeComposition);
    if (FAILED(hr) || pRangeComposition == nullptr)
    {
		if (FAILED(hr))
			debugPrint(L"CDIME::_SetCompositionLanguage() _pComposition->GetRange() failed");
		else
			debugPrint(L"CDIME::_SetCompositionLanguage() failed with null pRangeComposition");

        ret = FALSE;
        goto Exit;
    }

    // get our the language property
    hr = pContext->GetProperty(GUID_PROP_LANGID, &pLanguageProperty);
    if (FAILED(hr) || pLanguageProperty == nullptr)
    {
		if (FAILED(hr))
			debugPrint(L"CDIME::_SetCompositionLanguage() pContext->GetProperty() failed hr = %x", hr);
		else
			debugPrint(L"CDIME::_SetCompositionLanguage() failed with null pLanguageProperty");

        ret = FALSE;
        goto Exit;
    }

    VARIANT var;
    var.vt = VT_I4;   // we're going to set DWORD
    var.lVal = _langid; 

    hr = pLanguageProperty->SetValue(ec, pRangeComposition, &var);
    if (FAILED(hr) || pRangeComposition == nullptr)
    {
		if (FAILED(hr))
			debugPrint(L"CDIME::_SetCompositionLanguage() pLanguageProperty->SetValue() failed hr = %x", hr);
		else
			debugPrint(L"CDIME::_SetCompositionLanguage() failed with null pRangeComposition ");

        ret = FALSE;
        goto Exit;
    }

    pLanguageProperty->Release();
    pRangeComposition->Release();

Exit:
    return ret;
}
Beispiel #13
0
BOOL CDIME::_FindComposingRange(TfEditCookie ec, _In_ ITfContext *pContext, _In_ ITfRange *pSelection, _Outptr_result_maybenull_ ITfRange **ppRange)
{
	debugPrint(L"CDIME::_FindComposingRange()");

    if (pContext == nullptr || ppRange == nullptr)
    {
		debugPrint(L"CDIME::_FindComposingRange() failed with null pContext or pRange");
        return FALSE;
    }

    *ppRange = nullptr;

    // find GUID_PROP_COMPOSING
    ITfProperty* pPropComp = nullptr;
    IEnumTfRanges* enumComp = nullptr;

    HRESULT hr = pContext->GetProperty(GUID_PROP_COMPOSING, &pPropComp);
    if (FAILED(hr) || pPropComp == nullptr)
    {
		if (FAILED(hr))
			debugPrint(L"CDIME::_FindComposingRange()  pContext->GetProperty() failed");
		else
			debugPrint(L"CDIME::_FindComposingRange() failed with null pPropComp");
        return FALSE;
    }

    hr = pPropComp->EnumRanges(ec, &enumComp, pSelection);
    if (FAILED(hr) || enumComp == nullptr)
    {
		if (FAILED(hr))
			debugPrint(L"CDIME::_FindComposingRange()  pPropComp->EnumRanges failed");
		else
			debugPrint(L"CDIME::_FindComposingRange() failed with null enumComp");
        pPropComp->Release();
        return FALSE;
    }

    BOOL isCompExist = FALSE;
    VARIANT var;
    ULONG  fetched = 0;

    while (enumComp->Next(1, ppRange, &fetched) == S_OK && fetched == 1)
    {
		debugPrint(L"CDIME::_FindComposingRange() enumComp->Next() ");
        hr = pPropComp->GetValue(ec, *ppRange, &var);
        if (hr == S_OK)
        {
            if (var.vt == VT_I4 && var.lVal != 0)
            {
				WCHAR rangeText[4096];
				rangeText[0] = NULL;
				fetched = 0;
				hr = (*ppRange)->GetText(ec, 0, rangeText, 4096, &fetched);
				if (SUCCEEDED(hr))
				{
					if (lastReadingString.GetLength() > 0) // The last reading string is null when composing is just started and the range should be empty.
					{
						debugPrint(L"CDIME::_FindComposingRange() text in range = %s with %d character, last reading string = %s with %d characters",
							rangeText, fetched, lastReadingString.Get(), lastReadingString.GetLength());
						UINT cmpCount = (int)lastReadingString.GetLength();
						BOOL rangeMatchReadingString = FALSE;
						if (fetched > 0 && fetched > cmpCount)
						{
							LONG shifted = 0;
							// range contains other characters than last reading string, and these characters should be skipped with shifting the start anchor
							(*ppRange)->ShiftStart(ec, fetched - cmpCount, &shifted, NULL);
							debugPrint(L"CDIME::_FindComposingRange() shift start anchor with %d characters", shifted);
							rangeText[0] = NULL;
							hr = (*ppRange)->GetText(ec, 0, rangeText, 4096, &fetched);
							if (SUCCEEDED(hr))
								debugPrint(L"CDIME::_FindComposingRange() text in range with shited anchor = %s with %d character", rangeText, fetched);
						}
						// The range we are composing should always matched with the last reading string.
						rangeMatchReadingString = CompareString(GetLocale(), 0, rangeText, cmpCount, lastReadingString.Get(), cmpCount) == CSTR_EQUAL;
				
						if (rangeMatchReadingString)
						{
							isCompExist = TRUE;
							debugPrint(L"CDIME::_FindComposingRange() text in this range mathced with last reading string. isCompExist = TRUE");
							break;
						}
					}
					
				}
				else
				{
					debugPrint(L"CDIME::_FindComposingRange() cannot getText from range isCompExist = TRUE");
				}
            }
        }
		debugPrint(L"CDIME::_FindComposingRange() release *ppRange");
        if(*ppRange)
			(*ppRange)->Release();
        *ppRange = nullptr;
    }

    pPropComp->Release();
    enumComp->Release();

    return isCompExist;
}
HRESULT CTextService::_SetText(TfEditCookie ec, ITfContext *pContext, const std::wstring &text, LONG cchCursor, LONG cchOkuri, BOOL fixed)
{
	TF_SELECTION tfSelection;
	ULONG cFetched = 0;
	LONG cch, cchRes;

	if(pContext == NULL && _pCandidateList != NULL)	//辞書登録用
	{
		_pCandidateList->_SetText(text, fixed, FALSE, FALSE);
		return S_OK;
	}

	if(!_IsComposing())
	{
		if(!_StartComposition(pContext))
		{
			return S_FALSE;
		}
	}

	if(pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &cFetched) != S_OK)
	{
		return S_FALSE;
	}

	if(cFetched != 1)
	{
		SafeRelease(&tfSelection.range);
		return S_FALSE;
	}

	ITfRange *pRange;
	if(_IsComposing() && _pComposition->GetRange(&pRange) == S_OK)
	{
		if(_IsRangeCovered(ec, tfSelection.range, pRange))
		{
			pRange->SetText(ec, 0, text.c_str(), (LONG)text.size());

			// shift from end to start.
			// shift over mathematical operators (U+2200-U+22FF) is rejected by OneNote.
			if(cchCursor == 0)
			{
				cchRes = (LONG)cursoridx - (LONG)kana.size();
				if((complement && okuriidx != 0) ||
					(!cx_showmodemark && okuriidx != 0 && cursoridx <= okuriidx && cursoridx < kana.size()))
				{
					cchRes += 1;
				}
			}
			else
			{
				cchRes = cchCursor - (LONG)text.size();
				if(cchRes > 0)
				{
					cchRes = 0;
				}
				else if(cchRes < -(LONG)text.size())
				{
					cchRes = -(LONG)text.size();
				}
			}

			tfSelection.range->ShiftEndToRange(ec, pRange, TF_ANCHOR_END);
			tfSelection.range->ShiftStartToRange(ec, pRange, TF_ANCHOR_END);
			tfSelection.range->ShiftStart(ec, cchRes, &cch, NULL);
			//decide cursor position
			tfSelection.range->Collapse(ec, TF_ANCHOR_START);
			pContext->SetSelection(ec, 1, &tfSelection);

			//composition attribute
			if(!fixed)
			{
				ITfRange *pRangeClone;
				if(pRange->Clone(&pRangeClone) == S_OK)
				{
					pRangeClone->ShiftEndToRange(ec, pRange, TF_ANCHOR_END);
					pRangeClone->ShiftStartToRange(ec, pRange, TF_ANCHOR_START);

					if(cchCursor == 0 || !showentry)
					{
						if(inputkey)
						{
							_SetCompositionDisplayAttributes(ec, pContext, pRangeClone, _gaDisplayAttributeInputMark);
							if(cx_showmodemark)
							{
								pRangeClone->ShiftStart(ec, 1, &cch, NULL);
							}
						}

						if(!display_attribute_series[1] || !inputkey)
						{
							_SetCompositionDisplayAttributes(ec, pContext, pRangeClone, _gaDisplayAttributeInputText);
						}

						if(cchOkuri != 0)
						{
							pRangeClone->ShiftStartToRange(ec, pRange, TF_ANCHOR_START);
							pRangeClone->ShiftStart(ec, cchOkuri, &cch, NULL);
							if(!display_attribute_series[2])
							{
								_SetCompositionDisplayAttributes(ec, pContext, pRangeClone, _gaDisplayAttributeInputOkuri);
							}

							if(hintmode && text.find_first_of(CHAR_SKK_HINT) != std::wstring::npos)
							{
								LONG hintpos = (LONG)text.find_first_of(CHAR_SKK_HINT);
								if(cchOkuri < hintpos)
								{
									pRangeClone->ShiftStartToRange(ec, pRange, TF_ANCHOR_START);
									pRangeClone->ShiftStart(ec, hintpos, &cch, NULL);
									_SetCompositionDisplayAttributes(ec, pContext, pRangeClone, _gaDisplayAttributeInputText);
								}
							}
						}
					}
					else
					{
						_SetCompositionDisplayAttributes(ec, pContext, pRangeClone, _gaDisplayAttributeConvMark);
						if(cx_showmodemark)
						{
							pRangeClone->ShiftStart(ec, 1, &cch, NULL);
						}

						if(!display_attribute_series[4])
						{
							_SetCompositionDisplayAttributes(ec, pContext, pRangeClone, _gaDisplayAttributeConvText);
						}

						if(cchOkuri != 0)
						{
							pRangeClone->ShiftStartToRange(ec, pRange, TF_ANCHOR_START);
							pRangeClone->ShiftStart(ec, cchOkuri, &cch, NULL);
							if(!display_attribute_series[5])
							{
								_SetCompositionDisplayAttributes(ec, pContext, pRangeClone, _gaDisplayAttributeConvOkuri);
							}
						}

						pRangeClone->ShiftEndToRange(ec, pRange, TF_ANCHOR_END);
						pRangeClone->ShiftStartToRange(ec, tfSelection.range, TF_ANCHOR_END);
						if(!display_attribute_series[6])
						{
							_SetCompositionDisplayAttributes(ec, pContext, pRangeClone, _gaDisplayAttributeConvAnnot);
						}
					}
					SafeRelease(&pRangeClone);
				}
			}

			// for Excel's PHONETIC function
			if(fixed && !text.empty())
			{
				ITfProperty *pProperty;
				if(pContext->GetProperty(GUID_PROP_READING, &pProperty) == S_OK)
				{
					VARIANT var;
					var.vt = VT_BSTR;
					std::wstring phone(kana);
					if(okuriidx == 0)
					{
						switch(inputmode)
						{
						case im_hiragana:
						case im_katakana:
						case im_katakana_ank:
							//接辞
							if(!abbrevmode && kana.size() >= 2)
							{
								if(kana.front() == L'>')
								{
									phone = kana.substr(1);
								}
								else if(kana.back() == L'>')
								{
									phone = kana.substr(0, kana.size() - 1);
								}
							}
							break;
						default:
							break;
						}
					}
					else
					{
						if(kana.size() > (okuriidx + 1))
						{
							phone = kana.substr(0, okuriidx) + kana.substr(okuriidx + 1);
						}
						else if(kana.size() >= okuriidx)
						{
							phone = kana.substr(0, okuriidx);
						}
					}
					if(!phone.empty())
					{
						var.bstrVal = SysAllocString(phone.c_str());
						pProperty->SetValue(ec, pRange, &var);
						SysFreeString(var.bstrVal);
					}
					SafeRelease(&pProperty);
				}
			}
		}

		SafeRelease(&pRange);
	}

	SafeRelease(&tfSelection.range);

	return S_OK;
}
void CTSFEditWnd::_Save(IStream *pStream)
{
    if(pStream)
    {
        HRESULT hr;
        
        //write the plain UNICODE text into the stream
        LPWSTR          pwsz;
        LONG            cch;
        ULONG           uWritten;
        LARGE_INTEGER   li;
        ULONG           uSize;

        //set the stream pointer to the start of the stream
        li.QuadPart = 0;
        pStream->Seek(li, STREAM_SEEK_SET, NULL);
        
        //get the text
        if(SUCCEEDED(_GetText(&pwsz, &cch)))
        {
            TF_PERSISTENT_PROPERTY_HEADER_ACP   PropHeader;

            //write the size, in BYTES, of the text
            uSize = cch * sizeof(WCHAR);
            hr = pStream->Write(&uSize, sizeof(ULONG), &uWritten);

            //write the text, including the NULL_terminator, into the stream
            hr = pStream->Write(pwsz, uSize, &uWritten);

            //free the memory allocated by _GetText
            GlobalFree(pwsz);

            //enumerate the properties in the context
            IEnumTfProperties   *pEnumProps;
            hr = m_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(m_EditCookie, &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)
                            {
                                //reset the temporary stream's pointer
                                li.QuadPart = 0;
                                pTempStream->Seek(li, STREAM_SEEK_SET, NULL);
                                
                                //get the property header and data for the range
                                hr = m_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(TF_PERSISTENT_PROPERTY_HEADER_ACP), &uWritten);

                                //reset the temporary stream's 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(TF_PERSISTENT_PROPERTY_HEADER_ACP));
            hr = pStream->Write(&PropHeader, sizeof(TF_PERSISTENT_PROPERTY_HEADER_ACP), &uWritten);
        }
    }
}
void CTSFEditWnd::_Load(IStream *pStream)
{
    if(NULL == pStream)
    {
        return;
    }

    //can't do this if someone has a lock
    if(_IsLocked(TS_LF_READ))
    {
        return;
    }

    _ClearText();

    HRESULT         hr;
    ULONG           uRead;
    LARGE_INTEGER   li;
    ULONG           uSize;

    //set the stream pointer to the start of the stream
    li.QuadPart = 0;
    pStream->Seek(li, STREAM_SEEK_SET, NULL);

    //get the size of the text, in BYTES. This is the first ULONG in the stream
    hr = pStream->Read(&uSize, sizeof(ULONG), &uRead);
    if(SUCCEEDED(hr) && (sizeof(ULONG) == uRead))
    {
        LPWSTR  pwsz;
        
        //allocate a buffer for the text plus one NULL character
        pwsz = (LPWSTR)GlobalAlloc(GPTR, uSize + sizeof(WCHAR));
        if(NULL != pwsz)
        {
            //get the plain UNICODE text from the stream
            hr = pStream->Read(pwsz, uSize, &uRead);
            if(SUCCEEDED(hr) && (uSize == uRead))
            {
                TF_PERSISTENT_PROPERTY_HEADER_ACP   PropHeader;
                
                //put the text into the edit control, but don't send a change notification
                BOOL    fOldNotify = m_fNotify;
                m_fNotify = FALSE;
                SetWindowTextW(m_hwndEdit, pwsz);
                m_fNotify = fOldNotify;

                /*
                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(TF_PERSISTENT_PROPERTY_HEADER_ACP), &uRead);
                while(  SUCCEEDED(hr) && 
                        (sizeof(TF_PERSISTENT_PROPERTY_HEADER_ACP) == uRead) && 
                        (0 != PropHeader.cb))
                {
                    ITfProperty *pProp;

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

                        pProp->Release();
                    }

                    hr = pStream->Read(&PropHeader, sizeof(TF_PERSISTENT_PROPERTY_HEADER_ACP), &uRead);
                }
            }
            
            GlobalFree(pwsz);
        }
    }
}