ActiveX Controls: Localizing an ActiveX Control

OverviewHow Do IFAQSample

This article discusses procedures for localizing ActiveX control interfaces.

If you want to adapt an ActiveX control to an international market, you may want to localize the control. Windows supports several languages in addition to the default English, including German, French, and Swedish. This can present problems for the control if its interface is in English only.

In general, ActiveX controls should always base their locale on the ambient LocaleID property. There are three ways to do this:

  • Load resources, always on demand, based on the current value of the ambient LocaleID property. The MFC ActiveX controls sample LOCALIZE, listed under , uses this strategy.

  • Load resources when the first control is instanced, based on the ambient LocaleID property, and use these resources for all other instances. This article demonstrates this strategy.

    Note   This will not work correctly in some cases, if future instances have different locales.

  • Use the OnAmbientChanged notification function to dynamically load the proper resources for the container’s locale.

    Note   This will work for the control, but the run-time DLL will not dynamically update its own resources when the ambient LocaleID property changes. In addition, run-time DLLs for ActiveX controls use the thread locale to determine the locale for its resources.

The rest of this article describes two localizing strategies. The first strategy localizes the control’s programmability interface (names of properties, methods, and events). The second strategy localizes the control’s user interface, using the container’s ambient LocaleID property. For a demonstration of control localization, see the MFC ActiveX controls sample LOCALIZE, listed under .

Localizing the Control’s Programmability Interface

When localizing the control’s programmability interface (the interface used by programmers writing applications that use your control), you must create a modified version of the control .ODL file (a script for building the control type library) for each language you intend to support. This is the only place you need to localize the control property names.

When you develop a localized control, include the locale ID as an attribute at the type library level. For example, if you want to provide a type library with French localized property names, make a copy of your SAMPLE.ODL file, and call it SAMPLEFR.ODL. Add a locale ID attribute to the file (the locale ID for French is 0x040c), similar to the following:

[ uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx), version(1.0), lcid(0x040c) ]
library Sample
{

Change the property names in SAMPLEFR.ODL to their French equivalents, and then use MKTYPLIB.EXE to produce the French type library, SAMPLEFR.TLB.

To create multiple localized type libraries you can add any localized .ODL files to the project and they will be built automatically.

To add an .ODL file to your ActiveX control project

  1. With your control project open, on the Project menu, click Add to Project and then Files.

    The Insert Files Into Project dialog box appears.

  2. If necessary, select the drive and directory to view.

  3. In the Files of Type box, select Interface Definition Files (*.odl,*.idl).

  4. In the file list box, double-click the .ODL file you want to insert into the project.

  5. Close the Insert Files Into Project dialog box when you have added all necessary .ODL files.

Because the files have been added to the project, they will be built when the rest of the project is built. The localized type libraries are located in the current ActiveX control project directory.

Within your code, the internal property names (usually in English) are always used and are never localized. This includes the control dispatch map, the property exchange functions, and your property page data exchange code.

Only one type library (.TLB) file may be bound into the resources of the control implementation (.OCX) file. This is usually the version with the standardized (typically, English) names. To ship a localized version of your control you need to ship the .OCX (which has already been bound to the default .TLB version) and the .TLB for the appropriate locale. This means that only the .OCX is needed for English versions, since the correct .TLB has already been bound to it. For other locales, the localized type library also must be shipped with the .OCX.

To ensure that clients of your control can find the localized type library, register your locale-specific .TLB file(s) under the TypeLib section of the Windows system registry. The third parameter (normally optional) of the function is provided for this purpose. The following example registers a French type library for an ActiveX control:

STDAPI DllRegisterServer(void)
{
    AFX_MANAGE_STATE(_afxModuleAddrThis);

    if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid))
        return ResultFromScode(SELFREG_E_TYPELIB);
    AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid,
        _T("samplefr.tlb"))
    if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE))
        return ResultFromScode(SELFREG_E_CLASS);

    return NOERROR;
}

When your control is registered, the AfxOleRegisterTypeLib function automatically looks for the specified .TLB file in the same directory as the control and registers it in the Windows registration database. If the .TLB file isn’t found, the function has no effect.

Localizing the Control’s User Interface

To localize a control’s user interface, place all of the control’s user-visible resources (such as property pages and error messages) into language-specific resource DLLs. You then can use the container’s ambient LocaleID property to select the appropriate DLL for the user’s locale.

The following code example demonstrates one approach to locate and load the resource DLL for a specific locale. This member function, called GetLocalizedResourceHandle for this example, can be a member function of your ActiveX control class:

HINSTANCE CSampleCtrl::GetLocalizedResourceHandle(LCID lcid)
{
    LPCTSTR lpszResDll;
    HINSTANCE hResHandle = NULL;
    LANGID lang = LANGIDFROMLCID(lcid);
    switch (PRIMARYLANGID(lang))
    {
    case LANG_ENGLISH:
        lpszResDll = “myctlen.dll”;
        break;

    case LANG_FRENCH:
        lpszResDll = “myctlfr.dll”;
        break;

    case LANG_GERMAN:
        lpszResDll = “myctlde.dll”;
        break;

    case 0:
    default:
        lpszResDll = NULL;
    }

    if (lpszResDll != NULL)
        hResHandle = LoadLibrary(lpszResDll);
    #ifndef _WIN32
        if(hResHandle <= HINSTANCE_ERROR)
           hResHandle = NULL;
    #endif

    return hResHandle;
}

Note that the sublanguage ID could be checked in each case of the switch statement, to provide more specialized localization (for example, local dialects of German). For a demonstration of this function, see the GetResourceHandle function in the MFC ActiveX controls sample LOCALIZE, listed under .

When the control first loads itself into a container, it can call to retrieve the locale ID. The control can then pass the returned locale ID value to the GetLocalizedResourceHandle function, which loads the proper resource library. The control should pass the resulting handle, if any, to :

m_hResDll = GetLocalizedResourceHandle( AmbientLocaleID() );
if (m_hResDll != NULL)
    AfxSetResourceHandle(m_hResDll);

Place the code sample above into a member function of the control, such as an override of . In addition, m_hResDLL should be a member variable of the control class.

You can use similar logic for localizing a control’s property page. To localize the property page, add code similar to the following sample to your property page’s implementation file (in an override of ):

LPPROPERTYPAGESITE pSite;
LCID lcid = 0;
if((pSite = GetPageSite()) != NULL)
    pSite->GetLocaleID(&lcid);
HINSTANCE hResource = GetLocalizedResourceHandle(lcid);
HINSTANCE hResourceSave = NULL;

if (hResource != NULL)
{
    hResourceSave = AfxGetResourceHandle();
    AfxSetResourceHandle(hResource);
}

//  Load dialog template and caption string.
COlePropertyPage::OnSetPageSite( );

if (hResource != NULL)
    AfxSetResourceHandle(hResourceSave);