windows-talocale-20060829
[openafs.git] / src / WINNT / talocale / tal_main.cpp
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 extern "C" {
11 #include <afs/param.h>
12 #include <afs/stds.h>
13 #include <stdio.h>
14 #include <process.h>
15 }
16
17 #include <WINNT/TaLocale.h>
18
19
20 /*
21  * DEFINITIONS ________________________________________________________________
22  *
23  */
24
25 typedef struct
26    {
27    WORD wPriority;      // unused if MODULE_PRIORITY_REMOVED
28    HINSTANCE hInstance; // module handle
29    } MODULE, *LPMODULE;
30
31 #define cREALLOC_MODULES  4
32
33
34 /*
35  * VARIABLES __________________________________________________________________
36  *
37  */
38
39 static LANGID l_lang = LANG_USER_DEFAULT;
40 static CRITICAL_SECTION l_csModules;
41 static MODULE *l_aModules = NULL;
42 static size_t l_cModules = 0;
43
44
45 /*
46  * PROTOTYPES _________________________________________________________________
47  *
48  */
49
50 BOOL IsValidStringTemplate (LPCSTRINGTEMPLATE pTable);
51
52
53 /*
54  * ROUTINES ___________________________________________________________________
55  *
56  */
57
58 void TaLocale_Initialize (void)
59 {
60     static BOOL fInitialized = FALSE;
61
62     if (!fInitialized) {
63         char mutexName[256];
64         sprintf(mutexName, "TaLocale_Initialize pid=%d", getpid());
65         HANDLE hMutex = CreateMutex(NULL, TRUE, mutexName);
66         if ( GetLastError() == ERROR_ALREADY_EXISTS ) {
67             if ( WaitForSingleObject( hMutex, INFINITE ) != WAIT_OBJECT_0 ) {
68                 return;
69             }
70         }
71
72         if (!fInitialized)
73         {
74             InitCommonControls();
75             InitializeCriticalSection (&l_csModules);
76
77             fInitialized = TRUE;
78             ReleaseMutex(hMutex);
79             CloseHandle(hMutex);
80
81
82             TaLocale_SpecifyModule (GetModuleHandle(NULL));
83
84             LCID lcidUser = GetUserDefaultLCID();
85             TaLocale_SetLanguage (LANGIDFROMLCID (lcidUser));
86
87             LANGID LangOverride;
88             if ((LangOverride = TaLocale_GetLanguageOverride()) != (LANGID)0)
89                 TaLocale_SetLanguage (LangOverride);
90                 }
91     }
92 }
93
94
95 BOOL TaLocaleReallocFunction (LPVOID *ppTarget, size_t cbElement, size_t *pcTarget, size_t cReq, size_t cInc)
96 {
97    LPVOID pNew;
98    size_t cNew;
99
100    if (cReq <= *pcTarget)
101       return TRUE;
102
103    if ((cNew = cInc * ((cReq + cInc-1) / cInc)) <= 0)
104       return FALSE;
105
106    if ((pNew = Allocate (cbElement * cNew)) == NULL)
107       return FALSE;
108    memset (pNew, 0x00, cbElement * cNew);
109
110    if (*pcTarget != 0)
111       {
112       memcpy (pNew, *ppTarget, cbElement * (*pcTarget));
113       Free (*ppTarget);
114       }
115
116    *ppTarget = pNew;
117    *pcTarget = cNew;
118    return TRUE;
119 }
120
121
122 /*
123  *** TaLocale_GuessBestLangID
124  *
125  * This routine picks the most common language/sublanguage pair based on
126  * the current language and sublanguage. These values are based on the
127  * most likely targets for our localization teams--e.g., we usually put
128  * out a Simplified Chinese translation, so if we run under a Singapore
129  * locale it's a good bet that we should load the Simplified Chinese
130  * translation instead of just failing for lack of a Singapore binary.
131  *
132  */
133
134 LANGID TaLocale_GuessBestLangID (LANGID lang)
135 {
136    switch (PRIMARYLANGID(lang))
137       {
138       case LANG_KOREAN:
139          return MAKELANGID(LANG_KOREAN,SUBLANG_KOREAN);
140
141       case LANG_JAPANESE:
142          return MAKELANGID(LANG_JAPANESE,SUBLANG_DEFAULT);
143
144       case LANG_ENGLISH:
145          return MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US);
146
147       case LANG_CHINESE:
148          if (SUBLANGID(lang) != SUBLANG_CHINESE_TRADITIONAL)
149             return MAKELANGID(LANG_CHINESE,SUBLANG_CHINESE_SIMPLIFIED);
150
151       case LANG_GERMAN:
152          return MAKELANGID(LANG_GERMAN,SUBLANG_GERMAN);
153
154       case LANG_SPANISH:
155          return MAKELANGID(LANG_SPANISH,SUBLANG_SPANISH);
156
157       case LANG_PORTUGUESE:
158          return MAKELANGID(LANG_PORTUGUESE,SUBLANG_PORTUGUESE_BRAZILIAN);
159       }
160
161    return lang;
162 }
163
164
165 /*
166  *** TaLocale_SpecifyModule
167  *
168  * Adds the given module handle to TaLocale's known list of possible sources
169  * for localized resource data. By default, the list is initialized with
170  * the current executable's module handle; if resources are to be extracted
171  * from other .DLLs using the functions in the TaLocale library, this routine
172  * should be called first to notify TaLocale about each .DLL.
173  *
174  */
175
176 void TaLocale_SpecifyModule (HINSTANCE hInstance, WORD wSearchPriority)
177 {
178    TaLocale_Initialize();
179    EnterCriticalSection (&l_csModules);
180
181    // First see if the specified module handle already exists; if
182    // so, remove it (its priority may be changing, or we may actually
183    // have been asked to remove it).
184    //
185    size_t iModule;
186    for (iModule = 0; iModule < l_cModules; ++iModule)
187       {
188       if (l_aModules[ iModule ].wPriority == 0)  // end of list?
189          break;
190       if (l_aModules[ iModule ].hInstance == hInstance)  // found module?
191          break;
192       }
193    if (iModule < l_cModules)
194       {
195       for ( ; iModule < l_cModules-1; ++iModule)
196          memcpy (&l_aModules[ iModule ], &l_aModules[ iModule+1 ], sizeof(MODULE));
197       memset (&l_aModules[ iModule ], 0x00, sizeof(MODULE));
198       }
199
200    // Next, if we have been asked to add this module, find a good place for it.
201    // We want to leave {iModule} pointing to the slot where this module
202    // should be inserted--anything currently at or after {iModule} will have
203    // to be moved outward.
204    //
205    if (wSearchPriority != MODULE_PRIORITY_REMOVE)
206       {
207       for (iModule = 0; iModule < l_cModules; ++iModule)
208          {
209          if (l_aModules[ iModule ].wPriority == 0)  // end of list?
210             break;
211          if (l_aModules[ iModule ].wPriority > wSearchPriority)  // good place?
212             break;
213          }
214
215       size_t iTarget;
216       for (iTarget = iModule; iTarget < l_cModules; ++iTarget)
217          {
218          if (l_aModules[ iTarget ].wPriority == 0)  // end of list?
219             break;
220          }
221
222       if (REALLOC (l_aModules, l_cModules, 1+iTarget, cREALLOC_MODULES))
223          {
224          for (size_t iSource = iTarget -1; (LONG)iSource >= (LONG)iModule; --iSource, --iTarget)
225             memcpy (&l_aModules[ iTarget ], &l_aModules[ iSource ], sizeof(MODULE));
226
227          l_aModules[ iModule ].wPriority = wSearchPriority;
228          l_aModules[ iModule ].hInstance = hInstance;
229          }
230       }
231
232    LeaveCriticalSection (&l_csModules);
233 }
234
235
236 /*
237  *** FindAfsCommonPath
238  *
239  * Because our localized files are often placed in a single Common directory
240  * for the AFS product, we need to know how to find that directory. This
241  * routine, and its helper FindAfsCommonPathByComponent(), do that search.
242  *
243  */
244
245 BOOL FindAfsCommonPathByComponent (LPTSTR pszCommonPath, LPTSTR pszComponent)
246 {
247    *pszCommonPath = 0;
248
249    TCHAR szRegPath[ MAX_PATH ];
250    wsprintf (szRegPath, TEXT("Software\\TransarcCorporation\\%s\\CurrentVersion"), pszComponent);
251
252    HKEY hk;
253    if (RegOpenKey (HKEY_LOCAL_MACHINE, szRegPath, &hk) == 0)
254       {
255       DWORD dwType = REG_SZ;
256       DWORD dwSize = MAX_PATH;
257
258       if (RegQueryValueEx (hk, TEXT("PathName"), NULL, &dwType, (PBYTE)pszCommonPath, &dwSize) == 0)
259          {
260          *(LPTSTR)FindBaseFileName (pszCommonPath) = TEXT('\0');
261
262          if (pszCommonPath[0] && (pszCommonPath[ lstrlen(pszCommonPath)-1 ] == TEXT('\\')))
263             pszCommonPath[ lstrlen(pszCommonPath)-1 ] = TEXT('\0');
264
265          if (pszCommonPath[0])
266             lstrcat (pszCommonPath, TEXT("\\Common"));
267          }
268
269       RegCloseKey (hk);
270       }
271
272    return !!*pszCommonPath;
273 }
274
275
276 BOOL FindAfsCommonPath (LPTSTR pszCommonPath)
277 {
278    if (FindAfsCommonPathByComponent (pszCommonPath, TEXT("AFS Client")))
279       return TRUE;
280    if (FindAfsCommonPathByComponent (pszCommonPath, TEXT("AFS Control Center")))
281       return TRUE;
282    if (FindAfsCommonPathByComponent (pszCommonPath, TEXT("AFS Server")))
283       return TRUE;
284    if (FindAfsCommonPathByComponent (pszCommonPath, TEXT("AFS Supplemental Documentation")))
285       return TRUE;
286    return FALSE;
287 }
288
289
290 /*
291  *** TaLocale_LoadCorrespondingModule
292  *
293  * This routine looks for a .DLL named after the specified module, but
294  * with a suffix reflecting the current locale--it loads that library
295  * and calls TaLocale_SpecifyModule for the library.
296  *
297  */
298
299 HINSTANCE TaLocale_LoadCorrespondingModule (HINSTANCE hInstance, WORD wSearchPriority)
300 {
301    // If the caller was sloppy and didn't supply an instance handle,
302    // assume we should find the module corresponding with the current .EXE.
303    //
304    if (hInstance == NULL)
305       hInstance = GetModuleHandle(NULL);
306
307    TCHAR szFilename[ MAX_PATH ] = TEXT("");
308    GetModuleFileName (hInstance, szFilename, MAX_PATH);
309
310    return TaLocale_LoadCorrespondingModuleByName (hInstance, szFilename, wSearchPriority);
311 }
312
313 HINSTANCE TaLocale_LoadCorrespondingModuleByName (HINSTANCE hInstance, LPTSTR pszFilename, WORD wSearchPriority)
314 {
315    HINSTANCE hDLL = NULL;
316
317    TCHAR szFilename[ MAX_PATH ];
318    if (lstrchr (pszFilename, TEXT('\\')) != NULL)
319       lstrcpy (szFilename, pszFilename);
320    else
321       {
322       GetModuleFileName (hInstance, szFilename, MAX_PATH);
323       lstrcpy ((LPTSTR)FindBaseFileName (szFilename), pszFilename);
324       }
325
326
327    // If the caller was sloppy and didn't supply an instance handle,
328    // assume we should find the module corresponding with the current .EXE.
329    //
330    if (hInstance == NULL)
331       hInstance = GetModuleHandle(NULL);
332
333    LPTSTR pchExtension;
334    if ((pchExtension = (LPTSTR)FindExtension (szFilename)) != NULL)
335       {
336
337       // Find the filename associated with the specified module, remove its
338       // extension, and append "_409.dll" (where the 409 is, naturally, the
339       // current LANGID). Then try to load that library.
340       //
341       wsprintf (pchExtension, TEXT("_%lu.dll"), TaLocale_GetLanguage());
342       if ((hDLL = LoadLibrary (szFilename)) == NULL)
343          hDLL = LoadLibrary (FindBaseFileName (szFilename));
344
345       // If we couldn't find the thing under that name, it's possible we
346       // have a .DLL made for the proper language but not for the user's
347       // specific sublanguage (say, a US English .DLL but we're running
348       // in a Canadian English locale). Make an intelligent guess about
349       // what the valid ID would be.
350       //
351       if (hDLL == NULL)
352          {
353          wsprintf (pchExtension, TEXT("_%lu.dll"), TaLocale_GuessBestLangID (TaLocale_GetLanguage()));
354          if ((hDLL = LoadLibrary (szFilename)) == NULL)
355             hDLL = LoadLibrary (FindBaseFileName (szFilename));
356          }
357
358       // If we STILL couldn't find a corresponding resource library,
359       // we'll take anything we can find; this should cover the case
360       // where a Setup program asked the user what language to use,
361       // and just installed that matching library. Look in the
362       // appropriate directory for any .DLL that fits the naming convention.
363       //
364       if (hDLL == NULL)
365          {
366          wsprintf (pchExtension, TEXT("_*.dll"));
367
368          WIN32_FIND_DATA wfd;
369          memset (&wfd, 0x00, sizeof(wfd));
370
371          HANDLE hFind;
372          if ((hFind = FindFirstFile (szFilename, &wfd)) != NULL)
373             {
374             if (wfd.cFileName[0])
375                {
376                wsprintf ((LPTSTR)FindBaseFileName (szFilename), wfd.cFileName);
377                hDLL = LoadLibrary (szFilename);
378                }
379             FindClose (hFind);
380             }
381          }
382
383       // If we EVEN NOW couldn't find a corresponding resource library,
384       // we may have done our wildcard search in the wrong directory.
385       // Try to find the Common subdirectory of our AFS installation,
386       // and look for any matching DLL there.
387       //
388       if (hDLL == NULL)
389          {
390          wsprintf (pchExtension, TEXT("_*.dll"));
391
392          TCHAR szCommonPath[ MAX_PATH ];
393          if (FindAfsCommonPath (szCommonPath))
394             {
395             lstrcat (szCommonPath, TEXT("\\"));
396             lstrcat (szCommonPath, FindBaseFileName (szFilename));
397
398             WIN32_FIND_DATA wfd;
399             memset (&wfd, 0x00, sizeof(wfd));
400
401             HANDLE hFind;
402             if ((hFind = FindFirstFile (szCommonPath, &wfd)) != NULL)
403                {
404                if (wfd.cFileName[0])
405                   {
406                   wsprintf ((LPTSTR)FindBaseFileName (szCommonPath), wfd.cFileName);
407                   hDLL = LoadLibrary (szCommonPath);
408                   }
409                FindClose (hFind);
410                }
411             }
412          }
413
414       // If all else fails, we'll try to find the English library
415       // somewhere on our path.
416       //
417       if (hDLL == NULL)
418          {
419          wsprintf (pchExtension, TEXT("_%lu.dll"), MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US));
420          if ((hDLL = LoadLibrary (szFilename)) == NULL)
421             hDLL = LoadLibrary (FindBaseFileName (szFilename));
422          }
423
424       // If we were successful in loading the resource library, add it
425       // to our chain of modules-to-search
426       //
427       if (hDLL != NULL)
428          {
429          TaLocale_SpecifyModule (hDLL, wSearchPriority);
430          }
431       }
432
433    return hDLL;
434 }
435
436
437 /*
438  *** TaLocale_EnumModule
439  *
440  * Enables enumeration of each of the product modules which will be seached
441  * by TaLocale for resources. Use TaLocale_SpecifyModule to modify this list.
442  * Modules will be returned in priority order, with the highest-priority
443  * modules being searched first.
444  *
445  */
446
447 BOOL TaLocale_EnumModule (size_t iModule, HINSTANCE *phInstance, WORD *pwSearchPriority)
448 {
449    BOOL rc = FALSE;
450    TaLocale_Initialize();
451    EnterCriticalSection (&l_csModules);
452
453    if ( (iModule < l_cModules) && (l_aModules[ iModule ].wPriority != 0) )
454       {
455       rc = TRUE;
456       if (phInstance)
457          *phInstance = l_aModules[ iModule ].hInstance;
458       if (pwSearchPriority)
459          *pwSearchPriority = l_aModules[ iModule ].wPriority;
460       }
461
462    LeaveCriticalSection (&l_csModules);
463    return rc;
464 }
465
466
467 /*
468  *** TaLocale_GetLanguage
469  *** TaLocale_SetLanguage
470  *
471  * Allows specification of a default language for resources extracted by
472  * the functions exported by tal_string.h and tal_dialog.h. When a particular
473  * string or dialog resource is required, the resource which matches this
474  * specified language will be retrieved--if no localized resource is available,
475  * the default resource will instead be used.
476  *
477  */
478
479 LANGID TaLocale_GetLanguage (void)
480 {
481    TaLocale_Initialize();
482    return l_lang;
483 }
484
485
486 void TaLocale_SetLanguage (LANGID lang)
487 {
488    TaLocale_Initialize();
489    l_lang = lang;
490 }
491
492
493 /*
494  *** TaLocale_GetLanguageOverride
495  *** TaLocale_SetLanguageOverride
496  *
497  * Allows specification of a persistent default language for resources
498  * extracted by the functions exported by tal_string.h and tal_dialog.h.
499  * If a language override (which is really just a registry entry) exists,
500  * all TaLocale-based applications will default to that locale when first
501  * run.
502  *
503  */
504
505 static HKEY REGSTR_BASE_OVERRIDE = HKEY_LOCAL_MACHINE;
506 static TCHAR REGSTR_PATH_OVERRIDE[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Nls");
507 static TCHAR REGSTR_VAL_OVERRIDE[] = TEXT("Default Language");
508
509 LANGID TaLocale_GetLanguageOverride (void)
510 {
511    DWORD dwLang = 0;
512
513    HKEY hk;
514    if (RegOpenKey (REGSTR_BASE_OVERRIDE, REGSTR_PATH_OVERRIDE, &hk) == 0)
515       {
516       DWORD dwType = REG_DWORD;
517       DWORD dwSize = sizeof(DWORD);
518       if (RegQueryValueEx (hk, REGSTR_VAL_OVERRIDE, 0, &dwType, (PBYTE)&dwLang, &dwSize) != 0)
519          dwLang = 0;
520       RegCloseKey (hk);
521       }
522
523    return (LANGID)dwLang;
524 }
525
526
527 void TaLocale_SetLanguageOverride (LANGID lang)
528 {
529    HKEY hk;
530    if (RegCreateKey (REGSTR_BASE_OVERRIDE, REGSTR_PATH_OVERRIDE, &hk) == 0)
531       {
532       DWORD dwLang = (DWORD)lang;
533       RegSetValueEx (hk, REGSTR_VAL_OVERRIDE, 0, REG_DWORD, (PBYTE)&dwLang, sizeof(DWORD));
534       RegCloseKey (hk);
535       }
536 }
537
538
539 void TaLocale_RemoveLanguageOverride (void)
540 {
541    HKEY hk;
542    if (RegOpenKey (REGSTR_BASE_OVERRIDE, REGSTR_PATH_OVERRIDE, &hk) == 0)
543       {
544       RegDeleteValue (hk, REGSTR_VAL_OVERRIDE);
545       RegCloseKey (hk);
546       }
547 }
548
549
550 /*
551  *** TaLocale_GetResource
552  *
553  * Returns a pointer an in-memory image of a language-specific resource.
554  * The resource is found by searching all specified module handles
555  * (as determined automatically, or as specified by previous calls to
556  * TaLocale_SpecifyModule()) for an equivalent resource identifier matching
557  * the requested resource type and localized into the requested language.
558  * In the event that a matching localized resource cannot be found, the
559  * search is repeated using LANG_USER_DEFAULT.
560  *
561  * The pointer returned should be treated as read-only, and should not be freed.
562  *
563  */
564
565 LPCVOID TaLocale_GetResourceEx (LPCTSTR pszType, LPCTSTR pszRes, LANGID lang, HINSTANCE *phInstFound, BOOL fSearchDefaultLanguageToo)
566 {
567    PVOID pr = NULL;
568
569    HINSTANCE hInstance;
570    for (size_t iModule = 0; TaLocale_EnumModule (iModule, &hInstance); ++iModule)
571       {
572       HRSRC hr;
573       if ((hr = FindResourceEx (hInstance, pszType, pszRes, lang)) == NULL)
574          {
575          // Our translation teams don't usually change the language
576          // constants within .RC files, so we should look for English
577          // language translations too.
578          //
579          if ((hr = FindResourceEx (hInstance, pszType, pszRes, MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US))) == NULL)
580             {
581             // If we still can't find it, we'll take anything...
582             //
583             if ((hr = FindResource (hInstance, pszType, pszRes)) == NULL)
584                continue;
585             }
586          }
587
588       // Once we get here, we'll only fail for weird out-of-memory problems.
589       //
590       HGLOBAL hg;
591       if ((hg = LoadResource (hInstance, hr)) != NULL)
592          {
593          if ((pr = (PVOID)LockResource (hg)) != NULL)
594             {
595             if (phInstFound)
596                *phInstFound = hInstance;
597             break;
598             }
599          }
600       }
601
602    return pr;
603 }
604
605
606 LPCVOID TaLocale_GetResource (LPCTSTR pszType, LPCTSTR pszRes, LANGID lang, HINSTANCE *phInstFound)
607 {
608    return TaLocale_GetResourceEx (pszType, pszRes, lang, phInstFound, TRUE);
609 }
610
611
612
613 /*
614  *** TaLocale_GetStringResource
615  *** TaLocale_GetDialogResource
616  *
617  * Convenience wrappers around TaLocale_GetResource for obtaining resources
618  * of a particular type by numeric identifier; these routines specify the
619  * default language (from TaLocale_SetLanguage()) when calling
620  * TaLocale_GetResource().
621  *
622  */
623
624 LPCDLGTEMPLATE TaLocale_GetDialogResource (int idd, HINSTANCE *phInstFound)
625 {
626    return (LPCDLGTEMPLATE)TaLocale_GetResource (RT_DIALOG, MAKEINTRESOURCE( idd ), TaLocale_GetLanguage(), phInstFound);
627 }
628
629
630 LPCSTRINGTEMPLATE TaLocale_GetStringResource (int ids, HINSTANCE *phInstFound)
631 {
632     // Strings are organized into heaps of String Tables, each table
633     // holding 16 strings (regardless of their length). The first table's
634     // first string index is for string #1. When searching for a string,
635     // the string's table is the index given to FindResource.
636     //
637     LPCSTRINGTEMPLATE pst = NULL;
638     LANGID lang = TaLocale_GetLanguage();
639
640     int iTable = (ids / 16) + 1;           // 1 = first string table
641     int iIndex = ids - ((iTable-1) * 16);  // 0 = first string in the table
642
643     HINSTANCE hInstance = NULL;
644         size_t iModule = 0;
645     for (; !pst && TaLocale_EnumModule (iModule, &hInstance); ++iModule)
646     {
647         HRSRC hr;
648         if ((hr = FindResourceEx (hInstance, RT_STRING, MAKEINTRESOURCE( iTable ), lang)) == NULL)
649         {
650             // Our translation teams don't usually change the language
651             // constants within .RC files, so we should look for English
652             // language translations too.
653             //
654             if ((hr = FindResourceEx (hInstance, RT_STRING, MAKEINTRESOURCE( iTable ), MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US))) == NULL)
655             {
656                 // If we still can't find it, we'll take anything...
657                 //
658                 if ((hr = FindResource (hInstance, RT_STRING, MAKEINTRESOURCE( iTable ))) == NULL)
659                     continue;
660             }
661         }
662
663         HGLOBAL hg;
664         if ((hg = LoadResource (hInstance, hr)) != NULL)
665         {
666             const WORD *pTable;
667             if     ((pTable = (WORD*)LockResource (hg)) != NULL)
668             {
669                 try {
670                     // Skip words in the string table until we reach the string
671                     // index we're looking for.
672                     //
673                     for (int iIndexWalk = iIndex; iIndexWalk && ((LPCSTRINGTEMPLATE)pTable)->cchString; --iIndexWalk) {
674                         pTable += 1 + ((LPCSTRINGTEMPLATE)pTable)->cchString;
675                     }
676
677                     if (IsValidStringTemplate ((LPCSTRINGTEMPLATE)pTable))
678                     {
679                         pst = (LPCSTRINGTEMPLATE)pTable;
680                         if (phInstFound)
681                             *phInstFound = hInstance;
682                     } else {
683                         UnlockResource(pTable);
684                         FreeResource(hg);
685                     }
686                 }
687                 catch(...)
688                 {
689                     UnlockResource(pTable);
690                     FreeResource(hg);
691                     // If we walked off the end of the table, then the
692                     // string we want just wasn't there.
693                 }
694             }
695         }
696     }
697
698     return pst;
699 }
700
701
702 BOOL IsValidStringTemplate (LPCSTRINGTEMPLATE pTable)
703 {
704     if (!pTable->cchString)
705         return FALSE;
706
707     for (size_t ii = 0; ii < pTable->cchString; ++ii)
708     {
709         if (!pTable->achString[ii])
710             return FALSE;
711     }
712
713     return TRUE;
714 }
715
716
717 /*
718  *** TaLocale_LoadMenu
719  *** TaLocale_LoadImage
720  *
721  * Replacements for Win32 functions. By using these functions instead, the
722  * caller can load the appropriate resources regardless of the module in
723  * which they reside, or the language which is required.
724  *
725  */
726
727 HMENU TaLocale_LoadMenu (int idm)
728 {
729    const MENUTEMPLATE *pTemplate;
730    if ((pTemplate = (const MENUTEMPLATE *)TaLocale_GetResource (RT_MENU, MAKEINTRESOURCE( idm ), TaLocale_GetLanguage())) == NULL)
731       return NULL;
732    return LoadMenuIndirect (pTemplate);
733 }
734
735
736 HANDLE TaLocale_LoadImage (int idi, UINT imageType, int cx, int cy, UINT imageFlags)
737 {
738    HINSTANCE hInstance;
739    for (size_t iModule = 0; TaLocale_EnumModule (iModule, &hInstance); ++iModule)
740       {
741       HANDLE hImage;
742       if (imageType == IMAGE_ICON)
743          hImage = (HANDLE)LoadIcon (hInstance, MAKEINTRESOURCE(idi));
744       else
745          hImage = LoadImage (hInstance, MAKEINTRESOURCE(idi), imageType, cx, cy, imageFlags);
746
747       if (hImage != NULL)
748          return hImage;
749       }
750
751    return NULL;
752 }
753
754
755 HICON TaLocale_LoadIcon (int idi)
756 {
757    return (HICON)TaLocale_LoadImage (idi, IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
758 }
759
760
761 HACCEL TaLocale_LoadAccelerators (int ida)
762 {
763    HINSTANCE hInstance;
764    for (size_t iModule = 0; TaLocale_EnumModule (iModule, &hInstance); ++iModule)
765       {
766       HACCEL hAccel;
767       if ((hAccel = LoadAccelerators (hInstance, MAKEINTRESOURCE(ida))) != NULL)
768          return hAccel;
769       }
770
771    return NULL;
772 }
773
774
775