Skip to main content

Extended Linguistic Services in Windows 7

Windows National Language Support (NLS) exposes core globalization support for languages and regions around the world to enable developers to create globalized applications that work for a broad range of international customers. NLS APIs today provide international data and foundational support for basic text enablement, date/time/calendar formatting, keyboard layouts, sorting behavior, and more. As customer expectations around the world have become more sophisticated, each new release of Windows has seen the expansion of NLS support to include both more features and more locales. In Windows Vista, NLS added dozens of new locales for Windows Vista, extending the global reach of our in-box support to new languages and regions. We also made interoperability easier, moving from LCIDs (numeric locale identifiers) to IETF-conformant string-based identifiers that make it easier for developers add locales of their own and reach new customers with their applications.

Our core globalization services have been used by a variety of application developers, not only for so-called international applications but also for single-market components that rely on Windows for foundational text and formatting properties. As more and more customers have started leveraging this basic enablement support, we have turned our attention to the next set of foundational features that are required to create a truly globalized user experience for our customers. At conferences, on blogs, in person, developers creating globalized components today are increasingly requesting new kinds of linguistic functionality – new built-in support that allows developers to create more Windows applications for more customers, with lower development and deployment costs.

You asked, we listened. Extended Linguistic Services (ELS) in Windows 7 introduces a new API set that allows you to do a whole bunch of things with text. This document takes you through some of our key ELS features and tells you a little bit about how and where you might find them useful. We’re very excited to share this with you and we hope you’ll try them out and let us know what you think.

Why linguistic services?

Feedback has been very clear: developers want to bring their applications to new customers all over the world, which means that they need appropriate features first to discover what kind of language experience a particular user expects and then to enable that experience in the most relevant possible way.

One of the big gaps in Windows linguistic support until now has simply been in the system’s ability to tell developers what language a customer is using at any given time. Sure, you can check a variety of system settings to make a guess. You can check a user’s input locale to know what language her keyboard was designed for, but this is pretty incomplete. Multilingual users very often use a single keyboard to enter multiple languages that have similar character inventories, so there’s really no way to tell for sure the language of a particular piece of text just based on the keyboard that was used to enter it.

You can also query the default user locale setting to get some idea of a user’s standard language preference; most of the time, users set their locale to include the language that they use most frequently. However, here too multilingual users fall through the cracks. The best you can hope for from these system settings is a guess as to a user’s most-of-the-time preferences. Locale settings don’t really provide up to the minute information about the language or writing system that a customer is using at any point in time.

And having some information about the language and writing system that your customer is using is hugely valuable to helping you create the right user experience inside your application. Maybe you want to provide help content options and you need to make sure that your customer can read them. Maybe you want to guide your user to targeted advertising. Maybe you want to make smart font selection choices or present just the set of options that will make the most sense for your user. Or perhaps your application indexes content and you’d like to index your content by language to facilitate faster search results. Across a broad set of scenarios, by knowing the language that your customer is using, you can make sure that your application presents a truly market-relevant user experience that will be appropriate for your customer’s needs.

Many applications today are trying to work around the lack of functionality in this area, either by using system settings or by hard-coding Unicode ranges that they estimate will approximately match the languages that they care about. But both of these approaches are likely to make poor predictions about the experience that a user expects. System settings don’t tell you anything about the language of a particular string, and languages borrow strings from other languages all the time, making the definition of language-specific Unicode ranges an impossible task. With so much need for this kind of functionality, and so many developers working around the problem today, we figured that whatever else we build for ELS, we’d better start with providing accurate script and language detection support. This is really a key part of the foundation upon which all kinds of more advanced linguistic functionality relies. ELS script detection supports everything in Unicode 5.1, and language detection includes coverage for nearly 100 different languages. These pieces are new in Windows 7, and you can leverage them inside your application by using ELS APIs.

Top of page

 

What about features that will take me into new markets?

Even once you know a user’s language, you need platform features that you can rely on to help you create an appropriate experience. We’re happy to introduce a set of key linguistic functionality to help with adoption of your applications in key markets such as China, India, and Eastern Europe. In addition to language and script detection, ELS APIs expose several new transliteration character mappings, including:

  • Auto-conversion between Simplified and Traditional Chinese
  • Cyrillic to Latin phonetic normalization
  • Devanagari to Latin phonetic normalization
  • Malayalam to Latin phonetic normalization
  • Bengali to Latin phonetic normalization

This new transliteration functionality, which maps equivalent text from one writing system to another, makes it possible for users in different Chinese-speaking regions to share content with one another across linguistic boundaries. The phonetic normalization coverage makes it possible for users pronounce text in writing systems that are unfamiliar to them. We also wanted to make it possible for you to use our transliteration functionality in combination with language and script detection, so that you can use the language and script detection features in order to make informed decisions about the transliteration coverage that might be appropriate for your users.

Now that you know a little bit about some of our ELS features in Windows 7, let’s look at how you might use them.

Top of page

 

Make it simple, make it scalable

We tried to keep a few basic principles in mind when we designed the ELS APIs that will let you leverage these new linguistic features in Windows:

  • Keep it simple. The ELS pieces that we’re offering inside Windows 7 are just the beginning. We know that you’ll keep coming up with great ideas for new linguistic features that we ought to be exposing and we’re going to be adding more and more to meet your requests. We tried to keep this in mind when we designed the ELS API set. Instead of learning a different API for each linguistic service, we’ve designed a few basic APIs that you’ll be able to use flexibly to retrieve ELS services in different ways for different scenarios. By learning just this small set of APIs, you’ll be able to leverage everything that ELS provides in Windows 7 and beyond –without learning new APIs.
  • Keep it scalable. We know that developers who use some of our linguistic services are also likely to want to use others. As we discussed, the small number of ELS APIs means that you won’t need to learn new APIs every time you want to use a new service. Another advantage of the small, flexible API set is that you won’t need to write new code every time you want to use a new service. Not only won’t you have to learn new code to leverage a new service, but  depending on how you call ELS (and we’ll get into that more below), you may not even need to add new code.

Top of page

 

So what do the APIs look like, and how can I use them?

ELS is built using a plug-in model, where applications talk to a central platform that distributes text to different services. The architecture is layered like this:

First column has the service layer on the bottom, the platform layer in the middle, and the application layer on top. Second column has transliteration, script detection, language detection, and future services on the bottom layer (service layer), central text handler (platform layer) in the middle, and "Your application" (application layer) on the top.

 

Your application passes text in to the central text handler, which distributes it to the services that you’ve asked for. Services return results as relevant.

The ELS APIs achieve this using three main stages of communication:

  • Enumerate – Get the list of services that the caller cares about.
  • Recognize – Send text that the caller has passed to the services that the caller has selected and query the services for text recognition.
  • Results – Return results from the services back to the caller

Let’s explore each of these stages in some more detail.

 

Service enumeration

Callers use MappingGetServices() to discover the set of services that are available on the machine. As you’ll see in the ELS sample code, callers can enumerate once and then make any number of calls to ELS services thereafter according to their need.

 

Text recognition

Callers use MappingRecognizeText() to ask services for any relevant results on the text that the application cares about.

 

Release of data properties

Callers use MappingFreePropertyBag() to free the resources associated with the property bag. This function must be called every time when MappingRecognizeText() succeeds.

 

Service termination

Callers use MappingFreeServices() to free the resources associated with the services array pointer. This function must be called every time when MappingGetServices() succeeds.

 

Service callback

This is a callback used for asynchronous calling of MappingRecognizeText. When the callback is used, the user must make sure that the property bag, the input text, the options, and the service are all valid until after the callback has finished executing. The user must also make sure that MappingFreePropertyBag is called right after the bag is consumed by the callback. A good idea might be using the callback to free the resources after it has finished processing or copying them.

These basic APIs can be used in different combinations to leverage any ELS functionality that is available on the machine.

Top of page

 

Code Samples

To compile any of the code samples, the header files, elscore.h and elssrvc.h, as well as the lib file, elscore.lib, must be included in the version of the Windows SDK that you are using.

 

 

MappingGetServices and MappingFreeServices

This sample illustrates the usage of MappingGetServices and MappingFreeServices to enumerate and then free all available services on the system.

#include <windows.h>

#include <stdio.h>

#include <elscore.h>



int __cdecl main()

{

    PMAPPING_SERVICE_INFO   prgServices = NULL;

    DWORD                   dwServicesCount = 0;

    HRESULT                 Result;



    DWORD i;



    // Get all installed ELS services:

    Result = MappingGetServices(NULL, &prgServices, &dwServicesCount);



    if (Result == S_OK)

    {

        for (i = 0; i < dwServicesCount; ++i)

        {

            // Do something with each service:

            // ... prgServices[i] ...

            printf("Service: %ws, category: %ws\n",

                prgServices[i].pszDescription, prgServices[i].pszCategory);

        }



        MappingFreeServices(prgServices);

    }



    return 0;

}

Top of page

 

MappingRecognizeText - Microsoft Language Detection

This sample illustrates the usage of MappingRecognizeText on the Microsoft Language Detection service and prints all the results returned from it. The return format of this service is a single MAPPING_DATA_RANGE with its pData pointing to Unicode double null-terminated registry-formatted array of strings. Every string of the array is null-terminated and there's an empty string specifying the end of the array. The contents of the array are language names sorted by confidence.

#include <windows.h>

#include <stdio.h>

#include <elscore.h>

#include <elssrvc.h>



#define USER_TEXT ( \

    L"Skip This is a simple sentence. " \

    L"\x0422\x0445\x0438\x0441 \x0438\x0441 \x0415\x043d\x0433\x043b\x0438\x0441\x0445.")

#define USER_TEXT_SKIP (5)

int __cdecl main();

HRESULT CallMappingRecognizeText(PMAPPING_SERVICE_INFO pService);

void PrintAllResults(PMAPPING_PROPERTY_BAG pBag);



int __cdecl main()

{

    MAPPING_ENUM_OPTIONS    EnumOptions;

    PMAPPING_SERVICE_INFO   prgServices = NULL;

    DWORD                   dwServicesCount = 0;

    HRESULT                 hResult;



    ZeroMemory(&EnumOptions, sizeof (MAPPING_ENUM_OPTIONS));

    EnumOptions.Size = sizeof (MAPPING_ENUM_OPTIONS);

    // Using the Language Auto-Detection GUID to enumerate LAD only:

    EnumOptions.pGuid = (GUID *)&ELS_GUID_LANGUAGE_DETECTION;

    hResult = MappingGetServices(&EnumOptions, &prgServices, &dwServicesCount);

    if (SUCCEEDED(hResult))

    {

        hResult = CallMappingRecognizeText(&prgServices[0]);

        if (SUCCEEDED(hResult))

        {

            printf("Calling the service %ws has succeeded!\n",

                prgServices[0].pszDescription);

        }

        else

        {

            printf("Calling the service %ws has failed, failure = 0x%x!\n",

                prgServices[0].pszDescription, hResult);

        }

        MappingFreeServices(prgServices);

    }



    return 0;

}



HRESULT CallMappingRecognizeText(PMAPPING_SERVICE_INFO pService)

{

    MAPPING_PROPERTY_BAG bag;

    HRESULT hResult;



    ZeroMemory(&bag, sizeof (MAPPING_PROPERTY_BAG));

    bag.Size = sizeof (MAPPING_PROPERTY_BAG);



    // MappingRecognizeText's dwIndex parameter specifies the first

    // index inside the text from where the recognition should start.

    // We pass USER_TEXT_SKIP, thus skipping the "Skip " part

    // of the input string.

    // Calling without MAPPING_OPTIONS:

    hResult = MappingRecognizeText(pService, USER_TEXT, wcslen(USER_TEXT), USER_TEXT_SKIP, NULL, &bag);

    if (SUCCEEDED(hResult))

    {

        printf("Results from service: %ws\n", pService->pszDescription);

        PrintAllResults(&bag);

        hResult = MappingFreePropertyBag(&bag);

    }

    return hResult;

}



void PrintAllResults(PMAPPING_PROPERTY_BAG pBag)

{

    WCHAR * p;

    // The return format of the Language Auto-Detection is a

    // double null-terminated registry-formatted array of strings.

    // Every string of the array is null-terminated and there's an

    // empty string specifying the end of the array.

    for (p = (WCHAR *)pBag->prgResultRanges[0].pData; *p; p += wcslen(p) + 1)

    {

        printf("%ws\n", p);

    }

}

Top of page

 

MappingRecognizeText – Microsoft Script Detection

This sample illustrates the usage of MappingRecognizeText on the Microsoft Script Detection service and prints all the results returned from it. The return format of this service is an array of MAPPING_DATA_RANGEs each specifying text written in the same script, with common (Zyyy) characters being added to the previous range (or the next range if the previous one doesn’t exist). The pData pointer of each range points to a Unicode null-terminated string containing the standard Unicode name of the script for this range. The service complies to Unicode 5.1 as of Windows 7.

#include <windows.h>

#include <stdio.h>

#include <elscore.h>

#include <elssrvc.h>



#define USER_TEXT ( \

    L"Skip This is a simple sentence. " \

    L"\x0422\x0445\x0438\x0441 \x0438\x0441 \x0415\x043d\x0433\x043b\x0438\x0441\x0445.")

#define USER_TEXT_SKIP (5)



int __cdecl main();

HRESULT CallMappingRecognizeText(PMAPPING_SERVICE_INFO pService);

void PrintAllResults(PMAPPING_PROPERTY_BAG pBag);



int __cdecl main()

{

    MAPPING_ENUM_OPTIONS    EnumOptions;

    PMAPPING_SERVICE_INFO   prgServices = NULL;

    DWORD                   dwServicesCount = 0;

    HRESULT                 hResult;



    ZeroMemory(&EnumOptions, sizeof (MAPPING_ENUM_OPTIONS));

    EnumOptions.Size = sizeof (MAPPING_ENUM_OPTIONS);

    // Using the Script Detection GUID to enumerate SD only:

    EnumOptions.pGuid = (GUID *)&ELS_GUID_SCRIPT_DETECTION;

    hResult = MappingGetServices(&EnumOptions, &prgServices, &dwServicesCount);



    if (SUCCEEDED(hResult))

    {

        hResult = CallMappingRecognizeText(&prgServices[0]);

        if (SUCCEEDED(hResult))

        {

            printf("Calling the service %ws has succeeded!\n",

                prgServices[0].pszDescription);

        }

        else

        {

            printf("Calling the service %ws has failed, failure = 0x%x!\n",

                prgServices[0].pszDescription, hResult);

        }

        MappingFreeServices(prgServices);

    }



    return 0;

}



HRESULT CallMappingRecognizeText(PMAPPING_SERVICE_INFO pService)

{

    MAPPING_PROPERTY_BAG bag;

    HRESULT hResult;



    ZeroMemory(&bag, sizeof (MAPPING_PROPERTY_BAG));

    bag.Size = sizeof (MAPPING_PROPERTY_BAG);



    // MappingRecognizeText's dwIndex parameter specifies the first

    // index inside the text from where the recognition should start.

    // We pass USER_TEXT_SKIP, thus skipping the "Skip " part

    // of the input string.

    // Calling without MAPPING_OPTIONS:

    hResult = MappingRecognizeText(pService, USER_TEXT, wcslen(USER_TEXT), USER_TEXT_SKIP, NULL, &bag);

    if (SUCCEEDED(hResult))

    {

        printf("Results from service: %ws\n", pService->pszDescription);

        PrintAllResults(&bag);

        hResult = MappingFreePropertyBag(&bag);

    }

    return hResult;

}



void PrintAllResults(PMAPPING_PROPERTY_BAG pBag)

{

    DWORD dwRangeIndex;

    DWORD dwDataIndex;

    WCHAR c;



    for (dwRangeIndex = 0; dwRangeIndex < pBag->dwRangesCount; ++dwRangeIndex)

    {

        if (dwRangeIndex > 0)

        {

            printf("   ----\n");

        }

        printf("Range from %u to %u\n",

            (unsigned)pBag->prgResultRanges[dwRangeIndex].dwStartIndex,

            (unsigned)pBag->prgResultRanges[dwRangeIndex].dwEndIndex);

        printf("Data size in WCHARs: %u\n",

            (unsigned)pBag->prgResultRanges[dwRangeIndex].dwDataSize / 2);

        printf("\"%ws\"\n", (WCHAR *)pBag->prgResultRanges[dwRangeIndex].pData);

    }

}

Top of page

 

MappingRecognizeText – Microsoft Cyrillic to Latin Transliteration

This sample illustrates the usage of MappingRecognizeText on the Microsoft Cyrillic to Latin Transliteration service and prints the result returned from it. Please note the two different ways to enumerate that service – by GUID and by Category and Input Script. The output format of all available Transliteration services is the same. It is a single MAPPING_DATA_RANGE with its pData pointing to an array of Unicode characters representing the original text transliterated into the target script by applying only the rules of the specific transliteration service. This service doesn’t null-terminate its output if the input didn’t contain the null-terminating character.

#include <windows.h>

#include <stdio.h>

#include <elscore.h>

#include <elssrvc.h>



#define USER_TEXT (L"Skip The russian word for 'yes' is transliterated to Latin as '\x0434\x0430'.")

#define USER_TEXT_SKIP (5)



int __cdecl main();

HRESULT CallMappingRecognizeText(PMAPPING_SERVICE_INFO pService);

void PrintAllResults(PMAPPING_PROPERTY_BAG pBag);



int __cdecl main()

{

    MAPPING_ENUM_OPTIONS    EnumOptions;

    PMAPPING_SERVICE_INFO   prgServices;

    DWORD                   dwServicesCount;

    HRESULT                 hResult;



    // 1. Enumerate by GUID:

    prgServices = NULL;

    dwServicesCount = 0;

    ZeroMemory(&EnumOptions, sizeof (MAPPING_ENUM_OPTIONS));

    EnumOptions.Size = sizeof (MAPPING_ENUM_OPTIONS);

    // Use the Cyrl->Latn Transliteration GUID to enumerate only this service:

    EnumOptions.pGuid = (GUID *)&ELS_GUID_TRANSLITERATION_CYRILLIC_TO_LATIN;

    hResult = MappingGetServices(&EnumOptions, &prgServices, &dwServicesCount);



    if (SUCCEEDED(hResult))

    {

        hResult = CallMappingRecognizeText(&prgServices[0]);

        if (SUCCEEDED(hResult))

        {

            printf("Calling the service %ws has succeeded!\n",

                prgServices[0].pszDescription);

        }

        else

        {

            printf("Calling the service %ws has failed, failure = 0x%x!\n",

                prgServices[0].pszDescription, hResult);

        }

        MappingFreeServices(prgServices);

    }



    printf("--\n");



    // 2. Enumerate by input script and category:

    prgServices = NULL;

    dwServicesCount = 0;

    ZeroMemory(&EnumOptions, sizeof (MAPPING_ENUM_OPTIONS));

    EnumOptions.Size = sizeof (MAPPING_ENUM_OPTIONS);

    EnumOptions.pszCategory = L"Transliteration";

    EnumOptions.pszInputScript = L"Cyrl";

    hResult = MappingGetServices(&EnumOptions, &prgServices, &dwServicesCount);



    if (SUCCEEDED(hResult))

    {

        hResult = CallMappingRecognizeText(&prgServices[0]);

        if (SUCCEEDED(hResult))

        {

            printf("Calling the service %ws has succeeded!\n",

                prgServices[0].pszDescription);

        }

        else

        {

            printf("Calling the service %ws has failed, failure = 0x%x!\n",

                prgServices[0].pszDescription, hResult);

        }

        MappingFreeServices(prgServices);

    }



    return 0;

}



HRESULT CallMappingRecognizeText(PMAPPING_SERVICE_INFO pService)

{

    MAPPING_PROPERTY_BAG bag;

    HRESULT hResult;



    ZeroMemory(&bag, sizeof (MAPPING_PROPERTY_BAG));

    bag.Size = sizeof (MAPPING_PROPERTY_BAG);



    // MappingRecognizeText's dwIndex parameter specifies the first

    // index inside the text from where the recognition should start.

    // We pass USER_TEXT_SKIP, thus skipping the "Skip " part

    // of the input string.

    // Calling without MAPPING_OPTIONS:

    // We want the result to be null-terminated for display.

    // That's why we will also pass the input null terminator:

    hResult = MappingRecognizeText(pService, USER_TEXT, wcslen(USER_TEXT) + 1, USER_TEXT_SKIP, NULL, &bag);

    if (SUCCEEDED(hResult))

    {

        printf("Results from service: %ws\n", pService->pszDescription);

        PrintAllResults(&bag);

        hResult = MappingFreePropertyBag(&bag);

    }

    return hResult;

}



void PrintAllResults(PMAPPING_PROPERTY_BAG pBag)

{

    printf("\"%ws\"\n", (WCHAR *)pBag->prgResultRanges[0].pData);

}

Top of page

 

MappingRecognizeText – Calling all available services

This sample illustrates the usage of MappingRecognizeText on the all available services and prints the results returned from each of them. All results are printed and this is a good illustration of how each of the services works. By looking at the output of this program, it’s easy to find out what’s going on under the hood of the ELS services. This sample also shows that almost all of the code used to call any of the ELS services is the same.

#include <windows.h>

#include <stdio.h>

#include <elscore.h>



#define USER_TEXT ( \

    L"Skip This is a simple sentence. " \

    L"\x0422\x0445\x0438\x0441 \x0438\x0441 \x0415\x043d\x0433\x043b\x0438\x0441\x0445.")

#define USER_TEXT_SKIP (5)



int __cdecl main();

HRESULT CallMappingRecognizeText(PMAPPING_SERVICE_INFO pService);

void PrintAllResults(PMAPPING_PROPERTY_BAG pBag);



int __cdecl main()

{

    PMAPPING_SERVICE_INFO   prgServices = NULL;

    DWORD                   dwServicesCount = 0;

    HRESULT                 hResult;



    DWORD i;



    // Get all installed ELS services:

    hResult = MappingGetServices(NULL, &prgServices, &dwServicesCount);



    if (SUCCEEDED(hResult))

    {

        for (i = 0; i < dwServicesCount; ++i)

        {

            // Do something with each service:

            // ... prgServices[i] ...

            if (i > 0)

            {

                printf("--\n");

            }

            hResult = CallMappingRecognizeText(&prgServices[i]);

            if (SUCCEEDED(hResult))

            {

                printf("Calling the service %ws has succeeded!\n",

                    prgServices[i].pszDescription);

            }

            else

            {

                printf("Calling the service %ws has failed, failure = 0x%x!\n",

                    prgServices[i].pszDescription, hResult);

            }

        }

        MappingFreeServices(prgServices);

    }



    return 0;

}



HRESULT CallMappingRecognizeText(PMAPPING_SERVICE_INFO pService)

{

    MAPPING_PROPERTY_BAG bag;

    HRESULT hResult;



    ZeroMemory(&bag, sizeof (MAPPING_PROPERTY_BAG));

    bag.Size = sizeof (MAPPING_PROPERTY_BAG);



    // MappingRecognizeText's dwIndex parameter specifies the first

    // index inside the text from where the recognition should start.

    // We pass USER_TEXT_SKIP, thus skipping the "Skip " part

    // of the input string.

    // Calling without MAPPING_OPTIONS:

    hResult = MappingRecognizeText(pService, USER_TEXT, wcslen(USER_TEXT), USER_TEXT_SKIP, NULL, &bag);

    if (SUCCEEDED(hResult))

    {

        printf("Results from service: %ws\n", pService->pszDescription);

        PrintAllResults(&bag);

        hResult = MappingFreePropertyBag(&bag);

    }

    return hResult;

}



void PrintAllResults(PMAPPING_PROPERTY_BAG pBag)

{

    DWORD dwRangeIndex;

    DWORD dwDataIndex;

    WCHAR c;



    for (dwRangeIndex = 0; dwRangeIndex < pBag->dwRangesCount; ++dwRangeIndex)

    {

        if (dwRangeIndex > 0)

        {

            printf("   ----\n");

        }

        printf("Range from %u to %u\n",

            (unsigned)pBag->prgResultRanges[dwRangeIndex].dwStartIndex,

            (unsigned)pBag->prgResultRanges[dwRangeIndex].dwEndIndex);

        // Currently, we can treat all results as arrays of unicode WCHAR

        // characters, but there can be services in the future

        // that use different formatting, i.e. XML, HTML, etc.

        printf("Data size in WCHARs: %u\n",

            (unsigned)pBag->prgResultRanges[dwRangeIndex].dwDataSize / 2);

        printf("\"");

        for (dwDataIndex = 0; dwDataIndex < pBag->prgResultRanges[dwRangeIndex].dwDataSize / 2; ++dwDataIndex)

        {

            c = ((WCHAR *)pBag->prgResultRanges[dwRangeIndex].pData)[dwDataIndex];

            if (c >= 32 && c < 128 && c != '"') printf("%wc", c);

            else printf("#%x", (unsigned)c);

        }

        printf("\"\n");

    }

}

Top of page

 

MappingRecognizeText – Asynchronous call

This sample shows the usage of asynchronous calling of MappingRecognizeText. When the callback is used, the user must make sure that the property bag, the input text, the options, and the service are all valid until after the callback has finished executing. The user must also make sure that MappingFreePropertyBag is called right after the bag is consumed by the callback. A good idea might be using the callback to free the resources after it has finished processing or copying them.

#include <windows.h>

#include <stdio.h>

#include <elscore.h>

#include <elssrvc.h>



#define USER_TEXT ( \

    L"Skip This is a simple sentence. " \

    L"\x0422\x0445\x0438\x0441 \x0438\x0441 \x0415\x043d\x0433\x043b\x0438\x0441\x0445.")

#define USER_TEXT_SKIP (5)



int __cdecl main();

HRESULT CallMappingRecognizeText(PMAPPING_SERVICE_INFO pService);

void RecognizeCallback(PMAPPING_PROPERTY_BAG pBag, LPVOID data, DWORD dwDataSize, HRESULT Result);



int __cdecl main()

{

    MAPPING_ENUM_OPTIONS    EnumOptions;

    PMAPPING_SERVICE_INFO   prgServices = NULL;

    DWORD                   dwServicesCount = 0;

    HRESULT                 hResult;



    ZeroMemory(&EnumOptions, sizeof (MAPPING_ENUM_OPTIONS));

    EnumOptions.Size = sizeof (MAPPING_ENUM_OPTIONS);

    // Using the Language Auto-Detection GUID to enumerate LAD only:

    EnumOptions.pGuid = (GUID *)&ELS_GUID_LANGUAGE_DETECTION;

    hResult = MappingGetServices(&EnumOptions, &prgServices, &dwServicesCount);



    if (SUCCEEDED(hResult))

    {

        hResult = CallMappingRecognizeText(&prgServices[0]);

        if (SUCCEEDED(hResult))

        {

            printf("Calling the service %ws has succeeded!\n",

                prgServices[0].pszDescription);

        }

        else

        {

            printf("Calling the service %ws has failed, failure = 0x%x!\n",

                prgServices[0].pszDescription, hResult);

        }

        MappingFreeServices(prgServices);

    }



    return 0;

}



HRESULT CallMappingRecognizeText(PMAPPING_SERVICE_INFO pService)

{

    MAPPING_PROPERTY_BAG    bag;

    MAPPING_OPTIONS         Options;

    HRESULT                 hResult;

    HANDLE                  SyncEvent;

    DWORD                   dwWaitResult;



    SyncEvent = CreateEvent(NULL, FALSE, FALSE, NULL);



    if (SyncEvent == NULL)

    {

        hResult = E_FAIL;

    }

    else

    {

        ZeroMemory(&bag, sizeof (MAPPING_PROPERTY_BAG));

        bag.Size = sizeof (MAPPING_PROPERTY_BAG);



        ZeroMemory(&Options, sizeof (MAPPING_OPTIONS));

        Options.Size = sizeof (MAPPING_OPTIONS);

        Options.pfnRecognizeCallback = RecognizeCallback;

        Options.pRecognizeCallerData = &SyncEvent;

        Options.dwRecognizeCallerDataSize = sizeof (HANDLE);



        // MappingRecognizeText's dwIndex parameter specifies the first

        // index inside the text from where the recognition should start.

        // We pass USER_TEXT_SKIP, thus skipping the "Skip " part

        // of the input string.

        hResult = MappingRecognizeText(pService, USER_TEXT, wcslen(USER_TEXT), USER_TEXT_SKIP, &Options, &bag);

        if (SUCCEEDED(hResult))

        {

            // We are using an event to synchronize our waiting for the call to end,

            // because some objects have to be valid till the end of the callback call:

            // - the input text

            // - the property bag

            // - the options

            // - the service

            dwWaitResult = WaitForSingleObject(SyncEvent, INFINITE);

            if (dwWaitResult != WAIT_OBJECT_0)

            {

                hResult = E_FAIL;

            }

        }

        CloseHandle(SyncEvent);

    }

    return hResult;

}



void RecognizeCallback(PMAPPING_PROPERTY_BAG pBag, LPVOID data, DWORD dwDataSize, HRESULT Result)

{

    HANDLE SyncEvent;

    WCHAR * p;



    UNREFERENCED_PARAMETER(dwDataSize);



    if (SUCCEEDED(Result))

    {

        for (p = (WCHAR *)pBag->prgResultRanges[0].pData; *p; p += wcslen(p) + 1)

        {

            printf("%ws\n", p);

        }

        MappingFreePropertyBag(pBag);

    }



    SyncEvent = *((HANDLE *)data);

    SetEvent(SyncEvent);

}

Top of page

 

ELS in Windows 7

The features described in this document are all available in Windows 7. ELS APIs in Windows 7 focus on script detection and language detection, which you can use to help shape a user-appropriate experience for customers from a variety of linguistic backgrounds. Transliteration functionality facilitates inter-linguistic communication for several key markets where you’ve told us that you want to ship your applications. We hope that you give our APIs a try and let us know what you think, because these services are just the beginning for ELS.

Top of page