Home | Overview | How Do I | FAQ | Tutorial | Sample
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:
Note This will not work correctly in some cases, if future instances have different locales.
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 CONTROLS.
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
The Insert Files Into Project dialog box appears.
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 AfxOleRegisterTypeLib 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.
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 CONTROLS.
When the control first loads itself into a container, it can call COleControl::AmbientLocaleID 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 AfxSetResourceHandle:
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 COleControl::OnSetClientSite. 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 COlePropertyPage::OnSetPageSite):
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);